Merge branch 'multipole' into diff_tally5

This commit is contained in:
Sterling Harper 2016-04-18 15:35:49 -04:00
commit 68e6cf1d67
256 changed files with 36876 additions and 15417 deletions

1
.gitignore vendored
View file

@ -26,6 +26,7 @@ examples/python/**/*.xml
docs/build
docs/source/_images/*.pdf
docs/source/_images/*.aux
docs/source/pythonapi/generated/
# Source build
build

View file

@ -27,7 +27,7 @@ before_install:
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
- conda info -a
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy h5py pandas
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy h5py=2.5 pandas
- source activate test-environment
# Install GCC, MPICH, HDF5, PHDF5
@ -44,10 +44,10 @@ before_script:
- git clone --branch=master git://github.com/bhermanmit/nndc_xs nndc_xs
- cat nndc_xs/nndc.tar.gza* | tar xzvf -
- rm -rf nndc_xs
- export CROSS_SECTIONS=$PWD/nndc/cross_sections.xml
- wget http://web.mit.edu/smharper/Public/multipole_lib.tar.gz
- tar -xzf multipole_lib.tar.gz
- export MULTIPOLE_LIBRARY=$PWD/multipole_lib
- export OPENMC_CROSS_SECTIONS=$PWD/nndc/cross_sections.xml
- git clone --branch=master git://github.com/smharper/windowed_multipole_library.git wmp_lib
- tar xzvf wmp_lib/multipole_lib.tar.gz
- export OPENMC_MULTIPOLE_LIBRARY=$PWD/multipole_lib
- cd ..
script:

View file

@ -23,13 +23,13 @@ except ImportError:
cwd = os.getcwd()
sys.path.insert(0, os.path.join(cwd, '..'))
baseUrl = 'http://web.mit.edu/smharper/Public/'
files = ['multipole_lib.tar.gz']
baseUrl = 'https://github.com/smharper/windowed_multipole_library/blob/master/'
files = ['multipole_lib.tar.gz?raw=true']
checksums = ['9f0307132fe5beca78b8fc7a01fb401c']
block_size = 16384
# ==============================================================================
# DOWNLOAD FILES FROM ATHENA LOCKER
# DOWNLOAD FILES FROM GITHUB REPO
filesComplete = []
for f in files:
@ -44,23 +44,26 @@ for f in files:
file_size = req.length
downloaded = 0
# Remove GitHub junk from the file name.
fname = f[:-9] if f.endswith('?raw=true') else f
# Check if file already downloaded
if os.path.exists(f):
if os.path.getsize(f) == file_size:
print('Skipping ' + f)
filesComplete.append(f)
if os.path.exists(fname):
if os.path.getsize(fname) == file_size:
print('Skipping ' + fname)
filesComplete.append(fname)
continue
else:
if sys.version_info[0] < 3:
overwrite = raw_input('Overwrite {0}? ([y]/n) '.format(f))
overwrite = raw_input('Overwrite {0}? ([y]/n) '.format(fname))
else:
overwrite = input('Overwrite {0}? ([y]/n) '.format(f))
overwrite = input('Overwrite {0}? ([y]/n) '.format(fname))
if overwrite.lower().startswith('n'):
continue
# Copy file to disk
print('Downloading {0}... '.format(f), end='')
with open(f, 'wb') as fh:
with open(fname, 'wb') as fh:
while True:
chunk = req.read(block_size)
if not chunk: break
@ -69,14 +72,15 @@ for f in files:
status = '{0:10} [{1:3.2f}%]'.format(downloaded, downloaded * 100. / file_size)
print(status + chr(8)*len(status), end='')
print('')
filesComplete.append(f)
filesComplete.append(fname)
# ==============================================================================
# VERIFY MD5 CHECKSUMS
print('Verifying MD5 checksums...')
for f, checksum in zip(files, checksums):
downloadsum = hashlib.md5(open(f, 'rb').read()).hexdigest()
fname = f[:-9] if f.endswith('?raw=true') else f
downloadsum = hashlib.md5(open(fname, 'rb').read()).hexdigest()
if downloadsum != checksum:
raise IOError("MD5 checksum for {} does not match. If this is your first "
"time receiving this message, please re-run the script. "
@ -87,12 +91,13 @@ for f, checksum in zip(files, checksums):
# EXTRACT FILES FROM TGZ
for f in files:
if not f in filesComplete:
fname = f[:-9] if f.endswith('?raw=true') else f
if not fname in filesComplete:
continue
# Extract files
with tarfile.open(f, 'r') as tgz:
print('Extracting {0}...'.format(f))
with tarfile.open(fname, 'r') as tgz:
print('Extracting {0}...'.format(fname))
tgz.extractall(path='wmp/')
# Move data files down one level

View file

@ -0,0 +1,7 @@
{{ fullname }}
{{ underline }}
.. currentmodule:: {{ module }}
.. autoclass:: {{ objname }}
:members:

View file

@ -24,13 +24,8 @@ except ImportError:
from mock import Mock as MagicMock
class Mock(MagicMock):
@classmethod
def __getattr__(cls, name):
return Mock()
MOCK_MODULES = ['numpy', 'h5py', 'pandas', 'opencg']
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
sys.modules.update((mod_name, MagicMock()) for mod_name in MOCK_MODULES)
# If extensions (or modules to document with autodoc) are in another directory,
@ -48,6 +43,8 @@ extensions = ['sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.mathjax',
'sphinx.ext.autosummary',
'sphinx.ext.intersphinx',
'sphinx.ext.viewcode',
'sphinx_numfig',
'notebook_sphinxext']
@ -65,7 +62,7 @@ master_doc = 'index'
# General information about the project.
project = u'OpenMC'
copyright = u'2011-2015, Massachusetts Institute of Technology'
copyright = u'2011-2016, Massachusetts Institute of Technology'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -122,20 +119,13 @@ pygments_style = 'tango'
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
if on_rtd:
html_theme = 'default'
html_logo = '_images/openmc200px.png'
else:
html_theme = 'haiku'
html_theme_options = {'full_logo': True,
'linkcolor': '#0c3762',
'visitedlinkcolor': '#0c3762'}
html_logo = '_images/openmc.png'
# The theme to use for HTML and HTML Help pages
if not on_rtd:
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = ["_theme"]
html_logo = '_images/openmc200px.png'
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
@ -248,4 +238,12 @@ latex_elements = {
#Autodocumentation Flags
#autodoc_member_order = "groupwise"
#autoclass_content = "both"
#autosummary_generate = []
autosummary_generate = True
napoleon_use_ivar = True
intersphinx_mapping = {
'python': ('https://docs.python.org/3', None),
'numpy': ('http://docs.scipy.org/doc/numpy/', None),
'pandas': ('http://pandas.pydata.org/pandas-docs/stable/', None)
}

View file

@ -4,9 +4,10 @@ The OpenMC Monte Carlo Code
OpenMC is a Monte Carlo particle transport simulation code focused on neutron
criticality calculations. It is capable of simulating 3D models based on
constructive solid geometry with second-order surfaces. The particle interaction
data is based on ACE format cross sections, also used in the MCNP and Serpent
Monte Carlo codes.
constructive solid geometry with second-order surfaces. OpenMC supports either
continuous-energy or multi-group transport. The continuous-energy
particle interaction data is based on ACE format cross sections, also used
in the MCNP and Serpent Monte Carlo codes.
OpenMC was originally developed by members of the `Computational Reactor Physics
Group`_ at the `Massachusetts Institute of Technology`_ starting

View file

@ -63,6 +63,79 @@ Other Methods
A good survey of other energy grid techniques, including unionized energy grids,
can be found in a paper by Leppanen_.
---------------------------------
Windowed Multipole Representation
---------------------------------
In addition to the usual pointwise representation of cross sections, OpenMC
offers support for an experimental data format called windowed multipole (WMP).
This data format requires less memory than pointwise cross sections, and it
allows on-the-fly Doppler broadening to arbitrary temperature.
The multipole method was introduced by [Hwang]_ and the faster windowed
multipole method by [Josey]_. In the multipole format, cross section resonances
are represented by poles, :math:`p_j`, and residues, :math:`r_j`, in the complex
plane. The 0K cross sections in the resolved resonance region can be computed
by summing up a contribution from each pole:
.. math::
\sigma(E, T=0\text{K}) = \frac{1}{E} \sum_j \text{Re} \left[
\frac{i r_j}{\sqrt{E} - p_j} \right]
Assuming free-gas thermal motion, cross sections in the multipole form can be
analytically Doppler broadened to give the form:
.. math::
\sigma(E, T) = \frac{1}{2 E \sqrt{\xi}} \sum_j \text{Re} \left[i r_j
\sqrt{\pi} W_i(z) - \frac{r_j}{\sqrt{\pi}} C \left(\frac{p_j}{\sqrt{\xi}},
\frac{u}{2 \sqrt{\xi}}\right)\right]
.. math::
W_i(z) = \frac{i}{\pi} \int_{-\infty}^\infty dt \frac{e^{-t^2}}{z - t}
.. math::
C \left(\frac{p_j}{\sqrt{\xi}},\frac{u}{2 \sqrt{\xi}}\right) =
2p_j \int_0^\infty du' \frac{e^{-(u + u')^2/4\xi}}{p_j^2 - u'^2}
.. math::
z = \frac{\sqrt{E} - p_j}{2 \sqrt{\xi}}
.. math::
\xi = \frac{k_B T}{4 A}
.. math::
u = \sqrt{E}
where :math:`T` is the temperature of the resonant scatterer, :math:`k_B` is the
Boltzmann constant, :math:`A` is the mass of the target nucleus. For
:math:`E \gg k_b T/A`, the :math:`C` integral is approximately zero, simplifying
the cross section to:
.. math::
\sigma(E, T) = \frac{1}{2 E \sqrt{\xi}} \sum_j \text{Re} \left[i r_j
\sqrt{\pi} W_i(z)\right]
The :math:`W_i` integral simplifies down to an analytic form. We define the
Faddeeva function, :math:`W` as:
.. math::
W(z) = e^{-z^2} \text{Erfc}(-iz)
Through this, the integral transforms as follows:
.. math::
\text{Im} (z) > 0 : W_i(z) = W(z)
.. math::
\text{Im} (z) < 0 : W_i(z) = -W(z^*)^*
There are freely available algorithms_ to evaluate the Faddeeva function. For
many nuclides, the Faddeeva function needs to be evaluated thousands of times to
calculate a cross section. To mitigate that computational cost, the WMP method
only evaluates poles within a certain energy "window" around the incident
neutron energy and accounts for the effect of resonances outside that window
with a polynomial fit. This polynomial fit is then broadened exactly. This
exact broadening can make up for the removal of the :math:`C` integral, as
typically at low energies, only curve fits are used.
Note that the implementation of WMP in OpenMC currently assumes that inelastic
scattering does not occur in the resolved resonance region. This is usually,
but not always the case. Future library versions may eliminate this issue.
.. only:: html
.. rubric:: References
@ -70,8 +143,17 @@ can be found in a paper by Leppanen_.
.. [Brown] Forrest B. Brown, "New Hash-based Energy Lookup Algorithm for Monte
Carlo codes," LA-UR-14-24530, Los Alamos National Laboratory (2014).
.. [Hwang] R. N. Hwang, "A Rigorous Pole Representation of Multilevel Cross
Sections and Its Practical Application," *Nucl. Sci. Eng.*, **96**,
192-209 (1987).
.. [Josey] Colin Josey, Pablo Ducru, Benoit Forget, and Kord Smith, "Windowed
Multipole for Cross Section Doppler Broadening," *J. Comp. Phys*,
**307**, 715-727 (2016). http://dx.doi.org/10.1016/j.jcp.2015.08.013
.. _MCNP: http://mcnp.lanl.gov
.. _Serpent: http://montecarlo.vtt.fi
.. _NJOY: http://t2.lanl.gov/codes.shtml
.. _ENDF/B data: http://www.nndc.bnl.gov/endf
.. _Leppanen: http://dx.doi.org/10.1016/j.anucene.2009.03.019
.. _algorithms: http://ab-initio.mit.edu/wiki/index.php/Faddeeva_Package

View file

@ -84,10 +84,10 @@ to fully define the surface.
| Plane perpendicular | x-plane | :math:`x - x_0 = 0` | :math:`x_0` |
| to :math:`x`-axis | | | |
+----------------------+------------+------------------------------+-------------------------+
| Plane perpendicular | y-plane | :math:`x - x_0 = 0` | :math:`y_0` |
| Plane perpendicular | y-plane | :math:`y - y_0 = 0` | :math:`y_0` |
| to :math:`y`-axis | | | |
+----------------------+------------+------------------------------+-------------------------+
| Plane perpendicular | z-plane | :math:`x - x_0 = 0` | :math:`z_0` |
| Plane perpendicular | z-plane | :math:`z - z_0 = 0` | :math:`z_0` |
| to :math:`z`-axis | | | |
+----------------------+------------+------------------------------+-------------------------+
| Arbitrary plane | plane | :math:`Ax + By + Cz = D` | :math:`A\;B\;C\;D` |

View file

@ -1,8 +0,0 @@
.. _pythonapi_ace:
==========
ACE Format
==========
.. automodule:: openmc.ace
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_cmfd:
====
CMFD
====
.. automodule:: openmc.cmfd
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_element:
=======
Element
=======
.. automodule:: openmc.element
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_energy_groups:
=============
Energy Groups
=============
.. automodule:: openmc.mgxs.groups
:members:

View file

@ -141,15 +141,12 @@
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"import openmc\n",
"import openmc.mgxs as mgxs\n",
"from openmc.source import Source\n",
"from openmc.stats import Box\n",
"\n",
"%matplotlib inline"
"import openmc.mgxs as mgxs"
]
},
{
@ -341,10 +338,12 @@
"settings_file.batches = batches\n",
"settings_file.inactive = inactive\n",
"settings_file.particles = particles\n",
"settings_file.output = {'tallies': True, 'summary': True}\n",
"settings_file.output = {'tallies': True}\n",
"\n",
"# Create an initial uniform spatial source distribution over fissionable zones\n",
"bounds = [-0.63, -0.63, -0.63, 0.63, 0.63, 0.63]\n",
"settings_file.source = Source(space=Box(\n",
" bounds[:3], bounds[3:], only_fissionable=True))\n",
"uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)\n",
"settings_file.source = openmc.source.Source(space=uniform_dist)\n",
"\n",
"# Export to \"settings.xml\"\n",
"settings_file.export_to_xml()"
@ -423,22 +422,24 @@
"data": {
"text/plain": [
"OrderedDict([('flux', Tally\n",
" \tID =\t10000\n",
" \tName =\t\n",
" \tFilters =\t\n",
" \t\tcell\t[1]\n",
" \t\tenergy\t[ 0.00000000e+00 6.25000000e-07 2.00000000e+01]\n",
" \tNuclides =\ttotal \n",
" \tScores =\t['flux']\n",
" \tEstimator =\ttracklength), ('absorption', Tally\n",
" \tID =\t10001\n",
" \tName =\t\n",
" \tFilters =\t\n",
" \t\tcell\t[1]\n",
" \t\tenergy\t[ 0.00000000e+00 6.25000000e-07 2.00000000e+01]\n",
" \tNuclides =\ttotal \n",
" \tScores =\t['absorption']\n",
" \tEstimator =\ttracklength)])"
"\tID =\t10000\n",
"\tName =\t\n",
"\tFilters =\t\n",
" \t\tcell\t[1]\n",
" \t\tenergy\t[ 0.00000000e+00 6.25000000e-07 2.00000000e+01]\n",
"\tNuclides =\ttotal \n",
"\tScores =\t['flux']\n",
"\tEstimator =\ttracklength\n",
"), ('absorption', Tally\n",
"\tID =\t10001\n",
"\tName =\t\n",
"\tFilters =\t\n",
" \t\tcell\t[1]\n",
" \t\tenergy\t[ 0.00000000e+00 6.25000000e-07 2.00000000e+01]\n",
"\tNuclides =\ttotal \n",
"\tScores =\t['absorption']\n",
"\tEstimator =\ttracklength\n",
")])"
]
},
"execution_count": 13,
@ -518,8 +519,9 @@
" Copyright: 2011-2015 Massachusetts Institute of Technology\n",
" License: http://mit-crpg.github.io/openmc/license.html\n",
" Version: 0.7.1\n",
" Git SHA1: ea9fb637f63f9374c7436456141afa850b84acf9\n",
" Date/Time: 2016-01-14 07:16:05\n",
" Git SHA1: eeb5091ca3a34cc85df73a3318cae2b6c7097413\n",
" Date/Time: 2016-04-13 11:24:09\n",
" MPI Processes: 1\n",
"\n",
" ===========================================================================\n",
" ========================> INITIALIZATION <=========================\n",
@ -545,56 +547,56 @@
"\n",
" Bat./Gen. k Average k \n",
" ========= ======== ==================== \n",
" 1/1 1.19804 \n",
" 2/1 1.12945 \n",
" 3/1 1.15573 \n",
" 4/1 1.13929 \n",
" 5/1 1.16300 \n",
" 6/1 1.22117 \n",
" 7/1 1.19012 \n",
" 8/1 1.11299 \n",
" 9/1 1.16066 \n",
" 10/1 1.12566 \n",
" 11/1 1.20854 \n",
" 12/1 1.14691 1.17773 +/- 0.03082\n",
" 13/1 1.17204 1.17583 +/- 0.01789\n",
" 14/1 1.14148 1.16724 +/- 0.01529\n",
" 15/1 1.17272 1.16834 +/- 0.01189\n",
" 16/1 1.18575 1.17124 +/- 0.01014\n",
" 17/1 1.20498 1.17606 +/- 0.00983\n",
" 18/1 1.14754 1.17249 +/- 0.00923\n",
" 19/1 1.18141 1.17348 +/- 0.00820\n",
" 20/1 1.15074 1.17121 +/- 0.00768\n",
" 21/1 1.15914 1.17011 +/- 0.00703\n",
" 22/1 1.14586 1.16809 +/- 0.00673\n",
" 23/1 1.18999 1.16978 +/- 0.00642\n",
" 24/1 1.15101 1.16844 +/- 0.00609\n",
" 25/1 1.13791 1.16640 +/- 0.00602\n",
" 26/1 1.19791 1.16837 +/- 0.00597\n",
" 27/1 1.19818 1.17012 +/- 0.00587\n",
" 28/1 1.14160 1.16854 +/- 0.00576\n",
" 29/1 1.11487 1.16571 +/- 0.00614\n",
" 30/1 1.17538 1.16620 +/- 0.00584\n",
" 31/1 1.20210 1.16791 +/- 0.00581\n",
" 32/1 1.20078 1.16940 +/- 0.00574\n",
" 33/1 1.14624 1.16839 +/- 0.00558\n",
" 34/1 1.14618 1.16747 +/- 0.00542\n",
" 35/1 1.16866 1.16752 +/- 0.00520\n",
" 36/1 1.18565 1.16821 +/- 0.00504\n",
" 37/1 1.16824 1.16821 +/- 0.00485\n",
" 38/1 1.18299 1.16874 +/- 0.00471\n",
" 39/1 1.21418 1.17031 +/- 0.00480\n",
" 40/1 1.11167 1.16835 +/- 0.00504\n",
" 41/1 1.11545 1.16665 +/- 0.00516\n",
" 42/1 1.11114 1.16491 +/- 0.00529\n",
" 43/1 1.14227 1.16423 +/- 0.00517\n",
" 44/1 1.14104 1.16355 +/- 0.00506\n",
" 45/1 1.16756 1.16366 +/- 0.00492\n",
" 46/1 1.13065 1.16274 +/- 0.00487\n",
" 47/1 1.11251 1.16139 +/- 0.00492\n",
" 48/1 1.14731 1.16101 +/- 0.00481\n",
" 49/1 1.16691 1.16117 +/- 0.00469\n",
" 50/1 1.19679 1.16206 +/- 0.00465\n",
" 1/1 1.11184 \n",
" 2/1 1.15820 \n",
" 3/1 1.18468 \n",
" 4/1 1.17492 \n",
" 5/1 1.19645 \n",
" 6/1 1.18436 \n",
" 7/1 1.14070 \n",
" 8/1 1.15150 \n",
" 9/1 1.19202 \n",
" 10/1 1.17677 \n",
" 11/1 1.20272 \n",
" 12/1 1.21366 1.20819 +/- 0.00547\n",
" 13/1 1.15906 1.19181 +/- 0.01668\n",
" 14/1 1.14687 1.18058 +/- 0.01629\n",
" 15/1 1.14570 1.17360 +/- 0.01442\n",
" 16/1 1.13480 1.16713 +/- 0.01343\n",
" 17/1 1.17680 1.16852 +/- 0.01144\n",
" 18/1 1.16866 1.16853 +/- 0.00990\n",
" 19/1 1.19253 1.17120 +/- 0.00913\n",
" 20/1 1.18124 1.17220 +/- 0.00823\n",
" 21/1 1.19206 1.17401 +/- 0.00766\n",
" 22/1 1.17681 1.17424 +/- 0.00700\n",
" 23/1 1.17634 1.17440 +/- 0.00644\n",
" 24/1 1.13659 1.17170 +/- 0.00654\n",
" 25/1 1.17144 1.17169 +/- 0.00609\n",
" 26/1 1.20649 1.17386 +/- 0.00610\n",
" 27/1 1.11238 1.17024 +/- 0.00678\n",
" 28/1 1.18911 1.17129 +/- 0.00647\n",
" 29/1 1.14681 1.17000 +/- 0.00626\n",
" 30/1 1.12152 1.16758 +/- 0.00641\n",
" 31/1 1.12729 1.16566 +/- 0.00639\n",
" 32/1 1.15399 1.16513 +/- 0.00612\n",
" 33/1 1.13547 1.16384 +/- 0.00599\n",
" 34/1 1.17723 1.16440 +/- 0.00576\n",
" 35/1 1.09296 1.16154 +/- 0.00622\n",
" 36/1 1.19621 1.16287 +/- 0.00612\n",
" 37/1 1.12560 1.16149 +/- 0.00605\n",
" 38/1 1.17872 1.16211 +/- 0.00586\n",
" 39/1 1.17721 1.16263 +/- 0.00568\n",
" 40/1 1.13724 1.16178 +/- 0.00555\n",
" 41/1 1.18526 1.16254 +/- 0.00542\n",
" 42/1 1.13779 1.16177 +/- 0.00531\n",
" 43/1 1.15066 1.16143 +/- 0.00516\n",
" 44/1 1.12174 1.16026 +/- 0.00514\n",
" 45/1 1.17479 1.16068 +/- 0.00501\n",
" 46/1 1.14146 1.16014 +/- 0.00489\n",
" 47/1 1.20464 1.16135 +/- 0.00491\n",
" 48/1 1.15119 1.16108 +/- 0.00479\n",
" 49/1 1.17938 1.16155 +/- 0.00468\n",
" 50/1 1.15798 1.16146 +/- 0.00457\n",
" Creating state point statepoint.50.h5...\n",
"\n",
" ===========================================================================\n",
@ -604,27 +606,27 @@
"\n",
" =======================> TIMING STATISTICS <=======================\n",
"\n",
" Total time for initialization = 1.1720E+00 seconds\n",
" Reading cross sections = 9.0300E-01 seconds\n",
" Total time in simulation = 1.7319E+01 seconds\n",
" Time in transport only = 1.7310E+01 seconds\n",
" Time in inactive batches = 1.9120E+00 seconds\n",
" Time in active batches = 1.5407E+01 seconds\n",
" Time synchronizing fission bank = 2.0000E-03 seconds\n",
" Sampling source sites = 2.0000E-03 seconds\n",
" SEND/RECV source sites = 0.0000E+00 seconds\n",
" Total time for initialization = 4.6300E-01 seconds\n",
" Reading cross sections = 1.2100E-01 seconds\n",
" Total time in simulation = 1.6504E+01 seconds\n",
" Time in transport only = 1.6479E+01 seconds\n",
" Time in inactive batches = 1.9620E+00 seconds\n",
" Time in active batches = 1.4542E+01 seconds\n",
" Time synchronizing fission bank = 1.0000E-02 seconds\n",
" Sampling source sites = 4.0000E-03 seconds\n",
" SEND/RECV source sites = 3.0000E-03 seconds\n",
" Time accumulating tallies = 0.0000E+00 seconds\n",
" Total time for finalization = 1.0000E-03 seconds\n",
" Total time elapsed = 1.8507E+01 seconds\n",
" Calculation Rate (inactive) = 13075.3 neutrons/second\n",
" Calculation Rate (active) = 6490.56 neutrons/second\n",
" Total time for finalization = 0.0000E+00 seconds\n",
" Total time elapsed = 1.6977E+01 seconds\n",
" Calculation Rate (inactive) = 12742.1 neutrons/second\n",
" Calculation Rate (active) = 6876.63 neutrons/second\n",
"\n",
" ============================> RESULTS <============================\n",
"\n",
" k-effective (Collision) = 1.16131 +/- 0.00453\n",
" k-effective (Track-length) = 1.16206 +/- 0.00465\n",
" k-effective (Absorption) = 1.16096 +/- 0.00364\n",
" Combined k-effective = 1.16120 +/- 0.00325\n",
" k-effective (Collision) = 1.15984 +/- 0.00411\n",
" k-effective (Track-length) = 1.16146 +/- 0.00457\n",
" k-effective (Absorption) = 1.16177 +/- 0.00380\n",
" Combined k-effective = 1.16105 +/- 0.00364\n",
" Leakage Fraction = 0.00000 +/- 0.00000\n",
"\n"
]
@ -750,8 +752,8 @@
"\tDomain Type =\tcell\n",
"\tDomain ID =\t1\n",
"\tCross Sections [cm^-1]:\n",
" Group 1 [6.25e-07 - 20.0 MeV]:\t6.81e-01 +/- 1.88e-01%\n",
" Group 2 [0.0 - 6.25e-07 MeV]:\t1.40e+00 +/- 5.91e-01%\n",
" Group 1 [6.25e-07 - 20.0 MeV]:\t6.81e-01 +/- 2.69e-01%\n",
" Group 2 [0.0 - 6.25e-07 MeV]:\t1.40e+00 +/- 5.93e-01%\n",
"\n",
"\n",
"\n"
@ -779,7 +781,7 @@
{
"data": {
"text/html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -797,16 +799,16 @@
" <td>1</td>\n",
" <td>1</td>\n",
" <td>total</td>\n",
" <td>0.668323</td>\n",
" <td>0.001264</td>\n",
" <td>0.667787</td>\n",
" <td>0.001802</td>\n",
" </tr>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>2</td>\n",
" <td>total</td>\n",
" <td>1.293258</td>\n",
" <td>0.007624</td>\n",
" <td>1.292013</td>\n",
" <td>0.007642</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
@ -814,8 +816,8 @@
],
"text/plain": [
" cell group in nuclide mean std. dev.\n",
"1 1 1 total 0.668323 0.001264\n",
"0 1 2 total 1.293258 0.007624"
"1 1 1 total 0.667787 0.001802\n",
"0 1 2 total 1.292013 0.007642"
]
},
"execution_count": 20,
@ -890,13 +892,14 @@
{
"data": {
"text/html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>cell</th>\n",
" <th>energy [MeV]</th>\n",
" <th>energy low [MeV]</th>\n",
" <th>energy high [MeV]</th>\n",
" <th>nuclide</th>\n",
" <th>score</th>\n",
" <th>mean</th>\n",
@ -907,33 +910,35 @@
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>(0.0e+00 - 6.3e-07)</td>\n",
" <td>0.000000e+00</td>\n",
" <td>6.250000e-07</td>\n",
" <td>total</td>\n",
" <td>(((total / flux) - (absorption / flux)) - (sca...</td>\n",
" <td>4.884981e-15</td>\n",
" <td>0.011274</td>\n",
" <td>-3.774758e-15</td>\n",
" <td>0.011292</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1</td>\n",
" <td>(6.3e-07 - 2.0e+01)</td>\n",
" <td>6.250000e-07</td>\n",
" <td>2.000000e+01</td>\n",
" <td>total</td>\n",
" <td>(((total / flux) - (absorption / flux)) - (sca...</td>\n",
" <td>1.221245e-15</td>\n",
" <td>0.001802</td>\n",
" <td>1.443290e-15</td>\n",
" <td>0.002570</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" cell energy [MeV] nuclide \\\n",
"0 1 (0.0e+00 - 6.3e-07) total \n",
"1 1 (6.3e-07 - 2.0e+01) total \n",
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
"0 1 0.00e+00 6.25e-07 total \n",
"1 1 6.25e-07 2.00e+01 total \n",
"\n",
" score mean std. dev. \n",
"0 (((total / flux) - (absorption / flux)) - (sca... 4.884981e-15 0.011274 \n",
"1 (((total / flux) - (absorption / flux)) - (sca... 1.221245e-15 0.001802 "
" score mean std. dev. \n",
"0 (((total / flux) - (absorption / flux)) - (sca... -3.77e-15 1.13e-02 \n",
"1 (((total / flux) - (absorption / flux)) - (sca... 1.44e-15 2.57e-03 "
]
},
"execution_count": 23,
@ -966,13 +971,14 @@
{
"data": {
"text/html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>cell</th>\n",
" <th>energy [MeV]</th>\n",
" <th>energy low [MeV]</th>\n",
" <th>energy high [MeV]</th>\n",
" <th>nuclide</th>\n",
" <th>score</th>\n",
" <th>mean</th>\n",
@ -983,33 +989,35 @@
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>(0.0e+00 - 6.3e-07)</td>\n",
" <td>0.000000e+00</td>\n",
" <td>6.250000e-07</td>\n",
" <td>total</td>\n",
" <td>((absorption / flux) / (total / flux))</td>\n",
" <td>0.076219</td>\n",
" <td>0.000651</td>\n",
" <td>0.076115</td>\n",
" <td>0.000649</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1</td>\n",
" <td>(6.3e-07 - 2.0e+01)</td>\n",
" <td>6.250000e-07</td>\n",
" <td>2.000000e+01</td>\n",
" <td>total</td>\n",
" <td>((absorption / flux) / (total / flux))</td>\n",
" <td>0.019319</td>\n",
" <td>0.000086</td>\n",
" <td>0.019263</td>\n",
" <td>0.000095</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" cell energy [MeV] nuclide score \\\n",
"0 1 (0.0e+00 - 6.3e-07) total ((absorption / flux) / (total / flux)) \n",
"1 1 (6.3e-07 - 2.0e+01) total ((absorption / flux) / (total / flux)) \n",
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
"0 1 0.00e+00 6.25e-07 total \n",
"1 1 6.25e-07 2.00e+01 total \n",
"\n",
" mean std. dev. \n",
"0 0.076219 0.000651 \n",
"1 0.019319 0.000086 "
" score mean std. dev. \n",
"0 ((absorption / flux) / (total / flux)) 7.61e-02 6.49e-04 \n",
"1 ((absorption / flux) / (total / flux)) 1.93e-02 9.46e-05 "
]
},
"execution_count": 24,
@ -1035,13 +1043,14 @@
{
"data": {
"text/html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>cell</th>\n",
" <th>energy [MeV]</th>\n",
" <th>energy low [MeV]</th>\n",
" <th>energy high [MeV]</th>\n",
" <th>nuclide</th>\n",
" <th>score</th>\n",
" <th>mean</th>\n",
@ -1052,33 +1061,35 @@
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>(0.0e+00 - 6.3e-07)</td>\n",
" <td>0.000000e+00</td>\n",
" <td>6.250000e-07</td>\n",
" <td>total</td>\n",
" <td>((scatter / flux) / (total / flux))</td>\n",
" <td>0.923781</td>\n",
" <td>0.007714</td>\n",
" <td>0.923885</td>\n",
" <td>0.007736</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1</td>\n",
" <td>(6.3e-07 - 2.0e+01)</td>\n",
" <td>6.250000e-07</td>\n",
" <td>2.000000e+01</td>\n",
" <td>total</td>\n",
" <td>((scatter / flux) / (total / flux))</td>\n",
" <td>0.980681</td>\n",
" <td>0.002617</td>\n",
" <td>0.980737</td>\n",
" <td>0.003737</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" cell energy [MeV] nuclide score \\\n",
"0 1 (0.0e+00 - 6.3e-07) total ((scatter / flux) / (total / flux)) \n",
"1 1 (6.3e-07 - 2.0e+01) total ((scatter / flux) / (total / flux)) \n",
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
"0 1 0.00e+00 6.25e-07 total \n",
"1 1 6.25e-07 2.00e+01 total \n",
"\n",
" mean std. dev. \n",
"0 0.923781 0.007714 \n",
"1 0.980681 0.002617 "
" score mean std. dev. \n",
"0 ((scatter / flux) / (total / flux)) 9.24e-01 7.74e-03 \n",
"1 ((scatter / flux) / (total / flux)) 9.81e-01 3.74e-03 "
]
},
"execution_count": 25,
@ -1111,13 +1122,14 @@
{
"data": {
"text/html": [
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>cell</th>\n",
" <th>energy [MeV]</th>\n",
" <th>energy low [MeV]</th>\n",
" <th>energy high [MeV]</th>\n",
" <th>nuclide</th>\n",
" <th>score</th>\n",
" <th>mean</th>\n",
@ -1128,33 +1140,35 @@
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>(0.0e+00 - 6.3e-07)</td>\n",
" <td>0.000000e+00</td>\n",
" <td>6.250000e-07</td>\n",
" <td>total</td>\n",
" <td>(((absorption / flux) / (total / flux)) + ((sc...</td>\n",
" <td>1</td>\n",
" <td>0.007741</td>\n",
" <td>0.007763</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1</td>\n",
" <td>(6.3e-07 - 2.0e+01)</td>\n",
" <td>6.250000e-07</td>\n",
" <td>2.000000e+01</td>\n",
" <td>total</td>\n",
" <td>(((absorption / flux) / (total / flux)) + ((sc...</td>\n",
" <td>1</td>\n",
" <td>0.002619</td>\n",
" <td>0.003739</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" cell energy [MeV] nuclide \\\n",
"0 1 (0.0e+00 - 6.3e-07) total \n",
"1 1 (6.3e-07 - 2.0e+01) total \n",
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
"0 1 0.00e+00 6.25e-07 total \n",
"1 1 6.25e-07 2.00e+01 total \n",
"\n",
" score mean std. dev. \n",
"0 (((absorption / flux) / (total / flux)) + ((sc... 1 0.007741 \n",
"1 (((absorption / flux) / (total / flux)) + ((sc... 1 0.002619 "
" score mean std. dev. \n",
"0 (((absorption / flux) / (total / flux)) + ((sc... 1.00e+00 7.76e-03 \n",
"1 (((absorption / flux) / (total / flux)) + ((sc... 1.00e+00 3.74e-03 "
]
},
"execution_count": 26,
@ -1187,7 +1201,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.10"
"version": "2.7.6"
}
},
"nbformat": 4,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -1,8 +0,0 @@
.. _pythonapi_executor:
========
Executor
========
.. automodule:: openmc.executor
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_filter:
======
Filter
======
.. automodule:: openmc.filter
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_geometry:
========
Geometry
========
.. automodule:: openmc.geometry
:members:

View file

@ -13,62 +13,267 @@ online. We recommend going through the modules from Codecademy_ and/or the
`Scipy lectures`_. The full API documentation serves to provide more information
on a given module or class.
**Handling nuclear data:**
------------------------------------
:mod:`openmc` -- Basic Functionality
------------------------------------
.. toctree::
:maxdepth: 1
Handling nuclear data
---------------------
ace
Classes
+++++++
**Creating input files:**
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
.. toctree::
:maxdepth: 1
openmc.XSdata
openmc.MGXSLibraryFile
cmfd
element
filter
geometry
material
mesh
nuclide
opencg_compatible
plots
settings
source
stats
surface
tallies
trigger
universe
Functions
+++++++++
**Running OpenMC:**
.. autosummary::
:toctree: generated
:nosignatures:
.. toctree::
:maxdepth: 1
openmc.ace.ascii_to_binary
executor
Simulation Settings
-------------------
**Post-processing:**
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
.. toctree::
:maxdepth: 1
openmc.Source
openmc.ResonanceScattering
openmc.SettingsFile
particle_restart
statepoint
summary
tallies
Material Specification
----------------------
**Multi-Group Cross Section Generation**
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
.. toctree::
:maxdepth: 1
openmc.Nuclide
openmc.Element
openmc.Macroscopic
openmc.Material
openmc.MaterialsFile
mgxs
energy_groups
mgxs_library
Building geometry
-----------------
**Example Jupyter Notebooks:**
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Plane
openmc.XPlane
openmc.YPlane
openmc.ZPlane
openmc.XCylinder
openmc.YCylinder
openmc.ZCylinder
openmc.Sphere
openmc.Cone
openmc.XCone
openmc.YCone
openmc.ZCone
openmc.Quadric
openmc.Halfspace
openmc.Intersection
openmc.Union
openmc.Complement
openmc.Cell
openmc.Universe
openmc.RectLattice
openmc.HexLattice
openmc.Geometry
openmc.GeometryFile
Many of the above classes are derived from several abstract classes:
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Surface
openmc.Region
openmc.Lattice
Constructing Tallies
--------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Filter
openmc.Mesh
openmc.Trigger
openmc.Tally
openmc.TalliesFile
Coarse Mesh Finite Difference Acceleration
------------------------------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.CMFDMesh
openmc.CMFDFile
Plotting
--------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Plot
openmc.PlotsFile
Running OpenMC
--------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Executor
Post-processing
---------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Particle
openmc.StatePoint
openmc.Summary
Various classes may be created when performing tally slicing and/or arithmetic:
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.arithmetic.CrossScore
openmc.arithmetic.CrossNuclide
openmc.arithmetic.CrossFilter
openmc.arithmetic.AggregateScore
openmc.arithmetic.AggregateNuclide
openmc.arithmetic.AggregateFilter
---------------------------------
:mod:`openmc.stats` -- Statistics
---------------------------------
Univariate Probability Distributions
------------------------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.stats.Univariate
openmc.stats.Discrete
openmc.stats.Uniform
openmc.stats.Maxwell
openmc.stats.Watt
openmc.stats.Tabular
Angular Distributions
---------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.stats.UnitSphere
openmc.stats.PolarAzimuthal
openmc.stats.Isotropic
openmc.stats.Monodirectional
Spatial Distributions
---------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.stats.Spatial
openmc.stats.CartesianIndependent
openmc.stats.Box
openmc.stats.Point
----------------------------------------------------------
:mod:`openmc.mgxs` -- Multi-Group Cross Section Generation
----------------------------------------------------------
Energy Groups
-------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.mgxs.EnergyGroups
Multi-group Cross Sections
--------------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.mgxs.MGXS
openmc.mgxs.AbsorptionXS
openmc.mgxs.CaptureXS
openmc.mgxs.Chi
openmc.mgxs.FissionXS
openmc.mgxs.NuFissionXS
openmc.mgxs.NuScatterXS
openmc.mgxs.NuScatterMatrixXS
openmc.mgxs.ScatterXS
openmc.mgxs.ScatterMatrixXS
openmc.mgxs.TotalXS
openmc.mgxs.TransportXS
Multi-group Cross Section Libraries
-----------------------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.mgxs.Library
-------------------------
Example Jupyter Notebooks
-------------------------
.. toctree::
:maxdepth: 1

View file

@ -1,8 +0,0 @@
.. _pythonapi_material:
=========
Materials
=========
.. automodule:: openmc.material
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_mesh:
====
Mesh
====
.. automodule:: openmc.mesh
:members:

View file

@ -1,66 +0,0 @@
.. _pythonapi_mgxs:
==========================
Multi-Group Cross Sections
==========================
.. currentmodule:: openmc.mgxs.mgxs
----------------------------
Summary of Available Classes
----------------------------
.. autosummary::
MGXS
AbsorptionXS
CaptureXS
Chi
FissionXS
NuFissionXS
NuScatterXS
NuScatterMatrixXS
ScatterXS
ScatterMatrixXS
TotalXS
TransportXS
-------------------
Class Documentation
-------------------
.. autoclass:: MGXS
:members:
.. autoclass:: AbsorptionXS
:members:
.. autoclass:: CaptureXS
:members:
.. autoclass:: Chi
:members:
.. autoclass:: FissionXS
:members:
.. autoclass:: NuFissionXS
:members:
.. autoclass:: NuScatterXS
:members:
.. autoclass:: NuScatterMatrixXS
:members:
.. autoclass:: ScatterXS
:members:
.. autoclass:: ScatterMatrixXS
:members:
.. autoclass:: TotalXS
:members:
.. autoclass:: TransportXS
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_mgxs_library:
============
MGXS Library
============
.. automodule:: openmc.mgxs.library
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_nuclide:
=======
Nuclide
=======
.. automodule:: openmc.nuclide
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_particle_restart:
================
Particle Restart
================
.. automodule:: openmc.particle_restart
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_plots:
=====
Plots
=====
.. automodule:: openmc.plots
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_settings:
========
Settings
========
.. automodule:: openmc.settings
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_source:
======
Source
======
.. automodule:: openmc.source
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_statepoint:
==========
Statepoint
==========
.. automodule:: openmc.statepoint
:members:

View file

@ -1,58 +0,0 @@
.. _pythonapi_stats:
=====================
Statistical Functions
=====================
----------------------------
Summary of Available Classes
----------------------------
Univariate Probability Distributions
------------------------------------
.. currentmodule:: openmc.stats.univariate
.. autosummary::
Univariate
Discrete
Uniform
Maxwell
Watt
Tabular
Angular Distributions
---------------------
.. currentmodule:: openmc.stats.multivariate
.. autosummary::
UnitSphere
PolarAzimuthal
Isotropic
Monodirectional
Spatial Distributions
---------------------
.. autosummary::
Spatial
CartesianIndependent
Box
Point
Univariate Probability Distributions
------------------------------------
.. automodule:: openmc.stats.univariate
:members:
Multivariate Probability Distributions
--------------------------------------
.. automodule:: openmc.stats.multivariate
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_summary:
=======
Summary
=======
.. automodule:: openmc.summary
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_surface:
=======
Surface
=======
.. automodule:: openmc.surface
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_tallies:
=======
Tallies
=======
.. automodule:: openmc.tallies
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_trigger:
=======
Trigger
=======
.. automodule:: openmc.trigger
:members:

View file

@ -1,8 +0,0 @@
.. _pythonapi_universe:
========
Universe
========
.. automodule:: openmc.universe
:members:

View file

@ -12,7 +12,7 @@ In a nutshell, OpenMC simulates neutrons moving around randomly in a `nuclear
reactor`_ (or other fissile system). This is what's known as `Monte Carlo`_
simulation. Neutrons are important in nuclear reactors because they are the
particles that induce `fission`_ in uranium and other nuclides. Knowing the
behavior of neutrons allows you to figure out how often and where fission
behavior of neutrons allows you to determine how often and where fission
occurs. The amount of energy released is then directly proportional to the
fission reaction rate since most heat is produced by fission. By simulating many
neutrons (millions or billions), it is possible to determine the average

View file

@ -14,6 +14,7 @@ essential aspects of using OpenMC to perform simulations.
beginners
install
input
mgxs_library
output/index
processing
troubleshoot

View file

@ -112,9 +112,11 @@ standard deviation.
The ``<cross_sections>`` element has no attributes and simply indicates the path
to an XML cross section listing file (usually named cross_sections.xml). If this
element is absent from the settings.xml file, the :envvar:`CROSS_SECTIONS`
environment variable will be used to find the path to the XML cross section
listing.
element is absent from the settings.xml file, the
:envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used to find the
path to the XML cross section listing when in continuous-energy mode, and the
:envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable will be used in
multi-group mode.
``<cutoff>`` Element
--------------------
@ -212,8 +214,21 @@ cross section values between.
*Default*: logarithm
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
.. _LA-UR-14-24530: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf
.. _energy_mode:
``<energy_mode>`` Element
-------------------------
The ``<energy_mode>`` element tells OpenMC if the run-mode should be
continuous-energy or multi-group. Options for entry are: ``continuous-energy``
or ``multi-group``.
*Default*: continuous-energy
``<entropy>`` Element
---------------------
@ -264,6 +279,8 @@ based on the recommended value in LA-UR-14-24530_.
*Default*: 8000
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
``<multipole_library>`` Element
-------------------------------
@ -271,11 +288,24 @@ The ``<multipole_library>`` element indicates the directory containing a
windowed multipole library. If a windowed multipole library is available,
OpenMC can use it for on-the-fly Doppler-broadening of resolved resonance range
cross sections. If this element is absent from the settings.xml file, the
:envvar:`MULTIPOLE_LIBRARY` environment variable will be used.
:envvar:`OPENMC_MULTIPOLE_LIBRARY` environment variable will be used.
.. note:: The <use_windowed_multipole> element must also be set to "True"
.. note:: The <use_windowed_multipole> element must also be set to "true"
for windowed multipole functionality.
``<max_order>`` Element
---------------------------
The ``<max_order>`` element allows the user to set a maximum scattering order
to apply to every nuclide/material in the problem. That is, if the data
library has :math:`P_3` data available, but ``<max_order>`` was set to ``1``,
then, OpenMC will only use up to the :math:`P_1` data.
*Default*: Use the maximum order in the data library
.. note:: This element is not used in the continuous-energy
:ref:`energy_mode`.
.. _natural_elements:
``<natural_elements>`` Element
@ -324,10 +354,10 @@ out the file and "false" will not.
*Default*: false
:summary:
Writes out an ASCII summary file describing all of the user input files that
Writes out an HDF5 summary file describing all of the user input files that
were read in.
*Default*: false
*Default*: true
:tallies:
Write out an ASCII file of tally results.
@ -355,6 +385,8 @@ or sub-elements and can be set to either "false" or "true".
*Default*: true
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
``<resonance_scattering>`` Element
----------------------------------
@ -414,6 +446,8 @@ attributes or sub-elements:
*Defaults*: None (scatterer), ARES (method), 0.01 eV (E_min), 1.0 keV (E_max)
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
``<run_cmfd>`` Element
----------------------
@ -591,6 +625,8 @@ variable and whose sub-elements/attributes are as follows:
number :math:`a` that parameterizes the distribution :math:`p(x) dx = c x
e^{-x/a} dx`.
.. note:: The above format should be used even when using the multi-group
:ref:`energy_mode`.
:interpolation:
For a "tabular" distribution, ``interpolation`` can be set to "histogram" or
"linear-linear" thereby specifying how tabular points are to be interpolated.
@ -1216,10 +1252,18 @@ Each ``material`` element can have the following attributes or sub-elements:
``<element>`` sub-elements are to be interpreted as nuclide/element
densities in atom/b-cm, and the total density of the material is taken as
the sum of all nuclides/elements. The "sum" option cannot be used in
conjunction with weight percents.
conjunction with weight percents. The "macro" unit is used with
a ``macroscopic`` quantity to indicate that the density is already included
in the library and thus not needed here. However, if a value is provided
for the ``value``, then this is treated as a number density multiplier on
the macroscopic cross sections in the multi-group data. This can be used,
for example, when perturbing the density slightly.
*Default*: None
.. note:: A ``macroscopic`` quantity can not be used in conjunction with a
``nuclide``, ``element``, or ``sab`` quantity.
:nuclide:
An element with attributes/sub-elements called ``name``, ``xs``, and ``ao``
or ``wo``. The ``name`` attribute is the name of the cross-section for a
@ -1247,6 +1291,9 @@ Each ``material`` element can have the following attributes or sub-elements:
*Default*: None
.. note:: The ``scattering`` attribute/sub-element is not used in the
multi-group :ref:`energy_mode`.
:element:
Specifies that a natural element is present in the material. The natural
@ -1282,6 +1329,9 @@ Each ``material`` element can have the following attributes or sub-elements:
*Default*: None
.. note:: The ``scattering`` attribute/sub-element is not used in the
multi-group :ref:`energy_mode`.
:sab:
Associates an S(a,b) table with the material. This element has
attributes/sub-elements called ``name`` and ``xs``. The ``name`` attribute
@ -1290,6 +1340,26 @@ Each ``material`` element can have the following attributes or sub-elements:
*Default*: None
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
:macroscopic:
The ``macroscopic`` element is similar to the ``nuclide`` element, but,
recognizes that some multi-group libraries may be providing material
specific macroscopic cross sections instead of always providing nuclide
specific data like in the continuous-energy case. To that end, the
macroscopic element has attributes/sub-elements called ``name``, and ``xs``.
The ``name`` attribute is the name of the cross-section for a
desired nuclide while the ``xs`` attribute is the cross-section
identifier. One example would be as follows:
.. code-block:: xml
<macroscopic name="UO2" xs="71c" />
.. note:: This element is only used in the multi-group :ref:`energy_mode`.
*Default*: None
.. _IUPAC Isotopic Compositions of the Elements 2009:
http://pac.iupac.org/publications/pac/pdf/2011/pdf/8302x0397.pdf
@ -1376,7 +1446,8 @@ The ``<tally>`` element accepts the following sub-elements:
A list of universes for which the tally should be accumulated.
:energy:
A monotonically increasing list of bounding **pre-collision** energies
In continuous-energy mode, this filter should be provided as a
monotonically increasing list of bounding **pre-collision** energies
for a number of groups. For example, if this filter is specified as
.. code-block:: xml
@ -1386,17 +1457,24 @@ The ``<tally>`` element accepts the following sub-elements:
then two energy bins will be created, one with energies between 0 and
1 MeV and the other with energies between 1 and 20 MeV.
In multi-group mode the bins provided must match group edges
defined in the multi-group library.
:energyout:
A monotonically increasing list of bounding **post-collision**
energies for a number of groups. For example, if this filter is
specified as
In continuous-energy mode, this filter should be provided as a
monotonically increasing list of bounding **post-collision** energies
for a number of groups. For example, if this filter is specified as
.. code-block:: xml
<filter type="energyout" bins="0.0 1.0 20.0" />
then two post-collision energy bins will be created, one with energies
between 0 and 1 MeV and the other with energies between 1 and 20 MeV.
then two post-collision energy bins will be created, one with
energies between 0 and 1 MeV and the other with energies between
1 and 20 MeV.
In multi-group mode the bins provided must match group edges
defined in the multi-group library.
:mu:
A monotonically increasing list of bounding **post-collision** cosines
@ -1480,6 +1558,8 @@ The ``<tally>`` element accepts the following sub-elements:
<filter type="delayedgroup" bins="1 2 3 4 5 6" />
.. note:: This filter type is not used in the multi-group :ref:`energy_mode`.
:nuclides:
If specified, the scores listed will be for particular nuclides, not the
summation of reactions from all nuclides. The format for nuclides should be
@ -1659,7 +1739,8 @@ The ``<tally>`` element accepts the following sub-elements:
|Score | Description |
+======================+===================================================+
|delayed-nu-fission |Total production of delayed neutrons due to |
| |fission. |
| |fission. This score type is not used in the |
| |multi-group :ref:`energy_mode`. |
+----------------------+---------------------------------------------------+
|nu-fission |Total production of neutrons due to fission. |
+----------------------+---------------------------------------------------+
@ -1688,6 +1769,8 @@ The ``<tally>`` element accepts the following sub-elements:
+----------------------+---------------------------------------------------+
|inverse-velocity |The flux-weighted inverse velocity where the |
| |velocity is in units of centimeters per second. |
| |This score type is not used in the |
| |multi-group :ref:`energy_mode`. |
+----------------------+---------------------------------------------------+
|kappa-fission |The recoverable energy production rate due to |
| |fission. The recoverable energy is defined as the |

View file

@ -227,6 +227,7 @@ your PATH environment variable and subsequently uses it to determine library
locations and compile flags. If you have multiple installations of HDF5 or one
that does not appear on your PATH, you can set the HDF5_ROOT environment
variable to the root directory of the HDF5 installation, e.g.
.. code-block:: sh
export HDF5_ROOT=/opt/hdf5/1.8.15
@ -355,7 +356,7 @@ Testing Build
-------------
If you have ENDF/B-VII.1 cross sections from NNDC_ you can test your build.
Make sure the **CROSS_SECTIONS** environmental variable is set to the
Make sure the **OPENMC_CROSS_SECTIONS** environmental variable is set to the
*cross_sections.xml* file in the *data/nndc* directory.
There are two ways to run tests. The first is to use the Makefile present in
the source directory and run the following:
@ -380,11 +381,17 @@ Cross Section Configuration
---------------------------
In order to run a simulation with OpenMC, you will need cross section data for
each nuclide in your problem. Since OpenMC uses ACE format cross sections, you
can use nuclear data that was processed with NJOY_, such as that distributed
with MCNP_ or Serpent_. Several sources provide free processed ACE data as
described below. The TALYS-based evaluated nuclear data library, TENDL_, is also
openly available in ACE format.
each nuclide or material in your problem. OpenMC can be run in
continuous-energy or multi-group mode.
In continuous-energy mode OpenMC uses ACE format cross sections; in this case
you can use nuclear data that was processed with NJOY_, such as that
distributed with MCNP_ or Serpent_. Several sources provide free processed
ACE data as described below. The TALYS-based evaluated nuclear data library,
TENDL_, is also openly available in ACE format.
In multi-group mode, OpenMC utilizes an XML-based library format which can be
used to describe nuclide- or material-specific quantities.
Using ENDF/B-VII.1 Cross Sections from NNDC
-------------------------------------------
@ -399,9 +406,10 @@ extract, and set up a confiuration file:
cd openmc/data
python get_nndc_data.py
At this point, you should set the :envvar:`CROSS_SECTIONS` environment variable
to the absolute path of the file ``openmc/data/nndc/cross_sections.xml``. This
cross section set is used by the test suite.
At this point, you should set the :envvar:`OPENMC_CROSS_SECTIONS` environment
variable to the absolute path of the file
``openmc/data/nndc/cross_sections.xml``. This cross section set is used by the
test suite.
Using JEFF Cross Sections from OECD/NEA
---------------------------------------
@ -427,8 +435,8 @@ the following steps must be taken:
4. Additionally, you may need to change any occurrences of upper-case "ACE"
within the ``cross_sections.xml`` file to lower-case.
5. Either set the :ref:`cross_sections` in a settings.xml file or the
:envvar:`CROSS_SECTIONS` environment variable to the absolute path of the
``cross_sections.xml`` file.
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
the ``cross_sections.xml`` file.
Using Cross Sections from MCNP
------------------------------
@ -436,8 +444,9 @@ Using Cross Sections from MCNP
To use cross sections distributed with MCNP, change the <directory> element in
the ``cross_sections.xml`` file in the root directory of the OpenMC distribution
to the location of the MCNP cross sections. Then, either set the
:ref:`cross_sections` in a settings.xml file or the :envvar:`CROSS_SECTIONS`
environment variable to the absolute path of the ``cross_sections.xml`` file.
:ref:`cross_sections` in a settings.xml file or the
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
the ``cross_sections.xml`` file.
Using Cross Sections from Serpent
---------------------------------
@ -445,10 +454,21 @@ Using Cross Sections from Serpent
To use cross sections distributed with Serpent, change the <directory> element
in the ``cross_sections_serpent.xml`` file in the root directory of the OpenMC
distribution to the location of the Serpent cross sections. Then, either set the
:ref:`cross_sections` in a settings.xml file or the :envvar:`CROSS_SECTIONS`
environment variable to the absolute path of the ``cross_sections_serpent.xml``
:ref:`cross_sections` in a settings.xml file or the
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
the ``cross_sections_serpent.xml``
file.
Using Multi-Group Cross Sections
--------------------------------
Multi-group cross section libraries are generally tailored to the specific
calculation to be performed. Therefore, at this point in time, OpenMC is not
distributed with any pre-existing multi-group cross section libraries.
However, if the user has obtained or generated their own library, the user
should set the :envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable
to the absolute path of the file library expected to used most frequently.
.. _NJOY: http://t2.lanl.gov/nis/codes.shtml
.. _NNDC: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html
.. _NEA: http://www.oecd-nea.org

View file

@ -0,0 +1,302 @@
.. _usersguide_mgxs_library:
========================================
Multi-Group Cross Section Library Format
========================================
OpenMC can be run in continuous-energy mode or multi-group mode, provided the
nuclear data is available. In continuous-energy mode, the
``cross_sections.xml`` file contains necessary meta-data for each data set,
including the name and a file system location where the complete library
can be found. In multi-group mode, this ``cross_sections.xml`` file contains
this same meta-data describing the nuclide or material, but also contains the
group-wise nuclear data. This portion of the manual describes the format of
the multi-group data library required to be used in the ``cross_sections.xml``
file.
Similar to the other input file types, the multi-group library is provided in
the XML_ format. This library must provide some meta-data about the library
itself (such as the number of groups and the group structure, etc.) as well as
the actual cross section data itself for each of the necessary nuclides or
materials.
.. _XML: http://www.w3.org/XML/
------------------------------------------------
MGXS Library Specification -- cross_sections.xml
------------------------------------------------
The multi-group library meta-data is contained within the groups_,
group_structure_, and inverse_velocities_ elements.
The actual multi-group data itself is contained within the xsdata_ element.
.. _groups:
``<groups>`` Element
----------------------------------
The ``<groups>`` element has no attributes and simply provides the number of
energy groups contained within the library.
*Default*: None, this must be provided.
.. _group_structure:
``<group_structure>`` Element
-----------------------------
The ``<group_structure>`` element has no attributes and should be provided as a
monotonically increasing list of bounding energies, in MeV, for a number of
groups. To provide proper energy boundaries, the length of the data within the
``<group_structure>`` element should be one more than the number of groups in
the problem. For example, a two-group problem could be specified as:
.. code-block:: xml
<group_structure> 0.0 0.625E-6 20.0 </group_structure>
*Default*: None, this must be provided.
.. _inverse_velocities:
``<inverse_velocities>`` Element
--------------------------------
The ``<inverse_velocities>`` element optionally indicates the average
inverse velocity corresponding to each of the groups in the problem.
This element should therefore be an array with a length which matches the
number of groups set in the groups_ element.
*Default*: Should this be needed by the presence of an ``inverse-velocity``
score in the ``tallies.xml`` file and not provided in this element, OpenMC
will simply convert the group mid-point energy to an inverse of the velocity
and use this information for tallying.
.. _xsdata:
``<xsdata>`` Element
--------------------
The ``<xsdata>`` element contains the nuclide or material-specific meta-data as
well as the actual cross section data. The following are the
attributes/sub-elements required to describe the meta-data:
:name:
The name of the microscopic or macroscopic data set. An extension to the
name must be provided (e.g., the ``.300K`` in ``UO2.300K``). The name and
extension together must be twelve or less characters in length. This
extension must follow a period and be five characters or less in length.
similar to the equivalent in the continuous-energy ``cross_sections.xml``
file, is used to denote variants of the particular nuclide or material of
interest (i.e. the ``UO2`` data in this example could have been generated
at a temperature of 300K).
*Default*: None, this must be provided.
:alias:
An alternative name to use for the microscopic or macroscopic data set.
*Default*: If no alias is provided, it will adopt the value of ``name``.
:kT:
The temperature times Boltzmann's constant (in units of MeV) at which the
data was generated.
*Default*: Room temperature, 2.53E-8 MeV
:fissionable:
This element states whether or not the data in question is fissionable.
Accepted values are "true" or "false".
*Default*: None, this element must be provided.
:representation:
This element provides the method used to generate and represent the
multi-group cross sections. That is, whether they were generated with
scalar flux weighting (or reduced to an equivalent representation)
and thus are angle-independent, or if the data was generated with angular
dependent fluxes and thus the data is angle-dependent. The options are
either "isotropic" or "angle".
*Default*: "isotropic"
:num_azimuthal:
This element provides the number of equal width angular bins that the
azimuthal angular domain is subdivided in the case of angle-dependent
cross sections (i.e., "angle" is passed to the ``representation`` element).
Note that these bins are equal in azimuthal angle widths, not equal in the
cosine of the azimuthal angle widths.
*Default*: If ``representation`` is "angle", this must be provided. This
parameter is not used for other ``representation`` types.
:num_polar:
This element provides the number of equal width angular bins that the
polar angular domain is subdivided in the case of angle-dependent
cross sections (i.e., "angle" is passed to the ``representation`` element).
Note that these bins are equal in polar angle widths, not equal in the
cosine of the polar angle widths.
*Default*: If ``representation`` is "angle", this must be provided. This
parameter is not used for other ``representation`` types.
:scatt_type:
This element provides the representation of the angular distribution
associated with each group-to-group transfer probability. The options are
either "legendre", "histogram", or "tabular".
The "legendre" option means the angular distribution has been
expanded via Legendre polynomials of the order provided in the "order"
element.
The "histogram" option means the angular distribution is provided in
an equi-width histogram format with a number of bins as provided in the
"order" element. This is useful when the angular distribution was
obtained from a Monte Carlo tally and thus is natively in the histogram
format.
The "tabular" option means the angular distribution is provided in an
equi-spaced point-wise representation.
*Default*: "legendre"
:order:
This element provides either the Legendre order, number of bins, or number
of points used to describe the angular distribution associated with each
group-to-group transfer probability. The specific meaning of this bin
depends upon the value of ``scatt_type`` as discussed above.
*Default*: None, this element must be provided.
:tabular_legendre:
This optional element is used to set how the Legendre scattering kernel, if
provided via the ``scatt_type`` element above, is represented and thus used
during the scattering process. Specifically, the options are to either
convert the Legendre expansion to a tabular representation or leave it as
a set of Legendre coefficients. Converting to a tabular representation will
cost memory but is likely to decrease runtime compared to leaving as a
set of Legendre coefficients. This element has the following
attributes/sub-elements:
:enable:
This attribute/sub-element denotes whether or not the conversion to the
tabular format should be performed or not. A value of "true" means
the conversion should be performed, "false" means it should not.
*Default*: "true"
:num_points:
If the conversion is to take place the number of tabular points is
required. This attribute/sub-element allows the user to set the desired
number of points.
*Default*: 33
The following attributes/sub-elements are the cross section values to
be used during the transport process.
:total:
This element requires the group-wise total cross section ordered by
increasing group index (i.e., fast to thermal). If ``representation`` is
"isotropic", then the length of this list should equal the number of
groups described in the ``groups`` element. If ``representation`` is
"angle", then the length of this list should equal the number of groups
times the number of azimuthal angles times the number of polar angles,
with the inner-dimension being groups, intermediate-dimension being
azimuthal angles and outer-dimension being the polar angles.
*Default*: If not provided, it will be determined by summing the
absorption and scattering cross sections.
:absorption:
This element requires the group-wise absorption cross section ordered by
increasing group index (i.e., fast to thermal). If ``representation`` is
"isotropic", then the length of this list should equal the number of
groups described in the ``groups`` element. If ``representation`` is
"angle", then the length of this list should equal the number of groups
times the number of azimuthal angles times the number of polar angles,
with the inner-dimension being groups, intermediate-dimension being
azimuthal angles and outer-dimension being the polar angles.
*Default*: None, this must be provided.
:scatter:
This element requires the scattering moment matrices presented with the
columns representing incoming group and rows representing the outgoing
group. That is, down-scatter will be above the diagonal of the resultant
matrix. This matrix is repeated for every Legendre order (in order of
increasing orders) if ``scatt_type`` is "legendre"; otherwise, this
matrix is repeated for every bin of the histogram or tabular
representation. Finally, if ``representation`` is "angle", the above
is repeated for every azimuthal angle and every polar angle, in that
order.
*Default*: None, this must be provided.
:multiplicity:
This element provides the ratio of neutrons produced in scattering
collisions to the neutrons which undergo scattering collisions; that is,
the multiplicity provides the code with a scaling factor to account for
neutrons being produced in (n,xn) reactions. This information is assumed
isotropic and therefore does not need to be repeated for every Legendre
moment or histogram/tabular bin. This matrix follows the same arrangement
as described for the ``scatter`` element, with the exception of the
data needed to provide the scattering type information.
*Default*: Multiplicities of 1.0 are assumed (i.e., (n,xn) reactions are
neglected).
The following fission-specific data are only needed should ``fissionable``
be "true".
:fission:
This element requires the group-wise fission cross section ordered by
increasing group index (i.e., fast to thermal). If ``representation`` is
"isotropic", then the length of this list should equal the number of
groups described in the ``groups`` element. If ``representation`` is
"angle", then the length of this list should equal the number of groups
times the number of azimuthal angles times the number of polar angles,
with the inner-dimension being groups, intermediate-dimension being
azimuthal angles and outer-dimension being the polar angles.
*Default*: None, this is required only if fission tallies are
requested and the material is fissionable.
:kappa_fission:
This element requires the group-wise kappa-fission cross section ordered by
increasing group index (i.e., fast to thermal). If ``representation`` is
"isotropic", then the length of this list should equal the number of
groups described in the ``groups`` element. If ``representation`` is
"angle", then the length of this list should equal the number of groups
times the number of azimuthal angles times the number of polar angles,
with the inner-dimension being groups, intermediate-dimension being
azimuthal angles and outer-dimension being the polar angles.
*Default*: None, this is required only if kappa_fission tallies are
requested and the material is fissionable.
:chi:
This element requires the group-wise fission spectra ordered by
increasing group index (i.e., fast to thermal). This element should be
used if making the common approximation that the fission spectra does
not depend on incoming energy. If the user does not wish to make this
approximation, then this should not be provided and this information
included in the ``nu_fission`` element instead. If ``representation`` is
"isotropic", then the length of this list should equal the number of
groups described in the ``groups`` element. If ``representation`` is
"angle", then the length of this list should equal the number of groups
times the number of azimuthal angles times the number of polar angles,
with the inner-dimension being groups, intermediate-dimension being
azimuthal angles and outer-dimension being the polar angles.
*Default*: None, either this element is provided or ``nu_fission`` is
provided in fission matrix form, or the material is not fissionable.
:nu_fission:
This element provides either the group-wise fission production cross
section vector (i.e., if ``chi`` is provided), or is the group-wise fission
production matrix. If providing the vector, it should be ordered the same
as the ``fission`` data. If providing the matrix, it should be ordered
the same as the ``multiplicity`` matrix.
*Default*: None, either this element must be provided if the material
is fissionable.

View file

@ -46,7 +46,8 @@ The current revision of the particle restart file format is 1.
**/energy** (*double*)
Energy of the particle in MeV.
Energy of the particle in MeV for continuous-energy mode, or the energy
group of the particle for multi-group mode.
**/xyz** (*double[3]*)

View file

@ -15,5 +15,6 @@ is that documented here.
**/source_bank** (Compound type)
Source bank information for each particle. The compound type has fields
``wgt``, ``xyz``, ``uvw``, and ``E`` which represent the weight, position,
direction, and energy of the source particle, respectively.
``wgt``, ``xyz``, ``uvw``, ``E``, and ``delayed_group``, which
represent the weight, position, direction, energy, energy group, and
delayed_group of the source particle, respectively.

View file

@ -4,7 +4,7 @@
State Point File Format
=======================
The current revision of the statepoint file format is 14.
The current revision of the statepoint file format is 15.
**/filetype** (*char[]*)
@ -39,6 +39,12 @@ The current revision of the statepoint file format is 14.
Pseudo-random number generator seed.
**/run_CE** (*int*)
Flag to denote continuous-energy or multi-group mode. A value of 1
indicates a continuous-energy run while a value of 0 indicates a
multi-group run.
**/run_mode** (*char[]*)
Run mode used. A value of 1 indicates a fixed-source run and a value of 2
@ -258,7 +264,7 @@ if run_mode == 'k-eigenvalue':
Accumulated sum and sum-of-squares for each global tally. The compound type
has fields named ``sum`` and ``sum_sq``.
**tallies_present** (*int*)
**/tallies_present** (*int*)
Flag indicated if tallies are present in the file.
@ -267,5 +273,72 @@ if (run_mode == 'k-eigenvalue' and source_present > 0)
**/source_bank** (Compound type)
Source bank information for each particle. The compound type has fields
``wgt``, ``xyz``, ``uvw``, and ``E`` which represent the weight,
position, direction, and energy of the source particle, respectively.
``wgt``, ``xyz``, ``uvw``, ``E``, ``g``, and ``delayed_group``, which
represent the weight, position, direction, energy, energy group, and
delayed_group of the source particle, respectively.
**/runtime/total initialization** (*double*)
Time (in seconds on the master process) spent reading inputs, allocating
arrays, etc.
**/runtime/reading cross sections** (*double*)
Time (in seconds on the master process) spent loading cross section
libraries (this is a subset of initialization).
**/runtime/simulation** (*double*)
Time (in seconds on the master process) spent between initialization and
finalization.
**/runtime/transport** (*double*)
Time (in seconds on the master process) spent transporting particles.
**/runtime/inactive batches** (*double*)
Time (in seconds on the master process) spent in the inactive batches
(including non-transport activities like communcating sites).
**/runtime/active batches** (*double*)
Time (in seconds on the master process) spent in the active batches
(including non-transport activities like communicating sites).
**/runtime/synchronizing fission bank** (*double*)
Time (in seconds on the master process) spent sampling source particles
from fission sites and communicating them to other processes for load
balancing.
**/runtime/sampling source sites** (*double*)
Time (in seconds on the master process) spent sampling source particles
from fission sites.
**/runtime/SEND-RECV source sites** (*double*)
Time (in seconds on the master process) spent communicating source sites
between processes for load balancing.
**/runtime/accumulating tallies** (*double*)
Time (in seconds on the master process) spent communicating tally results
and evaluating their statistics.
**/runtime/CMFD** (*double*)
Time (in seconds on the master process) spent evaluating CMFD.
**/runtime/CMFD building matrices** (*double*)
Time (in seconds on the master process) spent buliding CMFD matrices.
**/runtime/CMFD solving matrices** (*double*)
Time (in seconds on the master process) spent solving CMFD matrices.
**/runtime/total** (*double*)
Total time spent (in seconds on the master process) in the program.

View file

@ -297,6 +297,13 @@ The current revision of the summary file format is 1.
Filter offset (used for distribcell filter).
**/tallies/tally <uid>/filter <j>/paths** (*char[][]*)
The paths traversed through the CSG tree to reach each distribcell
instance (for 'distribcell' filters only). This consists of the integer
IDs for each universe, cell and lattice delimited by '->'. Each lattice
cell is specified by its (x,y) or (x,y,z) indices.
**/tallies/tally <uid>/filter <j>/n_bins** (*int*)
Number of bins for the j-th filter.

View file

@ -196,10 +196,10 @@ Data Extraction
A great deal of information is available in statepoint files (See
:ref:`usersguide_statepoint`), all of which is accessible through the Python
API. The ``openmc.statepoint`` module (see :ref:`pythonapi_statepoint`) provides
a class to load statepoints and access data as requested; it is used in many of
the provided plotting utilities, OpenMC's regression test suite, and can be used
in user-created scripts to carry out manipulations of the data.
API. The :class:`openmc.StatePoint` class can load statepoints and access data
as requested; it is used in many of the provided plotting utilities, OpenMC's
regression test suite, and can be used in user-created scripts to carry out
manipulations of the data.
An :ref:`example IPython notebook <notebook_post_processing>` demonstrates how
to extract data from a statepoint using the Python API.

View file

@ -1,6 +1,5 @@
import openmc
from openmc.source import Source
from openmc.stats import Box
###############################################################################
# Simulation Input File Parameters
@ -94,7 +93,12 @@ settings_file = openmc.SettingsFile()
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
settings_file.source = Source(space=Box([-4, -4, -4], [4, 4, 4]))
# Create an initial uniform spatial source distribution over fissionable zones
bounds = [-4., -4., -4., 4., 4., 4.]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
settings_file.source = openmc.source.Source(space=uniform_dist)
settings_file.export_to_xml()
@ -109,29 +113,20 @@ energyout_filter = openmc.Filter(type='energyout', bins=[0., 20.])
# Instantiate the first Tally
first_tally = openmc.Tally(tally_id=1, name='first tally')
first_tally.add_filter(cell_filter)
scores = ['total', 'scatter', 'nu-scatter', \
first_tally.filters = [cell_filter]
scores = ['total', 'scatter', 'nu-scatter',
'absorption', 'fission', 'nu-fission']
for score in scores:
first_tally.add_score(score)
first_tally.scores = scores
# Instantiate the second Tally
second_tally = openmc.Tally(tally_id=2, name='second tally')
second_tally.add_filter(cell_filter)
second_tally.add_filter(energy_filter)
scores = ['total', 'scatter', 'nu-scatter', \
'absorption', 'fission', 'nu-fission']
for score in scores:
second_tally.add_score(score)
second_tally.filters = [cell_filter, energy_filter]
second_tally.scores = scores
# Instantiate the third Tally
third_tally = openmc.Tally(tally_id=3, name='third tally')
third_tally.add_filter(cell_filter)
third_tally.add_filter(energy_filter)
third_tally.add_filter(energyout_filter)
scores = ['scatter', 'nu-scatter', 'nu-fission']
for score in scores:
third_tally.add_score(score)
third_tally.filters = [cell_filter, energy_filter, energyout_filter]
third_tally.scores = ['scatter', 'nu-scatter', 'nu-fission']
# Instantiate a TalliesFile, register all Tallies, and export to XML
tallies_file = openmc.TalliesFile()

View file

@ -1,8 +1,5 @@
import numpy as np
import openmc
from openmc.source import Source
from openmc.stats import Box
###############################################################################
# Simulation Input File Parameters
@ -119,7 +116,11 @@ settings_file = openmc.SettingsFile()
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
settings_file.source = Source(space=Box(*outer_cube.bounding_box))
# Create an initial uniform spatial source distribution over fissionable zones
uniform_dist = openmc.stats.Box(*outer_cube.bounding_box, only_fissionable=True)
settings_file.source = openmc.source.Source(space=uniform_dist)
settings_file.export_to_xml()
###############################################################################

View file

@ -1,6 +1,4 @@
import openmc
from openmc.source import Source
from openmc.stats import Box
###############################################################################
# Simulation Input File Parameters
@ -126,8 +124,12 @@ settings_file = openmc.SettingsFile()
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
settings_file.source = Source(space=Box(
[-1, -1, -1], [1, 1, 1]))
# Create an initial uniform spatial source distribution over fissionable zones
bounds = [-1, -1, -1, 1, 1, 1]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
settings_file.source = openmc.source.Source(space=uniform_dist)
settings_file.keff_trigger = {'type' : 'std_dev', 'threshold' : 5E-4}
settings_file.trigger_active = True
settings_file.trigger_max_batches = 100
@ -166,8 +168,8 @@ plot_file.export_to_xml()
# Instantiate a distribcell Tally
tally = openmc.Tally(tally_id=1)
tally.add_filter(openmc.Filter(type='distribcell', bins=[cell2.id]))
tally.add_score('total')
tally.filters = [openmc.Filter(type='distribcell', bins=[cell2.id])]
tally.scores = ['total']
# Instantiate a TalliesFile, register Tally/Mesh, and export to XML
tallies_file = openmc.TalliesFile()

View file

@ -1,6 +1,4 @@
import openmc
from openmc.source import Source
from openmc.stats import Box
###############################################################################
# Simulation Input File Parameters
@ -137,8 +135,12 @@ settings_file = openmc.SettingsFile()
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
settings_file.source = Source(space=Box(
[-1, -1, -1], [1, 1, 1]))
# Create an initial uniform spatial source distribution over fissionable zones
bounds = [-1, -1, -1, 1, 1, 1]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
settings_file.source = openmc.source.Source(space=uniform_dist)
settings_file.export_to_xml()
@ -175,8 +177,8 @@ mesh_filter.mesh = mesh
# Instantiate the Tally
tally = openmc.Tally(tally_id=1)
tally.add_filter(mesh_filter)
tally.add_score('total')
tally.filters = [mesh_filter]
tally.scores = ['total']
# Instantiate a TalliesFile, register Tally/Mesh, and export to XML
tallies_file = openmc.TalliesFile()

View file

@ -1,6 +1,4 @@
import openmc
from openmc.source import Source
from openmc.stats import Box
###############################################################################
# Simulation Input File Parameters
@ -127,8 +125,12 @@ settings_file = openmc.SettingsFile()
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
settings_file.source = Source(space=Box(
[-1, -1, -1], [1, 1, 1]))
# Create an initial uniform spatial source distribution over fissionable zones
bounds = [-1, -1, -1, 1, 1, 1]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
settings_file.source = openmc.source.Source(space=uniform_dist)
settings_file.trigger_active = True
settings_file.trigger_max_batches = 100
settings_file.export_to_xml()
@ -167,13 +169,13 @@ mesh_filter.mesh = mesh
# Instantiate tally Trigger
trigger = openmc.Trigger(trigger_type='rel_err', threshold=1E-2)
trigger.add_score('all')
trigger.scores = ['all']
# Instantiate the Tally
tally = openmc.Tally(tally_id=1)
tally.add_filter(mesh_filter)
tally.add_score('total')
tally.add_trigger(trigger)
tally.filters = [mesh_filter]
tally.scores = ['total']
tally.triggers = [trigger]
# Instantiate a TalliesFile, register Tally/Mesh, and export to XML
tallies_file = openmc.TalliesFile()

View file

@ -1,6 +1,4 @@
import openmc
from openmc.source import Source
from openmc.stats import Box
###############################################################################
# Simulation Input File Parameters
@ -170,8 +168,12 @@ settings_file = openmc.SettingsFile()
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
settings_file.source = Source(space=Box(
[-0.62992, -0.62992, -1], [0.62992, 0.62992, 1]))
# Create an initial uniform spatial source distribution over fissionable zones
bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
settings_file.source = openmc.source.Source(space=uniform_dist)
settings_file.entropy_lower_left = [-0.39218, -0.39218, -1.e50]
settings_file.entropy_upper_right = [0.39218, 0.39218, 1.e50]
settings_file.entropy_dimension = [10, 10, 1]
@ -196,11 +198,8 @@ mesh_filter.mesh = mesh
# Instantiate the Tally
tally = openmc.Tally(tally_id=1, name='tally 1')
tally.add_filter(energy_filter)
tally.add_filter(mesh_filter)
tally.add_score('flux')
tally.add_score('fission')
tally.add_score('nu-fission')
tally.filters = [energy_filter, mesh_filter]
tally.scores = ['flux', 'fission', 'nu-fission']
# Instantiate a TalliesFile, register all Tallies, and export to XML
tallies_file = openmc.TalliesFile()

View file

@ -0,0 +1,184 @@
import numpy as np
import openmc
import openmc.mgxs
###############################################################################
# Simulation Input File Parameters
###############################################################################
# OpenMC simulation parameters
batches = 100
inactive = 10
particles = 1000
###############################################################################
# Exporting to OpenMC mg_cross_sections.xml File
###############################################################################
# Instantiate the energy group data
groups = openmc.mgxs.EnergyGroups(group_edges=[1E-11, 0.0635E-6, 10.0E-6,
1.0E-4, 1.0E-3, 0.5, 1.0, 20.0])
# Instantiate the 7-group (C5G7) cross section data
uo2_xsdata = openmc.XSdata('UO2.300K', groups)
uo2_xsdata.order = 0
uo2_xsdata.total = np.array([0.1779492, 0.3298048, 0.4803882, 0.5543674,
0.3118013, 0.3951678, 0.5644058])
uo2_xsdata.absorption = np.array([8.0248E-03, 3.7174E-03, 2.6769E-02, 9.6236E-02,
3.0020E-02, 1.1126E-01, 2.8278E-01])
scatter = [[[0.1275370, 0.0423780, 0.0000094, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
[0.0000000, 0.3244560, 0.0016314, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
[0.0000000, 0.0000000, 0.4509400, 0.0026792, 0.0000000, 0.0000000, 0.0000000],
[0.0000000, 0.0000000, 0.0000000, 0.4525650, 0.0055664, 0.0000000, 0.0000000],
[0.0000000, 0.0000000, 0.0000000, 0.0001253, 0.2714010, 0.0102550, 0.0000000],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0012968, 0.2658020, 0.0168090],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0085458, 0.2730800]]]
uo2_xsdata.scatter = np.array(scatter[:][:])
uo2_xsdata.fission = np.array([7.21206E-03, 8.19301E-04, 6.45320E-03,
1.85648E-02, 1.78084E-02, 8.30348E-02,
2.16004E-01])
uo2_xsdata.nu_fission = np.array([2.005998E-02, 2.027303E-03, 1.570599E-02,
4.518301E-02, 4.334208E-02, 2.020901E-01,
5.257105E-01])
uo2_xsdata.chi = np.array([5.8791E-01, 4.1176E-01, 3.3906E-04, 1.1761E-07,
0.0000E+00, 0.0000E+00, 0.0000E+00])
h2o_xsdata = openmc.XSdata('LWTR.300K', groups)
h2o_xsdata.order = 0
h2o_xsdata.total = np.array([0.15920605, 0.412969593, 0.59030986, 0.58435,
0.718, 1.2544497, 2.650379])
h2o_xsdata.absorption = np.array([6.0105E-04, 1.5793E-05, 3.3716E-04,
1.9406E-03, 5.7416E-03, 1.5001E-02,
3.7239E-02])
scatter = [[[0.0444777, 0.1134000, 0.0007235, 0.0000037, 0.0000001, 0.0000000, 0.0000000],
[0.0000000, 0.2823340, 0.1299400, 0.0006234, 0.0000480, 0.0000074, 0.0000010],
[0.0000000, 0.0000000, 0.3452560, 0.2245700, 0.0169990, 0.0026443, 0.0005034],
[0.0000000, 0.0000000, 0.0000000, 0.0910284, 0.4155100, 0.0637320, 0.0121390],
[0.0000000, 0.0000000, 0.0000000, 0.0000714, 0.1391380, 0.5118200, 0.0612290],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0022157, 0.6999130, 0.5373200],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.1324400, 2.4807000]]]
h2o_xsdata.scatter = np.array(scatter)
mg_cross_sections_file = openmc.MGXSLibraryFile(groups)
mg_cross_sections_file.add_xsdatas([uo2_xsdata,h2o_xsdata])
mg_cross_sections_file.export_to_xml()
###############################################################################
# Exporting to OpenMC materials.xml File
###############################################################################
# Instantiate some Macroscopic Data
uo2_data = openmc.Macroscopic('UO2', '300K')
h2o_data = openmc.Macroscopic('LWTR', '300K')
# Instantiate some Materials and register the appropriate Macroscopic objects
uo2 = openmc.Material(material_id=1, name='UO2 fuel')
uo2.set_density('macro', 1.0)
uo2.add_macroscopic(uo2_data)
water = openmc.Material(material_id=2, name='Water')
water.set_density('macro', 1.0)
water.add_macroscopic(h2o_data)
# Instantiate a MaterialsFile, register all Materials, and export to XML
materials_file = openmc.MaterialsFile()
materials_file.default_xs = '300K'
materials_file.add_materials([uo2, water])
materials_file.export_to_xml()
###############################################################################
# Exporting to OpenMC geometry.xml File
###############################################################################
# Instantiate ZCylinder surfaces
fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, R=0.54, name='Fuel OR')
left = openmc.XPlane(surface_id=4, x0=-0.63, name='left')
right = openmc.XPlane(surface_id=5, x0=0.63, name='right')
bottom = openmc.YPlane(surface_id=6, y0=-0.63, name='bottom')
top = openmc.YPlane(surface_id=7, y0=0.63, name='top')
left.boundary_type = 'reflective'
right.boundary_type = 'reflective'
top.boundary_type = 'reflective'
bottom.boundary_type = 'reflective'
# Instantiate Cells
fuel = openmc.Cell(cell_id=1, name='cell 1')
moderator = openmc.Cell(cell_id=2, name='cell 2')
# Use surface half-spaces to define regions
fuel.region = -fuel_or
moderator.region = +fuel_or & +left & -right & +bottom & -top
# Register Materials with Cells
fuel.fill = uo2
moderator.fill = water
# Instantiate Universe
root = openmc.Universe(universe_id=0, name='root universe')
# Register Cells with Universe
root.add_cells([fuel, moderator])
# Instantiate a Geometry and register the root Universe
geometry = openmc.Geometry()
geometry.root_universe = root
# Instantiate a GeometryFile, register Geometry, and export to XML
geometry_file = openmc.GeometryFile()
geometry_file.geometry = geometry
geometry_file.export_to_xml()
###############################################################################
# Exporting to OpenMC settings.xml File
###############################################################################
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
settings_file = openmc.SettingsFile()
settings_file.energy_mode = "multi-group"
settings_file.cross_sections = "./mg_cross_sections.xml"
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
# Create an initial uniform spatial source distribution over fissionable zones
bounds = [-0.63, -0.63, -1, 0.63, 0.63, 1]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:])
settings_file.source = openmc.source.Source(space=uniform_dist)
settings_file.export_to_xml()
###############################################################################
# Exporting to OpenMC tallies.xml File
###############################################################################
# Instantiate a tally mesh
mesh = openmc.Mesh(mesh_id=1)
mesh.type = 'regular'
mesh.dimension = [100, 100, 1]
mesh.lower_left = [-0.63, -0.63, -1.e50]
mesh.upper_right = [0.63, 0.63, 1.e50]
# Instantiate some tally Filters
energy_filter = openmc.Filter(type='energy',
bins=[1E-11, 0.0635E-6, 10.0E-6, 1.0E-4, 1.0E-3,
0.5, 1.0, 20.0])
mesh_filter = openmc.Filter()
mesh_filter.mesh = mesh
# Instantiate the Tally
tally = openmc.Tally(tally_id=1, name='tally 1')
tally.add_filter(energy_filter)
tally.add_filter(mesh_filter)
tally.add_score('flux')
tally.add_score('fission')
tally.add_score('nu-fission')
# Instantiate a TalliesFile, register all Tallies, and export to XML
tallies_file = openmc.TalliesFile()
tallies_file.add_mesh(mesh)
tallies_file.add_tally(tally)
tallies_file.export_to_xml()

View file

@ -1,8 +1,5 @@
import numpy as np
import openmc
from openmc.stats import Box
from openmc.source import Source
###############################################################################
# Simulation Input File Parameters
@ -86,5 +83,10 @@ settings_file = openmc.SettingsFile()
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
settings_file.source = Source(space=Box(*cell.region.bounding_box))
# Create an initial uniform spatial source distribution over fissionable zones
uniform_dist = openmc.stats.Box(*cell.region.bounding_box,
only_fissionable=True)
settings_file.source = openmc.source.Source(space=uniform_dist)
settings_file.export_to_xml()

View file

@ -0,0 +1,16 @@
<geometry>
<surface coeffs="0. 0. 0.540" id="1" type="z-cylinder" />
<surface boundary="reflective" coeffs="-0.63" id="20" type="x-plane" />
<surface boundary="reflective" coeffs=" 0.63" id="21" type="x-plane" />
<surface boundary="reflective" coeffs="-0.63" id="22" type="y-plane" />
<surface boundary="reflective" coeffs=" 0.63" id="23" type="y-plane" />
<cell id="1" material="1" region=" -1" />
<cell id="2" material="7" region="1 20 -21 22 -23" />
</geometry>

View file

@ -0,0 +1,54 @@
<?xml version="1.0"?>
<materials>
<!-- Set default xs set to use 300K data -->
<default_xs>300K</default_xs>
<!-- UO2 -->
<material id="1">
<density units="macro" value="1.0" />
<macroscopic name="UO2"/>
</material>
<!-- 4.3% MOX -->
<material id="2">
<density units="macro" value="1.0" />
<macroscopic name="MOX1"/>
</material>
<!-- 7.0 MOX -->
<material id="3">
<density units="macro" value="1.0" />
<macroscopic name="MOX2"/>
</material>
<!-- 8.0% MOX -->
<material id="4">
<density units="macro" value="1.0" />
<macroscopic name="MOX3"/>
</material>
<!-- Fission Chamber -->
<material id="5">
<density units="macro" value="1.0" />
<macroscopic name="FC"/>
</material>
<!-- Guide Tube -->
<material id="6">
<density units="macro" value="1.0" />
<macroscopic name="GT"/>
</material>
<!-- Water -->
<material id="7">
<density units="macro" value="1.0" />
<macroscopic name="LWTR"/>
</material>
<!-- Control Rod -->
<material id="8">
<density units="macro" value="1.0" />
<macroscopic name="CR"/>
</material>
</materials>

View file

@ -0,0 +1,383 @@
<?xml version="1.0"?>
<library>
<!-- Before getting to the data, set common information -->
<groups> 7 </groups>
<group_structure>
1E-11 0.0635E-6 10.0E-6 1.0E-4 1.0E-3 0.5 1.0 20.0
</group_structure>
<!--
Move on to the data. Each <xsdata> has a unique id and label.
-->
<xsdata>
<!-- Meta data for this data -->
<name>UO2.300K</name>
<alias>UO2.300K</alias>
<kT> 2.53E-8 </kT> <!-- in MeV -->
<order>0</order>
<fissionable>true</fissionable>
<!-- Optional (default is isotropic) -->
<representation>isotropic</representation>
<!-- The data itself, like tallies,
goes from low energies (groups) to high energies
-->
<absorption>
8.0248E-03 3.7174E-03 2.6769E-02 9.6236E-02 3.0020E-02 1.1126E-01 2.8278E-01
</absorption>
<nu_fission>
2.005998E-02 2.027303E-03 1.570599E-02 4.518301E-02 4.334208E-02 2.020901E-01 5.257105E-01
</nu_fission>
<chi>
5.8791E-01 4.1176E-01 3.3906E-04 1.1761E-07 0.0000E+00 0.0000E+00 0.0000E+00
</chi>
<fission>
7.21206E-03 8.19301E-04 6.45320E-03 1.85648E-02 1.78084E-02 8.30348E-02 2.16004E-01
</fission>
<!-- units of MeV/cm -->
<!-- If no kappa fission tallies, this is not needed; it will not be loaded
if there is no kappa fission scores anyways -->
<k_fission>
1.0 1.0 1.0 1.0 1.0 1.0 1.0
</k_fission>
<!-- for consistency must include nu-scatter -->
<!-- will be a matrix of (order+1) x g_in x g_out -->
<scatter>
0.1275370 0.0423780 0.0000094 0.0000000 0.0000000 0.0000000 0.0000000
0.0000000 0.3244560 0.0016314 0.0000000 0.0000000 0.0000000 0.0000000
0.0000000 0.0000000 0.4509400 0.0026792 0.0000000 0.0000000 0.0000000
0.0000000 0.0000000 0.0000000 0.4525650 0.0055664 0.0000000 0.0000000
0.0000000 0.0000000 0.0000000 0.0001253 0.2714010 0.0102550 0.0000000
0.0000000 0.0000000 0.0000000 0.0000000 0.0012968 0.2658020 0.0168090
0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0085458 0.2730800
</scatter>
<!-- If total is not provided, it will be calculated.
However, in the C5G7 problems, we want to use a transport-corrected value
so we dont want to have it be calculated -->
<total>
0.1779492 0.3298048 0.4803882 0.5543674000000001 0.3118013 0.39516779999999996 0.5644058
</total>
</xsdata>
<xsdata>
<!-- Meta data for this data -->
<name>MOX1.300K</name>
<alias>MOX1.300K</alias>
<kT> 2.53E-8 </kT> <!-- in MeV -->
<order>0</order>
<fissionable>true</fissionable>
<!-- The data itself, like tallies,
goes from low energies (groups) to high energies
-->
<absorption>
8.4339E-03 3.7577E-03 2.7970E-02 1.0421E-01 1.3994E-01 4.0918E-01 4.0935E-01
</absorption>
<!--
Since chi_vector is false, this will be a matrix
Matrix is g_in, g_out.
This is to show that you can either use a chi vector + nu_fission vector,
like in the UO2 data, or a nu_fission matrix like here.
-->
<nu_fission>
1.27888062E-02 8.95701528E-03 7.37557218E-06 2.55837033E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
1.49041240E-03 1.04385401E-03 8.59552023E-07 2.98153464E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
9.56411400E-03 6.69850756E-03 5.51582469E-06 1.91327830E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
3.84928781E-02 2.69596154E-02 2.21996483E-05 7.70040890E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
1.80629998E-02 1.26509513E-02 1.04173100E-05 3.61346022E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
3.91930789E-01 2.74500216E-01 2.26034688E-04 7.84048241E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
4.19762096E-01 2.93992687E-01 2.42085585E-04 8.39724109E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
</nu_fission>
<fission>
7.62704E-03 8.76898E-04 5.69835E-03 2.28872E-02 1.07635E-02 2.32757E-01 2.48968E-01
</fission>
<!-- units of MeV/cm -->
<k_fission>
1.0 1.0 1.0 1.0 1.0 1.0 1.0
</k_fission>
<!-- for consistency must include nu-scatter -->
<!-- will be a matrix of (order+1) x g_in x g_out -->
<scatter>
1.27537000E-01 4.23780000E-02 9.43740000E-06 5.51630000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 3.24456000E-01 1.63140000E-03 3.14270000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 4.50940000E-01 2.67920000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 4.52565000E-01 5.56640000E-03 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 1.25250000E-04 2.71401000E-01 1.02550000E-02 1.00210000E-08
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 1.29680000E-03 2.65802000E-01 1.68090000E-02
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 8.54580000E-03 2.73080000E-01
</scatter>
<total>
0.1783583429163 0.3298451031427 0.4815892 0.5623414 0.421721260021 0.6930878 0.6909757999999999
</total>
</xsdata>
<xsdata>
<!-- Meta data for this data -->
<name>MOX2.300K</name>
<alias>MOX2.300K</alias>
<kT> 2.53E-8 </kT> <!-- in MeV -->
<order>0</order>
<fissionable>true</fissionable>
<!-- The data itself, like tallies,
goes from low energies (groups) to high energies
-->
<absorption>
0.0090657 0.0042967 0.032881 0.12203 0.18298 0.56846 0.58521
</absorption>
<!--
Since chi_vector is false, this will be a matrix
Matrix is g_in, g_out !!! Need to get these values looking right to match
output of a nu-fission tally with <energy> filter above <energyout> filter
-->
<nu_fission>
1.40004593E-02 9.80563205E-03 8.07435789E-06 2.80075866E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
2.26856185E-03 1.58885378E-03 1.30832709E-06 4.53820413E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
1.41886199E-02 9.93741584E-03 8.18287404E-06 2.83839974E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
5.54788444E-02 3.88562347E-02 3.19958106E-05 1.10984111E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
2.69085702E-02 1.88462058E-02 1.55187355E-05 5.38299559E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
5.45687127E-01 3.82187973E-01 3.14709185E-04 1.09163414E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
6.13307712E-01 4.29548032E-01 3.53707392E-04 1.22690752E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
</nu_fission>
<fission>
0.00825446 0.00132565 0.00842156 0.032873 0.0159636 0.323794 0.362803
</fission>
<!-- units of MeV/cm -->
<k_fission>
1.0 1.0 1.0 1.0 1.0 1.0 1.0
</k_fission>
<!-- for consistency must include nu-scatter -->
<!-- will be a matrix of (order+1) x g_in x g_out -->
<scatter>
1.30457000E-01 4.17920000E-02 8.51050000E-06 5.13290000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 3.28428000E-01 1.64360000E-03 2.20170000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 4.58371000E-01 2.53310000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 4.63709000E-01 5.47660000E-03 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 1.76190000E-04 2.82313000E-01 8.72890000E-03 9.00160000E-09
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 2.27600000E-03 2.49751000E-01 1.31140000E-02
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 8.86450000E-03 2.59529000E-01
</scatter>
<total>
0.1813232156329 0.3343683022017 0.4937851 0.5912156 0.47419809900160004 0.833601 0.8536035
</total>
</xsdata>
<xsdata>
<!-- Meta data for this data -->
<name>MOX3.300K</name>
<alias>MOX3.300K</alias>
<kT> 2.53E-8 </kT> <!-- in MeV -->
<order>0</order>
<fissionable>true</fissionable>
<!-- The data itself, like tallies,
goes from low energies (groups) to high energies
-->
<absorption>
9.48620000E-03 4.65560000E-03 3.62400000E-02 1.32720000E-01 2.08400000E-01 6.58700000E-01 6.90170000E-01
</absorption>
<!--
Since chi_vector is false, this will be a matrix
Matrix is g_in, g_out !!! Need to get these values looking right to match
output of a nu-fission tally with <energy> filter above <energyout> filter
-->
<nu_fission>
1.48071013E-02 1.03705874E-02 8.53956516E-06 2.96212546E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
2.78640474E-03 1.95154023E-03 1.60697792E-06 5.57413653E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
1.73304404E-02 1.21378819E-02 9.99482763E-06 3.46691346E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
6.59928975E-02 4.62200600E-02 3.80594850E-05 1.32017225E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
3.25131926E-02 2.27715674E-02 1.87510386E-05 6.50418701E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
6.32002662E-01 4.42641588E-01 3.64489161E-04 1.26430632E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
7.28595687E-01 5.10293344E-01 4.20196380E-04 1.45753838E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
</nu_fission>
<fission>
8.67209000E-03 1.62426000E-03 1.02716000E-02 3.90447000E-02 1.92576000E-02 3.74888000E-01 4.30599000E-01
</fission>
<!-- units of MeV/cm -->
<k_fission>
1.0 1.0 1.0 1.0 1.0 1.0 1.0
</k_fission>
<!-- for consistency must include nu-scatter -->
<!-- will be a matrix of (order+1) x g_in x g_out -->
<scatter>
1.31504000E-01 4.20460000E-02 8.69720000E-06 5.19380000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 3.30403000E-01 1.64630000E-03 2.60060000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 4.61792000E-01 2.47490000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 4.68021000E-01 5.43300000E-03 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 1.85970000E-04 2.85771000E-01 8.39730000E-03 8.92800000E-09
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 2.39160000E-03 2.47614000E-01 1.23220000E-02
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 8.96810000E-03 2.56093000E-01
</scatter>
<total>
1.83044902E-01 3.36704903E-01 5.00506900E-01 6.06174000E-01 5.02754279E-01 9.21027600E-01 9.55231100E-01
</total>
</xsdata>
<xsdata>
<!-- Meta data for this data -->
<name>FC.300K</name>
<alias>FC.300K</alias>
<kT> 2.53E-8 </kT> <!-- in MeV -->
<order>0</order>
<fissionable>true</fissionable>
<!-- The data itself, like tallies,
goes from low energies (groups) to high energies
-->
<absorption>
5.1132E-04 7.5813E-05 3.1643E-04 1.1675E-03 3.3977E-03 9.1886E-03 2.3244E-02
</absorption>
<nu_fission>
1.323401E-08 1.434500E-08 1.128599E-06 1.276299E-05 3.538502E-07 1.740099E-06 5.063302E-06
</nu_fission>
<chi>
5.8791E-01 4.1176E-01 3.3906E-04 1.1761E-07 0.0000E+00 0.0000E+00 0.0000E+00
</chi>
<fission>
4.79002E-09 5.82564E-09 4.63719E-07 5.24406E-06 1.45390E-07 7.14972E-07 2.08041E-06
</fission>
<!-- units of MeV/cm -->
<k_fission>
1.0 1.0 1.0 1.0 1.0 1.0 1.0
</k_fission>
<!-- for consistency must include nu-scatter -->
<!-- will be a matrix of (order+1) x g_in x g_out -->
<scatter>
6.61659000E-02 5.90700000E-02 2.83340000E-04 1.46220000E-06 2.06420000E-08 0.00000000E00 0.00000000E00
0.00000000E00 2.40377000E-01 5.24350000E-02 2.49900000E-04 1.92390000E-05 2.98750000E-06 4.21400000E-07
0.00000000E00 0.00000000E00 1.83425000E-01 9.22880000E-02 6.93650000E-03 1.07900000E-03 2.05430000E-04
0.00000000E00 0.00000000E00 0.00000000E00 7.90769000E-02 1.69990000E-01 2.58600000E-02 4.92560000E-03
0.00000000E00 0.00000000E00 0.00000000E00 3.73400000E-05 9.97570000E-02 2.06790000E-01 2.44780000E-02
0.00000000E00 0.00000000E00 0.00000000E00 0.00000000E00 9.17420000E-04 3.16774000E-01 2.38760000E-01
0.00000000E00 0.00000000E00 0.00000000E00 0.00000000E00 0.00000000E00 4.97930000E-02 1.09910000E00
</scatter>
<total>
1.26032048E-01 2.93160367E-01 2.84250824E-01 2.81025244E-01 3.34460185E-01 5.65640735E-01 1.17213908E00
</total>
</xsdata>
<xsdata>
<!-- Meta data for this data -->
<name>GT.300K</name>
<alias>GT.300K</alias>
<kT> 2.53E-8 </kT> <!-- in MeV -->
<order>0</order>
<fissionable>false</fissionable>
<!-- The data itself, like tallies,
goes from low energies (groups) to high energies
-->
<absorption>
5.11320000E-04 7.58010000E-05 3.15720000E-04 1.15820000E-03 3.39750000E-03 9.18780000E-03 2.32420000E-02
</absorption>
<!-- for consistency must include nu-scatter -->
<!-- will be a matrix of (order+1) x g_in x g_out -->
<scatter>
6.61659000E-02 5.90700000E-02 2.83340000E-04 1.46220000E-06 2.06420000E-08 0.00000000E+00 0.00000000E+00
0.00000000E+00 2.40377000E-01 5.24350000E-02 2.49900000E-04 1.92390000E-05 2.98750000E-06 4.21400000E-07
0.00000000E+00 0.00000000E+00 1.83297000E-01 9.23970000E-02 6.94460000E-03 1.08030000E-03 2.05670000E-04
0.00000000E+00 0.00000000E+00 0.00000000E+00 7.88511000E-02 1.70140000E-01 2.58810000E-02 4.92970000E-03
0.00000000E+00 0.00000000E+00 0.00000000E+00 3.73330000E-05 9.97372000E-02 2.06790000E-01 2.44780000E-02
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 9.17260000E-04 3.16765000E-01 2.38770000E-01
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 4.97920000E-02 1.09912000E+00
</scatter>
<total>
1.26032043E-01 2.93160349E-01 2.84240290E-01 2.80960000E-01 3.34440033E-01 5.65640060E-01 1.17215400E+00
</total>
</xsdata>
<xsdata>
<!-- Meta data for this data -->
<name>LWTR.300K</name>
<alias>LWTR.300K</alias>
<kT> 2.53E-8 </kT> <!-- in MeV -->
<order>0</order>
<fissionable>false</fissionable>
<!-- The data itself, like tallies,
goes from low energies (groups) to high energies
-->
<absorption>
6.0105E-04 1.5793E-05 3.3716E-04 1.9406E-03 5.7416E-03 1.5001E-02 3.7239E-02
</absorption>
<!-- for consistency must include nu-scatter -->
<!-- will be a matrix of (order+1) x g_in x g_out -->
<scatter>
0.0444777 0.1134000 0.0007235 0.0000037 0.0000001 0.0000000 0.0000000
0.0000000 0.2823340 0.1299400 0.0006234 0.0000480 0.0000074 0.0000010
0.0000000 0.0000000 0.3452560 0.2245700 0.0169990 0.0026443 0.0005034
0.0000000 0.0000000 0.0000000 0.0910284 0.4155100 0.0637320 0.0121390
0.0000000 0.0000000 0.0000000 0.0000714 0.1391380 0.5118200 0.0612290
0.0000000 0.0000000 0.0000000 0.0000000 0.0022157 0.6999130 0.5373200
0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.1324400 2.4807000
</scatter>
<total>
0.15920605 0.41296959299999997 0.59030986 0.5843499999999999 0.7180000000000001 1.2544497000000001 2.650379
</total>
</xsdata>
<xsdata>
<!-- Meta data for this data -->
<name>CR.300K</name>
<alias>CR.300K</alias>
<kT> 2.53E-8 </kT> <!-- in MeV -->
<order>0</order>
<fissionable>false</fissionable>
<!-- The data itself, like tallies,
goes from low energies (groups) to high energies
-->
<absorption>
1.70490000E-03 8.36224000E-03 8.37901000E-02 3.97797000E-01 6.98763000E-01 9.29508000E-01 1.17836000E+00
</absorption>
<!-- for consistency must include nu-scatter -->
<!-- will be a matrix of (order+1) x g_in x g_out -->
<scatter>
1.70563000E-01 4.44012000E-02 9.83670000E-05 1.27786000E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 4.71050000E-01 6.85480000E-04 3.91395000E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 8.01859000E-01 7.20132000E-04 0.00000000E+00 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 5.70752000E-01 1.46015000E-03 0.00000000E+00 0.00000000E+00
0.00000000E+00 0.00000000E+00 0.00000000E+00 6.55562000E-05 2.07838000E-01 3.81486000E-03 3.69760000E-09
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 1.02427000E-03 2.02465000E-01 4.75290000E-03
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 3.53043000E-03 6.58597000E-01
</scatter>
<total>
2.16767595E-01 4.80097720E-01 8.86369232E-01 9.70009150E-01 9.10481420E-01 1.13775017E+00 1.84048743E+00
</total>
</xsdata>
</library>

View file

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<plots>
<plot>
<id>1</id>
<filename>mat</filename>
<color>material</color>
<origin>0 0 0</origin>
<width>1.26 1.26</width>
<type>slice</type>
<pixels>1000 1000 </pixels>
<col_spec id="1" rgb="255 0 0" />
<col_spec id="2" rgb="0 0 0" />
<col_spec id="3" rgb="0 255 0" />
<col_spec id="4" rgb="0 0 255" />
</plot>
<plot>
<id>2</id>
<filename>cell</filename>
<color>cell</color>
<origin>0 0 0</origin>
<width>1.26 1.26</width>
<type>slice</type>
<pixels>1000 1000 </pixels>
</plot>
</plots>

View file

@ -0,0 +1,40 @@
<?xml version="1.0"?>
<settings>
<energy_mode>multi-group</energy_mode>
<!--
Define how many particles to run and for how many batches
in an eigenvalue calculation mode
-->
<eigenvalue>
<batches>100</batches>
<inactive>10</inactive>
<particles>1000</particles>
</eigenvalue>
<!--
Start with uniformally distributed neutron source
with the default energy spectrum of a Maxwellian
and isotropic distribution.
-->
<source>
<space type="box">
<parameters>
-0.63 -0.63 -1E50
0.63 0.63 1E50
</parameters>
</space>
</source>
<output>
<cross_sections>true</cross_sections>
<summary>true</summary>
<tallies>true</tallies>
</output>
<survival_biasing>false</survival_biasing>
<cross_sections>./mg_cross_sections.xml</cross_sections>
</settings>

View file

@ -0,0 +1,13 @@
<?xml version="1.0"?>
<tallies>
<mesh id="1" type="regular">
<dimension>100 100 1</dimension>
<lower_left>-0.63 -0.63 -1e+50</lower_left>
<upper_right>0.63 0.63 1e+50</upper_right>
</mesh>
<tally id="1" name="tally 1">
<filter bins="1e-11 6.35e-08 1e-05 0.0001 0.001 0.5 1.0 20.0" type="energy" />
<filter bins="1" type="mesh" />
<scores>flux fission nu-fission</scores>
</tally>
</tallies>

View file

@ -1,11 +1,15 @@
from openmc.cell import *
from openmc.lattice import *
from openmc.element import *
from openmc.geometry import *
from openmc.nuclide import *
from openmc.macroscopic import *
from openmc.material import *
from openmc.plots import *
from openmc.settings import *
from openmc.surface import *
from openmc.universe import *
from openmc.mgxs_library import *
from openmc.mesh import *
from openmc.filter import *
from openmc.trigger import *
@ -15,6 +19,9 @@ from openmc.cmfd import *
from openmc.executor import *
from openmc.statepoint import *
from openmc.summary import *
from openmc.region import *
from openmc.source import *
from openmc.particle_restart import *
try:
from openmc.opencg_compatible import *

View file

@ -1,5 +1,7 @@
import sys
import copy
from numbers import Integral
from collections import Iterable
import numpy as np
@ -14,7 +16,7 @@ if sys.version_info[0] >= 3:
_TALLY_ARITHMETIC_OPS = ['+', '-', '*', '/', '^']
# Acceptable tally aggregation operations
_TALLY_AGGREGATE_OPS = ['sum', 'mean']
_TALLY_AGGREGATE_OPS = ['sum', 'avg']
class CrossScore(object):
@ -186,6 +188,23 @@ class CrossNuclide(object):
return existing
def __repr__(self):
return self.name
@property
def left_nuclide(self):
return self._left_nuclide
@property
def right_nuclide(self):
return self._right_nuclide
@property
def binary_op(self):
return self._binary_op
@property
def name(self):
string = ''
@ -207,18 +226,6 @@ class CrossNuclide(object):
return string
@property
def left_nuclide(self):
return self._left_nuclide
@property
def right_nuclide(self):
return self._right_nuclide
@property
def binary_op(self):
return self._binary_op
@left_nuclide.setter
def left_nuclide(self, left_nuclide):
cv.check_type('left_nuclide', left_nuclide,
@ -430,7 +437,7 @@ class CrossFilter(object):
filter_index = left_index * self.right_filter.num_bins + right_index
return filter_index
def get_pandas_dataframe(self, datasize, summary=None):
def get_pandas_dataframe(self, data_size, summary=None):
"""Builds a Pandas DataFrame for the CrossFilter's bins.
This method constructs a Pandas DataFrame object for the CrossFilter
@ -445,7 +452,7 @@ class CrossFilter(object):
Parameters
----------
datasize : Integral
data_size : 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
@ -472,19 +479,18 @@ class CrossFilter(object):
# If left and right filters are identical, do not combine bins
if self.left_filter == self.right_filter:
df = self.left_filter.get_pandas_dataframe(datasize, summary)
df = self.left_filter.get_pandas_dataframe(data_size, summary)
# If left and right filters are different, combine their bins
else:
left_df = self.left_filter.get_pandas_dataframe(datasize, summary)
right_df = self.right_filter.get_pandas_dataframe(datasize, summary)
left_df = self.left_filter.get_pandas_dataframe(data_size, summary)
right_df = self.right_filter.get_pandas_dataframe(data_size, summary)
left_df = left_df.astype(str)
right_df = right_df.astype(str)
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.
@ -494,7 +500,7 @@ class AggregateScore(object):
scores : Iterable of str or CrossScore
The scores included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's scores with this AggregateScore
Attributes
@ -502,7 +508,7 @@ class AggregateScore(object):
scores : Iterable of str or CrossScore
The scores included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's scores with this AggregateScore
"""
@ -556,10 +562,16 @@ class AggregateScore(object):
def aggregate_op(self):
return self._aggregate_op
@property
def name(self):
# Append each score in the aggregate to the string
string = '(' + ', '.join(self.scores) + ')'
return string
@scores.setter
def scores(self, scores):
cv.check_iterable_type('scores', scores,
(basestring, CrossScore, AggregateScore))
cv.check_iterable_type('scores', scores, basestring)
self._scores = scores
@aggregate_op.setter
@ -578,7 +590,7 @@ class AggregateNuclide(object):
nuclides : Iterable of str or Nuclide or CrossNuclide
The nuclides included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's nuclides with this AggregateNuclide
Attributes
@ -586,7 +598,7 @@ class AggregateNuclide(object):
nuclides : Iterable of str or Nuclide or CrossNuclide
The nuclides included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally's nuclides with this AggregateNuclide
"""
@ -644,10 +656,19 @@ class AggregateNuclide(object):
def aggregate_op(self):
return self._aggregate_op
@property
def name(self):
# Append each nuclide in the aggregate to the string
names = [nuclide.name if isinstance(nuclide, Nuclide) else str(nuclide)
for nuclide in self.nuclides]
string = '(' + ', '.join(map(str, names)) + ')'
return string
@nuclides.setter
def nuclides(self, nuclides):
cv.check_iterable_type('nuclides', nuclides,
(basestring, Nuclide, CrossNuclide, AggregateNuclide))
(basestring, Nuclide, CrossNuclide))
self._nuclides = nuclides
@aggregate_op.setter
@ -668,7 +689,7 @@ class AggregateFilter(object):
bins : Iterable of tuple
The filter bins included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
to aggregate across a tally filter's bins with this AggregateFilter
Attributes
@ -678,7 +699,7 @@ class AggregateFilter(object):
aggregate_filter : filter
The filter included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
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
@ -715,6 +736,21 @@ class AggregateFilter(object):
def __ne__(self, other):
return not self == other
def __gt__(self, other):
if self.type != other.type:
if self.aggregate_filter.type in _FILTER_TYPES and \
other.aggregate_filter.type in _FILTER_TYPES:
delta = _FILTER_TYPES.index(self.aggregate_filter.type) - \
_FILTER_TYPES.index(other.aggregate_filter.type)
return delta > 0
else:
return False
else:
return False
def __lt__(self, other):
return not self > other
def __repr__(self):
string = 'AggregateFilter\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.type)
@ -759,7 +795,7 @@ class AggregateFilter(object):
@property
def num_bins(self):
return 1 if self.aggregate_filter else 0
return len(self.bins) if self.aggregate_filter else 0
@property
def stride(self):
@ -776,14 +812,13 @@ class AggregateFilter(object):
@aggregate_filter.setter
def aggregate_filter(self, aggregate_filter):
cv.check_type('aggregate_filter', aggregate_filter,
(Filter, CrossFilter, AggregateFilter))
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
cv.check_iterable_type('bins', bins, Iterable)
self._bins = list(map(tuple, bins))
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
@ -823,15 +858,14 @@ class AggregateFilter(object):
"""
if filter_bin not in self.bins and \
filter_bin != self._aggregate_filter.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
return self.bins.index(filter_bin)
def get_pandas_dataframe(self, datasize, summary=None):
def get_pandas_dataframe(self, data_size, summary=None):
"""Builds a Pandas DataFrame for the AggregateFilter's bins.
This method constructs a Pandas DataFrame object for the AggregateFilter
@ -840,7 +874,7 @@ class AggregateFilter(object):
Parameters
----------
datasize : Integral
data_size : 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
@ -868,14 +902,80 @@ class AggregateFilter(object):
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)) + ')'
# Create NumPy array of the bin tuples for repeating / tiling
filter_bins = np.empty(self.num_bins, dtype=tuple)
for i, bin in enumerate(self.bins):
filter_bins[i] = bin
# 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)
# Repeat and tile bins as needed for DataFrame
filter_bins = np.repeat(filter_bins, self.stride)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
# Construct Pandas DataFrame for the AggregateFilter
df = pd.DataFrame({self.type: aggregate_bin_array})
# Create DataFrame with aggregated bins
df = pd.DataFrame({self.type: filter_bins})
return df
def can_merge(self, other):
"""Determine if AggregateFilter can be merged with another.
Parameters
----------
other : AggregateFilter
Filter to compare with
Returns
-------
bool
Whether the filter can be merged
"""
if not isinstance(other, AggregateFilter):
return False
# Filters must be of the same type
elif self.type != other.type:
return False
# None of the bins in this filter should match in the other filter
for bin in self.bins:
if bin in other.bins:
return False
# If all conditional checks passed then filters are mergeable
return True
def merge(self, other):
"""Merge this aggregatefilter with another.
Parameters
----------
other : AggregateFilter
Filter to merge with
Returns
-------
merged_filter : AggregateFilter
Filter resulting from the merge
"""
if not self.can_merge(other):
msg = 'Unable to merge "{0}" with "{1}" ' \
'filters'.format(self.type, other.type)
raise ValueError(msg)
# Create deep copy of filter to return as merged filter
merged_filter = copy.deepcopy(self)
# Merge unique filter bins
merged_bins = self.bins + other.bins
# Sort energy bin edges
if 'energy' in self.type:
merged_bins = sorted(merged_bins)
# Assign merged bins to merged filter
merged_filter.bins = list(merged_bins)
return merged_filter

478
openmc/cell.py Normal file
View file

@ -0,0 +1,478 @@
from collections import OrderedDict, Iterable
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
import warnings
import openmc
import openmc.checkvalue as cv
from openmc.surface import Halfspace
from openmc.region import Region, Intersection, Complement
if sys.version_info[0] >= 3:
basestring = str
# A static variable for auto-generated Cell IDs
AUTO_CELL_ID = 10000
def reset_auto_cell_id():
global AUTO_CELL_ID
AUTO_CELL_ID = 10000
class Cell(object):
"""A region of space defined as the intersection of half-space created by
quadric surfaces.
Parameters
----------
cell_id : int, optional
Unique identifier for the cell. If not specified, an identifier will
automatically be assigned.
name : str, optional
Name of the cell. If not specified, the name is the empty string.
Attributes
----------
id : int
Unique identifier for the cell
name : str
Name of the cell
fill : openmc.Material or openmc.Universe or openmc.Lattice or 'void' or iterable of openmc.Material
Indicates what the region of space is filled with
region : openmc.Region
Region of space that is assigned to the cell.
rotation : numpy.ndarray
If the cell is filled with a universe, this array specifies the angles
in degrees about the x, y, and z axes that the filled universe should be
rotated.
temperature : float or iterable of float
Temperature of the cell in Kelvin. Multiple temperatures can be given
to give each distributed cell instance a unique temperature.
translation : numpy.ndarray
If the cell is filled with a universe, this array specifies a vector
that is used to translate (shift) the universe.
offsets : ndarray
Array of offsets used for distributed cell searches
distribcell_index : int
Index of this cell in distribcell arrays
"""
def __init__(self, cell_id=None, name=''):
# Initialize Cell class attributes
self.id = cell_id
self.name = name
self._fill = None
self._type = None
self._region = None
self._temperature = None
self._rotation = None
self._translation = None
self._offsets = None
self._distribcell_index = None
def __eq__(self, other):
if not isinstance(other, Cell):
return False
elif self.id != other.id:
return False
elif self.name != other.name:
return False
elif self.fill != other.fill:
return False
elif self.region != other.region:
return False
elif self.rotation != other.rotation:
return False
elif self.temperature != other.temperature:
return False
elif self.translation != other.translation:
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(repr(self))
def __repr__(self):
string = 'Cell\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
if isinstance(self._fill, openmc.Material):
string += '{0: <16}{1}{2}\n'.format('\tMaterial', '=\t',
self._fill._id)
elif isinstance(self._fill, Iterable):
string += '{0: <16}{1}'.format('\tMaterial', '=\t')
string += '['
string += ', '.join(['void' if m == 'void' else str(m.id)
for m in self.fill])
string += ']\n'
elif isinstance(self._fill, (openmc.Universe, openmc.Lattice)):
string += '{0: <16}{1}{2}\n'.format('\tFill', '=\t',
self._fill._id)
else:
string += '{0: <16}{1}{2}\n'.format('\tFill', '=\t', self._fill)
string += '{0: <16}{1}{2}\n'.format('\tRegion', '=\t', self._region)
string += '{0: <16}{1}{2}\n'.format('\tRotation', '=\t',
self._rotation)
if self.fill_type == 'material':
string += '\t{0: <15}=\t{1}\n'.format('Temperature',
self.temperature)
string += '{0: <16}{1}{2}\n'.format('\tTranslation', '=\t',
self._translation)
string += '{0: <16}{1}{2}\n'.format('\tOffset', '=\t', self._offsets)
string += '{0: <16}{1}{2}\n'.format('\tDistribcell index', '=\t',
self._distribcell_index)
return string
@property
def id(self):
return self._id
@property
def name(self):
return self._name
@property
def fill(self):
return self._fill
@property
def fill_type(self):
if isinstance(self.fill, openmc.Material):
return 'material'
elif isinstance(self.fill, openmc.Universe):
return 'universe'
elif isinstance(self.fill, openmc.Lattice):
return 'lattice'
else:
return None
@property
def region(self):
return self._region
@property
def rotation(self):
return self._rotation
@property
def temperature(self):
return self._temperature
@property
def translation(self):
return self._translation
@property
def offsets(self):
return self._offsets
@property
def distribcell_index(self):
return self._distribcell_index
@id.setter
def id(self, cell_id):
if cell_id is None:
global AUTO_CELL_ID
self._id = AUTO_CELL_ID
AUTO_CELL_ID += 1
else:
cv.check_type('cell ID', cell_id, Integral)
cv.check_greater_than('cell ID', cell_id, 0, equality=True)
self._id = cell_id
@name.setter
def name(self, name):
if name is not None:
cv.check_type('cell name', name, basestring)
self._name = name
else:
self._name = ''
@fill.setter
def fill(self, fill):
if isinstance(fill, basestring):
if fill.strip().lower() == 'void':
self._type = 'void'
else:
msg = 'Unable to set Cell ID="{0}" to use a non-Material or ' \
'Universe fill "{1}"'.format(self._id, fill)
raise ValueError(msg)
elif isinstance(fill, openmc.Material):
self._type = 'normal'
elif isinstance(fill, Iterable):
cv.check_type('cell.fill', fill, Iterable,
(openmc.Material, basestring))
self._type = 'normal'
elif isinstance(fill, openmc.Universe):
self._type = 'fill'
elif isinstance(fill, openmc.Lattice):
self._type = 'lattice'
else:
msg = 'Unable to set Cell ID="{0}" to use a non-Material or ' \
'Universe fill "{1}"'.format(self._id, fill)
raise ValueError(msg)
self._fill = fill
@rotation.setter
def rotation(self, rotation):
cv.check_type('cell rotation', rotation, Iterable, Real)
cv.check_length('cell rotation', rotation, 3)
self._rotation = rotation
@translation.setter
def translation(self, translation):
cv.check_type('cell translation', translation, Iterable, Real)
cv.check_length('cell translation', translation, 3)
self._translation = translation
@temperature.setter
def temperature(self, temperature):
cv.check_type('cell temperature', temperature, (Iterable, Real))
if isinstance(temperature, Iterable):
cv.check_type('cell temperature', temperature, Iterable, Real)
for T in temperature:
cv.check_greater_than('cell temperature', T, 0.0, True)
else:
cv.check_greater_than('cell temperature', temperature, 0.0, True)
self._temperature = temperature
@offsets.setter
def offsets(self, offsets):
cv.check_type('cell offsets', offsets, Iterable)
self._offsets = offsets
@region.setter
def region(self, region):
cv.check_type('cell region', region, Region)
self._region = region
@distribcell_index.setter
def distribcell_index(self, ind):
cv.check_type('distribcell index', ind, Integral)
self._distribcell_index = ind
def add_surface(self, surface, halfspace):
"""Add a half-space to the list of half-spaces whose intersection defines the
cell.
.. deprecated:: 0.7.1
Use the :attr:`Cell.region` property to directly specify a Region
expression.
Parameters
----------
surface : openmc.Surface
Quadric surface dividing space
halfspace : {-1, 1}
Indicate whether the negative or positive half-space is to be used
"""
warnings.warn("Cell.add_surface(...) has been deprecated and may be "
"removed in a future version. The region for a Cell "
"should be defined using the region property directly.",
DeprecationWarning)
if not isinstance(surface, openmc.Surface):
msg = 'Unable to add Surface "{0}" to Cell ID="{1}" since it is ' \
'not a Surface object'.format(surface, self._id)
raise ValueError(msg)
if halfspace not in [-1, +1]:
msg = 'Unable to add Surface "{0}" to Cell ID="{1}" with halfspace ' \
'"{2}" since it is not +/-1'.format(surface, self._id, halfspace)
raise ValueError(msg)
# If no region has been assigned, simply use the half-space. Otherwise,
# take the intersection of the current region and the half-space
# specified
region = +surface if halfspace == 1 else -surface
if self.region is None:
self.region = region
else:
if isinstance(self.region, Intersection):
self.region.nodes.append(region)
else:
self.region = Intersection(self.region, region)
def get_cell_instance(self, path, distribcell_index):
# If the Cell is filled by a Material
if self._type == 'normal' or self._type == 'void':
offset = 0
# If the Cell is filled by a Universe
elif self._type == 'fill':
offset = self.offsets[distribcell_index-1]
offset += self.fill.get_cell_instance(path, distribcell_index)
# If the Cell is filled by a Lattice
else:
offset = self.fill.get_cell_instance(path, distribcell_index)
return offset
def get_all_nuclides(self):
"""Return all nuclides contained in the cell
Returns
-------
nuclides : dict
Dictionary whose keys are nuclide names and values are 2-tuples of
(nuclide, density)
"""
nuclides = OrderedDict()
if self._type != 'void':
nuclides.update(self._fill.get_all_nuclides())
return nuclides
def get_all_cells(self):
"""Return all cells that are contained within this one if it is filled with a
universe or lattice
Returns
-------
cells : dict
Dictionary whose keys are cell IDs and values are :class:`Cell`
instances
"""
cells = OrderedDict()
if self._type == 'fill' or self._type == 'lattice':
cells.update(self._fill.get_all_cells())
return cells
def get_all_materials(self):
"""Return all materials that are contained within the cell
Returns
-------
materials : dict
Dictionary whose keys are material IDs and values are
:class:`Material` instances
"""
materials = OrderedDict()
if self.fill_type == 'material':
materials[self.fill.id] = self.fill
# Append all Cells in each Cell in the Universe to the dictionary
cells = self.get_all_cells()
for cell_id, cell in cells.items():
materials.update(cell.get_all_materials())
return materials
def get_all_universes(self):
"""Return all universes that are contained within this one if any of
its cells are filled with a universe or lattice.
Returns
-------
universes : dict
Dictionary whose keys are universe IDs and values are
:class:`Universe` instances
"""
universes = OrderedDict()
if self._type == 'fill':
universes[self._fill._id] = self._fill
universes.update(self._fill.get_all_universes())
elif self._type == 'lattice':
universes.update(self._fill.get_all_universes())
return universes
def create_xml_subelement(self, xml_element):
element = ET.Element("cell")
element.set("id", str(self.id))
if len(self._name) > 0:
element.set("name", str(self.name))
if isinstance(self.fill, basestring):
element.set("material", "void")
elif isinstance(self.fill, openmc.Material):
element.set("material", str(self.fill.id))
elif isinstance(self.fill, Iterable):
element.set("material", ' '.join([m if m == 'void' else str(m.id)
for m in self.fill]))
elif isinstance(self.fill, (openmc.Universe, openmc.Lattice)):
element.set("fill", str(self.fill.id))
self.fill.create_xml_subelement(xml_element)
else:
element.set("fill", str(self.fill))
self.fill.create_xml_subelement(xml_element)
if self.region is not None:
# Set the region attribute with the region specification
element.set("region", str(self.region))
# Only surfaces that appear in a region are added to the geometry
# file, so the appropriate check is performed here. First we create
# a function which is called recursively to navigate through the CSG
# tree. When it reaches a leaf (a Halfspace), it creates a <surface>
# element for the corresponding surface if none has been created
# thus far.
def create_surface_elements(node, element):
if isinstance(node, Halfspace):
path = './surface[@id=\'{0}\']'.format(node.surface.id)
if xml_element.find(path) is None:
surface_subelement = node.surface.create_xml_subelement()
xml_element.append(surface_subelement)
elif isinstance(node, Complement):
create_surface_elements(node.node, element)
else:
for subnode in node.nodes:
create_surface_elements(subnode, element)
# Call the recursive function from the top node
create_surface_elements(self.region, xml_element)
if self.temperature is not None:
if isinstance(self.temperature, Iterable):
element.set("temperature", ' '.join(
str(t) for t in self.temperature))
else:
element.set("temperature", str(self.temperature))
if self.translation is not None:
element.set("translation", ' '.join(map(str, self.translation)))
if self.rotation is not None:
element.set("rotation", ' '.join(map(str, self.rotation)))
return element

View file

@ -41,25 +41,36 @@ def check_type(name, value, expected_type, expected_iter_type=None):
Description of value being checked
value : object
Object to check type of
expected_type : type
expected_type : type or Iterable of type
type to check object against
expected_iter_type : type or None, optional
expected_iter_type : type or Iterable of type or None, optional
Expected type of each element in value, assuming it is iterable. If
None, no check will be performed.
"""
if not _isinstance(value, expected_type):
msg = 'Unable to set "{0}" to "{1}" which is not of type "{2}"'.format(
name, value, expected_type.__name__)
if isinstance(expected_type, Iterable):
msg = 'Unable to set "{0}" to "{1}" which is not one of the ' \
'following types: "{2}"'.format(name, value, ', '.join(
[t.__name__ for t in expected_type]))
else:
msg = 'Unable to set "{0}" to "{1}" which is not of type "{2}"'.format(
name, value, expected_type.__name__)
raise ValueError(msg)
if expected_iter_type:
for item in value:
if not _isinstance(item, expected_iter_type):
msg = 'Unable to set "{0}" to "{1}" since each item must be ' \
'of type "{2}"'.format(name, value,
expected_iter_type.__name__)
if isinstance(expected_iter_type, Iterable):
msg = 'Unable to set "{0}" to "{1}" since each item must be ' \
'one of the following types: "{2}"'.format(
name, value, ', '.join([t.__name__ for t in
expected_iter_type]))
else:
msg = 'Unable to set "{0}" to "{1}" since each item must be ' \
'of type "{2}"'.format(name, value,
expected_iter_type.__name__)
raise ValueError(msg)
@ -127,7 +138,7 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1):
# But first, have we exceeded the max depth?
if len(tree) > max_depth:
msg = 'Error setting {0}: Found an iterable at {1}, items '\
'in that iterable excceed the maximum depth of {2}' \
'in that iterable exceed the maximum depth of {2}' \
.format(name, ind_str, max_depth)
raise ValueError(msg)
@ -245,3 +256,50 @@ def check_greater_than(name, value, minimum, equality=False):
msg = 'Unable to set "{0}" to "{1}" since it is less than ' \
'or equal to "{2}"'.format(name, value, minimum)
raise ValueError(msg)
class CheckedList(list):
"""A list for which each element is type-checked as it's added
Parameters
----------
expected_type : type or Iterable of type
Type(s) which each element should be
name : str
Name of data being checked
items : Iterable, optional
Items to initialize the list with
"""
def __init__(self, expected_type, name, items=[]):
self.expected_type = expected_type
self.name = name
for item in items:
self.append(item)
def append(self, item):
"""Append item to list
Parameters
----------
item : object
Item to append
"""
check_type(self.name, item, self.expected_type)
super(CheckedList, self).append(item)
def insert(self, index, item):
"""Insert item before index
Parameters
----------
index : int
Index in list
item : object
Item to insert
"""
check_type(self.name, item, self.expected_type)
super(CheckedList, self).insert(index, item)

View file

@ -69,7 +69,7 @@ class CMFDMesh(object):
to any tallies far away from fission source neutron regions. A ``2``
must be used to identify any fission source region.
"""
"""
def __init__(self):
self._lower_left = None
@ -219,7 +219,7 @@ class CMFDFile(object):
inner tolerance for Gauss-Seidel iterations when performing CMFD.
ktol : float
Tolerance on the eigenvalue when performing CMFD power iteration
cmfd_mesh : CMFDMesh
cmfd_mesh : openmc.CMFDMesh
Structured mesh to be used for acceleration
norm : float
Normalization factor applied to the CMFD fission source distribution

View file

@ -24,7 +24,7 @@ class Element(object):
Chemical symbol of the element, e.g. Pu
xs : str
Cross section identifier, e.g. 71c
scattering : 'data' or 'iso-in-lab' or None
scattering : {'data', 'iso-in-lab', None}
The type of angular scattering distribution to use
"""
@ -57,6 +57,15 @@ class Element(object):
def __ne__(self, other):
return not self == other
def __gt__(self, other):
return repr(self) > repr(other)
def __lt__(self, other):
return not self > other
def __hash__(self):
return hash(repr(self))
def __hash__(self):
return hash(repr(self))

View file

@ -27,7 +27,8 @@ class Executor(object):
# Launch a subprocess to run OpenMC
p = subprocess.Popen(command, shell=True,
cwd=self._working_directory,
stdout=subprocess.PIPE)
stdout=subprocess.PIPE,
universal_newlines=True)
# Capture and re-print OpenMC output in real-time
while True:

View file

@ -40,11 +40,14 @@ class Filter(object):
The bins for the filter
num_bins : Integral
The number of filter bins
mesh : Mesh or None
mesh : openmc.Mesh or None
A Mesh object for 'mesh' type filters.
stride : Integral
The number of filter, nuclide and score bins within each of this
filter's bins.
distribcell_paths : list of str
The paths traversed through the CSG tree to reach each distribcell
instance (for 'distribcell' filters only)
"""
@ -56,6 +59,7 @@ class Filter(object):
self._bins = None
self._mesh = None
self._stride = None
self._distribcell_paths = None
if type is not None:
self.type = type
@ -77,6 +81,25 @@ class Filter(object):
def __ne__(self, other):
return not self == other
def __gt__(self, other):
if self.type != other.type:
if self.type in _FILTER_TYPES and other.type in _FILTER_TYPES:
delta = _FILTER_TYPES.index(self.type) - \
_FILTER_TYPES.index(other.type)
return delta > 0
else:
return False
else:
# Compare largest/smallest energy bin edges in energy filters
# This logic is used when merging tallies with energy filters
if 'energy' in self.type and 'energy' in other.type:
return self.bins[0] >= other.bins[-1]
else:
return max(self.bins) > max(other.bins)
def __lt__(self, other):
return not self > other
def __hash__(self):
return hash(repr(self))
@ -91,6 +114,7 @@ class Filter(object):
clone._num_bins = self.num_bins
clone._mesh = copy.deepcopy(self.mesh, memo)
clone._stride = self.stride
clone._distribcell_paths = copy.deepcopy(self.distribcell_paths)
memo[id(self)] = clone
@ -133,6 +157,10 @@ class Filter(object):
def stride(self):
return self._stride
@property
def distribcell_paths(self):
return self._distribcell_paths
@type.setter
def type(self, type):
if type is None:
@ -227,12 +255,17 @@ class Filter(object):
self._stride = stride
@distribcell_paths.setter
def distribcell_paths(self, distribcell_paths):
cv.check_iterable_type('distribcell_paths', distribcell_paths, str)
self._distribcell_paths = distribcell_paths
def can_merge(self, other):
"""Determine if filter can be merged with another.
Parameters
----------
other : Filter
other : openmc.Filter
Filter to compare with
Returns
@ -246,20 +279,28 @@ class Filter(object):
return False
# Filters must be of the same type
elif self.type != other.type:
if self.type != other.type:
return False
# Distribcell filters cannot have more than one bin
elif self.type == 'distribcell':
if self.type == 'distribcell':
return False
# Mesh filters cannot have more than one bin
elif self.type == 'mesh':
return False
# Different energy bins are not mergeable
# Different energy bins structures must be mutually exclusive and
# share only one shared bin edge at the minimum or maximum energy
elif 'energy' in self.type:
return False
# This low energy edge coincides with other's high energy edge
if self.bins[0] == other.bins[-1]:
return True
# This high energy edge coincides with other's low energy edge
elif self.bins[-1] == other.bins[0]:
return True
else:
return False
else:
return True
@ -269,12 +310,12 @@ class Filter(object):
Parameters
----------
other : Filter
other : openmc.Filter
Filter to merge with
Returns
-------
merged_filter : Filter
merged_filter : openmc.Filter
Filter resulting from the merge
"""
@ -288,9 +329,21 @@ class Filter(object):
merged_filter = copy.deepcopy(self)
# Merge unique filter bins
merged_bins = list(set(np.concatenate((self.bins, other.bins))))
merged_filter.bins = merged_bins
merged_filter.num_bins = len(merged_bins)
merged_bins = np.concatenate((self.bins, other.bins))
merged_bins = np.unique(merged_bins)
# Sort energy bin edges
if 'energy' in self.type:
merged_bins = sorted(merged_bins)
# Assign merged bins to merged filter
merged_filter.bins = list(merged_bins)
# Count bins in the merged filter
if 'energy' in merged_filter.type:
merged_filter.num_bins = len(merged_bins) - 1
else:
merged_filter.num_bins = len(merged_bins)
return merged_filter
@ -302,7 +355,7 @@ class Filter(object):
Parameters
----------
other : Filter
other : openmc.Filter
The filter to query as a subset of this filter
Returns
@ -466,8 +519,8 @@ class Filter(object):
"""Builds a Pandas DataFrame for the Filter's bins.
This method constructs a Pandas DataFrame object for the filter with
columns annotated by filter bin information. This is a helper method
for the Tally.get_pandas_dataframe(...) method.
columns annotated by filter bin information. This is a helper method for
:meth:`Tally.get_pandas_dataframe`.
This capability has been tested for Pandas >=0.13.1. However, it is
recommended to use v0.16 or newer versions of Pandas since this method
@ -477,7 +530,7 @@ class Filter(object):
----------
data_size : Integral
The total number of bins in the tally corresponding to this filter
summary : None or Summary
summary : None or openmc.Summary
An optional Summary object to be used to construct columns for
distribcell tally filters (default is None). The geometric
information in the Summary object is embedded into a Multi-index
@ -502,9 +555,9 @@ class Filter(object):
2. separate columns for the cell IDs, universe IDs, and lattice IDs
and x,y,z cell indices corresponding to each (with summary info).
For 'energy' and 'energyout' filters, the DataFrame include a single
column with each element comprising a string with the lower, upper
energy bounds for each filter bin.
For 'energy' and 'energyout' filters, the DataFrame includes one
column for the lower energy bound and one column for the upper
energy bound for each filter bin.
For 'mesh' filters, the DataFrame includes three columns for the
x,y,z mesh cell indices corresponding to each filter bin.
@ -521,14 +574,8 @@ class Filter(object):
"""
# Attempt to import Pandas
try:
import pandas as pd
except ImportError:
msg = 'The Pandas Python package must be installed on your system'
raise ImportError(msg)
# Initialize Pandas DataFrame
import pandas as pd
df = pd.DataFrame()
# mesh filters
@ -599,18 +646,10 @@ class Filter(object):
# offsets to OpenCG LocalCoords linked lists
offsets_to_coords = {}
# Use OpenCG to compute LocalCoords linked list for
# each region and store in dictionary
for region in range(num_regions):
for offset, path in enumerate(self.distribcell_paths):
region = opencg_geometry.get_region_from_path(path)
coords = opencg_geometry.find_region(region)
path = opencg.get_path(coords)
cell_id = path[-1]
# If this region is in Cell corresponding to the
# distribcell filter bin, store it in dictionary
if cell_id == self.bins[0]:
offset = openmc_geometry.get_cell_instance(path)
offsets_to_coords[offset] = coords
offsets_to_coords[offset] = coords
# Each distribcell offset is a DataFrame bin
# Unravel the paths into DataFrame columns
@ -707,7 +746,6 @@ class Filter(object):
filter_bins = np.repeat(filter_bins, self.stride)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
filter_bins = filter_bins
df = pd.DataFrame({self.type : filter_bins})
# If OpenCG level info DataFrame was created, concatenate
@ -719,21 +757,30 @@ class Filter(object):
# energy, energyout filters
elif 'energy' in self.type:
bins = self.bins
num_bins = self.num_bins
# Extract the lower and upper energy bounds, then repeat and tile
# them as necessary to account for other filters.
lo_bins = np.repeat(self.bins[:-1], self.stride)
hi_bins = np.repeat(self.bins[1:], self.stride)
tile_factor = data_size / len(lo_bins)
lo_bins = np.tile(lo_bins, tile_factor)
hi_bins = np.tile(hi_bins, tile_factor)
# Create strings for
template = '({0:.1e} - {1:.1e})'
filter_bins = []
for i in range(num_bins):
filter_bins.append(template.format(bins[i], bins[i+1]))
# Add the new energy columns to the DataFrame.
df.loc[:, self.type + ' low [MeV]'] = lo_bins
df.loc[:, self.type + ' high [MeV]'] = hi_bins
# Tile the energy bins into a DataFrame column
filter_bins = np.repeat(filter_bins, self.stride)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
filter_bins = filter_bins
df = pd.concat([df, pd.DataFrame({self.type + ' [MeV]' : filter_bins})])
elif self.type in ('azimuthal', 'polar'):
# Extract the lower and upper angle bounds, then repeat and tile
# them as necessary to account for other filters.
lo_bins = np.repeat(self.bins[:-1], self.stride)
hi_bins = np.repeat(self.bins[1:], self.stride)
tile_factor = data_size / len(lo_bins)
lo_bins = np.tile(lo_bins, tile_factor)
hi_bins = np.tile(hi_bins, tile_factor)
# Add the new angle columns to the DataFrame.
df.loc[:, self.type + ' low'] = lo_bins
df.loc[:, self.type + ' high'] = hi_bins
# universe, material, surface, cell, and cellborn filters
else:

View file

@ -17,7 +17,7 @@ class Geometry(object):
Attributes
----------
root_universe : openmc.universe.Universe
root_universe : openmc.Universe
Root universe which contains all others
"""
@ -63,13 +63,19 @@ class Geometry(object):
"""
# Extract the cell id from the path
last_index = path.rfind('>')
cell_id = int(path[last_index+1:])
# Find the distribcell index of the cell.
cells = self.get_all_cells()
if path[-1] in cells:
distribcell_index = cells[path[-1]].distribcell_index
for cell in cells:
if cell.id == cell_id:
distribcell_index = cell.distribcell_index
break
else:
raise RuntimeError('Could not find cell {} specified in a \
distribcell filter'.format(path[-1]))
distribcell filter'.format(cell_id))
# Return memoize'd offset if possible
if (path, distribcell_index) in self._offsets:
@ -89,31 +95,48 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Cell
list of openmc.Cell
Cells in the geometry
"""
return self._root_universe.get_all_cells()
all_cells = self._root_universe.get_all_cells()
cells = set()
for cell in all_cells.values():
if cell._type == 'normal':
cells.add(cell)
cells = list(cells)
cells.sort(key=lambda x: x.id)
return cells
def get_all_universes(self):
"""Return all universes defined
Returns
-------
list of openmc.universe.Universe
list of openmc.Universe
Universes in the geometry
"""
return self._root_universe.get_all_universes()
all_universes = self._root_universe.get_all_universes()
universes = set()
for universe in all_universes.values():
universes.add(universe)
universes = list(universes)
universes.sort(key=lambda x: x.id)
return universes
def get_all_nuclides(self):
"""Return all nuclides assigned to a material in the geometry
Returns
-------
list of openmc.nuclide.Nuclide
list of openmc.Nuclide
Nuclides in the geometry
"""
@ -131,7 +154,7 @@ class Geometry(object):
Returns
-------
list of openmc.material.Material
list of openmc.Material
Materials in the geometry
"""
@ -150,10 +173,19 @@ class Geometry(object):
return materials
def get_all_material_cells(self):
"""Return all cells filled by a material
Returns
-------
list of openmc.Cell
Cells filled by Materials in the geometry
"""
all_cells = self.get_all_cells()
material_cells = set()
for cell_id, cell in all_cells.items():
for cell in all_cells:
if cell._type == 'normal':
material_cells.add(cell)
@ -166,7 +198,7 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Universe
list of openmc.Universe
Universes with non-fill cells
"""
@ -174,9 +206,9 @@ class Geometry(object):
all_universes = self.get_all_universes()
material_universes = set()
for universe_id, universe in all_universes.items():
cells = universe._cells
for cell_id, cell in cells.items():
for universe in all_universes:
cells = universe.cells
for cell in cells:
if cell._type == 'normal':
material_universes.add(universe)
@ -184,6 +216,227 @@ class Geometry(object):
material_universes.sort(key=lambda x: x.id)
return material_universes
def get_all_lattices(self):
"""Return all lattices defined
Returns
-------
list of openmc.Lattice
Lattices in the geometry
"""
cells = self.get_all_cells()
lattices = set()
for cell in cells:
if isinstance(cell.fill, openmc.Lattice):
lattices.add(cell.fill)
lattices = list(lattices)
lattices.sort(key=lambda x: x.id)
return lattices
def get_materials_by_name(self, name, case_sensitive=False, matching=False):
"""Return a list of materials with matching names.
Parameters
----------
name : str
The name to match
case_sensitive : bool
Whether to distinguish upper and lower case letters in each
material's name (default is True)
matching : bool
Whether the names must match completely (default is True)
Returns
-------
list of openmc.Material
Materials matching the queried name
"""
if not case_sensitive:
name = name.lower()
all_materials = self.get_all_materials()
materials = set()
for material in all_materials:
material_name = material.name
if not case_sensitive:
material_name = material_name.lower()
if material_name == name:
materials.add(material)
elif not matching and name in material_name:
materials.add(material)
materials = list(materials)
materials.sort(key=lambda x: x.id)
return materials
def get_cells_by_name(self, name, case_sensitive=False, matching=False):
"""Return a list of cells with matching names.
Parameters
----------
name : str
The name to search match
case_sensitive : bool
Whether to distinguish upper and lower case letters in each
cell's name (default is True)
matching : bool
Whether the names must match completely (default is True)
Returns
-------
list of openmc.Cell
Cells matching the queried name
"""
if not case_sensitive:
name = name.lower()
all_cells = self.get_all_cells()
cells = set()
for cell in all_cells:
cell_name = cell.name
if not case_sensitive:
cell_name = cell_name.lower()
if cell_name == name:
cells.add(cell)
elif not matching and name in cell_name:
cells.add(cell)
cells = list(cells)
cells.sort(key=lambda x: x.id)
return cells
def get_cells_by_fill_name(self, name, case_sensitive=False, matching=False):
"""Return a list of cells with fills with matching names.
Parameters
----------
name : str
The name to match
case_sensitive : bool
Whether to distinguish upper and lower case letters in each
cell's name (default is True)
matching : bool
Whether the names must match completely (default is True)
Returns
-------
list of openmc.Cell
Cells with fills matching the queried name
"""
if not case_sensitive:
name = name.lower()
all_cells = self.get_all_cells()
cells = set()
for cell in all_cells:
cell_fill_name = cell.fill.name
if not case_sensitive:
cell_fill_name = cell_fill_name.lower()
if cell_fill_name == name:
cells.add(cell)
elif not matching and name in cell_fill_name:
cells.add(cell)
cells = list(cells)
cells.sort(key=lambda x: x.id)
return cells
def get_universes_by_name(self, name, case_sensitive=False, matching=False):
"""Return a list of universes with matching names.
Parameters
----------
name : str
The name to match
case_sensitive : bool
Whether to distinguish upper and lower case letters in each
universe's name (default is True)
matching : bool
Whether the names must match completely (default is True)
Returns
-------
list of openmc.Universe
Universes matching the queried name
"""
if not case_sensitive:
name = name.lower()
all_universes = self.get_all_universes()
universes = set()
for universe in all_universes:
universe_name = universe.name
if not case_sensitive:
universe_name = universe_name.lower()
if universe_name == name:
universes.add(universe)
elif not matching and name in universe_name:
universes.add(universe)
universes = list(universes)
universes.sort(key=lambda x: x.id)
return universes
def get_lattices_by_name(self, name, case_sensitive=False, matching=False):
"""Return a list of lattices with matching names.
Parameters
----------
name : str
The name to match
case_sensitive : bool
Whether to distinguish upper and lower case letters in each
lattice's name (default is True)
matching : bool
Whether the names must match completely (default is True)
Returns
-------
list of openmc.Lattice
Lattices matching the queried name
"""
if not case_sensitive:
name = name.lower()
all_lattices = self.get_all_lattices()
lattices = set()
for lattice in all_lattices:
lattice_name = lattice.name
if not case_sensitive:
lattice_name = lattice_name.lower()
if lattice_name == name:
lattices.add(lattice)
elif not matching and name in lattice_name:
lattices.add(lattice)
lattices = list(lattices)
lattices.sort(key=lambda x: x.id)
return lattices
class GeometryFile(object):
"""Geometry file used for an OpenMC simulation. Corresponds directly to the
@ -191,7 +444,7 @@ class GeometryFile(object):
Attributes
----------
geometry : Geometry
geometry : openmc.Geometry
The geometry to be used
"""

871
openmc/lattice.py Normal file
View file

@ -0,0 +1,871 @@
import abc
from collections import OrderedDict, Iterable
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
import numpy as np
import openmc.checkvalue as cv
from openmc.universe import Universe, AUTO_UNIVERSE_ID
if sys.version_info[0] >= 3:
basestring = str
class Lattice(object):
"""A repeating structure wherein each element is a universe.
Parameters
----------
lattice_id : int, optional
Unique identifier for the lattice. If not specified, an identifier will
automatically be assigned.
name : str, optional
Name of the lattice. If not specified, the name is the empty string.
Attributes
----------
id : int
Unique identifier for the lattice
name : str
Name of the lattice
pitch : float
Pitch of the lattice in cm
outer : int
The unique identifier of a universe to fill all space outside the
lattice
universes : numpy.ndarray of openmc.Universe
An array of universes filling each element of the lattice
"""
# This is an abstract class which cannot be instantiated
__metaclass__ = abc.ABCMeta
def __init__(self, lattice_id=None, name=''):
# Initialize Lattice class attributes
self.id = lattice_id
self.name = name
self._pitch = None
self._outer = None
self._universes = None
def __eq__(self, other):
if not isinstance(other, Lattice):
return False
elif self.id != other.id:
return False
elif self.name != other.name:
return False
elif self.pitch != other.pitch:
return False
elif self.outer != other.outer:
return False
elif self.universes != other.universes:
return False
else:
return True
def __ne__(self, other):
return not self == other
@property
def id(self):
return self._id
@property
def name(self):
return self._name
@property
def pitch(self):
return self._pitch
@property
def outer(self):
return self._outer
@property
def universes(self):
return self._universes
@id.setter
def id(self, lattice_id):
if lattice_id is None:
global AUTO_UNIVERSE_ID
self._id = AUTO_UNIVERSE_ID
AUTO_UNIVERSE_ID += 1
else:
cv.check_type('lattice ID', lattice_id, Integral)
cv.check_greater_than('lattice ID', lattice_id, 0, equality=True)
self._id = lattice_id
@name.setter
def name(self, name):
if name is not None:
cv.check_type('lattice name', name, basestring)
self._name = name
else:
self._name = ''
@outer.setter
def outer(self, outer):
cv.check_type('outer universe', outer, Universe)
self._outer = outer
@universes.setter
def universes(self, universes):
cv.check_iterable_type('lattice universes', universes, Universe,
min_depth=2, max_depth=3)
self._universes = np.asarray(universes)
def get_unique_universes(self):
"""Determine all unique universes in the lattice
Returns
-------
universes : collections.OrderedDict
Dictionary whose keys are universe IDs and values are
:class:`Universe` instances
"""
univs = OrderedDict()
for k in range(len(self._universes)):
for j in range(len(self._universes[k])):
if isinstance(self._universes[k][j], Universe):
u = self._universes[k][j]
univs[u._id] = u
else:
for i in range(len(self._universes[k][j])):
u = self._universes[k][j][i]
assert isinstance(u, Universe)
univs[u._id] = u
if self.outer is not None:
univs[self.outer._id] = self.outer
return univs
def get_all_nuclides(self):
"""Return all nuclides contained in the lattice
Returns
-------
nuclides : collections.OrderedDict
Dictionary whose keys are nuclide names and values are 2-tuples of
(nuclide, density)
"""
nuclides = OrderedDict()
# Get all unique Universes contained in each of the lattice cells
unique_universes = self.get_unique_universes()
# Append all Universes containing each cell to the dictionary
for universe_id, universe in unique_universes.items():
nuclides.update(universe.get_all_nuclides())
return nuclides
def get_all_cells(self):
"""Return all cells that are contained within the lattice
Returns
-------
cells : collections.OrderedDict
Dictionary whose keys are cell IDs and values are :class:`Cell`
instances
"""
cells = OrderedDict()
unique_universes = self.get_unique_universes()
for universe_id, universe in unique_universes.items():
cells.update(universe.get_all_cells())
return cells
def get_all_materials(self):
"""Return all materials that are contained within the lattice
Returns
-------
materials : collections.OrderedDict
Dictionary whose keys are material IDs and values are
:class:`Material` instances
"""
materials = OrderedDict()
# Append all Cells in each Cell in the Universe to the dictionary
cells = self.get_all_cells()
for cell_id, cell in cells.items():
materials.update(cell.get_all_materials())
return materials
def get_all_universes(self):
"""Return all universes that are contained within the lattice
Returns
-------
universes : collections.OrderedDict
Dictionary whose keys are universe IDs and values are
:class:`Universe` instances
"""
# Initialize a dictionary of all Universes contained by the Lattice
# in each nested Universe level
all_universes = OrderedDict()
# Get all unique Universes contained in each of the lattice cells
unique_universes = self.get_unique_universes()
# Add the unique Universes filling each Lattice cell
all_universes.update(unique_universes)
# Append all Universes containing each cell to the dictionary
for universe_id, universe in unique_universes.items():
all_universes.update(universe.get_all_universes())
return all_universes
class RectLattice(Lattice):
"""A lattice consisting of rectangular prisms.
Parameters
----------
lattice_id : int, optional
Unique identifier for the lattice. If not specified, an identifier will
automatically be assigned.
name : str, optional
Name of the lattice. If not specified, the name is the empty string.
Attributes
----------
id : int
Unique identifier for the lattice
name : str
Name of the lattice
dimension : Iterable of int
An array of two or three integers representing the number of lattice
cells in the x- and y- (and z-) directions, respectively.
lower_left : Iterable of float
The coordinates of the lower-left corner of the lattice. If the lattice
is two-dimensional, only the x- and y-coordinates are specified.
"""
def __init__(self, lattice_id=None, name=''):
super(RectLattice, self).__init__(lattice_id, name)
# Initialize Lattice class attributes
self._dimension = None
self._lower_left = None
self._offsets = None
def __eq__(self, other):
if not isinstance(other, RectLattice):
return False
elif not super(RectLattice, self).__eq__(other):
return False
elif self.dimension != other.dimension:
return False
elif self.lower_left != other.lower_left:
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(repr(self))
def __repr__(self):
string = 'RectLattice\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\tDimension', '=\t',
self._dimension)
string += '{0: <16}{1}{2}\n'.format('\tLower Left', '=\t',
self._lower_left)
string += '{0: <16}{1}{2}\n'.format('\tPitch', '=\t', self._pitch)
if self._outer is not None:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer._id)
else:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer)
string += '{0: <16}\n'.format('\tUniverses')
# Lattice nested Universe IDs - column major for Fortran
for i, universe in enumerate(np.ravel(self._universes)):
string += '{0} '.format(universe._id)
# Add a newline character every time we reach end of row of cells
if (i+1) % self._dimension[-1] == 0:
string += '\n'
string = string.rstrip('\n')
if self._offsets is not None:
string += '{0: <16}\n'.format('\tOffsets')
# Lattice cell offsets
for i, offset in enumerate(np.ravel(self._offsets)):
string += '{0} '.format(offset)
# Add a newline character when we reach end of row of cells
if (i+1) % self._dimension[-1] == 0:
string += '\n'
string = string.rstrip('\n')
return string
@property
def dimension(self):
return self._dimension
@property
def lower_left(self):
return self._lower_left
@property
def offsets(self):
return self._offsets
@dimension.setter
def dimension(self, dimension):
cv.check_type('lattice dimension', dimension, Iterable, Integral)
cv.check_length('lattice dimension', dimension, 2, 3)
for dim in dimension:
cv.check_greater_than('lattice dimension', dim, 0)
self._dimension = dimension
@lower_left.setter
def lower_left(self, lower_left):
cv.check_type('lattice lower left corner', lower_left, Iterable, Real)
cv.check_length('lattice lower left corner', lower_left, 2, 3)
self._lower_left = lower_left
@offsets.setter
def offsets(self, offsets):
cv.check_type('lattice offsets', offsets, Iterable)
self._offsets = offsets
@Lattice.pitch.setter
def pitch(self, pitch):
cv.check_type('lattice pitch', pitch, Iterable, Real)
cv.check_length('lattice pitch', pitch, 2, 3)
for dim in pitch:
cv.check_greater_than('lattice pitch', dim, 0.0)
self._pitch = pitch
def get_cell_instance(self, path, distribcell_index):
# Extract the lattice element from the path
next_index = path.index('-')
lat_id_indices = path[:next_index]
path = path[next_index+2:]
# Extract the lattice cell indices from the path
i1 = lat_id_indices.index('(')
i2 = lat_id_indices.index(')')
i = lat_id_indices[i1+1:i2]
lat_x = int(i.split(',')[0]) - 1
lat_y = int(i.split(',')[1]) - 1
lat_z = int(i.split(',')[2]) - 1
# For 2D Lattices
if len(self._dimension) == 2:
offset = self._offsets[lat_z, lat_y, lat_x, distribcell_index-1]
offset += self._universes[lat_x][lat_y].get_cell_instance(path,
distribcell_index)
# For 3D Lattices
else:
offset = self._offsets[lat_z, lat_y, lat_x, distribcell_index-1]
offset += self._universes[lat_z][lat_y][lat_x].get_cell_instance(
path, distribcell_index)
return offset
def create_xml_subelement(self, xml_element):
# Determine if XML element already contains subelement for this Lattice
path = './lattice[@id=\'{0}\']'.format(self._id)
test = xml_element.find(path)
# If the element does contain the Lattice subelement, then return
if test is not None:
return
lattice_subelement = ET.Element("lattice")
lattice_subelement.set("id", str(self._id))
if len(self._name) > 0:
lattice_subelement.set("name", str(self._name))
# Export the Lattice cell pitch
pitch = ET.SubElement(lattice_subelement, "pitch")
pitch.text = ' '.join(map(str, self._pitch))
# Export the Lattice outer Universe (if specified)
if self._outer is not None:
outer = ET.SubElement(lattice_subelement, "outer")
outer.text = '{0}'.format(self._outer._id)
self._outer.create_xml_subelement(xml_element)
# Export Lattice cell dimensions
dimension = ET.SubElement(lattice_subelement, "dimension")
dimension.text = ' '.join(map(str, self._dimension))
# Export Lattice lower left
lower_left = ET.SubElement(lattice_subelement, "lower_left")
lower_left.text = ' '.join(map(str, self._lower_left))
# Export the Lattice nested Universe IDs - column major for Fortran
universe_ids = '\n'
# 3D Lattices
if len(self._dimension) == 3:
for z in range(self._dimension[2]):
for y in range(self._dimension[1]):
for x in range(self._dimension[0]):
universe = self._universes[z][y][x]
# Append Universe ID to the Lattice XML subelement
universe_ids += '{0} '.format(universe._id)
# Create XML subelement for this Universe
universe.create_xml_subelement(xml_element)
# Add newline character when we reach end of row of cells
universe_ids += '\n'
# Add newline character when we reach end of row of cells
universe_ids += '\n'
# 2D Lattices
else:
for y in range(self._dimension[1]):
for x in range(self._dimension[0]):
universe = self._universes[y][x]
# Append Universe ID to Lattice XML subelement
universe_ids += '{0} '.format(universe._id)
# Create XML subelement for this Universe
universe.create_xml_subelement(xml_element)
# Add newline character when we reach end of row of cells
universe_ids += '\n'
# Remove trailing newline character from Universe IDs string
universe_ids = universe_ids.rstrip('\n')
universes = ET.SubElement(lattice_subelement, "universes")
universes.text = universe_ids
# Append the XML subelement for this Lattice to the XML element
xml_element.append(lattice_subelement)
class HexLattice(Lattice):
"""A lattice consisting of hexagonal prisms.
Parameters
----------
lattice_id : int, optional
Unique identifier for the lattice. If not specified, an identifier will
automatically be assigned.
name : str, optional
Name of the lattice. If not specified, the name is the empty string.
Attributes
----------
id : int
Unique identifier for the lattice
name : str
Name of the lattice
num_rings : int
Number of radial ring positions in the xy-plane
num_axial : int
Number of positions along the z-axis.
center : Iterable of float
Coordinates of the center of the lattice. If the lattice does not have
axial sections then only the x- and y-coordinates are specified
"""
def __init__(self, lattice_id=None, name=''):
super(HexLattice, self).__init__(lattice_id, name)
# Initialize Lattice class attributes
self._num_rings = None
self._num_axial = None
self._center = None
def __eq__(self, other):
if not isinstance(other, HexLattice):
return False
elif not super(HexLattice, self).__eq__(other):
return False
elif self.num_rings != other.num_rings:
return False
elif self.num_axial != other.num_axial:
return False
elif self.center != other.center:
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(repr(self))
def __repr__(self):
string = 'HexLattice\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\t# Rings', '=\t', self._num_rings)
string += '{0: <16}{1}{2}\n'.format('\t# Axial', '=\t', self._num_axial)
string += '{0: <16}{1}{2}\n'.format('\tCenter', '=\t',
self._center)
string += '{0: <16}{1}{2}\n'.format('\tPitch', '=\t', self._pitch)
if self._outer is not None:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer._id)
else:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer)
string += '{0: <16}\n'.format('\tUniverses')
if self._num_axial is not None:
slices = [self._repr_axial_slice(x) for x in self._universes]
string += '\n'.join(slices)
else:
string += self._repr_axial_slice(self._universes)
return string
@property
def num_rings(self):
return self._num_rings
@property
def num_axial(self):
return self._num_axial
@property
def center(self):
return self._center
@num_rings.setter
def num_rings(self, num_rings):
cv.check_type('number of rings', num_rings, Integral)
cv.check_greater_than('number of rings', num_rings, 0)
self._num_rings = num_rings
@num_axial.setter
def num_axial(self, num_axial):
cv.check_type('number of axial', num_axial, Integral)
cv.check_greater_than('number of axial', num_axial, 0)
self._num_axial = num_axial
@center.setter
def center(self, center):
cv.check_type('lattice center', center, Iterable, Real)
cv.check_length('lattice center', center, 2, 3)
self._center = center
@Lattice.pitch.setter
def pitch(self, pitch):
cv.check_type('lattice pitch', pitch, Iterable, Real)
cv.check_length('lattice pitch', pitch, 1, 2)
for dim in pitch:
cv.check_greater_than('lattice pitch', dim, 0)
self._pitch = pitch
@Lattice.universes.setter
def universes(self, universes):
# Call Lattice.universes parent class setter property
Lattice.universes.fset(self, universes)
# NOTE: This routine assumes that the user creates a "ragged" list of
# lists, where each sub-list corresponds to one ring of Universes.
# The sub-lists are ordered from outermost ring to innermost ring.
# The Universes within each sub-list are ordered from the "top" in a
# clockwise fashion.
# Check to see if the given universes look like a 2D or a 3D array.
if isinstance(self._universes[0][0], Universe):
n_dims = 2
elif isinstance(self._universes[0][0][0], Universe):
n_dims = 3
else:
msg = 'HexLattice ID={0:d} does not appear to be either 2D or ' \
'3D. Make sure set_universes was given a two-deep or ' \
'three-deep iterable of universes.'.format(self._id)
raise RuntimeError(msg)
# Set the number of axial positions.
if n_dims == 3:
self.num_axial = len(self._universes)
else:
self._num_axial = None
# Set the number of rings and make sure this number is consistent for
# all axial positions.
if n_dims == 3:
self.num_rings = len(self._universes)
for rings in self._universes:
if len(rings) != self._num_rings:
msg = 'HexLattice ID={0:d} has an inconsistent number of ' \
'rings per axial positon'.format(self._id)
raise ValueError(msg)
else:
self.num_rings = len(self._universes)
# Make sure there are the correct number of elements in each ring.
if n_dims == 3:
for axial_slice in self._universes:
# Check the center ring.
if len(axial_slice[-1]) != 1:
msg = 'HexLattice ID={0:d} has the wrong number of ' \
'elements in the innermost ring. Only 1 element is ' \
'allowed in the innermost ring.'.format(self._id)
raise ValueError(msg)
# Check the outer rings.
for r in range(self._num_rings-1):
if len(axial_slice[r]) != 6*(self._num_rings - 1 - r):
msg = 'HexLattice ID={0:d} has the wrong number of ' \
'elements in ring number {1:d} (counting from the '\
'outermost ring). This ring should have {2:d} ' \
'elements.'.format(self._id, r,
6*(self._num_rings - 1 - r))
raise ValueError(msg)
else:
axial_slice = self._universes
# Check the center ring.
if len(axial_slice[-1]) != 1:
msg = 'HexLattice ID={0:d} has the wrong number of ' \
'elements in the innermost ring. Only 1 element is ' \
'allowed in the innermost ring.'.format(self._id)
raise ValueError(msg)
# Check the outer rings.
for r in range(self._num_rings-1):
if len(axial_slice[r]) != 6*(self._num_rings - 1 - r):
msg = 'HexLattice ID={0:d} has the wrong number of ' \
'elements in ring number {1:d} (counting from the '\
'outermost ring). This ring should have {2:d} ' \
'elements.'.format(self._id, r,
6*(self._num_rings - 1 - r))
raise ValueError(msg)
def create_xml_subelement(self, xml_element):
# Determine if XML element already contains subelement for this Lattice
path = './hex_lattice[@id=\'{0}\']'.format(self._id)
test = xml_element.find(path)
# If the element does contain the Lattice subelement, then return
if test is not None:
return
lattice_subelement = ET.Element("hex_lattice")
lattice_subelement.set("id", str(self._id))
if len(self._name) > 0:
lattice_subelement.set("name", str(self._name))
# Export the Lattice cell pitch
pitch = ET.SubElement(lattice_subelement, "pitch")
pitch.text = ' '.join(map(str, self._pitch))
# Export the Lattice outer Universe (if specified)
if self._outer is not None:
outer = ET.SubElement(lattice_subelement, "outer")
outer.text = '{0}'.format(self._outer._id)
self._outer.create_xml_subelement(xml_element)
lattice_subelement.set("n_rings", str(self._num_rings))
if self._num_axial is not None:
lattice_subelement.set("n_axial", str(self._num_axial))
# Export Lattice cell center
dimension = ET.SubElement(lattice_subelement, "center")
dimension.text = ' '.join(map(str, self._center))
# Export the Lattice nested Universe IDs.
# 3D Lattices
if self._num_axial is not None:
slices = []
for z in range(self._num_axial):
# Initialize the center universe.
universe = self._universes[z][-1][0]
universe.create_xml_subelement(xml_element)
# Initialize the remaining universes.
for r in range(self._num_rings-1):
for theta in range(6*(self._num_rings - 1 - r)):
universe = self._universes[z][r][theta]
universe.create_xml_subelement(xml_element)
# Get a string representation of the universe IDs.
slices.append(self._repr_axial_slice(self._universes[z]))
# Collapse the list of axial slices into a single string.
universe_ids = '\n'.join(slices)
# 2D Lattices
else:
# Initialize the center universe.
universe = self._universes[-1][0]
universe.create_xml_subelement(xml_element)
# Initialize the remaining universes.
for r in range(self._num_rings - 1):
for theta in range(6*(self._num_rings - 1 - r)):
universe = self._universes[r][theta]
universe.create_xml_subelement(xml_element)
# Get a string representation of the universe IDs.
universe_ids = self._repr_axial_slice(self._universes)
universes = ET.SubElement(lattice_subelement, "universes")
universes.text = '\n' + universe_ids
# Append the XML subelement for this Lattice to the XML element
xml_element.append(lattice_subelement)
def _repr_axial_slice(self, universes):
"""Return string representation for the given 2D group of universes.
The 'universes' argument should be a list of lists of universes where
each sub-list represents a single ring. The first list should be the
outer ring.
"""
# Find the largest universe ID and count the number of digits so we can
# properly pad the output string later.
largest_id = max([max([univ._id for univ in ring])
for ring in universes])
n_digits = len(str(largest_id))
pad = ' '*n_digits
id_form = '{: ^' + str(n_digits) + 'd}'
# Initialize the list for each row.
rows = [[] for i in range(1 + 4 * (self._num_rings-1))]
middle = 2 * (self._num_rings - 1)
# Start with the degenerate first ring.
universe = universes[-1][0]
rows[middle] = [id_form.format(universe._id)]
# Add universes one ring at a time.
for r in range(1, self._num_rings):
# r_prime increments down while r increments up.
r_prime = self._num_rings - 1 - r
theta = 0
y = middle + 2*r
# Climb down the top-right.
for i in range(r):
# Add the universe.
universe = universes[r_prime][theta]
rows[y].append(id_form.format(universe._id))
# Translate the indices.
y -= 1
theta += 1
# Climb down the right.
for i in range(r):
# Add the universe.
universe = universes[r_prime][theta]
rows[y].append(id_form.format(universe._id))
# Translate the indices.
y -= 2
theta += 1
# Climb down the bottom-right.
for i in range(r):
# Add the universe.
universe = universes[r_prime][theta]
rows[y].append(id_form.format(universe._id))
# Translate the indices.
y -= 1
theta += 1
# Climb up the bottom-left.
for i in range(r):
# Add the universe.
universe = universes[r_prime][theta]
rows[y].insert(0, id_form.format(universe._id))
# Translate the indices.
y += 1
theta += 1
# Climb up the left.
for i in range(r):
# Add the universe.
universe = universes[r_prime][theta]
rows[y].insert(0, id_form.format(universe._id))
# Translate the indices.
y += 2
theta += 1
# Climb up the top-left.
for i in range(r):
# Add the universe.
universe = universes[r_prime][theta]
rows[y].insert(0, id_form.format(universe._id))
# Translate the indices.
y += 1
theta += 1
# Flip the rows and join each row into a single string.
rows = [pad.join(x) for x in rows[::-1]]
# Pad the beginning of the rows so they line up properly.
for y in range(self._num_rings - 1):
rows[y] = (self._num_rings - 1 - y)*pad + rows[y]
rows[-1 - y] = (self._num_rings - 1 - y)*pad + rows[-1 - y]
for y in range(self._num_rings % 2, self._num_rings, 2):
rows[middle + y] = pad + rows[middle + y]
if y != 0:
rows[middle - y] = pad + rows[middle - y]
# Join the rows together and return the string.
universe_ids = '\n'.join(rows)
return universe_ids

85
openmc/macroscopic.py Normal file
View file

@ -0,0 +1,85 @@
from numbers import Integral
import sys
from openmc.checkvalue import check_type
if sys.version_info[0] >= 3:
basestring = str
class Macroscopic(object):
"""A Macroscopic object that can be used in a material.
Parameters
----------
name : str
Name of the macroscopic data, e.g. UO2
xs : str
Cross section identifier, e.g. 71c
Attributes
----------
name : str
Name of the nuclide, e.g. UO2
xs : str
Cross section identifier, e.g. 71c
"""
def __init__(self, name='', xs=None):
# Initialize class attributes
self._name = ''
self._xs = None
# Set the Material class attributes
self.name = name
if xs is not None:
self.xs = xs
def __eq__(self, other):
if isinstance(other, Macroscopic):
if self._name != other._name:
return False
elif self._xs != other._xs:
return False
else:
return True
elif isinstance(other, basestring) and other == self.name:
return True
else:
return False
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash((self._name, self._xs))
def __repr__(self):
string = 'Nuclide - {0}\n'.format(self._name)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
return string
@property
def name(self):
return self._name
@property
def xs(self):
return self._xs
@name.setter
def name(self, name):
check_type('name', name, basestring)
self._name = name
@xs.setter
def xs(self, xs):
check_type('cross-section identifier', xs, basestring)
self._xs = xs
def __repr__(self):
string = 'Macroscopic - {0}\n'.format(self._name)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self.xs)
return string

View file

@ -22,14 +22,15 @@ def reset_auto_material_id():
# Units for density supported by OpenMC
DENSITY_UNITS = ['g/cm3', 'g/cc', 'kg/cm3', 'atom/b-cm', 'atom/cm3', 'sum']
DENSITY_UNITS = ['g/cm3', 'g/cc', 'kg/cm3', 'atom/b-cm', 'atom/cm3', 'sum',
'macro']
# Constant for density when not needed
NO_DENSITY = 99999.
class Material(object):
"""A material composed of a collection of nuclides/elements that can be
"""A material composed of a collection of nuclides/elements that can be
assigned to a region of space.
Parameters
@ -49,7 +50,8 @@ class Material(object):
Density of the material (units defined separately)
density_units : str
Units used for `density`. Can be one of 'g/cm3', 'g/cc', 'kg/cm3',
'atom/b-cm', 'atom/cm3', or 'sum'.
'atom/b-cm', 'atom/cm3', 'sum', or 'macro'. The 'macro' unit only
applies in the case of a multi-group calculation.
"""
@ -65,6 +67,10 @@ class Material(object):
# Values - tuple (nuclide, percent, percent type)
self._nuclides = OrderedDict()
# The single instance of Macroscopic data present in this material
# (only one is allowed, hence this is different than _nuclides, etc)
self._macroscopic = None
# An ordered dictionary of Elements (order affects OpenMC results)
# Keys - Element names
# Values - tuple (element, percent, percent type)
@ -128,6 +134,10 @@ class Material(object):
string += '{0: <16}'.format('\t{0}'.format(nuclide))
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
if self._macroscopic is not None:
string += '{0: <16}\n'.format('\tMacroscopic Data')
string += '{0: <16}'.format('\t{0}'.format(self._macroscopic))
string += '{0: <16}\n'.format('\tElements')
for element in self._elements:
@ -149,6 +159,7 @@ class Material(object):
clone._density = self._density
clone._density_units = self._density_units
clone._nuclides = deepcopy(self._nuclides, memo)
clone._macroscopic = self._macroscopic
clone._elements = deepcopy(self._elements, memo)
clone._sab = deepcopy(self._sab, memo)
clone._convert_to_distrib_comps = self._convert_to_distrib_comps
@ -259,7 +270,7 @@ class Material(object):
Parameters
----------
nuclide : str or openmc.nuclide.Nuclide
nuclide : str or openmc.Nuclide
Nuclide to add
percent : float
Atom or weight percent
@ -268,6 +279,11 @@ class Material(object):
"""
if self._macroscopic is not None:
msg = 'Unable to add a Nuclide to Material ID="{0}" as a ' \
'macroscopic data-set has already been added'.format(self._id)
raise ValueError(msg)
if not isinstance(nuclide, (openmc.Nuclide, str)):
msg = 'Unable to add a Nuclide to Material ID="{0}" with a ' \
'non-Nuclide value "{1}"'.format(self._id, nuclide)
@ -297,7 +313,7 @@ class Material(object):
Parameters
----------
nuclide : openmc.nuclide.Nuclide
nuclide : openmc.Nuclide
Nuclide to remove
"""
@ -311,12 +327,70 @@ class Material(object):
if nuclide._name in self._nuclides:
del self._nuclides[nuclide._name]
def add_macroscopic(self, macroscopic):
"""Add a macroscopic to the material
Parameters
----------
macroscopic : str or openmc.Macroscopic
Macroscopic to add
"""
# Ensure no nuclides, elements, or sab are added since these would be
# incompatible with macroscopics
if self._nuclides or self._elements or self._sab:
msg = 'Unable to add a Macroscopic data set to Material ID="{0}" ' \
'with a macroscopic value "{1}" as an incompatible data ' \
'member (i.e., nuclide, element, or S(a,b) table) ' \
'has already been added'.format(self._id, macroscopic)
raise ValueError(msg)
if not isinstance(macroscopic, (openmc.Macroscopic, basestring)):
msg = 'Unable to add a Macroscopic to Material ID="{0}" with a ' \
'non-Macroscopic value "{1}"'.format(self._id, macroscopic)
raise ValueError(msg)
if isinstance(macroscopic, openmc.Macroscopic):
# Copy this Macroscopic to separate it from the Macroscopic in
# other Materials
macroscopic = deepcopy(macroscopic)
else:
macroscopic = openmc.Macroscopic(macroscopic)
if self._macroscopic is None:
self._macroscopic = macroscopic
else:
msg = 'Unable to add a Macroscopic to Material ID="{0}", ' \
'Only One Macroscopic allowed per ' \
'Material!'.format(self._id, macroscopic)
raise ValueError(msg)
def remove_macroscopic(self, macroscopic):
"""Remove a macroscopic from the material
Parameters
----------
macroscopic : openmc.Macroscopic
Macroscopic to remove
"""
if not isinstance(macroscopic, openmc.Macroscopic):
msg = 'Unable to remove a Macroscopic "{0}" in Material ID="{1}" ' \
'since it is not a Macroscopic'.format(self._id, macroscopic)
raise ValueError(msg)
# If the Material contains the Macroscopic, delete it
if macroscopic._name == self._macroscopic.name:
self._macroscopic = None
def add_element(self, element, percent, percent_type='ao'):
"""Add a natural element to the material
Parameters
----------
element : openmc.element.Element
element : openmc.Element
Element to add
percent : float
Atom or weight percent
@ -325,6 +399,11 @@ class Material(object):
"""
if self._macroscopic is not None:
msg = 'Unable to add an Element to Material ID="{0}" as a ' \
'macroscopic data-set has already been added'.format(self._id)
raise ValueError(msg)
if not isinstance(element, openmc.Element):
msg = 'Unable to add an Element to Material ID="{0}" with a ' \
'non-Element value "{1}"'.format(self._id, element)
@ -350,7 +429,7 @@ class Material(object):
Parameters
----------
element : openmc.element.Element
element : openmc.Element
Element to remove
"""
@ -371,6 +450,11 @@ class Material(object):
"""
if self._macroscopic is not None:
msg = 'Unable to add an S(a,b) table to Material ID="{0}" as a ' \
'macroscopic data-set has already been added'.format(self._id)
raise ValueError(msg)
if not isinstance(name, basestring):
msg = 'Unable to add an S(a,b) table to Material ID="{0}" with a ' \
'non-string table name "{1}"'.format(self._id, name)
@ -427,6 +511,15 @@ class Material(object):
return xml_element
def _get_macroscopic_xml(self, macroscopic):
xml_element = ET.Element("macroscopic")
xml_element.set("name", macroscopic._name)
if macroscopic.xs is not None:
xml_element.set("xs", macroscopic.xs)
return xml_element
def _get_element_xml(self, element, distrib=False):
xml_element = ET.Element("element")
xml_element.set("name", str(element[0]._name))
@ -482,14 +575,19 @@ class Material(object):
subelement.set("units", self._density_units)
if not self._convert_to_distrib_comps:
# Create nuclide XML subelements
subelements = self._get_nuclides_xml(self._nuclides)
for subelement in subelements:
element.append(subelement)
if self._macroscopic is None:
# Create nuclide XML subelements
subelements = self._get_nuclides_xml(self._nuclides)
for subelement in subelements:
element.append(subelement)
# Create element XML subelements
subelements = self._get_elements_xml(self._elements)
for subelement in subelements:
# Create element XML subelements
subelements = self._get_elements_xml(self._elements)
for subelement in subelements:
element.append(subelement)
else:
# Create macroscopic XML subelements
subelement = self._get_macroscopic_xml(self._macroscopic)
element.append(subelement)
else:
@ -516,15 +614,21 @@ class Material(object):
subsubelement = ET.SubElement(subelement, "otf_file_path")
subsubelement.text = self._distrib_otf_file
# Create nuclide XML subelements
subelements = self.get_nuclides_xml(self._nuclides, distrib=True)
for subelement_nuc in subelements:
subelement.append(subelement_nuc)
if self._macroscopic is None:
# Create nuclide XML subelements
subelements = self.get_nuclides_xml(self._nuclides, distrib=True)
for subelement_nuc in subelements:
subelement.append(subelement_nuc)
# Create element XML subelements
subelements = self._get_elements_xml(self._elements, distrib=True)
for subelement_ele in subelements:
subelement.append(subelement_ele)
# Create element XML subelements
subelements = self._get_elements_xml(self._elements, distrib=True)
for subsubelement in subelements:
subelement.append(subsubelement)
else:
# Create macroscopic XML subelements
subsubelement = self._get_macroscopic_xml(self._macroscopic,
distrib=True)
subelement.append(subsubelement)
if len(self._sab) > 0:
for sab in self._sab:
@ -567,7 +671,7 @@ class MaterialsFile(object):
Parameters
----------
material : Material
material : openmc.Material
Material to add
"""
@ -584,7 +688,7 @@ class MaterialsFile(object):
Parameters
----------
materials : tuple or list of Material
materials : tuple or list of openmc.Material
Materials to add
"""
@ -602,7 +706,7 @@ class MaterialsFile(object):
Parameters
----------
material : Material
material : openmc.Material
Material to remove
"""
@ -619,9 +723,8 @@ class MaterialsFile(object):
material.make_isotropic_in_lab()
def _create_material_subelements(self):
subelement = ET.SubElement(self._materials_file, "default_xs")
if self._default_xs is not None:
subelement = ET.SubElement(self._materials_file, "default_xs")
subelement.text = self._default_xs
for material in self._materials:

View file

@ -24,7 +24,7 @@ class EnergyGroups(object):
----------
group_edges : Iterable of Real
The energy group boundaries [MeV]
num_group : Integral
num_groups : int
The number of energy groups
"""
@ -54,10 +54,12 @@ class EnergyGroups(object):
def __eq__(self, other):
if not isinstance(other, EnergyGroups):
return False
elif self.group_edges != other.group_edges:
elif self.num_groups != other.num_groups:
return False
else:
elif np.allclose(self.group_edges, other.group_edges):
return True
else:
return False
def __ne__(self, other):
return not self == other
@ -84,7 +86,7 @@ class EnergyGroups(object):
Parameters
----------
energy : Real
energy : float
The energy of interest in MeV
Returns
@ -113,7 +115,7 @@ class EnergyGroups(object):
Parameters
----------
group : Integral
group : int
The energy group index, starting at 1 for the highest energies
Returns
@ -151,7 +153,7 @@ class EnergyGroups(object):
Returns
-------
ndarray
numpy.ndarray
The ndarray array indices for each energy group of interest
Raises
@ -198,7 +200,7 @@ class EnergyGroups(object):
Returns
-------
EnergyGroups
openmc.mgxs.EnergyGroups
A coarsened version of this EnergyGroups object.
Raises
@ -236,3 +238,64 @@ class EnergyGroups(object):
condensed_groups.group_edges = group_edges
return condensed_groups
def can_merge(self, other):
"""Determine if energy groups can be merged with another.
Parameters
----------
other : openmc.mgxs.EnergyGroups
EnergyGroups to compare with
Returns
-------
bool
Whether the energy groups can be merged
"""
if not isinstance(other, EnergyGroups):
return False
# If the energy group structures match then groups are mergeable
if self == other:
return True
# This low energy edge coincides with other's high energy edge
if self.group_edges[0] == other.group_edges[-1]:
return True
# This high energy edge coincides with other's low energy edge
elif self.group_edges[-1] == other.group_edges[0]:
return True
else:
return False
def merge(self, other):
"""Merge this energy groups with another.
Parameters
----------
other : openmc.mgxs.EnergyGroups
EnergyGroups to merge with
Returns
-------
merged_groups : openmc.mgxs.EnergyGroups
EnergyGroups resulting from the merge
"""
if not self.can_merge(other):
raise ValueError('Unable to merge energy groups')
# Create deep copy to return as merged energy groups
merged_groups = copy.deepcopy(self)
# Merge unique filter bins
merged_edges = np.concatenate((self.group_edges, other.group_edges))
merged_edges = np.unique(merged_edges)
merged_edges = sorted(merged_edges)
# Assign merged edges to merged groups
merged_groups.group_edges = list(merged_edges)
return merged_groups

View file

@ -53,22 +53,22 @@ class Library(object):
The types of cross sections in the library (e.g., ['total', 'scatter'])
domain_type : {'material', 'cell', 'distribcell', 'universe'}
Domain type for spatial homogenization
domains : Iterable of Material, Cell or Universe
domains : Iterable of openmc.Material, openmc.Cell or openmc.Universe
The spatial domain(s) for which MGXS in the Library are computed
correction : 'P0' or None
correction : {'P0', None}
Apply the P0 correction to scattering matrices if set to 'P0'
energy_groups : EnergyGroups
energy_groups : openmc.mgxs.EnergyGroups
Energy group structure for energy condensation
tally_trigger : Trigger
tally_trigger : openmc.Trigger
An (optional) tally precision trigger given to each tally used to
compute the cross section
all_mgxs : OrderedDict
all_mgxs : collections.OrderedDict
MGXS objects keyed by domain ID and cross section type
sp_filename : str
The filename of the statepoint with tally data used to the
compute cross sections
keff : Real or None
The combined keff from the statepoint file with tally data used to
The combined keff from the statepoint file with tally data used to
compute cross sections (for eigenvalue calculations only)
name : str, optional
Name of the multi-group cross section library. Used as a label to
@ -116,11 +116,11 @@ class Library(object):
clone._by_nuclide = self.by_nuclide
clone._mgxs_types = self.mgxs_types
clone._domain_type = self.domain_type
clone._domains = self.domains
clone._domains = copy.deepcopy(self.domains)
clone._correction = self.correction
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
clone._tally_trigger = copy.deepcopy(self.tally_trigger, memo)
clone._all_mgxs = self.all_mgxs
clone._all_mgxs = copy.deepcopy(self.all_mgxs)
clone._sp_filename = self._sp_filename
clone._keff = self._keff
clone._sparse = self.sparse
@ -308,7 +308,7 @@ class Library(object):
"""
cv.check_type('sparse', sparse, bool)
# Sparsify or densify each MGXS in the Library
for domain in self.domains:
for mgxs_type in self.mgxs_types:
@ -350,7 +350,7 @@ class Library(object):
def add_to_tallies_file(self, tallies_file, merge=True):
"""Add all tallies from all MGXS objects to a tallies file.
NOTE: This assumes that build_library() has been called
NOTE: This assumes that :meth:`Library.build_library` has been called
Parameters
----------
@ -426,7 +426,7 @@ class Library(object):
----------
domain : Material or Cell or Universe or Integral
The material, cell, or universe object of interest (or its ID)
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
The type of multi-group cross section object to return
Returns
@ -457,7 +457,7 @@ class Library(object):
break
else:
msg = 'Unable to find MGXS for {0} "{1}" in ' \
'library'.format(self.domain_type, domain)
'library'.format(self.domain_type, domain_id)
raise ValueError(msg)
else:
domain_id = domain.id
@ -537,7 +537,7 @@ class Library(object):
Returns
-------
Library
openmc.mgxs.Library
A new multi-group cross section library averaged across subdomains
Raises

View file

@ -25,6 +25,7 @@ MGXS_TYPES = ['total',
'capture',
'fission',
'nu-fission',
'kappa-fission',
'scatter',
'nu-scatter',
'scatter matrix',
@ -58,11 +59,11 @@ class MGXS(object):
Parameters
----------
domain : Material or Cell or Universe
domain : openmc.Material or openmc.Cell or openmc.Universe
The domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe'}
The domain type for spatial homogenization
energy_groups : EnergyGroups
energy_groups : openmc.mgxs.EnergyGroups
The energy group structure for energy condensation
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
@ -82,34 +83,38 @@ class MGXS(object):
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe'}
Domain type for spatial homogenization
energy_groups : EnergyGroups
energy_groups : openmc.mgxs.EnergyGroups
Energy group structure for energy condensation
tally_trigger : Trigger
tally_trigger : openmc.Trigger
An (optional) tally precision trigger given to each tally used to
compute the cross section
tallies : OrderedDict
tallies : collections.OrderedDict
OpenMC tallies needed to compute the multi-group cross section
rxn_rate_tally : Tally
rxn_rate_tally : openmc.Tally
Derived tally for the reaction rate tally used in the numerator to
compute the multi-group cross section. This attribute is None
unless the multi-group cross section has been computed.
xs_tally : Tally
xs_tally : openmc.Tally
Derived tally for the multi-group cross section. This attribute
is None unless the multi-group cross section has been computed.
num_subdomains : Integral
num_subdomains : int
The number of subdomains is unity for 'material', 'cell' and 'universe'
domain types. When the This is equal to the number of cell instances
for 'distribcell' domain types (it is equal to unity prior to loading
tally data from a statepoint file).
num_nuclides : Integral
num_nuclides : int
The number of nuclides for which the multi-group cross section is
being tracked. This is unity if the by_nuclide attribute is False.
nuclides : list of str or 'sum'
A list of nuclide string names (e.g., 'U-238', 'O-16') when by_nuclide
is True and 'sum' when by_nuclide is False.
nuclides : Iterable of str or 'sum'
The optional user-specified nuclides for which to compute cross
sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides
are not specified by the user, all nuclides in the spatial domain
are included. This attribute is 'sum' if by_nuclide is false.
sparse : bool
Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format
for compressed data storage
derived : bool
Whether or not the MGXS is merged from one or more other MGXS
"""
@ -122,6 +127,7 @@ class MGXS(object):
self._name = ''
self._rxn_type = None
self._by_nuclide = None
self._nuclides = None
self._domain = None
self._domain_type = None
self._energy_groups = None
@ -130,6 +136,7 @@ class MGXS(object):
self._rxn_rate_tally = None
self._xs_tally = None
self._sparse = False
self._derived = False
self.name = name
self.by_nuclide = by_nuclide
@ -150,6 +157,7 @@ class MGXS(object):
clone._name = self.name
clone._rxn_type = self.rxn_type
clone._by_nuclide = self.by_nuclide
clone._nuclides = copy.deepcopy(self._nuclides)
clone._domain = self.domain
clone._domain_type = self.domain_type
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
@ -157,6 +165,7 @@ class MGXS(object):
clone._rxn_rate_tally = copy.deepcopy(self._rxn_rate_tally, memo)
clone._xs_tally = copy.deepcopy(self._xs_tally, memo)
clone._sparse = self.sparse
clone._derived = self.derived
clone._tallies = OrderedDict()
for tally_type, tally in self.tallies.items():
@ -231,8 +240,7 @@ class MGXS(object):
@property
def num_subdomains(self):
tally = list(self.tallies.values())[0]
domain_filter = tally.find_filter(self.domain_type)
domain_filter = self.xs_tally.find_filter(self.domain_type)
return domain_filter.num_bins
@property
@ -249,6 +257,10 @@ class MGXS(object):
else:
return 'sum'
@property
def derived(self):
return self._derived
@name.setter
def name(self, name):
cv.check_type('name', name, basestring)
@ -259,6 +271,11 @@ class MGXS(object):
cv.check_type('by_nuclide', by_nuclide, bool)
self._by_nuclide = by_nuclide
@nuclides.setter
def nuclides(self, nuclides):
cv.check_iterable_type('nuclides', nuclides, basestring)
self._nuclides = nuclides
@domain.setter
def domain(self, domain):
cv.check_type('domain', domain, tuple(_DOMAINS))
@ -315,13 +332,13 @@ class MGXS(object):
Parameters
----------
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
The type of multi-group cross section object to return
domain : Material or Cell or Universe
domain : openmc.Material or openmc.Cell or openmc.Universe
The domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe'}
The domain type for spatial homogenization
energy_groups : EnergyGroups
energy_groups : openmc.mgxs.EnergyGroups
The energy group structure for energy condensation
by_nuclide : bool
If true, computes cross sections for each nuclide in domain.
@ -332,7 +349,7 @@ class MGXS(object):
Returns
-------
MGXS
openmc.mgxs.MGXS
A subclass of the abstract MGXS class for the multi-group cross
section type requested by the user
@ -352,6 +369,8 @@ class MGXS(object):
mgxs = FissionXS(domain, domain_type, energy_groups)
elif mgxs_type == 'nu-fission':
mgxs = NuFissionXS(domain, domain_type, energy_groups)
elif mgxs_type == 'kappa-fission':
mgxs = KappaFissionXS(domain, domain_type, energy_groups)
elif mgxs_type == 'scatter':
mgxs = ScatterXS(domain, domain_type, energy_groups)
elif mgxs_type == 'nu-scatter':
@ -386,8 +405,14 @@ class MGXS(object):
if self.domain is None:
raise ValueError('Unable to get all nuclides without a domain')
nuclides = self.domain.get_all_nuclides()
return nuclides.keys()
# If the user defined nuclides, return them
if self._nuclides:
return self._nuclides
# Otherwise, return all nuclides in the spatial domain
else:
nuclides = self.domain.get_all_nuclides()
return nuclides.keys()
def get_nuclide_density(self, nuclide):
"""Get the atomic number density in units of atoms/b-cm for a nuclide
@ -400,7 +425,7 @@ class MGXS(object):
Returns
-------
Real
float
The atomic number density (atom/b-cm) for the nuclide of interest
Raises
@ -439,7 +464,7 @@ class MGXS(object):
Returns
-------
ndarray of Real
numpy.ndarray of float
An array of the atomic number densities (atom/b-cm) for each of the
nuclides in the spatial domain
@ -487,11 +512,11 @@ class MGXS(object):
----------
scores : Iterable of str
Scores for each tally
all_filters : Iterable of tuple of Filter
all_filters : Iterable of tuple of openmc.Filter
Tuples of non-spatial domain filters for each tally
keys : Iterable of str
Key string used to store each tally in the tallies dictionary
estimator : {'analog' or 'tracklength'}
estimator : {'analog', 'tracklength'}
Type of estimator to use for each tally
"""
@ -510,27 +535,27 @@ class MGXS(object):
# Create each Tally needed to compute the multi group cross section
for score, key, filters in zip(scores, keys, all_filters):
self.tallies[key] = openmc.Tally(name=self.name)
self.tallies[key].add_score(score)
self.tallies[key].scores = [score]
self.tallies[key].estimator = estimator
self.tallies[key].add_filter(domain_filter)
self.tallies[key].filters = [domain_filter]
# If a tally trigger was specified, add it to each tally
if self.tally_trigger:
trigger_clone = copy.deepcopy(self.tally_trigger)
trigger_clone.add_score(score)
self.tallies[key].add_trigger(trigger_clone)
trigger_clone.scores = [score]
self.tallies[key].triggers.append(trigger_clone)
# Add all non-domain specific Filters (e.g., 'energy') to the Tally
for add_filter in filters:
self.tallies[key].add_filter(add_filter)
self.tallies[key].filters.append(add_filter)
# If this is a by-nuclide cross-section, add all nuclides to Tally
if self.by_nuclide and score != 'flux':
all_nuclides = self.domain.get_all_nuclides()
all_nuclides = self.get_all_nuclides()
for nuclide in all_nuclides:
self.tallies[key].add_nuclide(nuclide)
self.tallies[key].nuclides.append(nuclide)
else:
self.tallies[key].add_nuclide('total')
self.tallies[key].nuclides.append('total')
def _compute_xs(self):
"""Performs generic cleanup after a subclass' uses tally arithmetic to
@ -550,9 +575,9 @@ class MGXS(object):
# If computing xs for each nuclide, replace CrossNuclides with originals
if self.by_nuclide:
self.xs_tally._nuclides = []
nuclides = self.domain.get_all_nuclides()
nuclides = self.get_all_nuclides()
for nuclide in nuclides:
self.xs_tally.add_nuclide(openmc.Nuclide(nuclide))
self.xs_tally.nuclides.append(openmc.Nuclide(nuclide))
# Remove NaNs which may have resulted from divide-by-zero operations
self.xs_tally._mean = np.nan_to_num(self.xs_tally.mean)
@ -659,7 +684,7 @@ class MGXS(object):
Returns
-------
ndarray
numpy.ndarray
A NumPy array of the multi-group cross section indexed in the order
each group, subdomain and nuclide is listed in the parameters.
@ -679,7 +704,7 @@ class MGXS(object):
# Construct a collection of the domain filter bins
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=2)
for subdomain in subdomains:
filters.append(self.domain_type)
filter_bins.append((subdomain,))
@ -734,10 +759,9 @@ class MGXS(object):
# Reverse energies to align with increasing energy groups
xs = xs[:, ::-1, :]
# Eliminate trivial dimensions
xs = np.squeeze(xs)
xs = np.atleast_1d(xs)
# Eliminate trivial dimensions
xs = np.squeeze(xs)
xs = np.atleast_1d(xs)
return xs
def get_condensed_xs(self, coarse_groups):
@ -831,7 +855,7 @@ class MGXS(object):
Returns
-------
MGXS
openmc.mgxs.MGXS
A new MGXS averaged across the subdomains of interest
Raises
@ -852,19 +876,181 @@ class MGXS(object):
# Clone this MGXS to initialize the subdomain-averaged version
avg_xs = copy.deepcopy(self)
avg_xs._rxn_rate_tally = None
avg_xs._xs_tally = None
# Average each of the tallies across subdomains
for tally_type, tally in avg_xs.tallies.items():
tally_avg = tally.summation(filter_type=self.domain_type,
filter_bins=subdomains)
avg_xs.tallies[tally_type] = tally_avg
if self.derived:
avg_xs._rxn_rate_tally = avg_xs.rxn_rate_tally.average(
filter_type=self.domain_type, filter_bins=subdomains)
else:
avg_xs._rxn_rate_tally = None
avg_xs._xs_tally = None
avg_xs._domain_type = 'sum({0})'.format(self.domain_type)
# Average each of the tallies across subdomains
for tally_type, tally in avg_xs.tallies.items():
tally_avg = tally.average(filter_type=self.domain_type,
filter_bins=subdomains)
avg_xs.tallies[tally_type] = tally_avg
avg_xs._domain_type = 'avg({0})'.format(self.domain_type)
avg_xs.sparse = self.sparse
return avg_xs
def get_slice(self, nuclides=[], groups=[]):
"""Build a sliced MGXS for the specified nuclides and energy groups.
This method constructs a new MGXS to encapsulate a subset of the data
represented by this MGXS. The subset of data to include in the tally
slice is determined by the nuclides and energy groups specified in
the input parameters.
Parameters
----------
nuclides : list of str
A list of nuclide name strings
(e.g., ['U-235', 'U-238']; default is [])
groups : list of int
A list of energy group indices starting at 1 for the high energies
(e.g., [1, 2, 3]; default is [])
Returns
-------
openmc.mgxs.MGXS
A new tally which encapsulates the subset of data requested for the
nuclide(s) and/or energy group(s) requested in the parameters.
"""
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('energy_groups', groups, Integral)
# Build lists of filters and filter bins to slice
if len(groups) == 0:
filters = []
filter_bins = []
else:
filter_bins = []
for group in groups:
group_bounds = self.energy_groups.get_group_bounds(group)
filter_bins.append(group_bounds)
filter_bins = [tuple(filter_bins)]
filters = ['energy']
# Clone this MGXS to initialize the sliced version
slice_xs = copy.deepcopy(self)
slice_xs._rxn_rate_tally = None
slice_xs._xs_tally = None
# Slice each of the tallies across nuclides and energy groups
for tally_type, tally in slice_xs.tallies.items():
slice_nuclides = [nuc for nuc in nuclides if nuc in tally.nuclides]
if len(groups) != 0 and tally.contains_filter('energy'):
tally_slice = tally.get_slice(filters=filters,
filter_bins=filter_bins, nuclides=slice_nuclides)
else:
tally_slice = tally.get_slice(nuclides=slice_nuclides)
slice_xs.tallies[tally_type] = tally_slice
# Assign sliced energy group structure to sliced MGXS
if groups:
new_group_edges = []
for group in groups:
group_edges = self.energy_groups.get_group_bounds(group)
new_group_edges.extend(group_edges)
new_group_edges = np.unique(new_group_edges)
slice_xs.energy_groups.group_edges = sorted(new_group_edges)
# Assign sliced nuclides to sliced MGXS
if nuclides:
slice_xs.nuclides = nuclides
slice_xs.sparse = self.sparse
return slice_xs
def can_merge(self, other):
"""Determine if another MGXS can be merged with this one
If results have been loaded from a statepoint, then MGXS are only
mergeable along one and only one of enegy groups or nuclides.
Parameters
----------
other : openmc.mgxs.MGXS
MGXS to check for merging
"""
if not isinstance(other, type(self)):
return False
# Compare reaction type, energy groups, nuclides, domain type
if self.rxn_type != other.rxn_type:
return False
elif not self.energy_groups.can_merge(other.energy_groups):
return False
elif self.by_nuclide != other.by_nuclide:
return False
elif self.domain_type != other.domain_type:
return False
elif 'distribcell' not in self.domain_type and self.domain != other.domain:
return False
elif not self.xs_tally.can_merge(other.xs_tally):
return False
elif not self.rxn_rate_tally.can_merge(other.rxn_rate_tally):
return False
# If all conditionals pass then MGXS are mergeable
return True
def merge(self, other):
"""Merge another MGXS with this one
MGXS are only mergeable if their energy groups and nuclides are either
identical or mutually exclusive. If results have been loaded from a
statepoint, then MGXS are only mergeable along one and only one of
energy groups or nuclides.
Parameters
----------
other : openmc.mgxs.MGXS
MGXS to merge with this one
Returns
-------
merged_mgxs : openmc.mgxs.MGXS
Merged MGXS
"""
if not self.can_merge(other):
raise ValueError('Unable to merge MGXS')
# Create deep copy of tally to return as merged tally
merged_mgxs = copy.deepcopy(self)
merged_mgxs._derived = True
# Merge energy groups
if self.energy_groups != other.energy_groups:
merged_groups = self.energy_groups.merge(other.energy_groups)
merged_mgxs.energy_groups = merged_groups
# Merge nuclides
if self.nuclides != other.nuclides:
# The nuclides must be mutually exclusive
for nuclide in self.nuclides:
if nuclide in other.nuclides:
msg = 'Unable to merge MGXS with shared nuclides'
raise ValueError(msg)
# Concatenate lists of nuclides for the merged MGXS
merged_mgxs.nuclides = self.nuclides + other.nuclides
# Null base tallies but merge reaction rate and cross section tallies
merged_mgxs._tallies = OrderedDict()
merged_mgxs._rxn_rate_tally = self.rxn_rate_tally.merge(other.rxn_rate_tally)
merged_mgxs._xs_tally = self.xs_tally.merge(other.xs_tally)
return merged_mgxs
def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'):
"""Print a string representation for the multi-group cross section.
@ -1019,6 +1205,9 @@ class MGXS(object):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
elif self.domain_type == 'avg(distribcell)':
domain_filter = self.xs_tally.find_filter('avg(distribcell)')
subdomains = domain_filter.bins
else:
subdomains = [self.domain.id]
@ -1160,7 +1349,7 @@ class MGXS(object):
xs_type='macro', summary=None):
"""Build a Pandas DataFrame for the MGXS data.
This method leverages the Tally.get_pandas_dataframe(...) method, but
This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but
renames the columns with terminology appropriate for cross section data.
Parameters
@ -1177,7 +1366,7 @@ class MGXS(object):
xs_type: {'macro', 'micro'}
Return macro or micro cross section in units of cm^-1 or barns.
Defaults to 'macro'.
summary : None or Summary
summary : None or openmc.Summary
An optional Summary object to be used to construct columns for
distribcell tally filters (default is None). The geometric
information in the Summary object is embedded into a multi-index
@ -1232,28 +1421,34 @@ class MGXS(object):
# Override energy groups bounds with indices
all_groups = np.arange(self.num_groups, 0, -1, dtype=np.int)
all_groups = np.repeat(all_groups, self.num_nuclides)
if 'energy [MeV]' in df and 'energyout [MeV]' in df:
df.rename(columns={'energy [MeV]': 'group in'}, inplace=True)
if 'energy low [MeV]' in df and 'energyout low [MeV]' in df:
df.rename(columns={'energy low [MeV]': 'group in'},
inplace=True)
in_groups = np.tile(all_groups, self.num_subdomains)
in_groups = np.repeat(in_groups, self.num_groups)
in_groups = np.repeat(in_groups, df.shape[0] / in_groups.size)
df['group in'] = in_groups
del df['energy high [MeV]']
df.rename(columns={'energyout [MeV]': 'group out'}, inplace=True)
out_groups = \
np.tile(all_groups, self.num_subdomains * self.num_groups)
df.rename(columns={'energyout low [MeV]': 'group out'},
inplace=True)
out_groups = np.tile(all_groups, df.shape[0] / all_groups.size)
df['group out'] = out_groups
del df['energyout high [MeV]']
columns = ['group in', 'group out']
elif 'energyout [MeV]' in df:
df.rename(columns={'energyout [MeV]': 'group out'}, inplace=True)
elif 'energyout low [MeV]' in df:
df.rename(columns={'energyout low [MeV]': 'group out'},
inplace=True)
in_groups = np.tile(all_groups, self.num_subdomains)
df['group out'] = in_groups
del df['energyout high [MeV]']
columns = ['group out']
elif 'energy [MeV]' in df:
df.rename(columns={'energy [MeV]': 'group in'}, inplace=True)
elif 'energy low [MeV]' in df:
df.rename(columns={'energy low [MeV]': 'group in'}, inplace=True)
in_groups = np.tile(all_groups, self.num_subdomains)
df['group in'] = in_groups
del df['energy high [MeV]']
columns = ['group in']
# Select out those groups the user requested
@ -1275,8 +1470,7 @@ class MGXS(object):
# Sort the dataframe by domain type id (e.g., distribcell id) and
# energy groups such that data is from fast to thermal
df.sort([self.domain_type] + columns, inplace=True)
df.sort_values(by=[self.domain_type] + columns, inplace=True)
return df
@ -1319,7 +1513,7 @@ class TotalXS(MGXS):
@property
def rxn_rate_tally(self):
if self._rxn_rate_tally is None:
if self._rxn_rate_tally is None :
self._rxn_rate_tally = self.tallies['total']
self._rxn_rate_tally.sparse = self.sparse
return self._rxn_rate_tally
@ -1477,96 +1671,80 @@ class CaptureXS(MGXS):
self._rxn_rate_tally.sparse = self.sparse
return self._rxn_rate_tally
class FissionXSBase(MGXS):
"""A fission production multi-group cross section base class
for NuFission and KappaFission
"""
class FissionXS(MGXS):
# This is an abstract class which cannot be instantiated
__metaclass__ = abc.ABCMeta
def __init__(self, rxn_type, domain=None, domain_type=None,
groups=None, by_nuclide=False, name=''):
super(FissionXSBase, self).__init__(domain, domain_type,
groups, by_nuclide, name)
self._rxn_type = rxn_type
@property
def tallies(self):
"""Construct the OpenMC tallies needed to compute this cross section.
This method constructs two tracklength tallies to compute the 'flux'
and 'rxn_type' reaction rates in the spatial domain and energy
groups of interest.
"""
# Instantiate tallies if they do not exist
if self._tallies is None:
# Create a list of scores for each Tally to be created
scores = ['flux', self._rxn_type]
estimator = 'tracklength'
keys = scores
# Create the non-domain specific Filters for the Tallies
group_edges = self.energy_groups.group_edges
energy_filter = openmc.Filter('energy', group_edges)
filters = [[energy_filter], [energy_filter]]
# Initialize the Tallies
self._create_tallies(scores, filters, keys, estimator)
return self._tallies
@property
def rxn_rate_tally(self):
if self._rxn_rate_tally is None:
self._rxn_rate_tally = self.tallies[self._rxn_type]
self._rxn_rate_tally.sparse = self.sparse
return self._rxn_rate_tally
class FissionXS(FissionXSBase):
"""A fission multi-group cross section."""
def __init__(self, domain=None, domain_type=None,
groups=None, by_nuclide=False, name=''):
super(FissionXS, self).__init__(domain, domain_type,
super(FissionXS, self).__init__('fission', domain, domain_type,
groups, by_nuclide, name)
self._rxn_type = 'fission'
@property
def tallies(self):
"""Construct the OpenMC tallies needed to compute this cross section.
This method constructs two tracklength tallies to compute the 'flux'
and 'fission' reaction rates in the spatial domain and energy
groups of interest.
"""
# Instantiate tallies if they do not exist
if self._tallies is None:
# Create a list of scores for each Tally to be created
scores = ['flux', 'fission']
estimator = 'tracklength'
keys = scores
# Create the non-domain specific Filters for the Tallies
group_edges = self.energy_groups.group_edges
energy_filter = openmc.Filter('energy', group_edges)
filters = [[energy_filter], [energy_filter]]
# Initialize the Tallies
self._create_tallies(scores, filters, keys, estimator)
return self._tallies
@property
def rxn_rate_tally(self):
if self._rxn_rate_tally is None:
self._rxn_rate_tally = self.tallies['fission']
self._rxn_rate_tally.sparse = self.sparse
return self._rxn_rate_tally
class NuFissionXS(MGXS):
class NuFissionXS(FissionXSBase):
"""A fission production multi-group cross section."""
def __init__(self, domain=None, domain_type=None,
groups=None, by_nuclide=False, name=''):
super(NuFissionXS, self).__init__(domain, domain_type,
super(NuFissionXS, self).__init__('nu-fission', domain, domain_type,
groups, by_nuclide, name)
self._rxn_type = 'nu-fission'
@property
def tallies(self):
"""Construct the OpenMC tallies needed to compute this cross section.
This method constructs two tracklength tallies to compute the 'flux'
and 'nu-fission' reaction rates in the spatial domain and energy
groups of interest.
"""
# Instantiate tallies if they do not exist
if self._tallies is None:
# Create a list of scores for each Tally to be created
scores = ['flux', 'nu-fission']
estimator = 'tracklength'
keys = scores
# Create the non-domain specific Filters for the Tallies
group_edges = self.energy_groups.group_edges
energy_filter = openmc.Filter('energy', group_edges)
filters = [[energy_filter], [energy_filter]]
# Initialize the Tallies
self._create_tallies(scores, filters, keys, estimator)
return self._tallies
@property
def rxn_rate_tally(self):
if self._rxn_rate_tally is None:
self._rxn_rate_tally = self.tallies['nu-fission']
self._rxn_rate_tally.sparse = self.sparse
return self._rxn_rate_tally
class KappaFissionXS(FissionXSBase):
"""A recoverable fission energy production rate multi-group cross section."""
def __init__(self, domain=None, domain_type=None,
groups=None, by_nuclide=False, name=''):
super(KappaFissionXS, self).__init__('kappa-fission', domain, domain_type,
groups, by_nuclide, name)
class ScatterXS(MGXS):
"""A scatter multi-group cross section."""
@ -1741,6 +1919,58 @@ class ScatterMatrixXS(MGXS):
cv.check_value('correction', correction, ('P0', None))
self._correction = correction
def get_slice(self, nuclides=[], in_groups=[], out_groups=[]):
"""Build a sliced ScatterMatrix for the specified nuclides and
energy groups.
This method constructs a new MGXS to encapsulate a subset of the data
represented by this MGXS. The subset of data to include in the tally
slice is determined by the nuclides and energy groups specified in
the input parameters.
Parameters
----------
nuclides : list of str
A list of nuclide name strings
(e.g., ['U-235', 'U-238']; default is [])
in_groups : list of int
A list of incoming energy group indices starting at 1 for the high
energies (e.g., [1, 2, 3]; default is [])
out_groups : list of int
A list of outgoing energy group indices starting at 1 for the high
energies (e.g., [1, 2, 3]; default is [])
Returns
-------
openmc.mgxs.MGXS
A new tally which encapsulates the subset of data requested for the
nuclide(s) and/or energy group(s) requested in the parameters.
"""
# Call super class method and null out derived tallies
slice_xs = super(ScatterMatrixXS, self).get_slice(nuclides, in_groups)
slice_xs._rxn_rate_tally = None
slice_xs._xs_tally = None
# Slice outgoing energy groups if needed
if len(out_groups) != 0:
filter_bins = []
for group in out_groups:
group_bounds = self.energy_groups.get_group_bounds(group)
filter_bins.append(group_bounds)
filter_bins = [tuple(filter_bins)]
# Slice each of the tallies across energyout groups
for tally_type, tally in slice_xs.tallies.items():
if tally.contains_filter('energyout'):
tally_slice = tally.get_slice(filters=['energyout'],
filter_bins=filter_bins)
slice_xs.tallies[tally_type] = tally_slice
slice_xs.sparse = self.sparse
return slice_xs
def get_xs(self, in_groups='all', out_groups='all',
subdomains='all', nuclides='all', xs_type='macro',
order_groups='increasing', value='mean'):
@ -1795,7 +2025,7 @@ class ScatterMatrixXS(MGXS):
# Construct a collection of the domain filter bins
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=2)
for subdomain in subdomains:
filters.append(self.domain_type)
filter_bins.append((subdomain,))
@ -2080,10 +2310,117 @@ class Chi(MGXS):
super(Chi, self)._compute_xs()
# Add the coarse energy filter back to the nu-fission tally
nu_fission_in.add_filter(energy_filter)
nu_fission_in.filters.append(energy_filter)
return self._xs_tally
def get_slice(self, nuclides=[], groups=[]):
"""Build a sliced Chi for the specified nuclides and energy groups.
This method constructs a new MGXS to encapsulate a subset of the data
represented by this MGXS. The subset of data to include in the tally
slice is determined by the nuclides and energy groups specified in
the input parameters.
Parameters
----------
nuclides : list of str
A list of nuclide name strings
(e.g., ['U-235', 'U-238']; default is [])
groups : list of Integral
A list of energy group indices starting at 1 for the high energies
(e.g., [1, 2, 3]; default is [])
Returns
-------
MGXS
A new tally which encapsulates the subset of data requested for the
nuclide(s) and/or energy group(s) requested in the parameters.
"""
# Temporarily remove energy filter from nu-fission-in since its
# group structure will work in super MGXS.get_slice(...) method
nu_fission_in = self.tallies['nu-fission-in']
energy_filter = nu_fission_in.find_filter('energy')
nu_fission_in.remove_filter(energy_filter)
# Call super class method and null out derived tallies
slice_xs = super(Chi, self).get_slice(nuclides, groups)
slice_xs._rxn_rate_tally = None
slice_xs._xs_tally = None
# Slice energy groups if needed
if len(groups) != 0:
filter_bins = []
for group in groups:
group_bounds = self.energy_groups.get_group_bounds(group)
filter_bins.append(group_bounds)
filter_bins = [tuple(filter_bins)]
# Slice nu-fission-out tally along energyout filter
nu_fission_out = slice_xs.tallies['nu-fission-out']
tally_slice = nu_fission_out.get_slice(filters=['energyout'],
filter_bins=filter_bins)
slice_xs._tallies['nu-fission-out'] = tally_slice
# Add energy filter back to nu-fission-in tallies
self.tallies['nu-fission-in'].add_filter(energy_filter)
slice_xs._tallies['nu-fission-in'].add_filter(energy_filter)
slice_xs.sparse = self.sparse
return slice_xs
def merge(self, other):
"""Merge another Chi with this one
If results have been loaded from a statepoint, then Chi are only
mergeable along one and only one of energy groups or nuclides.
Parameters
----------
other : openmc.mgxs.MGXS
MGXS to merge with this one
Returns
-------
merged_mgxs : openmc.mgxs.MGXS
Merged MGXS
"""
if not self.can_merge(other):
raise ValueError('Unable to merge Chi')
# Create deep copy of tally to return as merged tally
merged_mgxs = copy.deepcopy(self)
merged_mgxs._derived = True
merged_mgxs._rxn_rate_tally = None
merged_mgxs._xs_tally = None
# Merge energy groups
if self.energy_groups != other.energy_groups:
merged_groups = self.energy_groups.merge(other.energy_groups)
merged_mgxs.energy_groups = merged_groups
# Merge nuclides
if self.nuclides != other.nuclides:
# The nuclides must be mutually exclusive
for nuclide in self.nuclides:
if nuclide in other.nuclides:
msg = 'Unable to merge Chi with shared nuclides'
raise ValueError(msg)
# Concatenate lists of nuclides for the merged MGXS
merged_mgxs.nuclides = self.nuclides + other.nuclides
# Merge tallies
for tally_key in self.tallies:
merged_tally = self.tallies[tally_key].merge(other.tallies[tally_key])
merged_mgxs.tallies[tally_key] = merged_tally
return merged_mgxs
def get_xs(self, groups='all', subdomains='all', nuclides='all',
xs_type='macro', order_groups='increasing', value='mean'):
"""Returns an array of the fission spectrum.
@ -2115,7 +2452,7 @@ class Chi(MGXS):
Returns
-------
ndarray
numpy.ndarray
A NumPy array of the multi-group cross section indexed in the order
each group, subdomain and nuclide is listed in the parameters.
@ -2135,7 +2472,7 @@ class Chi(MGXS):
# Construct a collection of the domain filter bins
if not isinstance(subdomains, basestring):
cv.check_iterable_type('subdomains', subdomains, Integral)
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=2)
for subdomain in subdomains:
filters.append(self.domain_type)
filter_bins.append((subdomain,))
@ -2172,7 +2509,7 @@ class Chi(MGXS):
xs_tally = nu_fission_out / nu_fission_in
# Add the coarse energy filter back to the nu-fission tally
nu_fission_in.add_filter(energy_filter)
nu_fission_in.filters.append(energy_filter)
xs = xs_tally.get_values(filters=filters,
filter_bins=filter_bins, value=value)
@ -2223,7 +2560,7 @@ class Chi(MGXS):
xs_type='macro', summary=None):
"""Build a Pandas DataFrame for the MGXS data.
This method leverages the Tally.get_pandas_dataframe(...) method, but
This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but
renames the columns with terminology appropriate for cross section data.
Parameters
@ -2240,7 +2577,7 @@ class Chi(MGXS):
xs_type: {'macro', 'micro'}
Return macro or micro cross section in units of cm^-1 or barns.
Defaults to 'macro'.
summary : None or Summary
summary : None or openmc.Summary
An optional Summary object to be used to construct columns for
distribcell tally filters (default is None). The geometric
information in the Summary object is embedded into a multi-index

797
openmc/mgxs_library.py Normal file
View file

@ -0,0 +1,797 @@
from collections import Iterable
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import warnings
import sys
if sys.version_info[0] >= 3:
basestring = str
import numpy as np
import openmc
from openmc.mgxs import EnergyGroups
from openmc.checkvalue import check_type, check_value, check_greater_than, \
check_iterable_type
from openmc.clean_xml import *
# Supported incoming particle MGXS angular treatment representations
_REPRESENTATIONS = ['isotropic', 'angle']
def ndarray_to_string(arr):
"""Converts a numpy ndarray in to a join with spaces between entries
similar to ' '.join(map(str,arr)) but applied to all sub-dimensions.
Parameters
----------
arr : numpy.ndarray
Array to combine in to a string
Returns
-------
text : str
String representation of array in arr
"""
shape = arr.shape
ndim = arr.ndim
tab = ' '
indent = '\n' + tab + tab
text = indent
if ndim == 1:
text += tab
for i in range(shape[0]):
text += '{:.7E} '.format(arr[i])
text += indent
elif ndim == 2:
for i in range(shape[0]):
text += tab
for j in range(shape[1]):
text += '{:.7E} '.format(arr[i, j])
text += indent
elif ndim == 3:
for i in range(shape[0]):
for j in range(shape[1]):
text += tab
for k in range(shape[2]):
text += '{:.7E} '.format(arr[i, j, k])
text += indent
elif ndim == 4:
for i in range(shape[0]):
for j in range(shape[1]):
for k in range(shape[2]):
text += tab
for l in range(shape[3]):
text += '{:.7E} '.format(arr[i, j, k, l])
text += indent
elif ndim == 5:
for i in range(shape[0]):
for j in range(shape[1]):
for k in range(shape[2]):
for l in range(shape[3]):
text += tab
for m in range(shape[4]):
text += '{:.7E} '.format(arr[i, j, k, l, m])
text += indent
return text
class XSdata(object):
"""A multi-group cross section data set providing all the
multi-group data necessary for a multi-group OpenMC calculation.
Parameters
----------
name : str, optional
Name of the mgxs data set.
energy_groups : openmc.mgxs.EnergyGroups
Energygroup structure
representation : {'isotropic', 'angle'}, optional
Method used in generating the MGXS (isotropic or angle-dependent flux
weighting). Defaults to 'isotropic'
Attributes
----------
name : str
Unique identifier for the xsdata object
alias : str
Separate unique identifier for the xsdata object
kT : float
Temperature (in units of MeV).
energy_groups : openmc.mgxs.EnergyGroups
Energy group structure
fissionable : bool
Whether or not this is a fissionable data set.
scatt_type : {'legendre', 'histogram', or 'tabular'}
Angular distribution representation (legendre, histogram, or tabular)
order : int
Either the Legendre order, number of bins, or number of points used to
describe the angular distribution associated with each group-to-group
transfer probability.
tabular_legendre : dict
Set how to treat the Legendre scattering kernel (tabular or leave in
Legendre polynomial form). Dict contains two keys: 'enable' and
'num_points'. 'enable' is a boolean and 'num_points' is the
number of points to use, if 'enable' is True.
num_azimuthal : int
Number of equal width angular bins that the azimuthal angular domain is
subdivided into. This only applies when ``representation`` is "angle".
num_polar : int
Number of equal width angular bins that the polar angular domain is
subdivided into. This only applies when ``representation`` is "angle".
total : numpy.ndarray
Group-wise total cross section ordered by increasing group index (i.e.,
fast to thermal). If ``representation`` is "isotropic", then the length
of this list should equal the number of groups described in the
``groups`` element. If ``representation`` is "angle", then the length
of this list should equal the number of groups times the number of
azimuthal angles times the number of polar angles, with the
inner-dimension being groups, intermediate-dimension being azimuthal
angles and outer-dimension being the polar angles.
absorption : numpy.ndarray
Group-wise absorption cross section ordered by increasing group index
(i.e., fast to thermal). If ``representation`` is "isotropic", then the
length of this list should equal the number of groups described in the
``groups`` attribute. If ``representation`` is "angle", then the length
of this list should equal the number of groups times the number of
azimuthal angles times the number of polar angles, with the
inner-dimension being groups, intermediate-dimension being azimuthal
angles and outer-dimension being the polar angles.
scatter : numpy.ndarray
Scattering moment matrices presented with the columns representing
incoming group and rows representing the outgoing group. That is,
down-scatter will be above the diagonal of the resultant matrix. This
matrix is repeated for every Legendre order (in order of increasing
orders) if ``scatt_type`` is "legendre"; otherwise, this matrix is
repeated for every bin of the histogram or tabular representation.
Finally, if ``representation`` is "angle", the above is repeated for
every azimuthal angle and every polar angle, in that order.
multiplicity : numpy.ndarray
Ratio of neutrons produced in scattering collisions to the neutrons
which undergo scattering collisions; that is, the multiplicity provides
the code with a scaling factor to account for neutrons being produced in
(n,xn) reactions. This information is assumed isotropic and therefore
does not need to be repeated for every Legendre moment or
histogram/tabular bin. This matrix follows the same arrangement as
described for the ``scatter`` attribute, with the exception of the data
needed to provide the scattering type information.
fission : numpy.ndarray
Group-wise fission cross section ordered by increasing group index
(i.e., fast to thermal). If ``representation`` is "isotropic", then the
length of this list should equal the number of groups described in the
``groups`` attribute. If ``representation`` is "angle", then the length
of this list should equal the number of groups times the number of
azimuthal angles times the number of polar angles, with the
inner-dimension being groups, intermediate-dimension being azimuthal
angles and outer-dimension being the polar angles.
k_fission : numpy.ndarray
Group-wise kappa-fission cross section ordered by increasing group index
(i.e., fast to thermal). If ``representation`` is "isotropic", then the
length of this list should equal the number of groups described in the
``groups`` attribute. If ``representation`` is "angle", then the length
of this list should equal the number of groups times the number of
azimuthal angles times the number of polar angles, with the
inner-dimension being groups, intermediate-dimension being azimuthal
angles and outer-dimension being the polar angles.
chi : numpy.ndarray
Group-wise fission spectra ordered by increasing group index (i.e., fast
to thermal). This attribute should be used if making the common
approximation that the fission spectra does not depend on incoming
energy. If the user does not wish to make this approximation, then this
should not be provided and this information included in the
``nu_fission`` element instead. If ``representation`` is "isotropic",
then the length of this list should equal the number of groups described
in the ``groups`` element. If ``representation`` is "angle", then the
length of this list should equal the number of groups times the number
of azimuthal angles times the number of polar angles, with the
inner-dimension being groups, intermediate-dimension being azimuthal
angles and outer-dimension being the polar angles.
nu_fission : numpy.ndarray
Group-wise fission production cross section vector (i.e., if ``chi`` is
provided), or is the group-wise fission production matrix. If providing
the vector, it should be ordered the same as the ``fission`` data. If
providing the matrix, it should be ordered the same as the
``multiplicity`` matrix.
"""
def __init__(self, name, energy_groups, representation="isotropic"):
# Initialize class attributes
self._name = name
self._energy_groups = energy_groups
self._representation = representation
self._alias = None
self._kT = None
self._fissionable = False
self._scatt_type = 'legendre'
self._order = None
self._tabular_legendre = None
self._num_polar = None
self._num_azimuthal = None
self._total = None
self._absorption = None
self._scatter = None
self._multiplicity = None
self._fission = None
self._nu_fission = None
self._k_fission = None
self._chi = None
self._use_chi = None
@property
def name(self):
return self._name
@property
def energy_groups(self):
return self._energy_groups
@property
def representation(self):
return self._representation
@property
def alias(self):
return self._alias
@property
def kT(self):
return self._kT
@property
def scatt_type(self):
return self._scatt_type
@property
def order(self):
return self._order
@property
def tabular_legendre(self):
return self._tabular_legendre
@property
def num_polar(self):
return self._num_polar
@property
def num_azimuthal(self):
return self._num_azimuthal
@property
def total(self):
return self._total
@property
def absorption(self):
return self._absorption
@property
def scatter(self):
return self._scatter
@property
def multiplicity(self):
return self._multiplicity
@property
def fission(self):
return self._fission
@property
def nu_fission(self):
return self._nu_fission
@property
def k_fission(self):
return self._k_fission
@property
def chi(self):
return self._chi
@property
def num_orders(self):
if (self._order is not None) and (self._scatt_type is not None):
if self._scatt_type is 'legendre':
return self._order + 1
else:
return self._order
@name.setter
def name(self, name):
check_type('name for XSdata', name, basestring)
self._name = name
@energy_groups.setter
def energy_groups(self, energy_groups):
# Check validity of energy_groups
check_type("energy_groups", energy_groups, EnergyGroups)
# Check that there is one or more groups
if ((energy_groups.num_groups is None) or
(energy_groups.num_groups < 1)):
msg = 'energy_groups object incorrectly initialized.'
raise ValueError(msg)
self._energy_groups = energy_groups
@representation.setter
def representation(self, representation):
# Check it is of valid type.
check_value('representation', representation, _REPRESENTATIONS)
self._representation = representation
@alias.setter
def alias(self, alias):
if alias is not None:
check_type('alias', alias, basestring)
self._alias = alias
else:
self._alias = self._name
@kT.setter
def kT(self, kT):
# Check validity of type and that the kT value is >= 0
check_type("kT", kT, Real)
check_greater_than("kT", kT, 0.0, equality=True)
self._kT = kT
@scatt_type.setter
def scatt_type(self, scatt_type):
# check to see it is of a valid type and value
check_value("scatt_type", scatt_type, ['legendre', 'histogram',
'tabular'])
self._scatt_type = scatt_type
@order.setter
def order(self, order):
# Check type and value
check_type("order", order, Integral)
check_greater_than("order", order, 0, equality=True)
self._order = order
@tabular_legendre.setter
def tabular_legendre(self, tabular_legendre):
# Check to make sure this is a dict and it has our keys with the
# right values.
check_type("tabular_legendre", tabular_legendre, dict)
if 'enable' in tabular_legendre:
enable = tabular_legendre['enable']
check_type('enable', enable, bool)
else:
msg = "enable must be provided in tabular_legendre"
raise ValueError(msg)
if 'num_points' in tabular_legendre:
num_points = tabular_legendre['num_points']
check_value('num_points', num_points, Integral)
check_greater_than('num_points', num_points, 0)
else:
if not enable:
num_points = 1
else:
num_points = 33
self._tabular_legendre = {'enable': enable, 'num_points': num_points}
@num_polar.setter
def num_polar(self, num_polar):
# Make sure we have positive ints
check_value("num_polar", num_polar, Integral)
check_greater_than("num_polar", num_polar, 0)
self._num_polar = num_polar
@num_azimuthal.setter
def num_azimuthal(self, num_azimuthal):
check_value("num_azimuthal", num_azimuthal, Integral)
check_greater_than("num_azimuthal", num_azimuthal, 0)
self._num_azimuthal = num_azimuthal
@total.setter
def total(self, total):
if self._representation is 'isotropic':
shape = (self._energy_groups.num_groups,)
elif self._representation is 'angle':
shape = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups)
# check we have a numpy list
check_type("total", total, np.ndarray, expected_iter_type=Real)
if total.shape == shape:
self._total = np.copy(total)
else:
msg = 'Shape of provided total "{0}" does not match shape ' \
'required, "{1}"'.format(total.shape, shape)
raise ValueError(msg)
@absorption.setter
def absorption(self, absorption):
if self._representation is 'isotropic':
shape = (self._energy_groups.num_groups,)
elif self._representation is 'angle':
shape = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups)
# check we have a numpy list
check_type("absorption", absorption, np.ndarray, expected_iter_type=Real)
if absorption.shape == shape:
self._absorption = np.copy(absorption)
else:
msg = 'Shape of provided absorption "{0}" does not match shape ' \
'required, "{1}"'.format(absorption.shape, shape)
raise ValueError(msg)
@fission.setter
def fission(self, fission):
if self._representation is 'isotropic':
shape = (self._energy_groups.num_groups,)
elif self._representation is 'angle':
shape = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups)
# check we have a numpy list
check_type("fission", fission, np.ndarray, expected_iter_type=Real)
if fission.shape == shape:
self._fission = np.copy(fission)
if np.sum(self._fission) > 0.0:
self._fissionable = True
else:
msg = 'Shape of provided fission "{0}" does not match shape ' \
'required, "{1}"'.format(fission.shape, shape)
raise ValueError(msg)
@k_fission.setter
def k_fission(self, k_fission):
if self._representation is 'isotropic':
shape = (self._energy_groups.num_groups,)
elif self._representation is 'angle':
shape = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups)
# check we have a numpy list
check_type("k_fission", k_fission, np.ndarray, expected_iter_type=Real)
if k_fission.shape == shape:
self._k_fission = np.copy(k_fission)
if np.sum(self._k_fission) > 0.0:
self._fissionable = True
else:
msg = 'Shape of provided k_fission "{0}" does not match shape ' \
'required, "{1}"'.format(k_fission.shape, shape)
raise ValueError(msg)
@chi.setter
def chi(self, chi):
if not self._use_chi:
msg = 'Providing chi when nu_fission already provided as matrix!'
raise ValueError(msg)
if self._representation is 'isotropic':
shape = (self._energy_groups.num_groups,)
elif self._representation is 'angle':
shape = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups)
# check we have a numpy list
check_type("chi", chi, np.ndarray, expected_iter_type=Real)
if chi.shape == shape:
self._chi = np.copy(chi)
else:
msg = 'Shape of provided chi "{0}" does not match shape ' \
'required, "{1}"'.format(chi.shape, shape)
raise ValueError(msg)
if self._use_chi is not None:
self._use_chi = True
@scatter.setter
def scatter(self, scatter):
if self._representation is 'isotropic':
shape = (self.num_orders, self._energy_groups.num_groups,
self._energy_groups.num_groups)
max_depth = 3
elif self._representation is 'angle':
shape = (self._num_polar, self._num_azimuthal, self.num_orders,
self._energy_groups.num_groups,
self._energy_groups.num_groups)
max_depth = 5
# check we have a numpy list
check_iterable_type("scatter", scatter, expected_type=Real,
max_depth=max_depth)
if scatter.shape == shape:
self._scatter = np.copy(scatter)
else:
msg = 'Shape of provided scatter "{0}" does not match shape ' \
'required, "{1}"'.format(scatter.shape, shape)
raise ValueError(msg)
@multiplicity.setter
def multiplicity(self, multiplicity):
if self._representation is 'isotropic':
shape = (self._energy_groups.num_groups,
self._energy_groups.num_groups)
max_depth = 2
elif self._representation is 'angle':
shape = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups,
self._energy_groups.num_groups)
max_depth = 4
# check we have a numpy list
check_iterable_type("multiplicity", multiplicity, expected_type=Real,
max_depth=max_depth)
if multiplicity.shape == shape:
self._multiplicity = np.copy(multiplicity)
else:
msg = 'Shape of provided multiplicity "{0}" does not match shape ' \
'required, "{1}"'.format(multiplicity.shape, shape)
raise ValueError(msg)
@nu_fission.setter
def nu_fission(self, nu_fission):
# nu_fission can be given as a vector or a matrix
# Vector is used when chi also exists.
# Matrix is used when chi does not exist.
# We have to check that the correct form is given, but only if
# chi already has been set. If not, we just check that this is OK
# and set the use_chi flag.
# First lets set our dimensions here since they get used repeatedly
# throughout this code.
if self._representation is 'isotropic':
shape_vec = (self._energy_groups.num_groups,)
shape_mat = (self._energy_groups.num_groups,
self._energy_groups.num_groups)
elif self._representation is 'angle':
shape_vec = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups)
shape_mat = (self._num_polar, self._num_azimuthal,
self._energy_groups.num_groups,
self._energy_groups.num_groups)
# Begin by checking the case when chi has already been given and thus
# the rules for filling in nu_fission are set.
if self._use_chi is not None:
if self._use_chi:
shape = shape_vec
else:
shape = shape_mat
if nu_fission.shape != shape:
msg = "Invalid Shape of Nu_fission!"
raise ValueError(msg)
else:
# Get shape of nu_fission so we can figure if we need chi or not
if nu_fission.shape == shape_vec:
self._use_chi = True
shape = shape_vec
elif nu_fission.shape == shape_mat:
self._use_chi = False
shape = shape_mat
else:
msg = "Invalid Shape of Nu_fission!"
raise ValueError(msg)
# check we have a numpy list
check_type("nu_fission", nu_fission, np.ndarray, expected_iter_type=Real)
self._nu_fission = np.copy(nu_fission)
if np.sum(self._nu_fission) > 0.0:
self._fissionable = True
def _get_xsdata_xml(self):
element = ET.Element("xsdata")
element.set("name", self._name)
if self._alias is not None:
subelement = ET.SubElement(element, 'alias')
subelement.text = self.alias
if self._kT is not None:
subelement = ET.SubElement(element, 'kT')
subelement.text = str(self._kT)
if self._fissionable is not None:
subelement = ET.SubElement(element, 'fissionable')
subelement.text = str(self._fissionable)
if self._representation is not None:
subelement = ET.SubElement(element, 'representation')
subelement.text = self._representation
if self._representation == 'angle':
if self._num_azimuthal is not None:
subelement = ET.SubElement(element, 'num_azimuthal')
subelement.text = str(self._num_azimuthal)
if self._num_polar is not None:
subelement = ET.SubElement(element, 'num_polar')
subelement.text = str(self._num_polar)
if self._scatt_type is not None:
subelement = ET.SubElement(element, 'scatt_type')
subelement.text = self._scatt_type
if self._order is not None:
subelement = ET.SubElement(element, 'order')
subelement.text = str(self._order)
if self._tabular_legendre is not None:
subelement = ET.SubElement(element, 'tabular_legendre')
subelement.set('enable', str(self._tabular_legendre['enable']))
subelement.set('num_points', str(self._tabular_legendre['num_points']))
if self._total is not None:
subelement = ET.SubElement(element, 'total')
subelement.text = ndarray_to_string(self._total)
if self._absorption is not None:
subelement = ET.SubElement(element, 'absorption')
subelement.text = ndarray_to_string(self._absorption)
if self._scatter is not None:
subelement = ET.SubElement(element, 'scatter')
subelement.text = ndarray_to_string(self._scatter)
if self._multiplicity is not None:
subelement = ET.SubElement(element, 'multiplicity')
subelement.text = ndarray_to_string(self._multiplicity)
if self._fissionable:
if self._fission is not None:
subelement = ET.SubElement(element, 'fission')
subelement.text = ndarray_to_string(self._fission)
if self._k_fission is not None:
subelement = ET.SubElement(element, 'k_fission')
subelement.text = ndarray_to_string(self._k_fission)
if self._nu_fission is not None:
subelement = ET.SubElement(element, 'nu_fission')
subelement.text = ndarray_to_string(self._nu_fission)
if self._chi is not None:
subelement = ET.SubElement(element, 'chi')
subelement.text = ndarray_to_string(self._chi)
return element
class MGXSLibraryFile(object):
"""Multi-Group Cross Sections file used for an OpenMC simulation.
Corresponds directly to the MG version of the cross_sections.xml input file.
Attributes
----------
energy_groups : openmc.mgxs.EnergyGroups
Energy group structure.
inverse_velocities : Iterable of Real
Inverse of velocities, units of sec/cm
xsdatas : Iterable of openmc.XSdata
Iterable of multi-Group cross section data objects
"""
def __init__(self, energy_groups):
# Initialize MGXSLibraryFile class attributes
self._xsdatas = []
self._energy_groups = energy_groups
self._inverse_velocities = None
self._cross_sections_file = ET.Element("cross_sections")
@property
def inverse_velocities(self):
return self._inverse_velocities
@property
def energy_groups(self):
return self._energy_groups
@inverse_velocities.setter
def inverse_velocities(self, inverse_velocities):
cv.check_type('inverse_velocities', inverse_velocities, Iterable, Real)
cv.check_greater_than('number of inverse_velocities',
len(inverse_velocities), 0.0)
self._inverse_velocities = np.array(inverse_velocities)
@energy_groups.setter
def energy_groups(self, energy_groups):
check_type("energy groups", energy_groups, EnergyGroups)
self._energy_groups = energy_groups
def add_xsdata(self, xsdata):
"""Add an XSdata entry to the file.
Parameters
----------
xsdata : openmc.XSdata
MGXS information to add
"""
# Check the type
if not isinstance(xsdata, XSdata):
msg = 'Unable to add a non-XSdata "{0}" to the ' \
'MGXSLibraryFile'.format(xsdata)
raise ValueError(msg)
# Make sure energy groups match.
if xsdata.energy_groups != self._energy_groups:
msg = 'Energy groups of XSdata do not match that of MGXSLibraryFile!'
raise ValueError(msg)
self._xsdatas.append(xsdata)
def add_xsdatas(self, xsdatas):
"""Add multiple xsdatas to the file.
Parameters
----------
xsdatas : tuple or list of openmc.XSdata
XSdatas to add
"""
if not isinstance(xsdatas, Iterable):
msg = 'Unable to create OpenMC xsdatas.xml file from "{0}" which ' \
'is not iterable'.format(xsdatas)
raise ValueError(msg)
for xsdata in xsdatas:
self.add_xsdata(xsdata)
def remove_xsdata(self, xsdata):
"""Remove a xsdata from the file
Parameters
----------
xsdata : openmc.XSdata
XSdata to remove
"""
if not isinstance(xsdata, XSdata):
msg = 'Unable to remove a non-XSdata "{0}" from the ' \
'XSdatasFile'.format(xsdata)
raise ValueError(msg)
self._xsdatas.remove(xsdata)
def _create_groups_subelement(self):
if self._energy_groups is not None:
element = ET.SubElement(self._cross_sections_file, "groups")
element.text = str(self._energy_groups.num_groups)
def _create_group_structure_subelement(self):
if self._energy_groups is not None:
element = ET.SubElement(self._cross_sections_file,
"group_structure")
element.text = ' '.join(map(str, self._energy_groups.group_edges))
def _create_inverse_velocities_subelement(self):
if self._inverse_velocities is not None:
element = ET.SubElement(self._cross_sections_file,
"inverse_velocities")
element.text = ' '.join(map(str, self._inverse_velocities))
def _create_xsdata_subelements(self):
for xsdata in self._xsdatas:
xml_element = xsdata._get_xsdata_xml()
self._cross_sections_file.append(xml_element)
def export_to_xml(self, filename='mg_cross_sections.xml'):
"""Create an mg_cross_sections.xml file that can be used for a
simulation.
Parameters
----------
filename : str, optional
filename of file, default is mg_cross_sections.xml
"""
# Reset xml element tree
self._cross_sections_file.clear()
self._create_groups_subelement()
self._create_group_structure_subelement()
self._create_inverse_velocities_subelement()
self._create_xsdata_subelements()
# Clean the indentation in the file to be user-readable
sort_xml_elements(self._cross_sections_file)
clean_xml_indentation(self._cross_sections_file)
# Write the XML Tree to the xsdatas.xml file
tree = ET.ElementTree(self._cross_sections_file)
tree.write(filename, xml_declaration=True,
encoding='utf-8', method="xml")

View file

@ -60,6 +60,12 @@ class Nuclide(object):
def __ne__(self, other):
return not self == other
def __gt__(self, other):
return repr(self) > repr(other)
def __lt__(self, other):
return not self > other
def __hash__(self):
return hash(repr(self))

View file

@ -11,6 +11,7 @@ except ImportError:
import openmc
from openmc.region import Intersection
from openmc.surface import Halfspace
import openmc.checkvalue as cv
# A dictionary of all OpenMC Materials created
@ -79,10 +80,7 @@ def get_opencg_material(openmc_material):
"""
if not isinstance(openmc_material, openmc.Material):
msg = 'Unable to create an OpenCG Material from "{0}" ' \
'which is not an OpenMC Material'.format(openmc_material)
raise ValueError(msg)
cv.check_type('openmc_material', openmc_material, openmc.Material)
global OPENCG_MATERIALS
material_id = openmc_material.id
@ -119,10 +117,7 @@ def get_openmc_material(opencg_material):
"""
if not isinstance(opencg_material, opencg.Material):
msg = 'Unable to create an OpenMC Material from "{0}" ' \
'which is not an OpenCG Material'.format(opencg_material)
raise ValueError(msg)
cv.check_type('opencg_material', opencg_material, opencg.Material)
global OPENMC_MATERIALS
material_id = opencg_material.id
@ -165,10 +160,7 @@ def is_opencg_surface_compatible(opencg_surface):
"""
if not isinstance(opencg_surface, opencg.Surface):
msg = 'Unable to check if OpenCG Surface is compatible' \
'since "{0}" is not a Surface'.format(opencg_surface)
raise ValueError(msg)
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
if opencg_surface.type in ['x-squareprism',
'y-squareprism', 'z-squareprism']:
@ -192,10 +184,7 @@ def get_opencg_surface(openmc_surface):
"""
if not isinstance(openmc_surface, openmc.Surface):
msg = 'Unable to create an OpenCG Surface from "{0}" ' \
'which is not an OpenMC Surface'.format(openmc_surface)
raise ValueError(msg)
cv.check_type('openmc_surface', openmc_surface, openmc.Surface)
global OPENCG_SURFACES
surface_id = openmc_surface.id
@ -278,10 +267,7 @@ def get_openmc_surface(opencg_surface):
"""
if not isinstance(opencg_surface, opencg.Surface):
msg = 'Unable to create an OpenMC Surface from "{0}" which ' \
'is not an OpenCG Surface'.format(opencg_surface)
raise ValueError(msg)
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
global openmc_surface
surface_id = opencg_surface.id
@ -369,10 +355,7 @@ def get_compatible_opencg_surfaces(opencg_surface):
"""
if not isinstance(opencg_surface, opencg.Surface):
msg = 'Unable to create an OpenMC Surface from "{0}" which ' \
'is not an OpenCG Surface'.format(opencg_surface)
raise ValueError(msg)
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
global OPENMC_SURFACES
surface_id = opencg_surface.id
@ -451,10 +434,7 @@ def get_opencg_cell(openmc_cell):
"""
if not isinstance(openmc_cell, openmc.Cell):
msg = 'Unable to create an OpenCG Cell from "{0}" which ' \
'is not an OpenMC Cell'.format(openmc_cell)
raise ValueError(msg)
cv.check_type('openmc_cell', openmc_cell, openmc.Cell)
global OPENCG_CELLS
cell_id = openmc_cell.id
@ -469,9 +449,9 @@ def get_opencg_cell(openmc_cell):
fill = openmc_cell.fill
if (openmc_cell.fill_type == 'material'):
if openmc_cell.fill_type == 'material':
opencg_cell.fill = get_opencg_material(fill)
elif (openmc_cell.fill_type == 'universe'):
elif openmc_cell.fill_type == 'universe':
opencg_cell.fill = get_opencg_universe(fill)
else:
opencg_cell.fill = get_opencg_lattice(fill)
@ -533,20 +513,10 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
OpenMC
"""
if not isinstance(opencg_cell, opencg.Cell):
msg = 'Unable to create compatible OpenMC Cell from "{0}" which ' \
'is not an OpenCG Cell'.format(opencg_cell)
raise ValueError(msg)
elif not isinstance(opencg_surface, opencg.Surface):
msg = 'Unable to create compatible OpenMC Cell since "{0}" is ' \
'not an OpenCG Surface'.format(opencg_surface)
raise ValueError(msg)
elif halfspace not in [-1, +1]:
msg = 'Unable to create compatible Cell since "{0}"' \
'is not a +/-1 halfspace'.format(halfspace)
raise ValueError(msg)
cv.check_type('opencg_cell', opencg_cell, opencg.Cell)
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
cv.check_value('halfspace', halfspace, (-1, +1))
# Initialize an empty list for the new compatible cells
compatible_cells = []
@ -575,7 +545,7 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
num_clones = 8
for clone_id in range(num_clones):
# Create a cloned OpenCG Cell with Surfaces compatible with OpenMC
# Create cloned OpenCG Cell with Surfaces compatible with OpenMC
clone = opencg_cell.clone()
compatible_cells.append(clone)
@ -641,10 +611,7 @@ def make_opencg_cells_compatible(opencg_universe):
"""
if not isinstance(opencg_universe, opencg.Universe):
msg = 'Unable to make compatible OpenCG Cells for "{0}" which ' \
'is not an OpenCG Universe'.format(opencg_universe)
raise ValueError(msg)
cv.check_type('opencg_universe', opencg_universe, opencg.Universe)
# Check all OpenCG Cells in this Universe for compatibility with OpenMC
opencg_cells = opencg_universe.cells
@ -700,10 +667,7 @@ def get_openmc_cell(opencg_cell):
"""
if not isinstance(opencg_cell, opencg.Cell):
msg = 'Unable to create an OpenMC Cell from "{0}" which ' \
'is not an OpenCG Cell'.format(opencg_cell)
raise ValueError(msg)
cv.check_type('opencg_cell', opencg_cell, opencg.Cell)
global OPENMC_CELLS
cell_id = opencg_cell.id
@ -718,9 +682,9 @@ def get_openmc_cell(opencg_cell):
fill = opencg_cell.fill
if (opencg_cell.type == 'universe'):
if opencg_cell.type == 'universe':
openmc_cell.fill = get_openmc_universe(fill)
elif (opencg_cell.type == 'lattice'):
elif opencg_cell.type == 'lattice':
openmc_cell.fill = get_openmc_lattice(fill)
else:
openmc_cell.fill = get_openmc_material(fill)
@ -764,10 +728,7 @@ def get_opencg_universe(openmc_universe):
"""
if not isinstance(openmc_universe, openmc.Universe):
msg = 'Unable to create an OpenCG Universe from "{0}" which ' \
'is not an OpenMC Universe'.format(openmc_universe)
raise ValueError(msg)
cv.check_type('openmc_universe', openmc_universe, openmc.Universe)
global OPENCG_UNIVERSES
universe_id = openmc_universe.id
@ -811,10 +772,7 @@ def get_openmc_universe(opencg_universe):
"""
if not isinstance(opencg_universe, opencg.Universe):
msg = 'Unable to create an OpenMC Universe from "{0}" which ' \
'is not an OpenCG Universe'.format(opencg_universe)
raise ValueError(msg)
cv.check_type('opencg_universe', opencg_universe, opencg.Universe)
global OPENMC_UNIVERSES
universe_id = opencg_universe.id
@ -861,10 +819,7 @@ def get_opencg_lattice(openmc_lattice):
"""
if not isinstance(openmc_lattice, openmc.Lattice):
msg = 'Unable to create an OpenCG Lattice from "{0}" which ' \
'is not an OpenMC Lattice'.format(openmc_lattice)
raise ValueError(msg)
cv.check_type('openmc_lattice', openmc_lattice, openmc.Lattice)
global OPENCG_LATTICES
lattice_id = openmc_lattice.id
@ -958,10 +913,7 @@ def get_openmc_lattice(opencg_lattice):
"""
if not isinstance(opencg_lattice, opencg.Lattice):
msg = 'Unable to create an OpenMC Lattice from "{0}" which ' \
'is not an OpenCG Lattice'.format(opencg_lattice)
raise ValueError(msg)
cv.check_type('opencg_lattice', opencg_lattice, opencg.Lattice)
global OPENMC_LATTICES
lattice_id = opencg_lattice.id
@ -1032,10 +984,7 @@ def get_opencg_geometry(openmc_geometry):
"""
if not isinstance(openmc_geometry, openmc.Geometry):
msg = 'Unable to get OpenCG geometry from "{0}" which is ' \
'not an OpenMC Geometry object'.format(openmc_geometry)
raise ValueError(msg)
cv.check_type('openmc_geometry', openmc_geometry, openmc.Geometry)
# Clear dictionaries and auto-generated IDs
OPENMC_SURFACES.clear()
@ -1053,6 +1002,7 @@ def get_opencg_geometry(openmc_geometry):
opencg_geometry = opencg.Geometry()
opencg_geometry.root_universe = opencg_root_universe
opencg_geometry.initialize_cell_offsets()
opencg_geometry.assign_auto_ids()
return opencg_geometry
@ -1072,10 +1022,7 @@ def get_openmc_geometry(opencg_geometry):
"""
if not isinstance(opencg_geometry, opencg.Geometry):
msg = 'Unable to get OpenMC geometry from "{0}" which is ' \
'not an OpenCG Geometry object'.format(opencg_geometry)
raise ValueError(msg)
cv.check_type('opencg_geometry', opencg_geometry, opencg.Geometry)
# Deep copy the goemetry since it may be modified to make all Surfaces
# compatible with OpenMC's specifications

View file

@ -275,7 +275,7 @@ class Plot(object):
The random number seed used to generate the color scheme
"""
cv.check_type('geometry', geometry, openmc.Geometry)
cv.check_type('seed', seed, Integral)
cv.check_greater_than('seed', seed, 1, equality=True)
@ -417,7 +417,7 @@ class PlotsFile(object):
Parameters
----------
plot : Plot
plot : openmc.Plot
Plot to add
"""
@ -433,7 +433,7 @@ class PlotsFile(object):
Parameters
----------
plot : Plot
plot : openmc.Plot
Plot to remove
"""

View file

@ -9,10 +9,11 @@ from openmc.checkvalue import check_type
class Region(object):
"""Region of space that can be assigned to a cell.
Region is an abstract base class that is inherited by Halfspace,
Intersection, Union, and Complement. Each of those respective classes are
typically not instantiated directly but rather are created through operators
of the Surface and Region classes.
Region is an abstract base class that is inherited by
:class:`openmc.Halfspace`, :class:`openmc.Intersection`,
:class:`openmc.Union`, and :class:`openmc.Complement`. Each of those
respective classes are typically not instantiated directly but rather are
created through operators of the Surface and Region classes.
"""
@ -201,11 +202,11 @@ class Intersection(Region):
"""Intersection of two or more regions.
Instances of Intersection are generally created via the __and__ operator
applied to two instances of Region. This is illustrated in the following
example:
applied to two instances of :class:`openmc.Region`. This is illustrated in
the following example:
>>> equator = openmc.surface.ZPlane(z0=0.0)
>>> earth = openmc.surface.Sphere(R=637.1e6)
>>> equator = openmc.ZPlane(z0=0.0)
>>> earth = openmc.Sphere(R=637.1e6)
>>> northern_hemisphere = -earth & +equator
>>> southern_hemisphere = -earth & -equator
>>> type(northern_hemisphere)
@ -213,12 +214,12 @@ class Intersection(Region):
Parameters
----------
*nodes
\*nodes
Regions to take the intersection of
Attributes
----------
nodes : tuple of Region
nodes : tuple of openmc.Region
Regions to take the intersection of
bounding_box : tuple of numpy.array
Lower-left and upper-right coordinates of an axis-aligned bounding box
@ -255,21 +256,22 @@ class Union(Region):
"""Union of two or more regions.
Instances of Union are generally created via the __or__ operator applied to
two instances of Region. This is illustrated in the following example:
two instances of :class:`openmc.Region`. This is illustrated in the
following example:
>>> s1 = openmc.surface.ZPlane(z0=0.0)
>>> s2 = openmc.surface.Sphere(R=637.1e6)
>>> s1 = openmc.ZPlane(z0=0.0)
>>> s2 = openmc.Sphere(R=637.1e6)
>>> type(-s2 | +s1)
<class 'openmc.region.Union'>
Parameters
----------
*nodes
\*nodes
Regions to take the union of
Attributes
----------
nodes : tuple of Region
nodes : tuple of openmc.Region
Regions to take the union of
bounding_box : tuple of numpy.array
Lower-left and upper-right coordinates of an axis-aligned bounding box
@ -305,13 +307,13 @@ class Union(Region):
class Complement(Region):
"""Complement of a region.
The Complement of an existing Region can be created by using the __invert__
operator as the following example demonstrates:
The Complement of an existing :class:`openmc.Region` can be created by using
the __invert__ operator as the following example demonstrates:
>>> xl = openmc.surface.XPlane(x0=-10.0)
>>> xr = openmc.surface.XPlane(x0=10.0)
>>> yl = openmc.surface.YPlane(y0=-10.0)
>>> yr = openmc.surface.YPlane(y0=10.0)
>>> xl = openmc.XPlane(x0=-10.0)
>>> xr = openmc.XPlane(x0=10.0)
>>> yl = openmc.YPlane(y0=-10.0)
>>> yr = openmc.YPlane(y0=10.0)
>>> inside_box = +xl & -xr & +yl & -yl
>>> outside_box = ~inside_box
>>> type(outside_box)
@ -319,12 +321,12 @@ class Complement(Region):
Parameters
----------
node : Region
node : openmc.Region
Region to take the complement of
Attributes
----------
node : Region
node : openmc.Region
Regions to take the complement of
bounding_box : tuple of numpy.array
Lower-left and upper-right coordinates of an axis-aligned bounding box

View file

@ -9,6 +9,7 @@ import numpy as np
from openmc.clean_xml import *
from openmc.checkvalue import (check_type, check_length, check_value,
check_greater_than, check_less_than)
from openmc import Nuclide
from openmc.source import Source
if sys.version_info[0] >= 3:
@ -37,7 +38,7 @@ class SettingsFile(object):
type are 'variance', 'std_dev', and 'rel_err'. The threshold value
should be a float indicating the variance, standard deviation, or
relative error used.
source : Iterable of openmc.source.Source
source : Iterable of openmc.Source
Distribution of source sites in space, angle, and energy
output : dict
Dictionary indicating what files to output. Valid keys are 'summary',
@ -70,15 +71,20 @@ class SettingsFile(object):
cross_sections : str
Indicates the path to an XML cross section listing file (usually named
cross_sections.xml). If it is not set, the :envvar:`CROSS_SECTIONS`
environment variable will be used to find the path to the XML cross
section listing.
environment variable will be used for continuous-energy calculations
and :envvar:`MG_CROSS_SECTIONS` will be used for multi-group
calculations to find the path to the XML cross section file.
multipole_library : str
Indicates the path to a directory containing a windowed multipole
cross section library. If it is not set, the :envvar:`MULTIPOLE_LIBRARY'
environment variable will be used. A multipole library is optional.
energy_grid : str
Set the method used to search energy grids. Acceptable values are
'nuclide', 'logarithm', and 'material-union'.
cross section library. If it is not set, the
:envvar:`OPENMC_MULTIPOLE_LIBRARY' environment variable will be used. A
multipole library is optional.
energy_grid : {'nuclide', 'logarithm', 'material-union'}
Set the method used to search energy grids.
energy_mode : {'continuous-energy', 'multi-group'}
Set whether the calculation should be continuous-energy or multi-group.
max_order : int
Maximum scattering order to apply globally when in multi-group mode.
ptables : bool
Determine whether probability tables are used.
run_cmfd : bool
@ -128,6 +134,8 @@ class SettingsFile(object):
use_windowed_multipole : bool
Whether or not windowed multipole can be used to evaluate resolved
resonance cross sections.
resonance_scattering : ResonanceScattering or iterable of ResonanceScattering
The elastic scattering model to use for resonant isotopes
"""
@ -141,6 +149,10 @@ class SettingsFile(object):
self._particles = None
self._keff_trigger = None
# Energy mode subelement
self._energy_mode = None
self._max_order = None
# Source subelement
self._source = None
@ -206,6 +218,8 @@ class SettingsFile(object):
self._source_element = None
self._multipole_active = None
self._resonance_scattering = None
@property
def run_mode(self):
return self._run_mode
@ -230,6 +244,14 @@ class SettingsFile(object):
def keff_trigger(self):
return self._keff_trigger
@property
def energy_mode(self):
return self._energy_mode
@property
def max_order(self):
return self._max_order
@property
def source(self):
return self._source
@ -394,9 +416,13 @@ class SettingsFile(object):
def use_windowed_multipole(self):
return self._multipole_active
@property
def resonance_scattering(self):
return self._resonance_scattering
@run_mode.setter
def run_mode(self, run_mode):
if 'run_mode' not in ['eigenvalue', 'fixed source']:
if run_mode not in ['eigenvalue', 'fixed source']:
msg = 'Unable to set run mode to "{0}". Only "eigenvalue" ' \
'and "fixed source" are supported."'.format(run_mode)
raise ValueError(msg)
@ -455,6 +481,18 @@ class SettingsFile(object):
self._keff_trigger = keff_trigger
@energy_mode.setter
def energy_mode(self, energy_mode):
check_value('energy mode', energy_mode,
['continuous-energy', 'multi-group'])
self._energy_mode = energy_mode
@max_order.setter
def max_order(self, max_order):
check_type('maximum scattering order', max_order, Integral)
check_greater_than('maximum scattering order', max_order, 0, True)
self._max_order = max_order
@source.setter
def source(self, source):
if isinstance(source, Source):
@ -763,6 +801,16 @@ class SettingsFile(object):
check_type('use_windowed_multipole', active, bool)
self._multipole_active = active
@resonance_scattering.setter
def resonance_scattering(self, res):
if isinstance(res, Iterable):
check_type('resonance_scattering', res, Iterable,
ResonanceScattering)
self._resonance_scattering = res
else:
check_type('resonance_scattering', res, ResonanceScattering)
self._resonance_scattering = [res]
def _create_run_mode_subelement(self):
if self.run_mode == 'eigenvalue':
@ -809,6 +857,16 @@ class SettingsFile(object):
subelement = ET.SubElement(element, key)
subelement.text = str(self._keff_trigger[key]).lower()
def _create_energy_mode_subelement(self):
if self._energy_mode is not None:
element = ET.SubElement(self._settings_file, "energy_mode")
element.text = str(self._energy_mode)
def _create_max_order_subelement(self):
if self._max_order is not None:
element = ET.SubElement(self._settings_file, "max_order")
element.text = str(self._max_order)
def _create_source_subelement(self):
if self.source is not None:
for source in self.source:
@ -1002,7 +1060,7 @@ class SettingsFile(object):
element = ET.SubElement(self._settings_file, "uniform_fs")
subelement = ET.SubElement(element, "dimension")
subelement.text = str(self._ufs_dimension)
subelement.text = ' '.join(map(str, self._ufs_dimension))
subelement = ET.SubElement(element, "lower_left")
subelement.text = ' '.join(map(str, self._ufs_lower_left))
@ -1043,6 +1101,17 @@ class SettingsFile(object):
"use_windowed_multipole")
element.text = str(self._multipole_active)
def _create_resonance_scattering_element(self):
if self.resonance_scattering is None: return
element = ET.SubElement(self._settings_file, "resonance_scattering")
for r in self.resonance_scattering:
if r.nuclide.name != r.nuclide_0K.name:
raise ValueError("The nuclide and nuclide_0K attributes of "
"a ResonantScattering object must have identical names.")
r.create_xml_subelement(element)
def export_to_xml(self):
"""Create a settings.xml file that can be used for a simulation.
@ -1064,6 +1133,8 @@ class SettingsFile(object):
self._create_cross_sections_subelement()
self._create_multipole_library_subelement()
self._create_energy_grid_subelement()
self._create_energy_mode_subelement()
self._create_max_order_subelement()
self._create_ptables_subelement()
self._create_run_cmfd_subelement()
self._create_seed_subelement()
@ -1079,6 +1150,7 @@ class SettingsFile(object):
self._create_ufs_subelement()
self._create_dd_subelement()
self._create_use_multipole_subelement()
self._create_resonance_scattering_element()
# Clean the indentation in the file to be user-readable
clean_xml_indentation(self._settings_file)
@ -1087,3 +1159,104 @@ class SettingsFile(object):
tree = ET.ElementTree(self._settings_file)
tree.write("settings.xml", xml_declaration=True,
encoding='utf-8', method="xml")
class ResonanceScattering(object):
"""Specification of the elastic scattering model for resonant isotopes
Attributes
----------
nuclide : openmc.Nuclide
The nuclide affected by this resonance scattering treatment.
nuclide_0K : openmc.Nuclide
This should be the same isotope as the nuclide attribute above, but it
should have an xs attribute that identifies 0 Kelvin data.
method : str
The method used to sample outgoing scattering energies. Valid options
are 'ARES', 'CXS' (constant cross section), 'DBRC' (Doppler broadening
rejection correction), and 'WCM' (weight correction method).
E_min : float
The minimum energy above which the specified method is applied. By
default, CXS will be used below E_min.
E_max : float
The maximum energy below which the specified method is applied. By
default, the asymptotic target-at-rest model is applied above E_max.
"""
def __init__(self):
self._nuclide = None
self._nuclide_0K = None
self._method = None
self._E_min = None
self._E_max = None
@property
def nuclide(self):
return self._nuclide
@property
def nuclide_0K(self):
return self._nuclide_0K
@property
def method(self):
return self._method
@property
def E_min(self):
return self._E_min
@property
def E_max(self):
return self._E_max
@nuclide.setter
def nuclide(self, nuc):
check_type('nuclide', nuc, Nuclide)
if nuc.zaid == None: raise ValueError("The nuclide must have an "
"explicitly defined zaid attribute.")
self._nuclide = nuc
@nuclide_0K.setter
def nuclide_0K(self, nuc):
check_type('nuclide_0K', nuc, Nuclide)
if nuc.zaid == None: raise ValueError("The nuclide_0K must have an "
"explicitly defined zaid attribute.")
self._nuclide_0K = nuc
@method.setter
def method(self, m):
check_value('method', m, ('ARES', 'CXS', 'DBRC', 'WCM'))
self._method = m
@E_min.setter
def E_min(self, E):
check_type('E_min', E, Real)
check_greater_than('E_min', E, 0, True)
self._E_min = E
@E_max.setter
def E_max(self, E):
check_type('E_max', E, Real)
check_greater_than('E_max', E, 0, True)
self._E_max = E
def create_xml_subelement(self, xml_element):
scatterer = ET.SubElement(xml_element, "scatterer")
subelement = ET.SubElement(scatterer, 'nuclide')
subelement.text = self.nuclide.name
if self.method is not None:
subelement = ET.SubElement(scatterer, 'method')
subelement.text = self.method
subelement = ET.SubElement(scatterer, 'xs_label')
subelement.text = str(self.nuclide.zaid) + '.' + str(self.nuclide.xs)
subelement = ET.SubElement(scatterer, 'xs_label_0K')
subelement.text = str(self.nuclide_0K.zaid) + '.' \
+ str(self.nuclide_0K.xs)
if self.E_min is not None:
subelement = ET.SubElement(scatterer, 'E_min')
subelement.text = str(self.E_min)
if self.E_max is not None:
subelement = ET.SubElement(scatterer, 'E_max')
subelement.text = str(self.E_max)

View file

@ -18,59 +18,62 @@ class StatePoint(object):
----------
cmfd_on : bool
Indicate whether CMFD is active
cmfd_balance : ndarray
cmfd_balance : numpy.ndarray
Residual neutron balance for each batch
cmfd_dominance
Dominance ratio for each batch
cmfd_entropy : ndarray
cmfd_entropy : numpy.ndarray
Shannon entropy of CMFD fission source for each batch
cmfd_indices : ndarray
cmfd_indices : numpy.ndarray
Number of CMFD mesh cells and energy groups. The first three indices
correspond to the x-, y-, and z- spatial directions and the fourth index
is the number of energy groups.
cmfd_srccmp : ndarray
cmfd_srccmp : numpy.ndarray
Root-mean-square difference between OpenMC and CMFD fission source for
each batch
cmfd_src : ndarray
cmfd_src : numpy.ndarray
CMFD fission source distribution over all mesh cells and energy groups.
current_batch : Integral
current_batch : int
Number of batches simulated
date_and_time : str
Date and time when simulation began
entropy : ndarray
entropy : numpy.ndarray
Shannon entropy of fission source at each batch
gen_per_batch : Integral
Number of fission generations per batch
global_tallies : ndarray of compound datatype
global_tallies : numpy.ndarray of compound datatype
Global tallies for k-effective estimates and leakage. The compound
datatype has fields 'name', 'sum', 'sum_sq', 'mean', and 'std_dev'.
k_combined : list
Combined estimator for k-effective and its uncertainty
k_col_abs : Real
k_col_abs : float
Cross-product of collision and absorption estimates of k-effective
k_col_tra : Real
k_col_tra : float
Cross-product of collision and tracklength estimates of k-effective
k_abs_tra : Real
k_abs_tra : float
Cross-product of absorption and tracklength estimates of k-effective
k_generation : ndarray
k_generation : numpy.ndarray
Estimate of k-effective for each batch/generation
meshes : dict
Dictionary whose keys are mesh IDs and whose values are Mesh objects
n_batches : Integral
n_batches : int
Number of batches
n_inactive : Integral
n_inactive : int
Number of inactive batches
n_particles : Integral
n_particles : int
Number of particles per generation
n_realizations : Integral
n_realizations : int
Number of tally realizations
path : str
Working directory for simulation
run_mode : str
Simulation run mode, e.g. 'k-eigenvalue'
seed : Integral
runtime : dict
Dictionary whose keys are strings describing various runtime metrics
and whose values are time values in seconds.
seed : int
Pseudorandom number generator seed
source : ndarray of compound datatype
source : numpy.ndarray of compound datatype
Array of source sites. The compound datatype has fields 'wgt', 'xyz',
'uvw', and 'E' corresponding to the weight, position, direction, and
energy of the source site.
@ -88,7 +91,7 @@ class StatePoint(object):
TallyDerivative objects
version: tuple of Integral
Version of OpenMC
summary : None or openmc.summary.Summary
summary : None or openmc.Summary
A summary object if the statepoint has been linked with a summary file
"""
@ -104,13 +107,14 @@ class StatePoint(object):
raise IOError('{} is not a statepoint file.'.format(filename))
except AttributeError:
raise IOError('Could not read statepoint file. This most likely '
'means the statepoint file was produced by a different '
'version of OpenMC than the one you are using.')
if self._f['revision'].value != 14:
'means the statepoint file was produced by a '
'different version of OpenMC than the one you are '
'using.')
if self._f['revision'].value != 15:
raise IOError('Statepoint file has a file revision of {} '
'which is not consistent with the revision this '
'version of OpenMC expects ({}).'.format(
self._f['revision'].value, 14))
self._f['revision'].value, 15))
# Set flags for what data has been read
self._meshes_read = False
@ -315,6 +319,11 @@ class StatePoint(object):
def run_mode(self):
return self._f['run_mode'].value.decode()
@property
def runtime(self):
return {name: dataset.value
for name, dataset in self._f['runtime'].items()}
@property
def seed(self):
return self._f['seed'].value
@ -399,7 +408,7 @@ class StatePoint(object):
new_filter.mesh = self.meshes[key]
# Add Filter to the Tally
tally.add_filter(new_filter)
tally.filters.append(new_filter)
# Read Nuclide bins
nuclide_names = \
@ -408,7 +417,7 @@ class StatePoint(object):
# Add all Nuclides to the Tally
for name in nuclide_names:
nuclide = openmc.Nuclide(name.decode().strip())
tally.add_nuclide(nuclide)
tally.nuclides.append(nuclide)
scores = self._f['{0}{1}/score_bins'.format(
base, tally_key)].value
@ -435,7 +444,7 @@ class StatePoint(object):
pattern = r'-n$|-pn$|-yn$'
score = re.sub(pattern, '-' + moments[j].decode(), score)
tally.add_score(score)
tally.scores.append(score)
# Add Tally to the global dictionary of all Tallies
tally.sparse = self.sparse
@ -542,7 +551,7 @@ class StatePoint(object):
Returns
-------
tally : Tally
tally : openmc.Tally
A tally matching the specified criteria
Raises
@ -639,7 +648,7 @@ class StatePoint(object):
Parameters
----------
summary : Summary
summary : openmc.Summary
A Summary object.
Raises
@ -656,11 +665,13 @@ class StatePoint(object):
raise ValueError(msg)
for tally_id, tally in self.tallies.items():
# Get the Tally name from the summary file
tally.name = summary.tallies[tally_id].name
summary_tally = summary.tallies[tally_id]
tally.name = summary_tally.name
tally.with_summary = True
for tally_filter in tally.filters:
summary_filter = summary_tally.find_filter(tally_filter.type)
if tally_filter.type == 'surface':
surface_ids = []
for bin in tally_filter.bins:
@ -673,6 +684,10 @@ class StatePoint(object):
distribcell_ids.append(summary.cells[bin].id)
tally_filter.bins = distribcell_ids
if tally_filter.type == 'distribcell':
tally_filter.distribcell_paths = \
summary_filter.distribcell_paths
if tally_filter.type == 'universe':
universe_ids = []
for bin in tally_filter.bins:

View file

@ -22,12 +22,12 @@ class UnitSphere(object):
Parameters
----------
reference_uvw : Iterable of Real
reference_uvw : Iterable of float
Direction from which polar angle is measured
Attributes
----------
reference_uvw : Iterable of Real
reference_uvw : Iterable of float
Direction from which polar angle is measured
"""
@ -62,19 +62,19 @@ class PolarAzimuthal(UnitSphere):
Parameters
----------
mu : Univariate
mu : openmc.stats.Univariate
Distribution of the cosine of the polar angle
phi : Univariate
phi : openmc.stats.Univariate
Distribution of the azimuthal angle in radians
reference_uvw : Iterable of Real
reference_uvw : Iterable of float
Direction from which polar angle is measured. Defaults to the positive
z-direction.
Attributes
----------
mu : Univariate
mu : openmc.stats.Univariate
Distribution of the cosine of the polar angle
phi : Univariate
phi : openmc.stats.Univariate
Distribution of the azimuthal angle in radians
"""
@ -142,7 +142,7 @@ class Monodirectional(UnitSphere):
Parameters
----------
reference_uvw : Iterable of Real
reference_uvw : Iterable of float
Direction from which polar angle is measured. Defaults to the positive
x-direction.
@ -186,20 +186,20 @@ class CartesianIndependent(Spatial):
Parameters
----------
x : Univariate
x : openmc.stats.Univariate
Distribution of x-coordinates
y : Univariate
y : openmc.stats.Univariate
Distribution of y-coordinates
z : Univariate
z : openmc.stats.Univariate
Distribution of z-coordinates
Attributes
----------
x : Univariate
x : openmc.stats.Univariate
Distribution of x-coordinates
y : Univariate
y : openmc.stats.Univariate
Distribution of y-coordinates
z : Univariate
z : openmc.stats.Univariate
Distribution of z-coordinates
"""
@ -252,9 +252,9 @@ class Box(Spatial):
Parameters
----------
lower_left : Iterable of Real
lower_left : Iterable of float
Lower-left coordinates of cuboid
upper_right : Iterable of Real
upper_right : Iterable of float
Upper-right coordinates of cuboid
only_fissionable : bool, optional
Whether spatial sites should only be accepted if they occur in
@ -262,9 +262,9 @@ class Box(Spatial):
Attributes
----------
lower_left : Iterable of Real
lower_left : Iterable of float
Lower-left coordinates of cuboid
upper_right : Iterable of Real
upper_right : Iterable of float
Upper-right coordinates of cuboid
only_fissionable : bool, optional
Whether spatial sites should only be accepted if they occur in
@ -328,12 +328,12 @@ class Point(Spatial):
Parameters
----------
xyz : Iterable of Real
xyz : Iterable of float
Cartesian coordinates of location
Attributes
----------
xyz : Iterable of Real
xyz : Iterable of float
Cartesian coordinates of location
"""

View file

@ -37,16 +37,16 @@ class Discrete(Univariate):
Parameters
----------
x : Iterable of Real
x : Iterable of float
Values of the random variable
p : Iterable of Real
p : Iterable of float
Discrete probability for each value
Attributes
----------
x : Iterable of Real
x : Iterable of float
Values of the random variable
p : Iterable of Real
p : Iterable of float
Discrete probability for each value
"""
@ -243,9 +243,9 @@ class Tabular(Univariate):
Parameters
----------
x : Iterable of Real
x : Iterable of float
Tabulated values of the random variable
p : Iterable of Real
p : Iterable of float
Tabulated probabilities
interpolation : {'histogram', 'linear-linear'}, optional
Indicate whether the density function is constant between tabulated
@ -253,9 +253,9 @@ class Tabular(Univariate):
Attributes
----------
x : Iterable of Real
x : Iterable of float
Tabulated values of the random variable
p : Iterable of Real
p : Iterable of float
Tabulated probabilities
interpolation : {'histogram', 'linear-linear'}, optional
Indicate whether the density function is constant between tabulated

View file

@ -60,6 +60,9 @@ class Summary(object):
# Read date and time
self.date_and_time = self._f['date_and_time'][...]
# Read if continuous-energy or multi-group
self.run_CE = (self._f['run_CE'].value == 1)
self.n_batches = self._f['n_batches'].value
self.n_particles = self._f['n_particles'].value
self.n_active = self._f['n_active'].value
@ -279,7 +282,7 @@ class Summary(object):
# Get the distribcell index
ind = self._f['geometry/cells'][key]['distribcell_index'].value
if ind != 0:
cell.distribcell_index = ind
cell.distribcell_index = ind
# Add the Cell to the global dictionary of all Cells
self.cells[index] = cell
@ -542,7 +545,7 @@ class Summary(object):
# If this is a moment, use generic moment order
pattern = r'-n$|-pn$|-yn$'
score = re.sub(pattern, '-' + moments[j].decode(), score)
tally.add_score(score)
tally.scores.append(score)
# Read filter metadata
num_filters = self._f['{0}/n_filters'.format(subbase)].value
@ -562,8 +565,14 @@ class Summary(object):
new_filter = openmc.Filter(filter_type, bins)
new_filter.num_bins = num_bins
# Read in distribcell paths
if filter_type == 'distribcell':
paths = self._f['{0}/paths'.format(subsubbase)][...]
paths = [str(path.decode()) for path in paths]
new_filter.distribcell_paths = paths
# Add Filter to the Tally
tally.add_filter(new_filter)
tally.filters.append(new_filter)
# Add Tally to the global dictionary of all Tallies
self.tallies[tally_id] = tally
@ -578,7 +587,7 @@ class Summary(object):
Returns
-------
material : openmc.material.Material
material : openmc.Material
Material with given id
"""
@ -599,7 +608,7 @@ class Summary(object):
Returns
-------
surface : openmc.surface.Surface
surface : openmc.Surface
Surface with given id
"""
@ -620,7 +629,7 @@ class Summary(object):
Returns
-------
cell : openmc.universe.Cell
cell : openmc.Cell
Cell with given id
"""
@ -641,7 +650,7 @@ class Summary(object):
Returns
-------
universe : openmc.universe.Universe
universe : openmc.Universe
Universe with given id
"""
@ -662,7 +671,7 @@ class Summary(object):
Returns
-------
lattice : openmc.universe.Lattice
lattice : openmc.Lattice
Lattice with given id
"""

View file

@ -153,10 +153,10 @@ class Surface(object):
Returns
-------
numpy.array
numpy.ndarray
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
numpy.ndarray
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
@ -278,8 +278,7 @@ class Plane(Surface):
class XPlane(Plane):
"""A plane perpendicular to the x axis, i.e. a surface of the form :math:`x -
x_0 = 0`
"""A plane perpendicular to the x axis of the form :math:`x - x_0 = 0`
Parameters
----------
@ -338,10 +337,10 @@ class XPlane(Plane):
Returns
-------
numpy.array
numpy.ndarray
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
numpy.ndarray
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
@ -356,8 +355,7 @@ class XPlane(Plane):
class YPlane(Plane):
"""A plane perpendicular to the y axis, i.e. a surface of the form :math:`y -
y_0 = 0`
"""A plane perpendicular to the y axis of the form :math:`y - y_0 = 0`
Parameters
----------
@ -416,10 +414,10 @@ class YPlane(Plane):
Returns
-------
numpy.array
numpy.ndarray
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
numpy.ndarray
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
@ -434,8 +432,7 @@ class YPlane(Plane):
class ZPlane(Plane):
"""A plane perpendicular to the z axis, i.e. a surface of the form :math:`z -
z_0 = 0`
"""A plane perpendicular to the z axis of the form :math:`z - z_0 = 0`
Parameters
----------
@ -494,10 +491,10 @@ class ZPlane(Plane):
Returns
-------
numpy.array
numpy.ndarray
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
numpy.ndarray
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
@ -641,10 +638,10 @@ class XCylinder(Cylinder):
Returns
-------
numpy.array
numpy.ndarray
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
numpy.ndarray
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
@ -740,10 +737,10 @@ class YCylinder(Cylinder):
Returns
-------
numpy.array
numpy.ndarray
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
numpy.ndarray
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
@ -839,10 +836,10 @@ class ZCylinder(Cylinder):
Returns
-------
numpy.array
numpy.ndarray
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
numpy.ndarray
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
@ -967,10 +964,10 @@ class Sphere(Surface):
Returns
-------
numpy.array
numpy.ndarray
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
numpy.ndarray
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
@ -1383,7 +1380,7 @@ class Halfspace(Region):
can be created from an existing Surface through the __neg__ and __pos__
operators, as the following example demonstrates:
>>> sphere = openmc.surface.Sphere(surface_id=1, R=10.0)
>>> sphere = openmc.Sphere(surface_id=1, R=10.0)
>>> inside_sphere = -sphere
>>> outside_sphere = +sphere
>>> type(inside_sphere)
@ -1391,18 +1388,18 @@ class Halfspace(Region):
Parameters
----------
surface : Surface
surface : openmc.Surface
Surface which divides Euclidean space.
side : {'+', '-'}
Indicates whether the positive or negative half-space is used.
Attributes
----------
surface : Surface
surface : openmc.Surface
Surface which divides Euclidean space.
side : {'+', '-'}
Indicates whether the positive or negative half-space is used.
bounding_box : tuple of numpy.array
bounding_box : tuple of numpy.ndarray
Lower-left and upper-right coordinates of an axis-aligned bounding box
"""

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,10 @@
from numbers import Real
from xml.etree import ElementTree as ET
import sys
import warnings
from collections import Iterable
from openmc.checkvalue import check_type, check_value
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
@ -46,9 +48,7 @@ class Trigger(object):
clone._trigger_type = self._trigger_type
clone._threshold = self._threshold
clone._scores = []
for score in self._scores:
clone.add_score(score)
clone.scores = self.scores
memo[id(self)] = clone
@ -88,15 +88,26 @@ class Trigger(object):
@trigger_type.setter
def trigger_type(self, trigger_type):
check_value('tally trigger type', trigger_type,
cv.check_value('tally trigger type', trigger_type,
['variance', 'std_dev', 'rel_err'])
self._trigger_type = trigger_type
@threshold.setter
def threshold(self, threshold):
check_type('tally trigger threshold', threshold, Real)
cv.check_type('tally trigger threshold', threshold, Real)
self._threshold = threshold
@scores.setter
def scores(self, scores):
cv.check_type('trigger scores', scores, Iterable, basestring)
# Set scores making sure not to have duplicates
self._scores = []
for score in scores:
if score not in self._scores:
self._scores.append(score)
def add_score(self, score):
"""Add a score to the list of scores to be checked against the trigger.
@ -107,16 +118,11 @@ class Trigger(object):
"""
if not isinstance(score, basestring):
msg = 'Unable to add score "{0}" to tally trigger since ' \
'it is not a string'.format(score)
raise ValueError(msg)
# If the score is already in the Tally, don't add it again
if score in self._scores:
return
else:
self._scores.append(score)
warnings.warn('Trigger.add_score(...) has been deprecated and may be '
'removed in a future version. Tally trigger scores should '
'be defined using the scores property directly.',
DeprecationWarning)
self.scores.append(score)
def get_trigger_xml(self, element):
"""Return XML representation of the trigger

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,7 @@ if have_setuptools:
# Optional dependencies
'extras_require': {
'pandas': ['pandas'],
'pandas': ['pandas>=0.17.0'],
'sparse' : ['scipy'],
'vtk': ['vtk', 'silomesh'],
'validate': ['lxml']

File diff suppressed because it is too large Load diff

View file

@ -1,298 +0,0 @@
module ace_header
use constants, only: MAX_FILE_LEN, ZERO
use dict_header, only: DictIntInt
use endf_header, only: Tab1
use multipole_header, only: MultipoleArray
use secondary_header, only: SecondaryDistribution, AngleEnergyContainer
use stl_vector, only: VectorInt
implicit none
!===============================================================================
! REACTION contains the cross-section and secondary energy and angle
! distributions for a single reaction in a continuous-energy ACE-format table
!===============================================================================
type Reaction
integer :: MT ! ENDF MT value
real(8) :: Q_value ! Reaction Q value
integer :: multiplicity ! Number of secondary particles released
type(Tab1), pointer :: multiplicity_E => null() ! Energy-dependent neutron yield
integer :: threshold ! Energy grid index of threshold
logical :: scatter_in_cm ! scattering system in center-of-mass?
logical :: multiplicity_with_E = .false. ! Flag to indicate E-dependent multiplicity
real(8), allocatable :: sigma(:) ! Cross section values
type(SecondaryDistribution) :: secondary
! Type-Bound procedures
contains
procedure :: clear => reaction_clear ! Deallocates Reaction
end type Reaction
!===============================================================================
! URRDATA contains probability tables for the unresolved resonance range.
!===============================================================================
type UrrData
integer :: n_energy ! # of incident neutron energies
integer :: n_prob ! # of probabilities
integer :: interp ! inteprolation (2=lin-lin, 5=log-log)
integer :: inelastic_flag ! inelastic competition flag
integer :: absorption_flag ! other absorption flag
logical :: multiply_smooth ! multiply by smooth cross section?
real(8), allocatable :: energy(:) ! incident energies
real(8), allocatable :: prob(:,:,:) ! actual probabibility tables
end type UrrData
!===============================================================================
! NUCLIDE contains all the data for an ACE-format continuous-energy cross
! section. The ACE format (A Compact ENDF format) is used in MCNP and several
! other Monte Carlo codes.
!===============================================================================
type Nuclide
character(10) :: name ! name of nuclide, e.g. 92235.03c
integer :: zaid ! Z and A identifier, e.g. 92235
integer :: listing ! index in xs_listings
real(8) :: awr ! weight of nucleus in neutron masses
real(8) :: kT ! temperature in MeV (k*T)
! Linked list of indices in nuclides array of instances of this same nuclide
type(VectorInt) :: nuc_list
! Energy grid information
integer :: n_grid ! # of nuclide grid points
integer, allocatable :: grid_index(:) ! log grid mapping indices
real(8), allocatable :: energy(:) ! energy values corresponding to xs
! Microscopic cross sections
real(8), allocatable :: total(:) ! total cross section
real(8), allocatable :: elastic(:) ! elastic scattering
real(8), allocatable :: fission(:) ! fission
real(8), allocatable :: nu_fission(:) ! neutron production
real(8), allocatable :: absorption(:) ! absorption (MT > 100)
real(8), allocatable :: heating(:) ! heating
! Resonance scattering info
logical :: resonant = .false. ! resonant scatterer?
character(10) :: name_0K = '' ! name of 0K nuclide, e.g. 92235.00c
character(16) :: scheme ! target velocity sampling scheme
integer :: n_grid_0K ! number of 0K energy grid points
real(8), allocatable :: energy_0K(:) ! energy grid for 0K xs
real(8), allocatable :: elastic_0K(:) ! Microscopic elastic cross section
real(8), allocatable :: xs_cdf(:) ! CDF of v_rel times cross section
real(8) :: E_min ! lower cutoff energy for res scattering
real(8) :: E_max ! upper cutoff energy for res scattering
! Fission information
logical :: fissionable ! nuclide is fissionable?
logical :: has_partial_fission ! nuclide has partial fission reactions?
integer :: n_fission ! # of fission reactions
integer, allocatable :: index_fission(:) ! indices in reactions
! Total fission neutron emission
integer :: nu_t_type
real(8), allocatable :: nu_t_data(:)
! Prompt fission neutron emission
integer :: nu_p_type
real(8), allocatable :: nu_p_data(:)
! Delayed fission neutron emission
integer :: nu_d_type
integer :: n_precursor ! # of delayed neutron precursors
real(8), allocatable :: nu_d_data(:)
real(8), allocatable :: nu_d_precursor_data(:)
type(AngleEnergyContainer), allocatable :: nu_d_edist(:)
! Unresolved resonance data
logical :: urr_present
integer :: urr_inelastic
type(UrrData), pointer :: urr_data => null()
! Multipole data
logical :: mp_present = .false.
type(MultipoleArray), pointer :: multipole => null()
! Reactions
integer :: n_reaction ! # of reactions
type(Reaction), allocatable :: reactions(:)
type(DictIntInt) :: reaction_index ! map MT values to index in reactions
! array; used at tally-time
! Type-Bound procedures
contains
procedure :: clear => nuclide_clear ! Deallocates Nuclide
end type Nuclide
!===============================================================================
! NUCLIDE0K temporarily contains all 0K cross section data and other parameters
! needed to treat resonance scattering before transferring them to NUCLIDE
!===============================================================================
type Nuclide0K
character(10) :: nuclide ! name of nuclide, e.g. U-238
character(16) :: scheme = 'ares' ! target velocity sampling scheme
character(10) :: name ! name of nuclide, e.g. 92235.03c
character(10) :: name_0K ! name of 0K nuclide, e.g. 92235.00c
real(8) :: E_min = 0.01e-6_8 ! lower cutoff energy for res scattering
real(8) :: E_max = 1000.0e-6_8 ! upper cutoff energy for res scattering
end type Nuclide0K
!===============================================================================
! DISTENERGYSAB contains the secondary energy/angle distributions for inelastic
! thermal scattering collisions which utilize a continuous secondary energy
! representation.
!===============================================================================
type DistEnergySab
integer :: n_e_out
real(8), allocatable :: e_out(:)
real(8), allocatable :: e_out_pdf(:)
real(8), allocatable :: e_out_cdf(:)
real(8), allocatable :: mu(:,:)
end type DistEnergySab
!===============================================================================
! SALPHABETA contains S(a,b) data for thermal neutron scattering, typically off
! of light isotopes such as water, graphite, Be, etc
!===============================================================================
type SAlphaBeta
character(10) :: name ! name of table, e.g. lwtr.10t
real(8) :: awr ! weight of nucleus in neutron masses
real(8) :: kT ! temperature in MeV (k*T)
integer :: n_zaid ! Number of valid zaids
integer, allocatable :: zaid(:) ! List of valid Z and A identifiers, e.g. 6012
! threshold for S(a,b) treatment (usually ~4 eV)
real(8) :: threshold_inelastic
real(8) :: threshold_elastic = ZERO
! Inelastic scattering data
integer :: n_inelastic_e_in ! # of incoming E for inelastic
integer :: n_inelastic_e_out ! # of outgoing E for inelastic
integer :: n_inelastic_mu ! # of outgoing angles for inelastic
integer :: secondary_mode ! secondary mode (equal/skewed/continuous)
real(8), allocatable :: inelastic_e_in(:)
real(8), allocatable :: inelastic_sigma(:)
! The following are used only if secondary_mode is 0 or 1
real(8), allocatable :: inelastic_e_out(:,:)
real(8), allocatable :: inelastic_mu(:,:,:)
! The following is used only if secondary_mode is 3
! The different implementation is necessary because the continuous
! representation has a variable number of outgoing energy points for each
! incoming energy
type(DistEnergySab), allocatable :: inelastic_data(:) ! One for each Ein
! Elastic scattering data
integer :: elastic_mode ! elastic mode (discrete/exact)
integer :: n_elastic_e_in ! # of incoming E for elastic
integer :: n_elastic_mu ! # of outgoing angles for elastic
real(8), allocatable :: elastic_e_in(:)
real(8), allocatable :: elastic_P(:)
real(8), allocatable :: elastic_mu(:,:)
end type SAlphaBeta
!===============================================================================
! XSLISTING contains data read from a cross_sections.xml file
!===============================================================================
type XsListing
character(12) :: name ! table name, e.g. 92235.70c
character(12) :: alias ! table alias, e.g. U-235.70c
integer :: type ! type of table (cont-E neutron, S(A,b), etc)
integer :: zaid ! ZAID identifier = 1000*Z + A
integer :: filetype ! ASCII or BINARY
integer :: location ! location of table within library
integer :: recl ! record length for library
integer :: entries ! number of entries per record
real(8) :: awr ! atomic weight ratio (# of neutron masses)
real(8) :: kT ! Boltzmann constant * temperature (MeV)
logical :: metastable ! is this nuclide metastable?
character(MAX_FILE_LEN) :: path ! path to library containing table
end type XsListing
!===============================================================================
! NUCLIDEMICROXS contains cached microscopic cross sections for a
! particular nuclide at the current energy
!===============================================================================
type NuclideMicroXS
integer :: index_grid ! index on nuclide energy grid
integer :: index_temp ! temperature index for nuclide
real(8) :: last_E = ZERO ! last evaluated energy
real(8) :: interp_factor ! interpolation factor on nuc. energy grid
real(8) :: total ! microscropic total xs
real(8) :: elastic ! microscopic elastic scattering xs
real(8) :: absorption ! microscopic absorption xs
real(8) :: fission ! microscopic fission xs
real(8) :: nu_fission ! microscopic production xs
! Information for S(a,b) use
integer :: index_sab ! index in sab_tables (zero means no table)
integer :: last_index_sab = 0 ! index in sab_tables last used by this nuclide
real(8) :: elastic_sab ! microscopic elastic scattering on S(a,b) table
! Information for URR probability table use
logical :: use_ptable ! in URR range with probability tables?
real(8) :: last_prn
! Information for Doppler broadening
real(8) :: last_sqrtkT = ZERO ! last temperature in sqrt(Boltzmann constant * temperature (MeV))
end type NuclideMicroXS
!===============================================================================
! MATERIALMACROXS contains cached macroscopic cross sections for the material a
! particle is traveling through
!===============================================================================
type MaterialMacroXS
real(8) :: total ! macroscopic total xs
real(8) :: elastic ! macroscopic elastic scattering xs
real(8) :: absorption ! macroscopic absorption xs
real(8) :: fission ! macroscopic fission xs
real(8) :: nu_fission ! macroscopic production xs
end type MaterialMacroXS
contains
!===============================================================================
! REACTION_CLEAR resets and deallocates data in Reaction.
!===============================================================================
subroutine reaction_clear(this)
class(Reaction), intent(inout) :: this ! The Reaction object to clear
if (associated(this % multiplicity_E)) deallocate(this % multiplicity_E)
end subroutine reaction_clear
!===============================================================================
! NUCLIDE_CLEAR resets and deallocates data in Nuclide.
!===============================================================================
subroutine nuclide_clear(this)
class(Nuclide), intent(inout) :: this
integer :: i ! Loop counter
if (associated(this % urr_data)) deallocate(this % urr_data)
if (this % mp_present) then
deallocate(this % multipole)
end if
if (allocated(this % reactions)) then
do i = 1, size(this % reactions)
call this % reactions(i) % clear()
end do
end if
call this % reaction_index % clear()
if (associated(this % multipole)) deallocate(this % multipole)
end subroutine nuclide_clear
end module ace_header

View file

@ -0,0 +1,29 @@
module angleenergy_header
!===============================================================================
! ANGLEENERGY (abstract) defines a correlated or uncorrelated angle-energy
! distribution that is a function of incoming energy. Each derived type must
! implement a sample() subroutine that returns an outgoing energy and scattering
! cosine given an incoming energy.
!===============================================================================
type, abstract :: AngleEnergy
contains
procedure(angleenergy_sample_), deferred :: sample
end type AngleEnergy
abstract interface
subroutine angleenergy_sample_(this, E_in, E_out, mu)
import AngleEnergy
class(AngleEnergy), intent(in) :: this
real(8), intent(in) :: E_in
real(8), intent(out) :: E_out
real(8), intent(out) :: mu
end subroutine angleenergy_sample_
end interface
type :: AngleEnergyContainer
class(AngleEnergy), allocatable :: obj
end type AngleEnergyContainer
end module angleenergy_header

View file

@ -14,7 +14,7 @@ module bank_header
real(C_DOUBLE) :: wgt ! weight of bank site
real(C_DOUBLE) :: xyz(3) ! location of bank particle
real(C_DOUBLE) :: uvw(3) ! diretional cosines
real(C_DOUBLE) :: E ! energy
real(C_DOUBLE) :: E ! energy / energy group if in MG mode.
integer(C_INT) :: delayed_group ! delayed group
end type Bank

View file

@ -49,13 +49,13 @@ contains
subroutine compute_xs()
use constants, only: FILTER_MESH, FILTER_ENERGYIN, FILTER_ENERGYOUT, &
FILTER_SURFACE, IN_RIGHT, OUT_RIGHT, IN_FRONT, &
OUT_FRONT, IN_TOP, OUT_TOP, CMFD_NOACCEL, ZERO, &
ONE, TINY_BIT
use constants, only: FILTER_MESH, FILTER_ENERGYIN, FILTER_ENERGYOUT, &
FILTER_SURFACE, IN_RIGHT, OUT_RIGHT, IN_FRONT, &
OUT_FRONT, IN_TOP, OUT_TOP, CMFD_NOACCEL, ZERO, &
ONE, TINY_BIT
use error, only: fatal_error
use global, only: cmfd, n_cmfd_tallies, cmfd_tallies, meshes,&
matching_bins
matching_bins
use mesh, only: mesh_indices_to_bin
use mesh_header, only: RegularMesh
use string, only: to_str
@ -625,10 +625,10 @@ contains
subroutine compute_dhat()
use constants, only: CMFD_NOACCEL, ZERO
use global, only: cmfd, cmfd_coremap, dhat_reset
use output, only: write_message
use string, only: to_str
use constants, only: CMFD_NOACCEL, ZERO
use global, only: cmfd, cmfd_coremap, dhat_reset
use output, only: write_message
use string, only: to_str
integer :: nx ! maximum number of cells in x direction
integer :: ny ! maximum number of cells in y direction

View file

@ -89,9 +89,9 @@ contains
subroutine calc_fission_source()
use constants, only: CMFD_NOACCEL, ZERO, TWO
use global, only: cmfd, cmfd_coremap, master, entropy_on, current_batch
use string, only: to_str
use constants, only: CMFD_NOACCEL, ZERO, TWO
use global, only: cmfd, cmfd_coremap, master, entropy_on, current_batch
use string, only: to_str
#ifdef MPI
use global, only: mpi_err

View file

@ -45,14 +45,14 @@ contains
subroutine read_cmfd_xml()
use constants, only: ZERO, ONE
use error, only: fatal_error, warning
use error, only: fatal_error, warning
use global
use output, only: write_message
use string, only: to_lower
use output, only: write_message
use string, only: to_lower
use xml_interface
use, intrinsic :: ISO_FORTRAN_ENV
integer :: i
integer :: i, g
integer :: ng
integer :: n_params
integer, allocatable :: iarray(:)
@ -70,7 +70,7 @@ contains
inquire(FILE=filename, EXIST=file_exists)
if (.not. file_exists) then
! CMFD is optional unless it is in on from settings
if (cmfd_on) then
if (cmfd_run) then
call fatal_error("No CMFD XML file, '" // trim(filename) // "' does not&
& exist!")
end if
@ -102,6 +102,23 @@ contains
if(.not.allocated(cmfd%egrid)) allocate(cmfd%egrid(ng))
call get_node_array(node_mesh, "energy", cmfd%egrid)
cmfd % indices(4) = ng - 1 ! sets energy group dimension
! If using MG mode, check to see if these egrid points at least match
! the MG Data breakpoints
if (.not. run_CE) then
do i = 1, ng
found = .false.
do g = 1, energy_groups + 1
if (cmfd % egrid(i) == energy_bins(g)) then
found = .true.
exit
end if
end do
if (.not. found) then
call fatal_error("CMFD energy mesh boundaries must align with&
& boundaries of multi-group data!")
end if
end do
end if
else
if(.not.allocated(cmfd % egrid)) allocate(cmfd % egrid(2))
cmfd % egrid = [ ZERO, 20.0_8 ]

View file

@ -11,10 +11,10 @@ module constants
integer, parameter :: VERSION_RELEASE = 1
! Revision numbers for binary files
integer, parameter :: REVISION_STATEPOINT = 14
integer, parameter :: REVISION_STATEPOINT = 15
integer, parameter :: REVISION_PARTICLE_RESTART = 1
integer, parameter :: REVISION_TRACK = 1
integer, parameter :: REVISION_SUMMARY = 2
integer, parameter :: REVISION_SUMMARY = 3
! ============================================================================
! ADJUSTABLE PARAMETERS
@ -163,9 +163,14 @@ module constants
! Angular distribution type
integer, parameter :: &
ANGLE_ISOTROPIC = 1, & ! Isotropic angular distribution
ANGLE_32_EQUI = 2, & ! 32 equiprobable bins
ANGLE_TABULAR = 3 ! Tabular angular distribution
ANGLE_ISOTROPIC = 1, & ! Isotropic angular distribution (CE)
ANGLE_32_EQUI = 2, & ! 32 equiprobable bins (CE)
ANGLE_TABULAR = 3, & ! Tabular angular distribution (CE or MG)
ANGLE_LEGENDRE = 4, & ! Legendre angular distribution (MG)
ANGLE_HISTOGRAM = 5 ! Histogram angular distribution (MG)
! Number of mu bins to use when converting Legendres to tabular type
integer, parameter :: DEFAULT_NMU = 33
! Secondary energy mode for S(a,b) inelastic scattering
integer, parameter :: &
@ -209,12 +214,23 @@ module constants
ACE_THERMAL = 2, & ! thermal S(a,b) scattering data
ACE_DOSIMETRY = 3 ! dosimetry cross sections
! MGXS Table Types
integer, parameter :: &
MGXS_ISOTROPIC = 1, & ! Isotropically Weighted Data
MGXS_ANGLE = 2 ! Data by Angular Bins
! Fission neutron emission (nu) type
integer, parameter :: &
NU_NONE = 0, & ! No nu values (non-fissionable)
NU_POLYNOMIAL = 1, & ! Nu values given by polynomial
NU_TABULAR = 2 ! Nu values given by tabular distribution
! Secondary particle emission type
integer, parameter :: &
EMISSION_PROMPT = 1, & ! Prompt emission of secondary particle
EMISSION_DELAYED = 2, & ! Delayed emission of secondary particle
EMISSION_TOTAL = 3 ! Yield represents total emission (prompt + delayed)
! Cross section filetypes
integer, parameter :: &
ASCII = 1, & ! ASCII cross section file
@ -363,10 +379,11 @@ module constants
! ============================================================================
! RANDOM NUMBER STREAM CONSTANTS
integer, parameter :: N_STREAMS = 3
integer, parameter :: STREAM_TRACKING = 1
integer, parameter :: STREAM_TALLIES = 2
integer, parameter :: STREAM_SOURCE = 3
integer, parameter :: N_STREAMS = 4
integer, parameter :: STREAM_TRACKING = 1
integer, parameter :: STREAM_TALLIES = 2
integer, parameter :: STREAM_SOURCE = 3
integer, parameter :: STREAM_URR_PTABLE = 4
! ============================================================================
! MISCELLANEOUS CONSTANTS

Some files were not shown because too many files have changed in this diff Show more