Fixed merge conflicts with develop

This commit is contained in:
wbinventor@gmail.com 2016-02-03 12:54:23 -05:00
commit b9a81df6bd
25 changed files with 900 additions and 679 deletions

View file

@ -0,0 +1,10 @@
/* override table width restrictions */
.wy-table-responsive table td, .wy-table-responsive table th {
white-space: normal;
}
.wy-table-responsive {
margin-bottom: 24px;
max-width: 100%;
overflow: visible;
}

View file

@ -152,7 +152,10 @@ html_title = "OpenMC Documentation"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['_static']
html_static_path = ['_static']
def setup(app):
app.add_stylesheet('theme_overrides.css')
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.

View file

@ -1475,114 +1475,203 @@ The ``<tally>`` element accepts the following sub-elements:
*Default*: ``tracklength`` but will revert to ``analog`` if necessary.
:scores:
A space-separated list of the desired responses to be accumulated. Accepted
options are "flux", "total", "scatter", "absorption", "fission",
"nu-fission", "delayed-nu-fission", "kappa-fission", "nu-scatter",
"scatter-N", "scatter-PN", "scatter-YN", "nu-scatter-N", "nu-scatter-PN",
"nu-scatter-YN", "flux-YN", "total-YN", "current", "inverse-velocity" and
"events". These correspond to the following physical quantities:
A space-separated list of the desired responses to be accumulated. The accepted
options are listed in the following tables:
:flux:
Total flux in particle-cm per source particle.
.. table:: **Flux scores: units are particle-cm per source particle.**
.. note::
The ``analog`` estimator is actually identical to the ``collision``
estimator for the flux score.
+----------------------+---------------------------------------------------+
|Score | Description |
+======================+===================================================+
|flux |Total flux. |
+----------------------+---------------------------------------------------+
|flux-YN |Spherical harmonic expansion of the direction of |
| |motion :math:`\left(\Omega\right)` of the total |
| |flux. This score will tally all of the harmonic |
| |moments of order 0 to N. N must be between 0 and |
| |10. |
+----------------------+---------------------------------------------------+
:total:
Total reaction rate in reactions per source particle.
.. table:: **Reaction scores: units are reactions per source particle.**
:scatter:
Total scattering rate. Can also be identified with the ``scatter-0``
response type. Units are reactions per source particle.
+----------------------+---------------------------------------------------+
|Score | Description |
+======================+===================================================+
|absorption |Total absorption rate. This accounts for all |
| |reactions which do not produce secondary neutrons. |
+----------------------+---------------------------------------------------+
|elastic |Elastic scattering reaction rate. |
+----------------------+---------------------------------------------------+
|fission |Total fission reaction rate. |
+----------------------+---------------------------------------------------+
|scatter |Total scattering rate. Can also be identified with |
| |the "scatter-0" response type. |
+----------------------+---------------------------------------------------+
|scatter-N |Tally the N\ :sup:`th` \ scattering moment, where N|
| |is the Legendre expansion order of the change in |
| |particle angle :math:`\left(\mu\right)`. N must be |
| |between 0 and 10. As an example, tallying the 2\ |
| |:sup:`nd` \ scattering moment would be specified as|
| |``<scores>scatter-2</scores>``. |
+----------------------+---------------------------------------------------+
|scatter-PN |Tally all of the scattering moments from order 0 to|
| |N, where N is the Legendre expansion order of the |
| |change in particle angle |
| |:math:`\left(\mu\right)`. That is, "scatter-P1" is |
| |equivalent to requesting tallies of "scatter-0" and|
| |"scatter-1". Like for "scatter-N", N must be |
| |between 0 and 10. As an example, tallying up to the|
| |2\ :sup:`nd` \ scattering moment would be specified|
| |as ``<scores> scatter-P2 </scores>``. |
+----------------------+---------------------------------------------------+
|scatter-YN |"scatter-YN" is similar to "scatter-PN" except an |
| |additional expansion is performed for the incoming |
| |particle direction :math:`\left(\Omega\right)` |
| |using the real spherical harmonics. This is useful|
| |for performing angular flux moment weighting of the|
| |scattering moments. Like "scatter-PN", "scatter-YN"|
| |will tally all of the moments from order 0 to N; N |
| |again must be between 0 and 10. |
+----------------------+---------------------------------------------------+
|total |Total reaction rate. |
+----------------------+---------------------------------------------------+
|total-YN |The total reaction rate expanded via spherical |
| |harmonics about the direction of motion of the |
| |neutron, :math:`\Omega`. This score will tally all |
| |of the harmonic moments of order 0 to N. N must be|
| |between 0 and 10. |
+----------------------+---------------------------------------------------+
|(n,2nd) |(n,2nd) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,2n) |(n,2n) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,3n) |(n,3n) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,na) |(n,n\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,n3a) |(n,n3\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,2na) |(n,2n\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,3na) |(n,3n\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,np) |(n,np) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,n2a) |(n,n2\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,2n2a) |(n,2n2\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,nd) |(n,nd) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,nt) |(n,nt) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,nHe-3) |(n,n\ :sup:`3`\ He) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,nd2a) |(n,nd2\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,nt2a) |(n,nt2\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,4n) |(n,4n) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,2np) |(n,2np) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,3np) |(n,3np) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,n2p) |(n,n2p) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,n*X*) |Level inelastic scattering reaction rate. The *X* |
| |indicates what which inelastic level, e.g., (n,n3) |
| |is third-level inelastic scattering. |
+----------------------+---------------------------------------------------+
|(n,nc) |Continuum level inelastic scattering reaction rate.|
+----------------------+---------------------------------------------------+
|(n,gamma) |Radiative capture reaction rate. |
+----------------------+---------------------------------------------------+
|(n,p) |(n,p) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,d) |(n,d) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,t) |(n,t) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,3He) |(n,\ :sup:`3`\ He) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,a) |(n,\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,2a) |(n,2\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,3a) |(n,3\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,2p) |(n,2p) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,pa) |(n,p\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,t2a) |(n,t2\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,d2a) |(n,d2\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,pd) |(n,pd) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,pt) |(n,pt) reaction rate. |
+----------------------+---------------------------------------------------+
|(n,da) |(n,d\ :math:`\alpha`\ ) reaction rate. |
+----------------------+---------------------------------------------------+
|*Arbitrary integer* |An arbitrary integer is interpreted to mean the |
| |reaction rate for a reaction with a given ENDF MT |
| |number. |
+----------------------+---------------------------------------------------+
:absorption:
Total absorption rate. This accounts for all reactions which do not
produce secondary neutrons. Units are reactions per source particle.
.. table:: **Particle production scores: units are particles produced per
source particles.**
:fission:
Total fission rate in reactions per source particle.
+----------------------+---------------------------------------------------+
|Score | Description |
+======================+===================================================+
|delayed-nu-fission |Total production of delayed neutrons due to |
| |fission. |
+----------------------+---------------------------------------------------+
|nu-fission |Total production of neutrons due to fission. |
+----------------------+---------------------------------------------------+
|nu-scatter, |These scores are similar in functionality to their |
|nu-scatter-N, |``scatter*`` equivalents except the total |
|nu-scatter-PN, |production of neutrons due to scattering is scored |
|nu-scatter-YN |vice simply the scattering rate. This accounts for |
| |multiplicity from (n,2n), (n,3n), and (n,4n) |
| |reactions. |
+----------------------+---------------------------------------------------+
:nu-fission:
Total production of neutrons due to fission. Units are neutrons produced
per source neutron.
.. table:: **Miscellaneous scores: units are indicated for each.**
:delayed-nu-fission:
Total production of delayed neutrons due to fission. Units are neutrons produced
per source neutron.
+----------------------+---------------------------------------------------+
|Score | Description |
+======================+===================================================+
|current |Partial currents on the boundaries of each cell in |
| |a mesh. Units are particles per source |
| |particle. Note that this score can only be used if |
| |a mesh filter has been specified. Furthermore, it |
| |may not be used in conjunction with any other |
| |score. |
+----------------------+---------------------------------------------------+
|events |Number of scoring events. Units are events per |
| |source particle. |
+----------------------+---------------------------------------------------+
|inverse-velocity |The flux-weighted inverse velocity where the |
| |velocity is in units of centimeters per second. |
+----------------------+---------------------------------------------------+
|kappa-fission |The recoverable energy production rate due to |
| |fission. The recoverable energy is defined as the |
| |fission product kinetic energy, prompt and delayed |
| |neutron kinetic energies, prompt and delayed |
| |:math:`\gamma`-ray total energies, and the total |
| |energy released by the delayed :math:`\beta` |
| |particles. The neutrino energy does not contribute |
| |to this response. The prompt and delayed |
| |:math:`\gamma`-rays are assumed to deposit their |
| |energy locally. Units are MeV per source particle. |
+----------------------+---------------------------------------------------+
:kappa-fission:
The recoverable energy production rate due to fission. The recoverable
energy is defined as the fission product kinetic energy, prompt and
delayed neutron kinetic energies, prompt and delayed :math:`\gamma`-ray
total energies, and the total energy released by the delayed :math:`\beta`
particles. The neutrino energy does not contribute to this response. The
prompt and delayed :math:`\gamma`-rays are assumed to deposit their energy
locally. Units are MeV per source particle.
:scatter-N:
Tally the N\ :sup:`th` \ scattering moment, where N is the Legendre
expansion order of the change in particle angle :math:`\left(\mu\right)`.
N must be between 0 and 10. As an example, tallying the 2\ :sup:`nd` \
scattering moment would be specified as ``<scores> scatter-2
</scores>``. Units are reactions per source particle.
:scatter-PN:
Tally all of the scattering moments from order 0 to N, where N is the
Legendre expansion order of the change in particle angle
:math:`\left(\mu\right)`. That is, ``scatter-P1`` is equivalent to
requesting tallies of ``scatter-0`` and ``scatter-1``. Like for
``scatter-N``, N must be between 0 and 10. As an example, tallying up to
the 2\ :sup:`nd` \ scattering moment would be specified as ``<scores>
scatter-P2 </scores>``. Units are reactions per source particle.
:scatter-YN:
``scatter-YN`` is similar to ``scatter-PN`` except an additional expansion
is performed for the incoming particle direction
:math:`\left(\Omega\right)` using the real spherical harmonics. This is
useful for performing angular flux moment weighting of the scattering
moments. Like ``scatter-PN``, ``scatter-YN`` will tally all of the moments
from order 0 to N; N again must be between 0 and 10. Units are reactions
per source particle.
:nu-scatter, nu-scatter-N, nu-scatter-PN, nu-scatter-YN:
These scores are similar in functionality to their ``scatter*``
equivalents except the total production of neutrons due to scattering is
scored vice simply the scattering rate. This accounts for multiplicity
from (n,2n), (n,3n), and (n,4n) reactions. Units are neutrons produced per
source particle.
:flux-YN:
Spherical harmonic expansion of the direction of motion
:math:`\left(\Omega\right)` of the total flux. This score will tally all
of the harmonic moments of order 0 to N. N must be between 0
and 10. Units are particle-cm per source particle.
:total-YN:
The total reaction rate expanded via spherical harmonics about the
direction of motion of the neutron, :math:`\Omega`.
This score will tally all of the harmonic moments of order 0 to N. N must
be between 0 and 10. Units are reactions per source particle.
:current:
Partial currents on the boundaries of each cell in a mesh. Units are
particles per source particle.
.. note::
This score can only be used if a mesh filter has been
specified. Furthermore, it may not be used in conjunction with any
other score.
:inverse-velocity:
The flux-weighted inverse velocity where the velocity is in units of
centimeters per second.
.. note::
The ``analog`` estimator is actually identical to the ``collision``
estimator for the inverse-velocity score.
:events:
Number of scoring events. Units are events per source particle.
.. note::
The ``analog`` estimator is actually identical to the ``collision``
estimator for the flux and inverse-velocity scores.
:trigger:
Precision trigger applied to all filter bins and nuclides for this tally.

View file

@ -1,409 +0,0 @@
import sys
from numbers import Integral
import numpy as np
from openmc import Filter, Nuclide
from openmc.cross import CrossScore, CrossNuclide, CrossFilter
from openmc.filter import _FILTER_TYPES
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
# Acceptable tally aggregation operations
_TALLY_AGGREGATE_OPS = ['sum', 'avg']
class AggregateScore(object):
"""A special-purpose tally score used to encapsulate an aggregate of a
subset or all of tally's scores for tally aggregation.
Parameters
----------
scores : Iterable of str or CrossScore
The scores included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's scores with this AggregateScore
Attributes
----------
scores : Iterable of str or CrossScore
The scores included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's scores with this AggregateScore
"""
def __init__(self, scores=None, aggregate_op=None):
self._scores = None
self._aggregate_op = None
if scores is not None:
self.scores = scores
if aggregate_op is not None:
self.aggregate_op = aggregate_op
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._scores = self.scores
clone._aggregate_op = self.aggregate_op
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
def __repr__(self):
string = ', '.join(map(str, self.scores))
string = '{0}({1})'.format(self.aggregate_op, string)
return string
@property
def scores(self):
return self._scores
@property
def aggregate_op(self):
return self._aggregate_op
@scores.setter
def scores(self, scores):
cv.check_iterable_type('scores', scores, basestring)
self._scores = scores
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, (basestring, CrossScore))
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
class AggregateNuclide(object):
"""A special-purpose tally nuclide used to encapsulate an aggregate of a
subset or all of tally's nuclides for tally aggregation.
Parameters
----------
nuclides : Iterable of str or Nuclide or CrossNuclide
The nuclides included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's nuclides with this AggregateNuclide
Attributes
----------
nuclides : Iterable of str or Nuclide or CrossNuclide
The nuclides included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's nuclides with this AggregateNuclide
"""
def __init__(self, nuclides=None, aggregate_op=None):
self._nuclides = None
self._aggregate_op = None
if nuclides is not None:
self.nuclides = nuclides
if aggregate_op is not None:
self.aggregate_op = aggregate_op
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._nuclides = self.nuclides
clone._aggregate_op = self._aggregate_op
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
def __repr__(self):
# Append each nuclide in the aggregate to the string
string = '{0}('.format(self.aggregate_op)
names = [nuclide.name if isinstance(nuclide, Nuclide) else str(nuclide)
for nuclide in self.nuclides]
string += ', '.join(map(str, names)) + ')'
return string
@property
def nuclides(self):
return self._nuclides
@property
def aggregate_op(self):
return self._aggregate_op
@nuclides.setter
def nuclides(self, nuclides):
cv.check_iterable_type('nuclides', nuclides,
(basestring, Nuclide, CrossNuclide))
self._nuclides = nuclides
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, basestring)
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
class AggregateFilter(object):
"""A special-purpose tally filter used to encapsulate an aggregate of a
subset or all of a tally filter's bins for tally aggregation.
Parameters
----------
aggregate_filter : Filter or CrossFilter
The filter included in the aggregation
bins : Iterable of tuple
The filter bins included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally filter's bins with this AggregateFilter
Attributes
----------
type : str
The type of the aggregatefilter (e.g., 'sum(energy)', 'sum(cell)')
aggregate_filter : filter
The filter included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally filter's bins with this AggregateFilter
bins : Iterable of tuple
The filter bins included in the aggregation
num_bins : Integral
The number of filter bins (always 1 if aggregate_filter is defined)
stride : Integral
The number of filter, nuclide and score bins within each of this
aggregatefilter's bins.
"""
def __init__(self, aggregate_filter=None, bins=None, aggregate_op=None):
self._type = '{0}({1})'.format(aggregate_op, aggregate_filter.type)
self._bins = None
self._stride = None
self._aggregate_filter = None
self._aggregate_op = None
if aggregate_filter is not None:
self.aggregate_filter = aggregate_filter
if bins is not None:
self.bins = bins
if aggregate_op is not None:
self.aggregate_op = aggregate_op
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __repr__(self):
string = 'AggregateFilter\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.type)
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', self.bins)
return string
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._type = self.type
clone._aggregate_filter = self.aggregate_filter
clone._aggregate_op = self.aggregate_op
clone._bins = self._bins
clone._stride = self.stride
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
@property
def aggregate_filter(self):
return self._aggregate_filter
@property
def aggregate_op(self):
return self._aggregate_op
@property
def type(self):
return self._type
@property
def bins(self):
return self._bins
@property
def num_bins(self):
return 1 if self.aggregate_filter else 0
@property
def stride(self):
return self._stride
@type.setter
def type(self, filter_type):
if filter_type not in _FILTER_TYPES.values():
msg = 'Unable to set AggregateFilter type to "{0}" since it ' \
'is not one of the supported types'.format(filter_type)
raise ValueError(msg)
self._type = filter_type
@aggregate_filter.setter
def aggregate_filter(self, aggregate_filter):
cv.check_type('aggregate_filter', aggregate_filter, (Filter, CrossFilter))
self._aggregate_filter = aggregate_filter
@bins.setter
def bins(self, bins):
cv.check_iterable_type('bins', bins, (Integral, tuple))
self._bins = bins
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, basestring)
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
@stride.setter
def stride(self, stride):
self._stride = stride
def get_bin_index(self, filter_bin):
"""Returns the index in the AggregateFilter for some bin.
Parameters
----------
filter_bin : Integral or tuple of Real
A tuple of value(s) corresponding to the bin of interest in
the aggregated filter. The bin is the integer ID for 'material',
'surface', 'cell', 'cellborn', and 'universe' Filters. The bin
is the integer cell instance ID for 'distribcell' Filters. The
bin is a 2-tuple of floats for 'energy' and 'energyout' filters
corresponding to the energy boundaries of the bin of interest.
The bin is a (x,y,z) 3-tuple for 'mesh' filters corresponding to
the mesh cell of interest.
Returns
-------
filter_index : Integral
The index in the Tally data array for this filter bin. For an
AggregateTally the filter bin index is always unity.
Raises
------
ValueError
When the filter_bin is not part of the aggregated filter's bins
"""
if filter_bin not in self.bins:
msg = 'Unable to get the bin index for AggregateFilter since ' \
'"{0}" is not one of the bins'.format(filter_bin)
raise ValueError(msg)
else:
return 0
def get_pandas_dataframe(self, datasize, summary=None):
"""Builds a Pandas DataFrame for the AggregateFilter's bins.
This method constructs a Pandas DataFrame object for the AggregateFilter
with columns annotated by filter bin information. This is a helper
method for the Tally.get_pandas_dataframe(...) method.
Parameters
----------
datasize : Integral
The total number of bins in the tally corresponding to this filter
summary : None or Summary
An optional Summary object to be used to construct columns for
distribcell tally filters (default is None). NOTE: This parameter
is not used by the AggregateFilter and simply mirrors the method
signature for the CrossFilter.
Returns
-------
pandas.DataFrame
A Pandas DataFrame with columns of strings that characterize the
aggregatefilter's bins. Each entry in the DataFrame will include
one or more aggregation operations used to construct the
aggregatefilter's bins. The number of rows in the DataFrame is the
same as the total number of bins in the corresponding tally, with
the filter bins appropriately tiled to map to the corresponding
tally bins.
See also
--------
Tally.get_pandas_dataframe(), Filter.get_pandas_dataframe(),
CrossFilter.get_pandas_dataframe()
"""
import pandas as pd
# Construct a sring representing the filter aggregation
aggregate_bin = '{0}('.format(self.aggregate_op)
aggregate_bin += ', '.join(map(str, self.bins)) + ')'
# Construct NumPy array of bin repeated for each element in dataframe
aggregate_bin_array = np.array([aggregate_bin])
aggregate_bin_array = np.repeat(aggregate_bin_array, datasize)
# Construct Pandas DataFrame for the AggregateFilter
df = pd.DataFrame({self.type: aggregate_bin_array})
return df

View file

@ -1,16 +1,21 @@
import sys
from numbers import Integral
import numpy as np
from openmc import Filter, Nuclide
from openmc.filter import _FILTER_TYPES
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
# Acceptable tally arithmetic binary operations
_TALLY_ARITHMETIC_OPS = ['+', '-', '*', '/', '^']
# Acceptable tally aggregation operations
_TALLY_AGGREGATE_OPS = ['sum', 'avg']
class CrossScore(object):
"""A special-purpose tally score used to encapsulate all combinations of two
@ -97,17 +102,19 @@ class CrossScore(object):
@left_score.setter
def left_score(self, left_score):
cv.check_type('left_score', left_score, (basestring, CrossScore))
cv.check_type('left_score', left_score,
(basestring, CrossScore, AggregateScore))
self._left_score = left_score
@right_score.setter
def right_score(self, right_score):
cv.check_type('right_score', right_score, (basestring, CrossScore))
cv.check_type('right_score', right_score,
(basestring, CrossScore, AggregateScore))
self._right_score = right_score
@binary_op.setter
def binary_op(self, binary_op):
cv.check_type('binary_op', binary_op, (basestring, CrossScore))
cv.check_type('binary_op', binary_op, basestring)
cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS)
self._binary_op = binary_op
@ -214,12 +221,14 @@ class CrossNuclide(object):
@left_nuclide.setter
def left_nuclide(self, left_nuclide):
cv.check_type('left_nuclide', left_nuclide, (Nuclide, CrossNuclide))
cv.check_type('left_nuclide', left_nuclide,
(Nuclide, CrossNuclide, AggregateNuclide))
self._left_nuclide = left_nuclide
@right_nuclide.setter
def right_nuclide(self, right_nuclide):
cv.check_type('right_nuclide', right_nuclide, (Nuclide, CrossNuclide))
cv.check_type('right_nuclide', right_nuclide,
(Nuclide, CrossNuclide, AggregateNuclide))
self._right_nuclide = right_nuclide
@binary_op.setter
@ -372,13 +381,15 @@ class CrossFilter(object):
@left_filter.setter
def left_filter(self, left_filter):
cv.check_type('left_filter', left_filter, (Filter, CrossFilter))
cv.check_type('left_filter', left_filter,
(Filter, CrossFilter, AggregateFilter))
self._left_filter = left_filter
self._bins['left'] = left_filter.bins
@right_filter.setter
def right_filter(self, right_filter):
cv.check_type('right_filter', right_filter, (Filter, CrossFilter))
cv.check_type('right_filter', right_filter,
(Filter, CrossFilter, AggregateFilter))
self._right_filter = right_filter
self._bins['right'] = right_filter.bins
@ -472,3 +483,395 @@ class CrossFilter(object):
df = '(' + left_df + ' ' + self.binary_op + ' ' + right_df + ')'
return df
class AggregateScore(object):
"""A special-purpose tally score used to encapsulate an aggregate of a
subset or all of tally's scores for tally aggregation.
Parameters
----------
scores : Iterable of str or CrossScore
The scores included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's scores with this AggregateScore
Attributes
----------
scores : Iterable of str or CrossScore
The scores included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's scores with this AggregateScore
"""
def __init__(self, scores=None, aggregate_op=None):
self._scores = None
self._aggregate_op = None
if scores is not None:
self.scores = scores
if aggregate_op is not None:
self.aggregate_op = aggregate_op
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._scores = self.scores
clone._aggregate_op = self.aggregate_op
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
def __repr__(self):
string = ', '.join(map(str, self.scores))
string = '{0}({1})'.format(self.aggregate_op, string)
return string
@property
def scores(self):
return self._scores
@property
def aggregate_op(self):
return self._aggregate_op
@scores.setter
def scores(self, scores):
cv.check_iterable_type('scores', scores, basestring)
self._scores = scores
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, (basestring, CrossScore))
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
class AggregateNuclide(object):
"""A special-purpose tally nuclide used to encapsulate an aggregate of a
subset or all of tally's nuclides for tally aggregation.
Parameters
----------
nuclides : Iterable of str or Nuclide or CrossNuclide
The nuclides included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's nuclides with this AggregateNuclide
Attributes
----------
nuclides : Iterable of str or Nuclide or CrossNuclide
The nuclides included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's nuclides with this AggregateNuclide
"""
def __init__(self, nuclides=None, aggregate_op=None):
self._nuclides = None
self._aggregate_op = None
if nuclides is not None:
self.nuclides = nuclides
if aggregate_op is not None:
self.aggregate_op = aggregate_op
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._nuclides = self.nuclides
clone._aggregate_op = self._aggregate_op
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
def __repr__(self):
# Append each nuclide in the aggregate to the string
string = '{0}('.format(self.aggregate_op)
names = [nuclide.name if isinstance(nuclide, Nuclide) else str(nuclide)
for nuclide in self.nuclides]
string += ', '.join(map(str, names)) + ')'
return string
@property
def nuclides(self):
return self._nuclides
@property
def aggregate_op(self):
return self._aggregate_op
@nuclides.setter
def nuclides(self, nuclides):
cv.check_iterable_type('nuclides', nuclides,
(basestring, Nuclide, CrossNuclide))
self._nuclides = nuclides
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, basestring)
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
class AggregateFilter(object):
"""A special-purpose tally filter used to encapsulate an aggregate of a
subset or all of a tally filter's bins for tally aggregation.
Parameters
----------
aggregate_filter : Filter or CrossFilter
The filter included in the aggregation
bins : Iterable of tuple
The filter bins included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally filter's bins with this AggregateFilter
Attributes
----------
type : str
The type of the aggregatefilter (e.g., 'sum(energy)', 'sum(cell)')
aggregate_filter : filter
The filter included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally filter's bins with this AggregateFilter
bins : Iterable of tuple
The filter bins included in the aggregation
num_bins : Integral
The number of filter bins (always 1 if aggregate_filter is defined)
stride : Integral
The number of filter, nuclide and score bins within each of this
aggregatefilter's bins.
"""
def __init__(self, aggregate_filter=None, bins=None, aggregate_op=None):
self._type = '{0}({1})'.format(aggregate_op, aggregate_filter.type)
self._bins = None
self._stride = None
self._aggregate_filter = None
self._aggregate_op = None
if aggregate_filter is not None:
self.aggregate_filter = aggregate_filter
if bins is not None:
self.bins = bins
if aggregate_op is not None:
self.aggregate_op = aggregate_op
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __repr__(self):
string = 'AggregateFilter\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.type)
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', self.bins)
return string
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._type = self.type
clone._aggregate_filter = self.aggregate_filter
clone._aggregate_op = self.aggregate_op
clone._bins = self._bins
clone._stride = self.stride
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
@property
def aggregate_filter(self):
return self._aggregate_filter
@property
def aggregate_op(self):
return self._aggregate_op
@property
def type(self):
return self._type
@property
def bins(self):
return self._bins
@property
def num_bins(self):
return 1 if self.aggregate_filter else 0
@property
def stride(self):
return self._stride
@type.setter
def type(self, filter_type):
if filter_type not in _FILTER_TYPES.values():
msg = 'Unable to set AggregateFilter type to "{0}" since it ' \
'is not one of the supported types'.format(filter_type)
raise ValueError(msg)
self._type = filter_type
@aggregate_filter.setter
def aggregate_filter(self, aggregate_filter):
cv.check_type('aggregate_filter', aggregate_filter, (Filter, CrossFilter))
self._aggregate_filter = aggregate_filter
@bins.setter
def bins(self, bins):
cv.check_iterable_type('bins', bins, (Integral, tuple))
self._bins = bins
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, basestring)
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
@stride.setter
def stride(self, stride):
self._stride = stride
def get_bin_index(self, filter_bin):
"""Returns the index in the AggregateFilter for some bin.
Parameters
----------
filter_bin : Integral or tuple of Real
A tuple of value(s) corresponding to the bin of interest in
the aggregated filter. The bin is the integer ID for 'material',
'surface', 'cell', 'cellborn', and 'universe' Filters. The bin
is the integer cell instance ID for 'distribcell' Filters. The
bin is a 2-tuple of floats for 'energy' and 'energyout' filters
corresponding to the energy boundaries of the bin of interest.
The bin is a (x,y,z) 3-tuple for 'mesh' filters corresponding to
the mesh cell of interest.
Returns
-------
filter_index : Integral
The index in the Tally data array for this filter bin. For an
AggregateTally the filter bin index is always unity.
Raises
------
ValueError
When the filter_bin is not part of the aggregated filter's bins
"""
if filter_bin not in self.bins:
msg = 'Unable to get the bin index for AggregateFilter since ' \
'"{0}" is not one of the bins'.format(filter_bin)
raise ValueError(msg)
else:
return 0
def get_pandas_dataframe(self, datasize, summary=None):
"""Builds a Pandas DataFrame for the AggregateFilter's bins.
This method constructs a Pandas DataFrame object for the AggregateFilter
with columns annotated by filter bin information. This is a helper
method for the Tally.get_pandas_dataframe(...) method.
Parameters
----------
datasize : Integral
The total number of bins in the tally corresponding to this filter
summary : None or Summary
An optional Summary object to be used to construct columns for
distribcell tally filters (default is None). NOTE: This parameter
is not used by the AggregateFilter and simply mirrors the method
signature for the CrossFilter.
Returns
-------
pandas.DataFrame
A Pandas DataFrame with columns of strings that characterize the
aggregatefilter's bins. Each entry in the DataFrame will include
one or more aggregation operations used to construct the
aggregatefilter's bins. The number of rows in the DataFrame is the
same as the total number of bins in the corresponding tally, with
the filter bins appropriately tiled to map to the corresponding
tally bins.
See also
--------
Tally.get_pandas_dataframe(), Filter.get_pandas_dataframe(),
CrossFilter.get_pandas_dataframe()
"""
import pandas as pd
# Construct a sring representing the filter aggregation
aggregate_bin = '{0}('.format(self.aggregate_op)
aggregate_bin += ', '.join(map(str, self.bins)) + ')'
# Construct NumPy array of bin repeated for each element in dataframe
aggregate_bin_array = np.array([aggregate_bin])
aggregate_bin_array = np.repeat(aggregate_bin_array, datasize)
# Construct Pandas DataFrame for the AggregateFilter
df = pd.DataFrame({self.type: aggregate_bin_array})
return df

View file

@ -673,9 +673,11 @@ class Filter(object):
# Assign entry to Lattice Multi-index column
else:
# Reverse y index per lattice ordering in OpenCG
level_dict[lat_id_key][offset] = coords._lattice._id
level_dict[lat_x_key][offset] = coords._lat_x
level_dict[lat_y_key][offset] = coords._lat_y
level_dict[lat_y_key][offset] = \
coords._lattice.dimension[1] - coords._lat_y - 1
level_dict[lat_z_key][offset] = coords._lat_z
# Move to next node in LocalCoords linked list

View file

@ -396,7 +396,7 @@ class Library(object):
cv.check_type('statepoint', statepoint, openmc.StatePoint)
if not statepoint.with_summary:
if statepoint.summary is None:
msg = 'Unable to load data from a statepoint which has not been ' \
'linked with a summary file'
raise ValueError(msg)
@ -568,8 +568,9 @@ class Library(object):
for domain in self.domains:
for mgxs_type in self.mgxs_types:
mgxs = subdomain_avg_library.get_mgxs(domain, mgxs_type)
avg_mgxs = mgxs.get_subdomain_avg_xs()
subdomain_avg_library.all_mgxs[domain.id][mgxs_type] = avg_mgxs
if mgxs.domain_type == 'distribcell':
avg_mgxs = mgxs.get_subdomain_avg_xs()
subdomain_avg_library.all_mgxs[domain.id][mgxs_type] = avg_mgxs
return subdomain_avg_library

View file

@ -583,7 +583,7 @@ class MGXS(object):
cv.check_type('statepoint', statepoint, openmc.statepoint.StatePoint)
if not statepoint.with_summary:
if statepoint.summary is None:
msg = 'Unable to load data from a statepoint which has not been ' \
'linked with a summary file'
raise ValueError(msg)
@ -612,6 +612,11 @@ class MGXS(object):
filters = []
filter_bins = []
# Clear any tallies previously loaded from a statepoint
self._tallies = None
self._xs_tally = None
self._rxn_rate_tally = None
# Find, slice and store Tallies from StatePoint
# The tally slicing is needed if tally merging was used
for tally_type, tally in self.tallies.items():
@ -673,14 +678,14 @@ class MGXS(object):
filter_bins = []
# Construct a collection of the domain filter bins
if subdomains != 'all':
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
for subdomain in subdomains:
filters.append(self.domain_type)
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if groups != 'all':
if not isinstance(groups, basestring):
cv.check_iterable_type('groups', groups, Integral)
for group in groups:
filters.append('energy')
@ -838,7 +843,7 @@ class MGXS(object):
"""
# Construct a collection of the subdomain filter bins to average across
if subdomains != 'all':
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains)
@ -881,7 +886,7 @@ class MGXS(object):
"""
# Construct a collection of the subdomains to report
if subdomains != 'all':
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
@ -1010,7 +1015,7 @@ class MGXS(object):
xs_results = h5py.File(filename, 'w')
# Construct a collection of the subdomains to report
if subdomains != 'all':
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
@ -1192,7 +1197,7 @@ class MGXS(object):
"""
if groups != 'all':
if not isinstance(groups, basestring):
cv.check_iterable_type('groups', groups, Integral)
if nuclides != 'all' and nuclides != 'sum':
cv.check_iterable_type('nuclides', nuclides, basestring)
@ -1252,7 +1257,7 @@ class MGXS(object):
columns = ['group in']
# Select out those groups the user requested
if groups != 'all':
if not isinstance(groups, basestring):
if 'group in' in df:
df = df[df['group in'].isin(groups)]
if 'group out' in df:
@ -1789,21 +1794,21 @@ class ScatterMatrixXS(MGXS):
filter_bins = []
# Construct a collection of the domain filter bins
if subdomains != 'all':
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
for subdomain in subdomains:
filters.append(self.domain_type)
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if in_groups != 'all':
if not isinstance(in_groups, basestring):
cv.check_iterable_type('groups', in_groups, Integral)
for group in in_groups:
filters.append('energy')
filter_bins.append((self.energy_groups.get_group_bounds(group),))
# Construct list of energy group bounds tuples for all requested groups
if out_groups != 'all':
if not isinstance(out_groups, basestring):
cv.check_iterable_type('groups', out_groups, Integral)
for group in out_groups:
filters.append('energyout')
@ -1887,7 +1892,7 @@ class ScatterMatrixXS(MGXS):
"""
# Construct a collection of the subdomains to report
if subdomains != 'all':
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
@ -1926,12 +1931,6 @@ class ScatterMatrixXS(MGXS):
bounds = self.energy_groups.get_group_bounds(group)
string += template.format('', group, bounds[0], bounds[1])
if subdomains == 'all':
if self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
else:
subdomains = [self.domain.id]
# Loop over all subdomains
for subdomain in subdomains:
@ -2135,14 +2134,14 @@ class Chi(MGXS):
filter_bins = []
# Construct a collection of the domain filter bins
if subdomains != 'all':
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
for subdomain in subdomains:
filters.append(self.domain_type)
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if groups != 'all':
if not isinstance(groups, basestring):
cv.check_iterable_type('groups', groups, Integral)
for group in groups:
filters.append('energyout')

View file

@ -449,10 +449,6 @@ class StatePoint(object):
def summary(self):
return self._summary
@property
def with_summary(self):
return False if self.summary is None else True
@sparse.setter
def sparse(self, sparse):
"""Convert tally data from NumPy arrays to SciPy list of lists (LIL)

View file

@ -367,9 +367,9 @@ class Summary(object):
# Set the universes for the lattice
lattice.universes = universes
# Set the distribcell offsets for the lattice
if offsets is not None:
offsets = np.swapaxes(offsets, 0, 2)
lattice.offsets = offsets
lattice.offsets = offsets[:, ::-1, :]
# Add the Lattice to the global dictionary of all Lattices
self.lattices[index] = lattice

View file

@ -12,8 +12,7 @@ import sys
import numpy as np
from openmc import Mesh, Filter, Trigger, Nuclide
from openmc.cross import CrossScore, CrossNuclide, CrossFilter
from openmc.aggregate import AggregateScore, AggregateNuclide, AggregateFilter
from openmc.arithmetic import *
from openmc.filter import _FILTER_TYPES
import openmc.checkvalue as cv
from openmc.clean_xml import *
@ -2680,11 +2679,11 @@ class Tally(object):
new_tally = copy.deepcopy(self)
new_tally.sparse = False
if self.sum is not None:
if not self.derived and self.sum is not None:
new_sum = self.get_values(scores, filters, filter_bins,
nuclides, 'sum')
new_tally.sum = new_sum
if self.sum_sq is not None:
if not self.derived and self.sum_sq is not None:
new_sum_sq = self.get_values(scores, filters, filter_bins,
nuclides, 'sum_sq')
new_tally.sum_sq = new_sum_sq
@ -2749,7 +2748,7 @@ class Tally(object):
bin_indices.append(bin_index)
num_bins += 1
find_filter.bins = set(find_filter.bins[bin_indices])
find_filter.bins = np.unique(find_filter.bins[bin_indices])
find_filter.num_bins = num_bins
# Update the new tally's filter strides
@ -3106,10 +3105,10 @@ class Tally(object):
diag_indices[start:end] = indices + (i * new_filter.num_bins**2)
# Inject this Tally's data along the diagonal of the diagonalized Tally
if self.sum is not None:
if not self.derived and self.sum is not None:
new_tally._sum = np.zeros(new_tally.shape, dtype=np.float64)
new_tally._sum[diag_indices, :, :] = self.sum
if self.sum_sq is not None:
if not self.derived and self.sum_sq is not None:
new_tally._sum_sq = np.zeros(new_tally.shape, dtype=np.float64)
new_tally._sum_sq[diag_indices, :, :] = self.sum_sq
if self.mean is not None:

View file

@ -1095,14 +1095,14 @@ class RectLattice(Lattice):
# For 2D Lattices
if len(self._dimension) == 2:
offset = self._offsets[i[1]-1, i[2]-1, 0, distribcell_index-1]
offset = self._offsets[i[3]-1, i[2]-1, i[1]-1, distribcell_index-1]
offset += self._universes[i[1]-1][i[2]-1].get_cell_instance(path,
distribcell_index)
# For 3D Lattices
else:
offset = self._offsets[i[1]-1, i[2]-1, i[3]-1, distribcell_index-1]
offset += self._universes[i[1]-1][i[2]-1][i[3]-1].get_cell_instance(
offset = self._offsets[i[3]-1, i[2]-1, i[1]-1, distribcell_index-1]
offset += self._universes[i[3]-1][i[2]-1][i[1]-1].get_cell_instance(
path, distribcell_index)
return offset

View file

@ -3,7 +3,7 @@ module ace
use ace_header, only: Nuclide, Reaction, SAlphaBeta, XsListing
use constants
use distribution_univariate, only: Uniform, Equiprobable, Tabular
use endf, only: reaction_name, is_fission, is_disappearance
use endf, only: is_fission, is_disappearance
use energy_distribution, only: TabularEquiprobable, LevelInelastic, &
ContinuousTabular, MaxwellEnergy, Evaporation, WattEnergy, NBodyPhaseSpace
use error, only: fatal_error, warning

View file

@ -17,6 +17,53 @@ contains
character(20) :: string
select case (MT)
! Special reactions for tallies
case (SCORE_FLUX)
string = "flux"
case (SCORE_TOTAL)
string = "total"
case (SCORE_SCATTER)
string = "scatter"
case (SCORE_NU_SCATTER)
string = "nu-scatter"
case (SCORE_SCATTER_N)
string = "scatter-n"
case (SCORE_SCATTER_PN)
string = "scatter-pn"
case (SCORE_NU_SCATTER_N)
string = "nu-scatter-n"
case (SCORE_NU_SCATTER_PN)
string = "nu-scatter-pn"
case (SCORE_TRANSPORT)
string = "transport"
case (SCORE_N_1N)
string = "n1n"
case (SCORE_ABSORPTION)
string = "absorption"
case (SCORE_FISSION)
string = "fission"
case (SCORE_NU_FISSION)
string = "nu-fission"
case (SCORE_DELAYED_NU_FISSION)
string = "delayed-nu-fission"
case (SCORE_KAPPA_FISSION)
string = "kappa-fission"
case (SCORE_CURRENT)
string = "current"
case (SCORE_FLUX_YN)
string = "flux-yn"
case (SCORE_TOTAL_YN)
string = "total-yn"
case (SCORE_SCATTER_YN)
string = "scatter-yn"
case (SCORE_NU_SCATTER_YN)
string = "nu-scatter-yn"
case (SCORE_EVENTS)
string = "events"
case (SCORE_INVERSE_VELOCITY)
string = "inverse-velocity"
! Normal ENDF-based reactions
case (TOTAL_XS)
string = '(n,total)'
case (ELASTIC)

View file

@ -5,6 +5,7 @@ module input_xml
use dict_header, only: DictIntInt, ElemKeyValueCI
use distribution_multivariate
use distribution_univariate
use endf, only: reaction_name
use energy_grid, only: grid_method, n_log_bins
use error, only: fatal_error, warning
use geometry_header, only: Cell, Lattice, RectLattice, HexLattice
@ -3246,7 +3247,7 @@ contains
call fatal_error("Cannot tally absorption rate with an outgoing &
&energy filter.")
end if
case ('fission')
case ('fission', '18')
t % score_bins(j) = SCORE_FISSION
if (t % find_filter(FILTER_ENERGYOUT) > 0) then
call fatal_error("Cannot tally fission rate with an outgoing &
@ -3426,6 +3427,32 @@ contains
! Deallocate temporary string array of scores
deallocate(sarray)
! Check that no duplicate scores exist
j = 1
do while (j < n_scores)
! Determine number of bins for scores with expansions
n_order = t % moment_order(j)
select case (t % score_bins(j))
case (SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN)
n_bins = n_order + 1
case (SCORE_FLUX_YN, SCORE_TOTAL_YN, SCORE_SCATTER_YN, &
SCORE_NU_SCATTER_YN)
n_bins = (n_order + 1)**2
case default
n_bins = 1
end select
do k = j + n_bins, n_scores
if (t % score_bins(j) == t % score_bins(k) .and. &
t % moment_order(j) == t % moment_order(k)) then
call fatal_error("Duplicate score of type '" // trim(&
reaction_name(t % score_bins(j))) // "' found in tally " &
// trim(to_str(t % id)))
end if
end do
j = j + n_bins
end do
else
call fatal_error("No <scores> specified on tally " &
&// trim(to_str(t % id)) // ".")

View file

@ -86,7 +86,7 @@ module particle_header
logical :: write_track = .false.
! Secondary particles created
integer :: n_secondary = 0
integer(8) :: n_secondary = 0
type(Bank) :: secondary_bank(MAX_SECONDARY)
contains

View file

@ -88,9 +88,14 @@ contains
! change when sampling fission sites. The following block handles all
! absorption (including fission)
if (nuc % fissionable .and. run_mode == MODE_EIGENVALUE) then
if (nuc % fissionable) then
call sample_fission(i_nuclide, i_reaction)
call create_fission_sites(p, i_nuclide, i_reaction)
if (run_mode == MODE_EIGENVALUE) then
call create_fission_sites(p, i_nuclide, i_reaction, fission_bank, n_bank)
elseif (run_mode == MODE_FIXEDSOURCE) then
call create_fission_sites(p, i_nuclide, i_reaction, &
p % secondary_bank, p % n_secondary)
end if
end if
! If survival biasing is being used, the following subroutine adjusts the
@ -1071,10 +1076,12 @@ contains
! neutrons produced from fission and creates appropriate bank sites.
!===============================================================================
subroutine create_fission_sites(p, i_nuclide, i_reaction)
subroutine create_fission_sites(p, i_nuclide, i_reaction, bank_array, size_bank)
type(Particle), intent(inout) :: p
integer, intent(in) :: i_nuclide
integer, intent(in) :: i_reaction
type(Bank), intent(inout) :: bank_array(:)
integer(8), intent(inout) :: size_bank
integer :: nu_d(MAX_DELAYED_GROUPS) ! number of delayed neutrons born
integer :: i ! loop index
@ -1123,27 +1130,35 @@ contains
nu = int(nu_t) + 1
end if
! Check for fission bank size getting hit
if (n_bank + nu > size(fission_bank)) then
if (master) call warning("Maximum number of sites in fission bank &
&reached. This can result in irreproducible results using different &
&numbers of processes/threads.")
! Check for bank size getting hit. For fixed source calculations, this is a
! fatal error. For eigenvalue calculations, it just means that k-effective
! was too high for a single batch.
if (size_bank + nu > size(bank_array)) then
if (run_mode == MODE_FIXEDSOURCE) then
call fatal_error("Secondary particle bank size limit reached. If you &
&are running a subcritical multiplication problem, k-effective &
&may be too close to one.")
else
if (master) call warning("Maximum number of sites in fission bank &
&reached. This can result in irreproducible results using different &
&numbers of processes/threads.")
end if
end if
! Bank source neutrons
if (nu == 0 .or. n_bank == size(fission_bank)) return
if (nu == 0 .or. size_bank == size(bank_array)) return
! Initialize counter of delayed neutrons encountered for each delayed group
! to zero.
nu_d(:) = 0
p % fission = .true. ! Fission neutrons will be banked
do i = int(n_bank,4) + 1, int(min(n_bank + nu, int(size(fission_bank),8)),4)
do i = int(size_bank,4) + 1, int(min(size_bank + nu, int(size(bank_array),8)),4)
! Bank source neutrons by copying particle data
fission_bank(i) % xyz = p % coord(1) % xyz
bank_array(i) % xyz = p % coord(1) % xyz
! Set weight of fission bank site
fission_bank(i) % wgt = ONE/weight
bank_array(i) % wgt = ONE/weight
! Sample cosine of angle -- fission neutrons are always emitted
! isotropically. Sometimes in ACE data, fission reactions actually have
@ -1153,17 +1168,17 @@ contains
! Sample azimuthal angle uniformly in [0,2*pi)
phi = TWO*PI*prn()
fission_bank(i) % uvw(1) = mu
fission_bank(i) % uvw(2) = sqrt(ONE - mu*mu) * cos(phi)
fission_bank(i) % uvw(3) = sqrt(ONE - mu*mu) * sin(phi)
bank_array(i) % uvw(1) = mu
bank_array(i) % uvw(2) = sqrt(ONE - mu*mu) * cos(phi)
bank_array(i) % uvw(3) = sqrt(ONE - mu*mu) * sin(phi)
! Sample secondary energy distribution for fission reaction and set energy
! in fission bank
fission_bank(i) % E = sample_fission_energy(nuc, nuc%reactions(&
i_reaction), p)
bank_array(i) % E = sample_fission_energy(nuc, &
nuc % reactions(i_reaction), p)
! Set the delayed group of the neutron
fission_bank(i) % delayed_group = p % delayed_group
bank_array(i) % delayed_group = p % delayed_group
! Increment the number of neutrons born delayed
if (p % delayed_group > 0) then
@ -1172,7 +1187,7 @@ contains
end do
! increment number of bank sites
n_bank = min(n_bank + nu, int(size(fission_bank),8))
size_bank = min(size_bank + nu, int(size(bank_array),8))
! Store total and delayed weight banked for analog fission tallies
p % n_bank = nu

View file

@ -310,54 +310,7 @@ contains
call write_dataset(tally_group, "n_score_bins", tally%n_score_bins)
allocate(str_array(size(tally%score_bins)))
do j = 1, size(tally%score_bins)
select case(tally%score_bins(j))
case (SCORE_FLUX)
str_array(j) = "flux"
case (SCORE_TOTAL)
str_array(j) = "total"
case (SCORE_SCATTER)
str_array(j) = "scatter"
case (SCORE_NU_SCATTER)
str_array(j) = "nu-scatter"
case (SCORE_SCATTER_N)
str_array(j) = "scatter-n"
case (SCORE_SCATTER_PN)
str_array(j) = "scatter-pn"
case (SCORE_NU_SCATTER_N)
str_array(j) = "nu-scatter-n"
case (SCORE_NU_SCATTER_PN)
str_array(j) = "nu-scatter-pn"
case (SCORE_TRANSPORT)
str_array(j) = "transport"
case (SCORE_N_1N)
str_array(j) = "n1n"
case (SCORE_ABSORPTION)
str_array(j) = "absorption"
case (SCORE_FISSION)
str_array(j) = "fission"
case (SCORE_NU_FISSION)
str_array(j) = "nu-fission"
case (SCORE_DELAYED_NU_FISSION)
str_array(j) = "delayed-nu-fission"
case (SCORE_KAPPA_FISSION)
str_array(j) = "kappa-fission"
case (SCORE_CURRENT)
str_array(j) = "current"
case (SCORE_FLUX_YN)
str_array(j) = "flux-yn"
case (SCORE_TOTAL_YN)
str_array(j) = "total-yn"
case (SCORE_SCATTER_YN)
str_array(j) = "scatter-yn"
case (SCORE_NU_SCATTER_YN)
str_array(j) = "nu-scatter-yn"
case (SCORE_EVENTS)
str_array(j) = "events"
case (SCORE_INVERSE_VELOCITY)
str_array(j) = "inverse-velocity"
case default
str_array(j) = reaction_name(tally%score_bins(j))
end select
str_array(j) = reaction_name(tally%score_bins(j))
end do
call write_dataset(tally_group, "score_bins", str_array)
call write_dataset(tally_group, "n_user_score_bins", tally%n_user_score_bins)

View file

@ -639,54 +639,7 @@ contains
call write_dataset(tally_group, "n_score_bins", t%n_score_bins)
allocate(str_array(size(t%score_bins)))
do j = 1, size(t%score_bins)
select case(t%score_bins(j))
case (SCORE_FLUX)
str_array(j) = "flux"
case (SCORE_TOTAL)
str_array(j) = "total"
case (SCORE_SCATTER)
str_array(j) = "scatter"
case (SCORE_NU_SCATTER)
str_array(j) = "nu-scatter"
case (SCORE_SCATTER_N)
str_array(j) = "scatter-n"
case (SCORE_SCATTER_PN)
str_array(j) = "scatter-pn"
case (SCORE_NU_SCATTER_N)
str_array(j) = "nu-scatter-n"
case (SCORE_NU_SCATTER_PN)
str_array(j) = "nu-scatter-pn"
case (SCORE_TRANSPORT)
str_array(j) = "transport"
case (SCORE_N_1N)
str_array(j) = "n1n"
case (SCORE_ABSORPTION)
str_array(j) = "absorption"
case (SCORE_FISSION)
str_array(j) = "fission"
case (SCORE_NU_FISSION)
str_array(j) = "nu-fission"
case (SCORE_DELAYED_NU_FISSION)
str_array(j) = "delayed-nu-fission"
case (SCORE_KAPPA_FISSION)
str_array(j) = "kappa-fission"
case (SCORE_CURRENT)
str_array(j) = "current"
case (SCORE_FLUX_YN)
str_array(j) = "flux-yn"
case (SCORE_TOTAL_YN)
str_array(j) = "total-yn"
case (SCORE_SCATTER_YN)
str_array(j) = "scatter-yn"
case (SCORE_NU_SCATTER_YN)
str_array(j) = "nu-scatter-yn"
case (SCORE_EVENTS)
str_array(j) = "events"
case (SCORE_INVERSE_VELOCITY)
str_array(j) = "inverse-velocity"
case default
str_array(j) = reaction_name(t%score_bins(j))
end select
str_array(j) = reaction_name(t%score_bins(j))
end do
call write_dataset(tally_group, "score_bins", str_array)

View file

@ -202,6 +202,7 @@ contains
if (p % n_secondary > 0) then
call p % initialize_from_source(p % secondary_bank(p % n_secondary))
p % n_secondary = p % n_secondary - 1
n_event = 0
! Enter new particle in particle track file
if (p % write_track) call add_particle_track()

View file

@ -0,0 +1 @@
fe07eb28fd0dbb56edaecd510f5e8e4db7271e5c9aecf3d880cce92b69872a0aacf825b8e88cd2e9b1ff709f578b269b1835f53cf2561a390062e1e7e03b5276

View file

@ -0,0 +1 @@
cea61172ecad5554ef86f52d6adad6ad5e21931cf3d67feb37b8bf9d75e618786f638685e458051d4a39afe1a924fd651cf6674a88cf1f1842fd69cd851e1f17

View file

@ -0,0 +1,129 @@
#!/usr/bin/env python
import os
import sys
import glob
import hashlib
sys.path.insert(0, os.pardir)
from testing_harness import PyAPITestHarness
import openmc
from openmc.source import Source
from openmc.stats import Box
class AsymmetricLatticeTestHarness(PyAPITestHarness):
def _build_inputs(self):
"""Build an axis-asymmetric lattice of fuel assemblies"""
# Build full core geometry from underlying input set
self._input_set.build_default_materials_and_geometry()
# Extract all universes from the full core geometry
geometry = self._input_set.geometry.geometry
all_univs = geometry.get_all_universes()
# Extract universes encapsulating fuel and water assemblies
water = all_univs[7]
fuel = all_univs[8]
# Construct a 3x3 lattice of fuel assemblies
core_lat = openmc.RectLattice(name='3x3 Core Lattice', lattice_id=202)
core_lat.dimension = (3, 3)
core_lat.lower_left = (-32.13, -32.13)
core_lat.pitch = (21.42, 21.42)
core_lat.universes = [[fuel, water, water],
[fuel, fuel, fuel],
[water, water, water]]
# Create bounding surfaces
min_x = openmc.XPlane(x0=-32.13, boundary_type='reflective')
max_x = openmc.XPlane(x0=+32.13, boundary_type='reflective')
min_y = openmc.YPlane(y0=-32.13, boundary_type='reflective')
max_y = openmc.YPlane(y0=+32.13, boundary_type='reflective')
min_z = openmc.ZPlane(z0=0, boundary_type='reflective')
max_z = openmc.ZPlane(z0=+32.13, boundary_type='reflective')
# Define root universe
root_univ = openmc.Universe(universe_id=0, name='root universe')
root_cell = openmc.Cell(cell_id=1)
root_cell.region = +min_x & -max_x & +min_y & -max_y & +min_z & -max_z
root_cell.fill = core_lat
root_univ.add_cell(root_cell)
# Over-ride geometry in the input set with this 3x3 lattice
self._input_set.geometry.geometry.root_universe = root_univ
# Initialize a "distribcell" filter for the fuel pin cell
distrib_filter = openmc.Filter(type='distribcell', bins=[27])
# Initialize the tallies
tally = openmc.Tally(name='distribcell tally', tally_id=27)
tally.add_filter(distrib_filter)
tally.add_score('nu-fission')
# Initialize the tallies file
tallies_file = openmc.TalliesFile()
tallies_file.add_tally(tally)
# Assign the tallies file to the input set
self._input_set.tallies = tallies_file
self._input_set.build_default_settings()
# Specify summary output and correct source sampling box
source = Source(space=Box([-32, -32, 0], [32, 32, 32]))
source.only_fissionable = True
self._input_set.settings.source = source
self._input_set.settings.output = {'summary': True}
# Write input XML files
self._input_set.export()
def _get_results(self, hash_output=True):
"""Digest info in statepoint and summary and return as a string."""
# Read the statepoint file
statepoint = glob.glob(os.path.join(os.getcwd(), self._sp_name))[0]
sp = openmc.StatePoint(statepoint)
# Read the summary file
summary = glob.glob(os.path.join(os.getcwd(), 'summary.h5'))[0]
su = openmc.Summary(summary)
sp.link_with_summary(su)
# Extract the tally of interest
tally = sp.get_tally(name='distribcell tally')
# Create a string of all mean, std. dev. values for both tallies
outstr = ''
outstr += ', '.join(map(str, tally.mean.flatten())) + '\n'
outstr += ', '.join(map(str, tally.std_dev.flatten())) + '\n'
# Extract fuel assembly lattices from the summary
all_cells = su.openmc_geometry.get_all_cells()
fuel = all_cells[80].fill
core = all_cells[1].fill
# Append a string of lattice distribcell offsets to the string
outstr += ', '.join(map(str, fuel.offsets.flatten())) + '\n'
outstr += ', '.join(map(str, core.offsets.flatten())) + '\n'
# Hash the results if necessary
if hash_output:
sha512 = hashlib.sha512()
sha512.update(outstr.encode('utf-8'))
outstr = sha512.hexdigest()
return outstr
def _cleanup(self):
super(AsymmetricLatticeTestHarness, self)._cleanup()
f = os.path.join(os.getcwd(), 'tallies.xml')
if os.path.exists(f): os.remove(f)
if __name__ == '__main__':
harness = AsymmetricLatticeTestHarness('statepoint.10.h5', True)
harness.main()

View file

@ -4,6 +4,7 @@
<material id="1">
<density value="7.5" units="g/cc" />
<nuclide name="O-16" xs="71c" ao="1.0" />
<nuclide name="U-238" xs="71c" ao="0.0001" />
</material>
</materials>

View file

@ -1,6 +1,6 @@
tally 1:
4.538791E+02
2.073271E+04
4.563929E+02
2.091711E+04
leakage:
9.830000E+00
9.663900E+00
9.780000E+00
9.566400E+00