Merge branch 'develop' into mg_docs

This commit is contained in:
Adam Nelson 2016-06-15 10:19:46 -04:00
commit 9924248d25
254 changed files with 25829 additions and 20840 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

View file

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

View file

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

View file

@ -0,0 +1,6 @@
{{ fullname }}
{{ underline }}
.. currentmodule:: {{ module }}
.. autofunction:: {{ objname }}

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

@ -13,11 +13,14 @@ OpenMC was originally developed by members of the `Computational Reactor Physics
Group`_ at the `Massachusetts Institute of Technology`_ starting
in 2011. Various universities, laboratories, and other organizations now
contribute to the development of OpenMC. For more information on OpenMC, feel
free to send a message to the User's Group `mailing list`_.
free to send a message to the User's Group `mailing list`_. Documentation for
the latest developmental version of the develop branch can be found on
`Read the Docs`_.
.. _Computational Reactor Physics Group: http://crpg.mit.edu
.. _Massachusetts Institute of Technology: http://web.mit.edu
.. _mailing list: https://groups.google.com/forum/?fromgroups=#!forum/openmc-users
.. _Read the Docs: http://openmc.readthedocs.io/en/latest/
.. only:: html

View file

@ -4,7 +4,7 @@
License Agreement
=================
Copyright © 2011-2015 Massachusetts Institute of Technology
Copyright © 2011-2016 Massachusetts Institute of Technology
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View file

@ -437,6 +437,8 @@ where :math:`(x_0, y_0, z_0)` are the coordinates to the lower-left-bottom
corner of the lattice, and :math:`p_0, p_1, p_2` are the pitches along the
:math:`x`, :math:`y`, and :math:`z` axes, respectively.
.. _hexagonal_indexing:
Hexagonal Lattice Indexing
--------------------------

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"
]
},
{
@ -204,7 +201,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"With our material, we can now create a `MaterialsFile` object that can be exported to an actual XML file."
"With our material, we can now create a `Materials` object that can be exported to an actual XML file."
]
},
{
@ -215,10 +212,9 @@
},
"outputs": [],
"source": [
"# Instantiate a MaterialsFile, register all Materials, and export to XML\n",
"materials_file = openmc.MaterialsFile()\n",
"# Instantiate a Materials collection and export to XML\n",
"materials_file = openmc.Materials([inf_medium])\n",
"materials_file.default_xs = '71c'\n",
"materials_file.add_material(inf_medium)\n",
"materials_file.export_to_xml()"
]
},
@ -293,7 +289,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"We now must create a geometry that is assigned a root universe, put the geometry into a `GeometryFile` object, and export it to XML."
"We now must create a geometry that is assigned a root universe and export it to XML."
]
},
{
@ -308,12 +304,8 @@
"openmc_geometry = openmc.Geometry()\n",
"openmc_geometry.root_universe = root_universe\n",
"\n",
"# Instantiate a GeometryFile\n",
"geometry_file = openmc.GeometryFile()\n",
"geometry_file.geometry = openmc_geometry\n",
"\n",
"# Export to \"geometry.xml\"\n",
"geometry_file.export_to_xml()"
"openmc_geometry.export_to_xml()"
]
},
{
@ -336,15 +328,17 @@
"inactive = 10\n",
"particles = 2500\n",
"\n",
"# Instantiate a SettingsFile\n",
"settings_file = openmc.SettingsFile()\n",
"# Instantiate a Settings object\n",
"settings_file = openmc.Settings()\n",
"settings_file.batches = batches\n",
"settings_file.inactive = inactive\n",
"settings_file.particles = particles\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()"
@ -378,10 +372,12 @@
"\n",
"* `TotalXS`\n",
"* `TransportXS`\n",
"* `NuTransportXS`\n",
"* `AbsorptionXS`\n",
"* `CaptureXS`\n",
"* `FissionXS`\n",
"* `NuFissionXS`\n",
"* `KappaFissionXS`\n",
"* `ScatterXS`\n",
"* `NuScatterXS`\n",
"* `ScatterMatrixXS`\n",
@ -400,9 +396,9 @@
"outputs": [],
"source": [
"# Instantiate a few different sections\n",
"total = mgxs.TotalXS(domain=cell, domain_type='cell', groups=groups)\n",
"absorption = mgxs.AbsorptionXS(domain=cell, domain_type='cell', groups=groups)\n",
"scattering = mgxs.ScatterXS(domain=cell, domain_type='cell', groups=groups)"
"total = mgxs.TotalXS(domain=cell, groups=groups)\n",
"absorption = mgxs.AbsorptionXS(domain=cell, groups=groups)\n",
"scattering = mgxs.ScatterXS(domain=cell, groups=groups)"
]
},
{
@ -423,22 +419,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,
@ -454,7 +452,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The `Absorption` object includes tracklength tallies for the 'absorption' and 'flux' scores in the 2-group structure in cell 1. Now that each `MGXS` object contains the tallies that it needs, we must add these tallies to a `TalliesFile` object to generate the \"tallies.xml\" input file for OpenMC."
"The `Absorption` object includes tracklength tallies for the 'absorption' and 'flux' scores in the 2-group structure in cell 1. Now that each `MGXS` object contains the tallies that it needs, we must add these tallies to a `Tallies` object to generate the \"tallies.xml\" input file for OpenMC."
]
},
{
@ -465,21 +463,18 @@
},
"outputs": [],
"source": [
"# Instantiate an empty TalliesFile\n",
"tallies_file = openmc.TalliesFile()\n",
"# Instantiate an empty Tallies object\n",
"tallies_file = openmc.Tallies()\n",
"\n",
"# Add total tallies to the tallies file\n",
"for tally in total.tallies.values():\n",
" tallies_file.add_tally(tally)\n",
"tallies_file += total.tallies.values()\n",
"\n",
"# Add absorption tallies to the tallies file\n",
"for tally in absorption.tallies.values():\n",
" tallies_file.add_tally(tally)\n",
"tallies_file += absorption.tallies.values()\n",
"\n",
"# Add scattering tallies to the tallies file\n",
"for tally in scattering.tallies.values():\n",
" tallies_file.add_tally(tally)\n",
" \n",
"tallies_file += scattering.tallies.values()\n",
"\n",
"# Export to \"tallies.xml\"\n",
"tallies_file.export_to_xml()"
]
@ -515,11 +510,11 @@
" 888\n",
" 888\n",
"\n",
" Copyright: 2011-2015 Massachusetts Institute of Technology\n",
" License: http://mit-crpg.github.io/openmc/license.html\n",
" Copyright: 2011-2016 Massachusetts Institute of Technology\n",
" License: http://openmc.readthedocs.io/en/latest/license.html\n",
" Version: 0.7.1\n",
" Git SHA1: 34381b40a9445a727e360873aaa6ef892af1cb6a\n",
" Date/Time: 2016-02-07 15:58:16\n",
" Git SHA1: 19feb55e6d5e8350398627f39fb55ee8e2e63011\n",
" Date/Time: 2016-05-13 10:19:16\n",
" MPI Processes: 1\n",
"\n",
" ===========================================================================\n",
@ -546,56 +541,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",
@ -605,27 +600,27 @@
"\n",
" =======================> TIMING STATISTICS <=======================\n",
"\n",
" Total time for initialization = 3.2100E-01 seconds\n",
" Reading cross sections = 7.4000E-02 seconds\n",
" Total time in simulation = 8.3830E+00 seconds\n",
" Time in transport only = 8.3670E+00 seconds\n",
" Time in inactive batches = 1.0330E+00 seconds\n",
" Time in active batches = 7.3500E+00 seconds\n",
" Time synchronizing fission bank = 4.0000E-03 seconds\n",
" Sampling source sites = 1.0000E-03 seconds\n",
" SEND/RECV source sites = 3.0000E-03 seconds\n",
" Total time for initialization = 4.2300E-01 seconds\n",
" Reading cross sections = 9.3000E-02 seconds\n",
" Total time in simulation = 1.6549E+01 seconds\n",
" Time in transport only = 1.6535E+01 seconds\n",
" Time in inactive batches = 2.3650E+00 seconds\n",
" Time in active batches = 1.4184E+01 seconds\n",
" Time synchronizing fission bank = 5.0000E-03 seconds\n",
" Sampling source sites = 3.0000E-03 seconds\n",
" SEND/RECV source sites = 0.0000E+00 seconds\n",
" Time accumulating tallies = 0.0000E+00 seconds\n",
" Total time for finalization = 1.0000E-03 seconds\n",
" Total time elapsed = 8.7140E+00 seconds\n",
" Calculation Rate (inactive) = 24201.4 neutrons/second\n",
" Calculation Rate (active) = 13605.4 neutrons/second\n",
" Total time for finalization = 0.0000E+00 seconds\n",
" Total time elapsed = 1.6981E+01 seconds\n",
" Calculation Rate (inactive) = 10570.8 neutrons/second\n",
" Calculation Rate (active) = 7050.20 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"
]
@ -643,8 +638,7 @@
],
"source": [
"# Run OpenMC\n",
"executor = openmc.Executor()\n",
"executor.run_simulation()"
"openmc.run()"
]
},
{
@ -677,20 +671,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"In addition to the statepoint file, our simulation also created a summary file which encapsulates information about the materials and geometry. This is necessary for the `openmc.mgxs` module to properly process the tally data. We first create a `Summary` object and link it with the statepoint."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Load the summary file and link it with the statepoint\n",
"su = openmc.Summary('summary.h5')\n",
"sp.link_with_summary(su)"
"In addition to the statepoint file, our simulation also created a summary file which encapsulates information about the materials and geometry. By default, a `Summary` object is automatically linked when a `StatePoint` is loaded. This is necessary for the `openmc.mgxs` module to properly process the tally data."
]
},
{
@ -702,7 +683,7 @@
},
{
"cell_type": "code",
"execution_count": 18,
"execution_count": 17,
"metadata": {
"collapsed": false
},
@ -737,7 +718,7 @@
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 18,
"metadata": {
"collapsed": false
},
@ -751,8 +732,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"
@ -772,7 +753,7 @@
},
{
"cell_type": "code",
"execution_count": 20,
"execution_count": 19,
"metadata": {
"collapsed": false
},
@ -780,7 +761,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",
@ -795,19 +776,19 @@
" <tbody>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> 1</td>\n",
" <td> 1</td>\n",
" <td> total</td>\n",
" <td> 0.668323</td>\n",
" <td> 0.001264</td>\n",
" <td>1</td>\n",
" <td>1</td>\n",
" <td>total</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</td>\n",
" <td>2</td>\n",
" <td>total</td>\n",
" <td>1.292013</td>\n",
" <td>0.007642</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
@ -815,11 +796,11 @@
],
"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,
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
@ -838,7 +819,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 20,
"metadata": {
"collapsed": true
},
@ -856,7 +837,7 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 21,
"metadata": {
"collapsed": false
},
@ -883,7 +864,7 @@
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": 22,
"metadata": {
"collapsed": false
},
@ -891,7 +872,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",
@ -908,23 +889,23 @@
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td> 1</td>\n",
" <td> 0.000000</td>\n",
" <td> 0.000001</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>1</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>-3.774758e-15</td>\n",
" <td>0.011292</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> 1</td>\n",
" <td> 0.000001</td>\n",
" <td> 20.000000</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</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.443290e-15</td>\n",
" <td>0.002570</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
@ -935,12 +916,12 @@
"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.88e-15 1.13e-02 \n",
"1 (((total / flux) - (absorption / flux)) - (sca... 1.22e-15 1.80e-03 "
" 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,
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
@ -962,7 +943,7 @@
},
{
"cell_type": "code",
"execution_count": 24,
"execution_count": 23,
"metadata": {
"collapsed": false
},
@ -970,7 +951,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",
@ -987,23 +968,23 @@
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td> 1</td>\n",
" <td> 0.000000</td>\n",
" <td> 0.000001</td>\n",
" <td> total</td>\n",
" <td> ((absorption / flux) / (total / flux))</td>\n",
" <td> 0.076219</td>\n",
" <td> 0.000651</td>\n",
" <td>1</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.076115</td>\n",
" <td>0.000649</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> 1</td>\n",
" <td> 0.000001</td>\n",
" <td> 20.000000</td>\n",
" <td> total</td>\n",
" <td> ((absorption / flux) / (total / flux))</td>\n",
" <td> 0.019319</td>\n",
" <td> 0.000086</td>\n",
" <td>1</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.019263</td>\n",
" <td>0.000095</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
@ -1015,11 +996,11 @@
"1 1 6.25e-07 2.00e+01 total \n",
"\n",
" score mean std. dev. \n",
"0 ((absorption / flux) / (total / flux)) 7.62e-02 6.51e-04 \n",
"1 ((absorption / flux) / (total / flux)) 1.93e-02 8.65e-05 "
"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,
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
@ -1034,7 +1015,7 @@
},
{
"cell_type": "code",
"execution_count": 25,
"execution_count": 24,
"metadata": {
"collapsed": false
},
@ -1042,7 +1023,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",
@ -1059,23 +1040,23 @@
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td> 1</td>\n",
" <td> 0.000000</td>\n",
" <td> 0.000001</td>\n",
" <td> total</td>\n",
" <td> ((scatter / flux) / (total / flux))</td>\n",
" <td> 0.923781</td>\n",
" <td> 0.007714</td>\n",
" <td>1</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.923885</td>\n",
" <td>0.007736</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> 1</td>\n",
" <td> 0.000001</td>\n",
" <td> 20.000000</td>\n",
" <td> total</td>\n",
" <td> ((scatter / flux) / (total / flux))</td>\n",
" <td> 0.980681</td>\n",
" <td> 0.002617</td>\n",
" <td>1</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.980737</td>\n",
" <td>0.003737</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
@ -1087,11 +1068,11 @@
"1 1 6.25e-07 2.00e+01 total \n",
"\n",
" score mean std. dev. \n",
"0 ((scatter / flux) / (total / flux)) 9.24e-01 7.71e-03 \n",
"1 ((scatter / flux) / (total / flux)) 9.81e-01 2.62e-03 "
"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,
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
@ -1113,7 +1094,7 @@
},
{
"cell_type": "code",
"execution_count": 26,
"execution_count": 25,
"metadata": {
"collapsed": false
},
@ -1121,7 +1102,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",
@ -1138,23 +1119,23 @@
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td> 1</td>\n",
" <td> 0.000000</td>\n",
" <td> 0.000001</td>\n",
" <td> total</td>\n",
" <td> (((absorption / flux) / (total / flux)) + ((sc...</td>\n",
" <td> 1</td>\n",
" <td> 0.007741</td>\n",
" <td>1</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.0</td>\n",
" <td>0.007763</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td> 1</td>\n",
" <td> 0.000001</td>\n",
" <td> 20.000000</td>\n",
" <td> total</td>\n",
" <td> (((absorption / flux) / (total / flux)) + ((sc...</td>\n",
" <td> 1</td>\n",
" <td> 0.002619</td>\n",
" <td>1</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.0</td>\n",
" <td>0.003739</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
@ -1166,11 +1147,11 @@
"1 1 6.25e-07 2.00e+01 total \n",
"\n",
" score mean std. dev. \n",
"0 (((absorption / flux) / (total / flux)) + ((sc... 1.00e+00 7.74e-03 \n",
"1 (((absorption / flux) / (total / flux)) + ((sc... 1.00e+00 2.62e-03 "
"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,
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
@ -1200,7 +1181,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

View file

@ -0,0 +1,13 @@
.. _notebook_mgxs_part_iv:
====================================================
MGXS Part IV: Multi-Group Mode Cross-Section Library
====================================================
.. only:: html
.. notebook:: mgxs-part-iv.ipynb
.. only:: latex
IPython notebooks must be viewed in the online HTML documentation.

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,9 @@ 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:**
.. toctree::
:maxdepth: 1
ace
**Creating input files:**
.. toctree::
:maxdepth: 1
cmfd
element
filter
geometry
material
mesh
nuclide
opencg_compatible
plots
settings
source
stats
surface
tallies
trigger
universe
**Running OpenMC:**
.. toctree::
:maxdepth: 1
executor
**Post-processing:**
.. toctree::
:maxdepth: 1
particle_restart
statepoint
summary
tallies
**Multi-Group Cross Section Generation**
.. toctree::
:maxdepth: 1
mgxs
energy_groups
mgxs_library
**Example Jupyter Notebooks:**
-------------------------
Example Jupyter Notebooks
-------------------------
.. toctree::
:maxdepth: 1
@ -79,6 +26,278 @@ on a given module or class.
examples/mgxs-part-i
examples/mgxs-part-ii
examples/mgxs-part-iii
examples/mgxs-part-iv
------------------------------------
:mod:`openmc` -- Basic Functionality
------------------------------------
Handling nuclear data
---------------------
Classes
+++++++
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.XSdata
openmc.MGXSLibrary
Functions
+++++++++
.. autosummary::
:toctree: generated
:nosignatures:
openmc.ace.ascii_to_binary
Simulation Settings
-------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Source
openmc.ResonanceScattering
openmc.Settings
Material Specification
----------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Nuclide
openmc.Element
openmc.Macroscopic
openmc.Material
openmc.Materials
Building geometry
-----------------
.. 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
Many of the above classes are derived from several abstract classes:
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Surface
openmc.Region
openmc.Lattice
One function is also available to create a hexagonal region defined by the
intersection of six surface half-spaces.
.. autosummary::
:toctree: generated
:nosignatures:
:template: myfunction.rst
openmc.make_hexagon_region
Constructing Tallies
--------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Filter
openmc.Mesh
openmc.Trigger
openmc.Tally
openmc.Tallies
Coarse Mesh Finite Difference Acceleration
------------------------------------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.CMFDMesh
openmc.CMFD
Plotting
--------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.Plot
openmc.Plots
Running OpenMC
--------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myfunction.rst
openmc.run
openmc.plot_geometry
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: myclassinherit.rst
openmc.mgxs.MGXS
openmc.mgxs.AbsorptionXS
openmc.mgxs.CaptureXS
openmc.mgxs.Chi
openmc.mgxs.FissionXS
openmc.mgxs.KappaFissionXS
openmc.mgxs.MultiplicityMatrixXS
openmc.mgxs.NuFissionXS
openmc.mgxs.NuFissionMatrixXS
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
.. _Jupyter: https://jupyter.org/
.. _NumPy: http://www.numpy.org/

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

@ -897,11 +897,19 @@ Each ``<surface>`` element can have the following attributes or sub-elements:
*Default*: None
:boundary:
The boundary condition for the surface. This can be "transmission",
"vacuum", or "reflective".
The boundary condition for the surface. This can be "transmission",
"vacuum", "reflective", or "periodic". Periodic boundary conditions can
only be applied to x-, y-, and z-planes. Only axis-aligned periodicity is
supported, i.e., x-planes can only be paired with x-planes. Specify which
planes are periodic and the code will automatically identify which planes
are paired together.
*Default*: "transmission"
:periodic_surface_id:
If a periodic boundary condition is applied, this attribute identifies the
``id`` of the corresponding periodic sufrace.
The following quadratic surfaces can be modeled:
:x-plane:
@ -1033,6 +1041,20 @@ Each ``<cell>`` element can have the following attributes or sub-elements:
<cell fill="..." rotation="0 0 90" />
The rotation applied is an intrinsic rotation whose Tait-Bryan angles are
given as those specified about the x, y, and z axes respectively. That is to
say, if the angles are :math:`(\phi, \theta, \psi)`, then the rotation
matrix applied is :math:`R_z(\psi) R_y(\theta) R_x(\phi)` or
.. math::
\left [ \begin{array}{ccc} \cos\theta \cos\psi & -\cos\theta \sin\psi +
\sin\phi \sin\theta \cos\psi & \sin\phi \sin\psi + \cos\phi \sin\theta
\cos\psi \\ \cos\theta \sin\psi & \cos\phi \cos\psi + \sin\phi \sin\theta
\sin\psi & -\sin\phi \cos\psi + \cos\phi \sin\theta \sin\psi \\
-\sin\theta & \sin\phi \cos\theta & \cos\phi \cos\theta \end{array}
\right ]
*Default*: None
:translation:
@ -1215,11 +1237,10 @@ Each ``material`` element can have the following attributes or sub-elements:
An element with attributes/sub-elements called ``value`` and ``units``. The
``value`` attribute is the numeric value of the density while the ``units``
can be "g/cm3", "kg/m3", "atom/b-cm", "atom/cm3", or "sum". The "sum" unit
indicates that values appearing in ``ao`` attributes for ``<nuclide>`` and
``<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. The "macro" unit is used with
indicates that values appearing in ``ao`` or ``wo`` attributes for ``<nuclide>``
and ``<element>`` sub-elements are to be interpreted as absolute nuclide/element
densities in atom/b-cm or g/cm3, and the total density of the material is
taken as the sum of all nuclides/elements. 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
@ -1258,6 +1279,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
@ -1293,6 +1317,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
@ -1301,6 +1328,8 @@ 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
@ -1569,7 +1598,8 @@ The ``<tally>`` element accepts the following sub-elements:
|Score | Description |
+======================+===================================================+
|absorption |Total absorption rate. This accounts for all |
| |reactions which do not produce secondary neutrons. |
| |reactions which do not produce secondary neutrons |
| |as well as fission. |
+----------------------+---------------------------------------------------+
|elastic |Elastic scattering reaction rate. |
+----------------------+---------------------------------------------------+

View file

@ -8,10 +8,10 @@ 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
can be found. In multi-group mode, this ``mgxs.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``
the multi-group data library required to be used in the ``mgxs.xml``
file.
Similar to the other input file types, the multi-group library is provided in
@ -22,9 +22,9 @@ materials.
.. _XML: http://www.w3.org/XML/
------------------------------------------------
MGXS Library Specification -- cross_sections.xml
------------------------------------------------
--------------------------------------
MGXS Library Specification -- mgxs.xml
--------------------------------------
The multi-group library meta-data is contained within the groups_,
group_structure_, and inverse_velocities_ elements.
@ -33,7 +33,7 @@ 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.
@ -172,7 +172,7 @@ attributes/sub-elements required to describe the meta-data:
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
cost memory but can allow for a decrease in runtime compared to leaving as a
set of Legendre coefficients. This element has the following
attributes/sub-elements:
@ -181,7 +181,7 @@ attributes/sub-elements required to describe the meta-data:
tabular format should be performed or not. A value of "true" means
the conversion should be performed, "false" means it should not.
*Default*: "true"
*Default*: "false"
:num_points:
If the conversion is to take place the number of tabular points is

View file

@ -248,7 +248,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.
@ -260,3 +260,69 @@ if (run_mode == 'k-eigenvalue' and source_present > 0)
``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

@ -293,6 +293,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
@ -13,7 +12,7 @@ particles = 10000
###############################################################################
# Exporting to OpenMC materials.xml File
# Exporting to OpenMC materials.xml file
###############################################################################
# Instantiate some Nuclides
@ -32,15 +31,14 @@ fuel = openmc.Material(material_id=40, name='fuel')
fuel.set_density('g/cc', 4.5)
fuel.add_nuclide(u235, 1.)
# Instantiate a MaterialsFile, register all Materials, and export to XML
materials_file = openmc.MaterialsFile()
# Instantiate a Materials collection and export to XML
materials_file = openmc.Materials([moderator, fuel])
materials_file.default_xs = '71c'
materials_file.add_materials([moderator, fuel])
materials_file.export_to_xml()
###############################################################################
# Exporting to OpenMC geometry.xml File
# Exporting to OpenMC geometry.xml file
###############################################################################
# Instantiate ZCylinder surfaces
@ -75,31 +73,31 @@ cell1.fill = universe1
universe1.add_cells([cell2, cell3])
root.add_cells([cell1, cell4])
# 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()
# Instantiate a Geometry, register the root Universe, and export to XML
geometry = openmc.Geometry(root)
geometry.export_to_xml()
###############################################################################
# Exporting to OpenMC settings.xml File
# Exporting to OpenMC settings.xml file
###############################################################################
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
settings_file = openmc.SettingsFile()
# Instantiate a Settings object, set all runtime parameters, and export to XML
settings_file = openmc.Settings()
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()
###############################################################################
# Exporting to OpenMC tallies.xml File
# Exporting to OpenMC tallies.xml file
###############################################################################
# Instantiate some tally Filters
@ -109,33 +107,21 @@ 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()
tallies_file.add_tally(first_tally)
tallies_file.add_tally(second_tally)
tallies_file.add_tally(third_tally)
# Instantiate a Tallies collection and export to XML
tallies_file = openmc.Tallies((first_tally, second_tally, third_tally))
tallies_file.export_to_xml()

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
@ -39,15 +36,14 @@ moderator.add_nuclide(h1, 2.)
moderator.add_nuclide(o16, 1.)
moderator.add_s_alpha_beta('HH2O', '71t')
# Instantiate a MaterialsFile, register all Materials, and export to XML
materials_file = openmc.MaterialsFile()
# Instantiate a Materials collection and export to XML
materials_file = openmc.Materials([fuel1, fuel2, moderator])
materials_file.default_xs = '71c'
materials_file.add_materials([fuel1, fuel2, moderator])
materials_file.export_to_xml()
###############################################################################
# Exporting to OpenMC geometry.xml File
# Exporting to OpenMC geometry.xml file
###############################################################################
# Instantiate planar surfaces
@ -100,26 +96,25 @@ outer_box.fill = moderator
root = openmc.Universe(universe_id=0, name='root universe')
root.add_cells([inner_box, middle_box, outer_box])
# 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()
# Instantiate a Geometry, register the root Universe, and export to XML
geometry = openmc.Geometry(root)
geometry.export_to_xml()
###############################################################################
# Exporting to OpenMC settings.xml File
###############################################################################
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
settings_file = openmc.SettingsFile()
# Instantiate a Settings object, set all runtime parameters, and export to XML
settings_file = openmc.Settings()
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()
###############################################################################
@ -132,7 +127,6 @@ plot.width = [20, 20]
plot.pixels = [200, 200]
plot.color = 'cell'
# Instantiate a PlotsFile, add Plot, and export to XML
plot_file = openmc.PlotsFile()
plot_file.add_plot(plot)
# Instantiate a Plots collection and export to XML
plot_file = openmc.Plots([plot])
plot_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
@ -37,15 +35,14 @@ iron = openmc.Material(material_id=3, name='iron')
iron.set_density('g/cc', 7.9)
iron.add_nuclide(fe56, 1.)
# Instantiate a MaterialsFile, register all Materials, and export to XML
materials_file = openmc.MaterialsFile()
# Instantiate a Materials collection and export to XML
materials_file = openmc.Materials([moderator, fuel, iron])
materials_file.default_xs = '71c'
materials_file.add_materials([moderator, fuel, iron])
materials_file.export_to_xml()
###############################################################################
# Exporting to OpenMC geometry.xml File
# Exporting to OpenMC geometry.xml file
###############################################################################
# Instantiate Surfaces
@ -107,27 +104,26 @@ lattice.outer = univ2
# Fill Cell with the Lattice
cell1.fill = lattice
# 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()
# Instantiate a Geometry, register the root Universe, and export to XML
geometry = openmc.Geometry(root)
geometry.export_to_xml()
###############################################################################
# Exporting to OpenMC settings.xml File
# Exporting to OpenMC settings.xml file
###############################################################################
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
settings_file = openmc.SettingsFile()
# Instantiate a Settings object, set all runtime parameters, and export to XML
settings_file = openmc.Settings()
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
@ -135,7 +131,7 @@ settings_file.export_to_xml()
###############################################################################
# Exporting to OpenMC plots.xml File
# Exporting to OpenMC plots.xml file
###############################################################################
plot_xy = openmc.Plot(plot_id=1)
@ -153,10 +149,8 @@ plot_yz.width = [8, 8]
plot_yz.pixels = [400, 400]
plot_yz.color = 'mat'
# Instantiate a PlotsFile, add Plot, and export to XML
plot_file = openmc.PlotsFile()
plot_file.add_plot(plot_xy)
plot_file.add_plot(plot_yz)
# Instantiate a Plots collection, add plots, and export to XML
plot_file = openmc.Plots((plot_xy, plot_yz))
plot_file.export_to_xml()
@ -166,10 +160,9 @@ 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()
tallies_file.add_tally(tally)
# Instantiate a Tallies collection and export to XML
tallies_file = openmc.Tallies([tally])
tallies_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
@ -13,7 +11,7 @@ particles = 10000
###############################################################################
# Exporting to OpenMC materials.xml File
# Exporting to OpenMC materials.xml file
###############################################################################
# Instantiate some Nuclides
@ -32,15 +30,14 @@ moderator.add_nuclide(h1, 2.)
moderator.add_nuclide(o16, 1.)
moderator.add_s_alpha_beta('HH2O', '71t')
# Instantiate a MaterialsFile, register all Materials, and export to XML
materials_file = openmc.MaterialsFile()
# Instantiate a Materials collection and export to XML
materials_file = openmc.Materials((moderator, fuel))
materials_file.default_xs = '71c'
materials_file.add_materials([moderator, fuel])
materials_file.export_to_xml()
###############################################################################
# Exporting to OpenMC geometry.xml File
# Exporting to OpenMC geometry.xml file
###############################################################################
# Instantiate Surfaces
@ -101,14 +98,12 @@ univ4.add_cell(cell2)
# Instantiate nested Lattices
lattice1 = openmc.RectLattice(lattice_id=4, name='4x4 assembly')
lattice1.dimension = [2, 2]
lattice1.lower_left = [-1., -1.]
lattice1.pitch = [1., 1.]
lattice1.universes = [[univ1, univ2],
[univ2, univ3]]
lattice2 = openmc.RectLattice(lattice_id=6, name='4x4 core')
lattice2.dimension = [2, 2]
lattice2.lower_left = [-2., -2.]
lattice2.pitch = [2., 2.]
lattice2.universes = [[univ4, univ4],
@ -118,32 +113,31 @@ lattice2.universes = [[univ4, univ4],
cell1.fill = lattice2
cell2.fill = lattice1
# 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()
# Instantiate a Geometry, register the root Universe, and export to XML
geometry = openmc.Geometry(root)
geometry.export_to_xml()
###############################################################################
# Exporting to OpenMC settings.xml File
# Exporting to OpenMC settings.xml file
###############################################################################
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
settings_file = openmc.SettingsFile()
# Instantiate a Settings object, set all runtime parameters, and export to XML
settings_file = openmc.Settings()
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()
###############################################################################
# Exporting to OpenMC plots.xml File
# Exporting to OpenMC plots.xml file
###############################################################################
plot = openmc.Plot(plot_id=1)
@ -152,14 +146,13 @@ plot.width = [4, 4]
plot.pixels = [400, 400]
plot.color = 'mat'
# Instantiate a PlotsFile, add Plot, and export to XML
plot_file = openmc.PlotsFile()
plot_file.add_plot(plot)
# Instantiate a Plots object and export to XML
plot_file = openmc.Plots([plot])
plot_file.export_to_xml()
###############################################################################
# Exporting to OpenMC tallies.xml File
# Exporting to OpenMC tallies.xml file
###############################################################################
# Instantiate a tally mesh
@ -175,11 +168,9 @@ 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()
tallies_file.add_mesh(mesh)
tallies_file.add_tally(tally)
# Instantiate a Tallies collection, register Tally/Mesh, and export to XML
tallies_file = openmc.Tallies([tally])
tallies_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
@ -13,7 +11,7 @@ particles = 10000
###############################################################################
# Exporting to OpenMC materials.xml File
# Exporting to OpenMC materials.xml file
###############################################################################
# Instantiate some Nuclides
@ -32,15 +30,14 @@ moderator.add_nuclide(h1, 2.)
moderator.add_nuclide(o16, 1.)
moderator.add_s_alpha_beta('HH2O', '71t')
# Instantiate a MaterialsFile, register all Materials, and export to XML
materials_file = openmc.MaterialsFile()
# Instantiate a Materials collection and export to XML
materials_file = openmc.Materials([moderator, fuel])
materials_file.default_xs = '71c'
materials_file.add_materials([moderator, fuel])
materials_file.export_to_xml()
###############################################################################
# Exporting to OpenMC geometry.xml File
# Exporting to OpenMC geometry.xml file
###############################################################################
# Instantiate Surfaces
@ -97,7 +94,6 @@ root.add_cell(cell1)
# Instantiate a Lattice
lattice = openmc.RectLattice(lattice_id=5)
lattice.dimension = [4, 4]
lattice.lower_left = [-2., -2.]
lattice.pitch = [1., 1.]
lattice.universes = [[univ1, univ2, univ1, univ2],
@ -108,34 +104,33 @@ lattice.universes = [[univ1, univ2, univ1, univ2],
# Fill Cell with the Lattice
cell1.fill = lattice
# 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()
# Instantiate a Geometry, register the root Universe, and export to XML
geometry = openmc.Geometry(root)
geometry.export_to_xml()
###############################################################################
# Exporting to OpenMC settings.xml File
# Exporting to OpenMC settings.xml file
###############################################################################
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
settings_file = openmc.SettingsFile()
# Instantiate a Settings object, set all runtime parameters, and export to XML
settings_file = openmc.Settings()
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()
###############################################################################
# Exporting to OpenMC plots.xml File
# Exporting to OpenMC plots.xml file
###############################################################################
plot = openmc.Plot(plot_id=1)
@ -144,14 +139,13 @@ plot.width = [4, 4]
plot.pixels = [400, 400]
plot.color = 'mat'
# Instantiate a PlotsFile, add Plot, and export to XML
plot_file = openmc.PlotsFile()
plot_file.add_plot(plot)
# Instantiate a Plots collection and export to XML
plot_file = openmc.Plots([plot])
plot_file.export_to_xml()
###############################################################################
# Exporting to OpenMC tallies.xml File
# Exporting to OpenMC tallies.xml file
###############################################################################
# Instantiate a tally mesh
@ -167,16 +161,14 @@ 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()
tallies_file.add_mesh(mesh)
tallies_file.add_tally(tally)
# Instantiate a Tallies collection and export to XML
tallies_file = openmc.Tallies([tally])
tallies_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
@ -13,7 +11,7 @@ particles = 1000
###############################################################################
# Exporting to OpenMC materials.xml File
# Exporting to OpenMC materials.xml file
###############################################################################
# Instantiate some Nuclides
@ -102,15 +100,14 @@ borated_water.add_nuclide(o16, 2.4672e-2)
borated_water.add_nuclide(o17, 6.0099e-5)
borated_water.add_s_alpha_beta('HH2O', '71t')
# Instantiate a MaterialsFile, register all Materials, and export to XML
materials_file = openmc.MaterialsFile()
# Instantiate a Materials collection and export to XML
materials_file = openmc.Materials([uo2, helium, zircaloy, borated_water])
materials_file.default_xs = '71c'
materials_file.add_materials([uo2, helium, zircaloy, borated_water])
materials_file.export_to_xml()
###############################################################################
# Exporting to OpenMC geometry.xml File
# Exporting to OpenMC geometry.xml file
###############################################################################
# Instantiate ZCylinder surfaces
@ -151,27 +148,26 @@ root = openmc.Universe(universe_id=0, name='root universe')
# Register Cells with Universe
root.add_cells([fuel, gap, clad, water])
# 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()
# Instantiate a Geometry, register the root Universe, and export to XML
geometry = openmc.Geometry(root)
geometry.export_to_xml()
###############################################################################
# Exporting to OpenMC settings.xml File
# Exporting to OpenMC settings.xml file
###############################################################################
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
settings_file = openmc.SettingsFile()
# Instantiate a Settings object, set all runtime parameters, and export to XML
settings_file = openmc.Settings()
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]
@ -179,7 +175,7 @@ settings_file.export_to_xml()
###############################################################################
# Exporting to OpenMC tallies.xml File
# Exporting to OpenMC tallies.xml file
###############################################################################
# Instantiate a tally mesh
@ -196,14 +192,9 @@ 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()
tallies_file.add_mesh(mesh)
tallies_file.add_tally(tally)
# Instantiate a Tallies collection and export to XML
tallies_file = openmc.Tallies([tally])
tallies_file.export_to_xml()

View file

@ -1,8 +1,5 @@
import openmc
import openmc.mgxs
from openmc.source import Source
from openmc.stats import Box
import numpy as np
###############################################################################
# Simulation Input File Parameters
@ -14,7 +11,7 @@ inactive = 10
particles = 1000
###############################################################################
# Exporting to OpenMC mg_cross_sections.xml File
# Exporting to OpenMC mgxs.xml file
###############################################################################
# Instantiate the energy group data
@ -24,50 +21,48 @@ groups = openmc.mgxs.EnergyGroups(group_edges=[1E-11, 0.0635E-6, 10.0E-6,
# 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])
uo2_xsdata.total = [0.1779492, 0.3298048, 0.4803882, 0.5543674,
0.3118013, 0.3951678, 0.5644058]
uo2_xsdata.absorption = [8.0248E-03, 3.7174E-03, 2.6769E-02, 9.6236E-02,
3.0020E-02, 1.1126E-01, 2.8278E-01]
uo2_xsdata.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.fission = [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 = [2.005998E-02, 2.027303E-03, 1.570599E-02,
4.518301E-02, 4.334208E-02, 2.020901E-01,
5.257105E-01]
uo2_xsdata.chi = [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)
h2o_xsdata.total = [0.15920605, 0.412969593, 0.59030986, 0.58435,
0.718, 1.2544497, 2.650379]
h2o_xsdata.absorption = [6.0105E-04, 1.5793E-05, 3.3716E-04,
1.9406E-03, 5.7416E-03, 1.5001E-02,
3.7239E-02]
h2o_xsdata.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]]]
mg_cross_sections_file = openmc.MGXSLibraryFile(groups)
mg_cross_sections_file.add_xsdatas([uo2_xsdata,h2o_xsdata])
mg_cross_sections_file = openmc.MGXSLibrary(groups)
mg_cross_sections_file.add_xsdatas([uo2_xsdata, h2o_xsdata])
mg_cross_sections_file.export_to_xml()
###############################################################################
# Exporting to OpenMC materials.xml File
# Exporting to OpenMC materials.xml file
###############################################################################
# Instantiate some Macroscopic Data
@ -83,15 +78,14 @@ 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()
# Instantiate a Materials collection and export to XML
materials_file = openmc.Materials([uo2, water])
materials_file.default_xs = '300K'
materials_file.add_materials([uo2, water])
materials_file.export_to_xml()
###############################################################################
# Exporting to OpenMC geometry.xml File
# Exporting to OpenMC geometry.xml file
###############################################################################
# Instantiate ZCylinder surfaces
@ -124,31 +118,32 @@ 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()
# Instantiate a Geometry, register the root Universe, and export to XML
geometry = openmc.Geometry(root)
geometry.export_to_xml()
###############################################################################
# Exporting to OpenMC settings.xml File
# Exporting to OpenMC settings.xml file
###############################################################################
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
settings_file = openmc.SettingsFile()
# Instantiate a Settings object, set all runtime parameters, and export to XML
settings_file = openmc.Settings()
settings_file.energy_mode = "multi-group"
settings_file.cross_sections = "./mg_cross_sections.xml"
settings_file.cross_sections = "./mgxs.xml"
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
settings_file.source = Source(space=Box([-0.63, -0.63, -1.], [0.63, 0.63, 1.]))
# 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
# Exporting to OpenMC tallies.xml file
###############################################################################
# Instantiate a tally mesh
@ -167,14 +162,9 @@ 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()
tallies_file.add_mesh(mesh)
tallies_file.add_tally(tally)
# Instantiate a Tallies collection, register all Tallies, and export to XML
tallies_file = openmc.Tallies([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
@ -15,7 +12,7 @@ particles = 10000
###############################################################################
# Exporting to OpenMC materials.xml File
# Exporting to OpenMC materials.xml file
###############################################################################
# Instantiate a Nuclides
@ -26,15 +23,14 @@ fuel = openmc.Material(material_id=1, name='fuel')
fuel.set_density('g/cc', 4.5)
fuel.add_nuclide(u235, 1.)
# Instantiate a MaterialsFile, register Material, and export to XML
materials_file = openmc.MaterialsFile()
# Instantiate a Materials collection and export to XML
materials_file = openmc.Materials([fuel])
materials_file.default_xs = '71c'
materials_file.add_material(fuel)
materials_file.export_to_xml()
###############################################################################
# Exporting to OpenMC geometry.xml File
# Exporting to OpenMC geometry.xml file
###############################################################################
# Instantiate Surfaces
@ -67,24 +63,24 @@ root = openmc.Universe(universe_id=0, name='root universe')
# Register Cell with Universe
root.add_cell(cell)
# 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()
# Instantiate a Geometry, register the root Universe, and export to XML
geometry = openmc.Geometry(root)
geometry.export_to_xml()
###############################################################################
# Exporting to OpenMC settings.xml File
# Exporting to OpenMC settings.xml file
###############################################################################
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
settings_file = openmc.SettingsFile()
# Instantiate a Settings object, set all runtime parameters, and export to XML
settings_file = openmc.Settings()
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

@ -1,3 +1,5 @@
from openmc.cell import *
from openmc.lattice import *
from openmc.element import *
from openmc.geometry import *
from openmc.nuclide import *
@ -16,6 +18,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 *

480
openmc/cell.py Normal file
View file

@ -0,0 +1,480 @@
from collections import OrderedDict, Iterable
from math import cos, sin, pi
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
import warnings
import numpy as np
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):
r"""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.
fill : openmc.Material or openmc.Universe or openmc.Lattice or None or iterable of openmc.Material, optional
Indicates what the region of space is filled with
region : openmc.Region, optional
Region of space that is assigned to the cell.
Attributes
----------
id : int
Unique identifier for the cell
name : str
Name of the cell
fill : openmc.Material or openmc.Universe or openmc.Lattice or None or iterable of openmc.Material
Indicates what the region of space is filled with. If None, the cell is
treated as a void. An iterable of materials is used to fill repeated
instances of a cell with different materials.
fill_type : {'material', 'universe', 'lattice', 'distribmat', 'void'}
Indicates what the cell is filled with.
region : openmc.Region or None
Region of space that is assigned to the cell.
rotation : Iterable of float
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. The rotation applied is an intrinsic rotation with specified
Tait-Bryan angles. That is to say, if the angles are :math:`(\phi,
\theta, \psi)`, then the rotation matrix applied is :math:`R_z(\psi)
R_y(\theta) R_x(\phi)` or
.. math::
\left [ \begin{array}{ccc} \cos\theta \cos\psi & -\cos\theta \sin\psi
+ \sin\phi \sin\theta \cos\psi & \sin\phi \sin\psi + \cos\phi
\sin\theta \cos\psi \\ \cos\theta \sin\psi & \cos\phi \cos\psi +
\sin\phi \sin\theta \sin\psi & -\sin\phi \cos\psi + \cos\phi
\sin\theta \sin\psi \\ -\sin\theta & \sin\phi \cos\theta & \cos\phi
\cos\theta \end{array} \right ]
rotation_matrix : numpy.ndarray
The rotation matrix defined by the angles specified in the
:attr:`Cell.rotation` property.
translation : Iterable of float
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='', fill=None, region=None):
# Initialize Cell class attributes
self.id = cell_id
self.name = name
self.fill = fill
self.region = region
self._rotation = None
self._rotation_matrix = None
self._translation = None
self._offsets = None
self._distribcell_index = None
def __contains__(self, point):
if self.region is None:
return True
else:
return point in self.region
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.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 += '{: <16}=\t{}\n'.format('\tID', self.id)
string += '{: <16}=\t{}\n'.format('\tName', self.name)
if self.fill_type == 'material':
string += '{: <16}=\tMaterial {}\n'.format('\tFill', self.fill.id)
elif self.fill_type == 'void':
string += '{: <16}=\tNone\n'.format('\tFill')
elif self.fill_type == 'distribmat':
string += '{: <16}=\t{}\n'.format('\tFill', list(map(
lambda m: m if m is None else m.id, self.fill)))
else:
string += '{: <16}=\t{}\n'.format('\tFill', self.fill.id)
string += '{: <16}=\t{}\n'.format('\tRegion', self.region)
string += '{: <16}=\t{}\n'.format('\tRotation', self.rotation)
string += '{: <16}=\t{}\n'.format('\tTranslation', self.translation)
string += '{: <16}=\t{}\n'.format('\tOffset', self.offsets)
string += '{: <16}=\t{}\n'.format('\tDistribcell index', 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'
elif isinstance(self.fill, Iterable):
return 'distribmat'
else:
return 'void'
@property
def region(self):
return self._region
@property
def rotation(self):
return self._rotation
@property
def rotation_matrix(self):
return self._rotation_matrix
@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 fill is not None:
if isinstance(fill, basestring):
if fill.strip().lower() != 'void':
msg = 'Unable to set Cell ID="{0}" to use a non-Material ' \
'or Universe fill "{1}"'.format(self._id, fill)
raise ValueError(msg)
fill = None
elif isinstance(fill, Iterable):
for i, f in enumerate(fill):
if f is not None:
cv.check_type('cell.fill[i]', f, openmc.Material)
elif not isinstance(fill, (openmc.Material, openmc.Lattice,
openmc.Universe)):
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):
if not isinstance(self.fill, openmc.Universe):
raise RuntimeError('Cell rotation can only be applied if the cell '
'is filled with a Universe')
cv.check_type('cell rotation', rotation, Iterable, Real)
cv.check_length('cell rotation', rotation, 3)
self._rotation = np.asarray(rotation)
# Save rotation matrix
phi, theta, psi = self.rotation*(-pi/180.)
c3, s3 = cos(phi), sin(phi)
c2, s2 = cos(theta), sin(theta)
c1, s1 = cos(psi), sin(psi)
self._rotation_matrix = np.array([
[c1*c2, c1*s2*s3 - c3*s1, s1*s3 + c1*c3*s2],
[c2*s1, c1*c3 + s1*s2*s3, c3*s1*s2 - c1*s3],
[-s2, c2*s3, c2*c3]])
@translation.setter
def translation(self, translation):
cv.check_type('cell translation', translation, Iterable, Real)
cv.check_length('cell translation', translation, 3)
self._translation = np.asarray(translation)
@offsets.setter
def offsets(self, offsets):
cv.check_type('cell offsets', offsets, Iterable)
self._offsets = offsets
@region.setter
def region(self, region):
if region is not None:
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.fill_type in ('material', 'distribmat', 'void'):
offset = 0
# If the Cell is filled by a Universe
elif self.fill_type == 'universe':
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.fill_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.fill_type in ('universe', '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.fill_type == 'universe':
universes[self.fill.id] = self.fill
universes.update(self.fill.get_all_universes())
elif self.fill_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 self.fill_type == 'void':
element.set("material", "void")
elif self.fill_type == 'material':
element.set("material", str(self.fill.id))
elif self.fill_type == 'distribmat':
element.set("material", ' '.join(['void' if m is None else str(m.id)
for m in self.fill]))
elif self.fill_type in ('universe', 'lattice'):
element.set("fill", str(self.fill.id))
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.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

@ -1,35 +1,9 @@
import copy
from collections import Iterable
from numbers import Integral, Real
import numpy as np
def _isinstance(value, expected_type):
"""A Numpy-aware replacement for isinstance
This function will be obsolete when Numpy v. >= 1.9 is established.
"""
# Declare numpy numeric types.
np_ints = (np.int_, np.intc, np.intp, np.int8, np.int16, np.int32, np.int64,
np.uint8, np.uint16, np.uint32, np.uint64)
np_floats = (np.float_, np.float16, np.float32, np.float64)
# Include numpy integers, if necessary.
if type(expected_type) is tuple:
if Integral in expected_type:
expected_type = expected_type + np_ints
elif expected_type is Integral:
expected_type = (Integral, ) + np_ints
# Include numpy floats, if necessary.
if type(expected_type) is tuple:
if Real in expected_type:
expected_type = expected_type + np_floats
elif expected_type is Real:
expected_type = (Real, ) + np_floats
# Now, make the instance check.
return isinstance(value, expected_type)
def check_type(name, value, expected_type, expected_iter_type=None):
"""Ensure that an object is of an expected type. Optionally, if the object is
@ -41,26 +15,45 @@ 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__)
raise ValueError(msg)
if not isinstance(value, expected_type):
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 TypeError(msg)
if expected_iter_type:
for item in value:
if not _isinstance(item, expected_iter_type):
if isinstance(value, np.ndarray):
if not issubclass(value.dtype.type, 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__)
raise ValueError(msg)
expected_iter_type.__name__)
else:
return
for item in value:
if not isinstance(item, expected_iter_type):
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 TypeError(msg)
def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1):
@ -106,12 +99,12 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1):
# If this item is of the expected type, then we've reached the bottom
# level of this branch.
if _isinstance(current_item, expected_type):
if isinstance(current_item, expected_type):
# Is this deep enough?
if len(tree) < min_depth:
msg = 'Error setting "{0}": The item at {1} does not meet the '\
'minimum depth of {2}'.format(name, ind_str, min_depth)
raise ValueError(msg)
raise TypeError(msg)
# This item is okay. Move on to the next item.
index[-1] += 1
@ -129,7 +122,7 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1):
msg = 'Error setting {0}: Found an iterable at {1}, items '\
'in that iterable exceed the maximum depth of {2}' \
.format(name, ind_str, max_depth)
raise ValueError(msg)
raise TypeError(msg)
else:
# This item is completely unexpected.
@ -137,7 +130,7 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1):
"item at {2} is of type '{3}'"\
.format(name, expected_type.__name__, ind_str,
type(current_item).__name__)
raise ValueError(msg)
raise TypeError(msg)
def check_length(name, value, length_min, length_max=None):
@ -245,3 +238,65 @@ 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 __add__(self, other):
new_instance = copy.copy(self)
new_instance += other
return new_instance
def __radd__(self, other):
return self + other
def __iadd__(self, other):
check_type('CheckedList add operand', other, Iterable,
self.expected_type)
for item in other:
self.append(item)
return self
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
@ -187,7 +187,7 @@ class CMFDMesh(object):
return element
class CMFDFile(object):
class CMFD(object):
"""Parameters that control the use of coarse-mesh finite difference acceleration
in OpenMC. This corresponds directly to the cmfd.xml input file.
@ -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

1
openmc/data/__init__.py Normal file
View file

@ -0,0 +1 @@
from .data import *

101
openmc/data/data.py Normal file
View file

@ -0,0 +1,101 @@
# Isotopic abundances from M. Berglund and M. E. Wieser, "Isotopic compositions
# of the elements 2009 (IUPAC Technical Report)", Pure. Appl. Chem. 83 (2),
# pp. 397--410 (2011).
natural_abundance = {
'H-1': 0.999885, 'H-2': 0.000115, 'He-3': 1.34e-06,
'He-4': 0.99999866, 'Li-6': 0.0759, 'Li-7': 0.9241,
'Be-9': 1.0, 'B-10': 0.199, 'B-11': 0.801,
'C-12': 0.9893, 'C-13': 0.0107, 'N-14': 0.99636,
'N-15': 0.00364, 'O-16': 0.99757, 'O-17': 0.00038,
'O-18': 0.00205, 'F-19': 1.0, 'Ne-20': 0.9048,
'Ne-21': 0.0027, 'Ne-22': 0.0925, 'Na-23': 1.0,
'Mg-24': 0.7899, 'Mg-25': 0.1, 'Mg-26': 0.1101,
'Al-27': 1.0, 'Si-28': 0.92223, 'Si-29': 0.04685,
'Si-30': 0.03092, 'P-31': 1.0, 'S-32': 0.9499,
'S-33': 0.0075, 'S-34': 0.0425, 'S-36': 0.0001,
'Cl-35': 0.7576, 'Cl-37': 0.2424, 'Ar-36': 0.003336,
'Ar-38': 0.000629, 'Ar-40': 0.996035, 'K-39': 0.932581,
'K-40': 0.000117, 'K-41': 0.067302, 'Ca-40': 0.96941,
'Ca-42': 0.00647, 'Ca-43': 0.00135, 'Ca-44': 0.02086,
'Ca-46': 4e-05, 'Ca-48': 0.00187, 'Sc-45': 1.0,
'Ti-46': 0.0825, 'Ti-47': 0.0744, 'Ti-48': 0.7372,
'Ti-49': 0.0541, 'Ti-50': 0.0518, 'V-50': 0.0025,
'V-51': 0.9975, 'Cr-50': 0.04345, 'Cr-52': 0.83789,
'Cr-53': 0.09501, 'Cr-54': 0.02365, 'Mn-55': 1.0,
'Fe-54': 0.05845, 'Fe-56': 0.91754, 'Fe-57': 0.02119,
'Fe-58': 0.00282, 'Co-59': 1.0, 'Ni-58': 0.68077,
'Ni-60': 0.26223, 'Ni-61': 0.011399, 'Ni-62': 0.036346,
'Ni-64': 0.009255, 'Cu-63': 0.6915, 'Cu-65': 0.3085,
'Zn-64': 0.4917, 'Zn-66': 0.2773, 'Zn-67': 0.0404,
'Zn-68': 0.1845, 'Zn-70': 0.0061, 'Ga-69': 0.60108,
'Ga-71': 0.39892, 'Ge-70': 0.2057, 'Ge-72': 0.2745,
'Ge-73': 0.0775, 'Ge-74': 0.365, 'Ge-76': 0.0773,
'As-75': 1.0, 'Se-74': 0.0089, 'Se-76': 0.0937,
'Se-77': 0.0763, 'Se-78': 0.2377, 'Se-80': 0.4961,
'Se-82': 0.0873, 'Br-79': 0.5069, 'Br-81': 0.4931,
'Kr-78': 0.00355, 'Kr-80': 0.02286, 'Kr-82': 0.11593,
'Kr-83': 0.115, 'Kr-84': 0.56987, 'Kr-86': 0.17279,
'Rb-85': 0.7217, 'Rb-87': 0.2783, 'Sr-84': 0.0056,
'Sr-86': 0.0986, 'Sr-87': 0.07, 'Sr-88': 0.8258,
'Y-89': 1.0, 'Zr-90': 0.5145, 'Zr-91': 0.1122,
'Zr-92': 0.1715, 'Zr-94': 0.1738, 'Zr-96': 0.028,
'Nb-93': 1.0, 'Mo-92': 0.1453, 'Mo-94': 0.0915,
'Mo-95': 0.1584, 'Mo-96': 0.1667, 'Mo-97': 0.096,
'Mo-98': 0.2439, 'Mo-100': 0.0982, 'Ru-96': 0.0554,
'Ru-98': 0.0187, 'Ru-99': 0.1276, 'Ru-100': 0.126,
'Ru-101': 0.1706, 'Ru-102': 0.3155, 'Ru-104': 0.1862,
'Rh-103': 1.0, 'Pd-102': 0.0102, 'Pd-104': 0.1114,
'Pd-105': 0.2233, 'Pd-106': 0.2733, 'Pd-108': 0.2646,
'Pd-110': 0.1172, 'Ag-107': 0.51839, 'Ag-109': 0.48161,
'Cd-106': 0.0125, 'Cd-108': 0.0089, 'Cd-110': 0.1249,
'Cd-111': 0.128, 'Cd-112': 0.2413, 'Cd-113': 0.1222,
'Cd-114': 0.2873, 'Cd-116': 0.0749, 'In-113': 0.0429,
'In-115': 0.9571, 'Sn-112': 0.0097, 'Sn-114': 0.0066,
'Sn-115': 0.0034, 'Sn-116': 0.1454, 'Sn-117': 0.0768,
'Sn-118': 0.2422, 'Sn-119': 0.0859, 'Sn-120': 0.3258,
'Sn-122': 0.0463, 'Sn-124': 0.0579, 'Sb-121': 0.5721,
'Sb-123': 0.4279, 'Te-120': 0.0009, 'Te-122': 0.0255,
'Te-123': 0.0089, 'Te-124': 0.0474, 'Te-125': 0.0707,
'Te-126': 0.1884, 'Te-128': 0.3174, 'Te-130': 0.3408,
'I-127': 1.0, 'Xe-124': 0.000952, 'Xe-126': 0.00089,
'Xe-128': 0.019102, 'Xe-129': 0.264006, 'Xe-130': 0.04071,
'Xe-131': 0.212324, 'Xe-132': 0.269086, 'Xe-134': 0.104357,
'Xe-136': 0.088573, 'Cs-133': 1.0, 'Ba-130': 0.00106,
'Ba-132': 0.00101, 'Ba-134': 0.02417, 'Ba-135': 0.06592,
'Ba-136': 0.07854, 'Ba-137': 0.11232, 'Ba-138': 0.71698,
'La-138': 0.0008881, 'La-139': 0.9991119, 'Ce-136': 0.00185,
'Ce-138': 0.00251, 'Ce-140': 0.8845, 'Ce-142': 0.11114,
'Pr-141': 1.0, 'Nd-142': 0.27152, 'Nd-143': 0.12174,
'Nd-144': 0.23798, 'Nd-145': 0.08293, 'Nd-146': 0.17189,
'Nd-148': 0.05756, 'Nd-150': 0.05638, 'Sm-144': 0.0307,
'Sm-147': 0.1499, 'Sm-148': 0.1124, 'Sm-149': 0.1382,
'Sm-150': 0.0738, 'Sm-152': 0.2675, 'Sm-154': 0.2275,
'Eu-151': 0.4781, 'Eu-153': 0.5219, 'Gd-152': 0.002,
'Gd-154': 0.0218, 'Gd-155': 0.148, 'Gd-156': 0.2047,
'Gd-157': 0.1565, 'Gd-158': 0.2484, 'Gd-160': 0.2186,
'Tb-159': 1.0, 'Dy-156': 0.00056, 'Dy-158': 0.00095,
'Dy-160': 0.02329, 'Dy-161': 0.18889, 'Dy-162': 0.25475,
'Dy-163': 0.24896, 'Dy-164': 0.2826, 'Ho-165': 1.0,
'Er-162': 0.00139, 'Er-164': 0.01601, 'Er-166': 0.33503,
'Er-167': 0.22869, 'Er-168': 0.26978, 'Er-170': 0.1491,
'Tm-169': 1.0, 'Yb-168': 0.00123, 'Yb-170': 0.02982,
'Yb-171': 0.1409, 'Yb-172': 0.2168, 'Yb-173': 0.16103,
'Yb-174': 0.32026, 'Yb-176': 0.12996, 'Lu-175': 0.97401,
'Lu-176': 0.02599, 'Hf-174': 0.0016, 'Hf-176': 0.0526,
'Hf-177': 0.186, 'Hf-178': 0.2728, 'Hf-179': 0.1362,
'Hf-180': 0.3508, 'Ta-180': 0.0001201, 'Ta-181': 0.9998799,
'W-180': 0.0012, 'W-182': 0.265, 'W-183': 0.1431,
'W-184': 0.3064, 'W-186': 0.2843, 'Re-185': 0.374,
'Re-187': 0.626, 'Os-184': 0.0002, 'Os-186': 0.0159,
'Os-187': 0.0196, 'Os-188': 0.1324, 'Os-189': 0.1615,
'Os-190': 0.2626, 'Os-192': 0.4078, 'Ir-191': 0.373,
'Ir-193': 0.627, 'Pt-190': 0.00012, 'Pt-192': 0.00782,
'Pt-194': 0.3286, 'Pt-195': 0.3378, 'Pt-196': 0.2521,
'Pt-198': 0.07356, 'Au-197': 1.0, 'Hg-196': 0.0015,
'Hg-198': 0.0997, 'Hg-199': 0.1687, 'Hg-200': 0.231,
'Hg-201': 0.1318, 'Hg-202': 0.2986, 'Hg-204': 0.0687,
'Tl-203': 0.2952, 'Tl-205': 0.7048, 'Pb-204': 0.014,
'Pb-206': 0.241, 'Pb-207': 0.221, 'Pb-208': 0.524,
'Bi-209': 1.0, 'Th-232': 1.0, 'Pa-231': 1.0,
'U-234': 5.4e-05, 'U-235': 0.007204, 'U-238': 0.992742
}

View file

@ -1,6 +1,8 @@
import sys
from openmc.checkvalue import check_type
import openmc
from openmc.checkvalue import check_type, check_length
from openmc.data import natural_abundance
if sys.version_info[0] >= 3:
basestring = str
@ -24,7 +26,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
"""
@ -97,7 +99,8 @@ class Element(object):
@name.setter
def name(self, name):
check_type('name', name, basestring)
check_type('element name', name, basestring)
check_length('element name', name, 1, 2)
self._name = name
@scattering.setter
@ -109,3 +112,22 @@ class Element(object):
raise ValueError(msg)
self._scattering = scattering
def expand(self):
"""Expand natural element into its naturally-occurring isotopes.
Returns
-------
isotopes : list
Naturally-occurring isotopes of the element. Each item of the list
is a tuple consisting of an openmc.Nuclide instance and the natural
abundance of the isotope.
"""
isotopes = []
for isotope, abundance in natural_abundance.items():
if isotope.startswith(self.name + '-'):
nuc = openmc.Nuclide(isotope, self.xs)
isotopes.append((nuc, abundance))
return isotopes

View file

@ -1,130 +1,103 @@
from __future__ import print_function
import subprocess
from numbers import Integral
import os
import sys
from openmc.checkvalue import check_type
if sys.version_info[0] >= 3:
basestring = str
class Executor(object):
"""Control execution of OpenMC
def _run(command, output, cwd):
# Launch a subprocess
p = subprocess.Popen(command, shell=True, cwd=cwd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, universal_newlines=True)
Attributes
# Capture and re-print OpenMC output in real-time
while True:
# If OpenMC is finished, break loop
line = p.stdout.readline()
if not line and p.poll() != None:
break
# If user requested output, print to screen
if output:
print(line, end='')
# Return the returncode (integer, zero if no problems encountered)
return p.returncode
def plot_geometry(output=True, openmc_exec='openmc', cwd='.'):
"""Run OpenMC in plotting mode
Parameters
----------
working_directory : str
Path to working directory to run in
output : bool
Capture OpenMC output from standard out
openmc_exec : str
Path to OpenMC executable
cwd : str, optional
Path to working directory to run in. Defaults to the current working directory.
"""
def __init__(self):
self._working_directory = '.'
return _run(openmc_exec + ' -p', output, cwd)
def _run_openmc(self, command, output):
# Launch a subprocess to run OpenMC
p = subprocess.Popen(command, shell=True,
cwd=self._working_directory,
stdout=subprocess.PIPE)
# Capture and re-print OpenMC output in real-time
while True:
# If OpenMC is finished, break loop
line = p.stdout.readline()
if not line and p.poll() != None:
break
def run(particles=None, threads=None, geometry_debug=False,
restart_file=None, tracks=False, mpi_procs=1, output=True,
openmc_exec='openmc', mpi_exec='mpiexec', cwd='.'):
"""Run an OpenMC simulation.
# If user requested output, print to screen
if output:
print(line, end='')
Parameters
----------
particles : int, optional
Number of particles to simulate per generation.
threads : int, optional
Number of OpenMP threads. If OpenMC is compiled with OpenMP threading
enabled, the default is implementation-dependent but is usually equal to
the number of hardware threads available (or a value set by the
OMP_NUM_THREADS environment variable).
geometry_debug : bool, optional
Turn on geometry debugging during simulation. Defaults to False.
restart_file : str, optional
Path to restart file to use
tracks : bool, optional
Write tracks for all particles. Defaults to False.
mpi_procs : int, optional
Number of MPI processes.
output : bool, optional
Capture OpenMC output from standard out. Defaults to True.
openmc_exec : str, optional
Path to OpenMC executable. Defaults to 'openmc'.
mpi_exec : str, optional
MPI execute command. Defaults to 'mpiexec'.
cwd : str, optional
Path to working directory to run in. Defaults to the current working directory.
# Return the returncode (integer, zero if no problems encountered)
return p.returncode
"""
@property
def working_directory(self):
return self._working_directory
post_args = ' '
pre_args = ''
@working_directory.setter
def working_directory(self, working_directory):
check_type("Executor's working directory", working_directory,
basestring)
if not os.path.isdir(working_directory):
msg = 'Unable to set Executor\'s working directory to "{0}" ' \
'which does not exist'.format(working_directory)
raise ValueError(msg)
if isinstance(particles, Integral) and particles > 0:
post_args += '-n {0} '.format(particles)
self._working_directory = working_directory
if isinstance(threads, Integral) and threads > 0:
post_args += '-s {0} '.format(threads)
def plot_geometry(self, output=True, openmc_exec='openmc'):
"""Run OpenMC in plotting mode"""
if geometry_debug:
post_args += '-g '
return self._run_openmc(openmc_exec + ' -p', output)
if isinstance(restart_file, basestring):
post_args += '-r {0} '.format(restart_file)
def run_simulation(self, particles=None, threads=None,
geometry_debug=False, restart_file=None,
tracks=False, mpi_procs=1, output=True,
openmc_exec='openmc', mpi_exec=None):
"""Run an OpenMC simulation.
if tracks:
post_args += '-t'
Parameters
----------
particles : int
Number of particles to simulate per generation
threads : int
Number of OpenMP threads
geometry_debug : bool
Turn on geometry debugging during simulation
restart_file : str
Path to restart file to use
tracks : bool
Write tracks for all particles
mpi_procs : int
Number of MPI processes
output : bool
Capture OpenMC output from standard out
openmc_exec : str
Path to OpenMC executable
if isinstance(mpi_procs, Integral) and mpi_procs > 1:
pre_args += '{} -n {} '.format(mpi_exec, mpi_procs)
"""
command = pre_args + openmc_exec + ' ' + post_args
post_args = ' '
pre_args = ''
if isinstance(particles, Integral) and particles > 0:
post_args += '-n {0} '.format(particles)
if isinstance(threads, Integral) and threads > 0:
post_args += '-s {0} '.format(threads)
if geometry_debug:
post_args += '-g '
if isinstance(restart_file, basestring):
post_args += '-r {0} '.format(restart_file)
if tracks:
post_args += '-t'
if isinstance(mpi_procs, Integral) and mpi_procs > 1:
np_present = True
else:
np_present = False
if mpi_exec is not None and isinstance(mpi_exec, basestring):
mpi_exec_present = True
else:
mpi_exec_present = False
if np_present or mpi_exec_present:
if mpi_exec_present:
pre_args += mpi_exec + ' '
else:
pre_args += 'mpirun '
pre_args += '-n {0} '.format(mpi_procs)
command = pre_args + openmc_exec + ' ' + post_args
return self._run_openmc(command, output)
return _run(command, output, cwd)

View file

@ -1,4 +1,4 @@
from collections import Iterable
from collections import Iterable, OrderedDict
import copy
from numbers import Real, Integral
import sys
@ -27,7 +27,8 @@ class Filter(object):
type : str
The type of the tally filter. Acceptable values are "universe",
"material", "cell", "cellborn", "surface", "mesh", "energy",
"energyout", and "distribcell".
"energyout", "distribcell", "mu", "polar", "azimuthal", and
"delayedgroup".
bins : Integral or Iterable of Integral or Iterable of Real
The bins for the filter. This takes on different meaning for different
filters. See the OpenMC online documentation for more details.
@ -40,11 +41,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 +60,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
@ -110,6 +115,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
@ -152,6 +158,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:
@ -186,7 +196,7 @@ class Filter(object):
elif self.type in ['energy', 'energyout']:
for edge in bins:
if not cv._isinstance(edge, Real):
if not isinstance(edge, Real):
msg = 'Unable to add bin edge "{0}" to a "{1}" Filter ' \
'since it is a non-integer or floating point ' \
'value'.format(edge, self.type)
@ -210,7 +220,7 @@ class Filter(object):
msg = 'Unable to add bins "{0}" to a mesh Filter since ' \
'only a single mesh can be used per tally'.format(bins)
raise ValueError(msg)
elif not cv._isinstance(bins[0], Integral):
elif not isinstance(bins[0], Integral):
msg = 'Unable to add bin "{0}" to mesh Filter since it ' \
'is a non-integer'.format(bins[0])
raise ValueError(msg)
@ -246,12 +256,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
@ -296,12 +311,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
"""
@ -341,7 +356,7 @@ class Filter(object):
Parameters
----------
other : Filter
other : openmc.Filter
The filter to query as a subset of this filter
Returns
@ -501,12 +516,12 @@ class Filter(object):
return filter_bin
def get_pandas_dataframe(self, data_size, summary=None):
def get_pandas_dataframe(self, data_size, distribcell_paths=True):
"""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
@ -516,12 +531,13 @@ class Filter(object):
----------
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
distribcell tally filters (default is None). The geometric
information in the Summary object is embedded into a Multi-index
column with a geometric "path" to each distribcell instance.
NOTE: This option requires the OpenCG Python package.
distribcell_paths : bool, optional
Construct columns for distribcell tally filters (default is True).
The geometric information in the Summary object is embedded into a
Multi-index column with a geometric "path" to each distribcell
instance. NOTE: This option assumes that all distribcell paths are
of the same length and do not have the same universes and cells but
different lattice cell indices.
Returns
-------
@ -539,7 +555,7 @@ class Filter(object):
1. a single column with the cell instance IDs (without summary info)
2. separate columns for the cell IDs, universe IDs, and lattice IDs
and x,y,z cell indices corresponding to each (with summary info).
and x,y,z cell indices corresponding to each (distribcell paths).
For 'energy' and 'energyout' filters, the DataFrame includes one
column for the lower energy bound and one column for the upper
@ -551,8 +567,7 @@ class Filter(object):
Raises
------
ImportError
When Pandas is not installed, or summary info is requested but
OpenCG is not installed.
When Pandas is not installed
See also
--------
@ -611,114 +626,117 @@ class Filter(object):
elif self.type == 'distribcell':
level_df = None
if isinstance(summary, Summary):
# Attempt to import the OpenCG package
try:
import opencg
except ImportError:
msg = 'The OpenCG package must be installed ' \
'to use a Summary for distribcell dataframes'
raise ImportError(msg)
# Create Pandas Multi-index columns for each level in CSG tree
if distribcell_paths:
# Extract the OpenCG geometry from the Summary
opencg_geometry = summary.opencg_geometry
openmc_geometry = summary.openmc_geometry
# Distribcell paths require linked metadata from the Summary
if self.distribcell_paths is None:
msg = 'Unable to construct distribcell paths since ' \
'the Summary is not linked to the StatePoint'
raise ValueError(msg)
# Use OpenCG to compute the number of regions
opencg_geometry.initialize_cell_offsets()
num_regions = opencg_geometry.num_regions
# Make copy of array of distribcell paths to use in
# Pandas Multi-index column construction
distribcell_paths = copy.deepcopy(self.distribcell_paths)
num_offsets = len(distribcell_paths)
# Initialize a dictionary mapping OpenMC distribcell
# 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):
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
# Each distribcell offset is a DataFrame bin
# Unravel the paths into DataFrame columns
num_offsets = len(offsets_to_coords)
# Initialize termination condition for while loop
# Loop over CSG levels in the distribcell paths
level_counter = 0
levels_remain = True
counter = 0
# Iterate over each level in the CSG tree hierarchy
while levels_remain:
levels_remain = False
# Initialize dictionary to build Pandas Multi-index
# column for this level in the CSG tree hierarchy
level_dict = {}
# Use level key as first index in Pandas Multi-index column
level_counter += 1
level_key = 'level {}'.format(level_counter)
# Initialize prefix Multi-index keys
counter += 1
level_key = 'level {0}'.format(counter)
univ_key = (level_key, 'univ', 'id')
cell_key = (level_key, 'cell', 'id')
lat_id_key = (level_key, 'lat', 'id')
lat_x_key = (level_key, 'lat', 'x')
lat_y_key = (level_key, 'lat', 'y')
lat_z_key = (level_key, 'lat', 'z')
# Use the first distribcell path to determine if level
# is a universe/cell or lattice level
first_path = distribcell_paths[0]
next_index = first_path.index('-')
level = first_path[:next_index]
# Allocate NumPy arrays for each CSG level and
# each Multi-index column in the DataFrame
level_dict[univ_key] = np.empty(num_offsets)
level_dict[cell_key] = np.empty(num_offsets)
level_dict[lat_id_key] = np.empty(num_offsets)
level_dict[lat_x_key] = np.empty(num_offsets)
level_dict[lat_y_key] = np.empty(num_offsets)
level_dict[lat_z_key] = np.empty(num_offsets)
# Trim universe/lattice info from path
first_path = first_path[next_index+2:]
# Initialize Multi-index columns to NaN - this is
# necessary since some distribcell instances may
# have very different LocalCoords linked lists
level_dict[univ_key][:] = np.NAN
level_dict[cell_key][:] = np.NAN
level_dict[lat_id_key][:] = np.NAN
level_dict[lat_x_key][:] = np.NAN
level_dict[lat_y_key][:] = np.NAN
level_dict[lat_z_key][:] = np.NAN
# Create a dictionary for this level for Pandas Multi-index
level_dict = OrderedDict()
# Iterate over all regions (distribcell instances)
for offset in range(num_offsets):
coords = offsets_to_coords[offset]
# This level is a lattice (e.g., ID(x,y,z))
if '(' in level:
level_type = 'lattice'
# If entire LocalCoords has been unraveled into
# Multi-index columns already, continue
if coords is None:
continue
# Initialize prefix Multi-index keys
lat_id_key = (level_key, 'lat', 'id')
lat_x_key = (level_key, 'lat', 'x')
lat_y_key = (level_key, 'lat', 'y')
lat_z_key = (level_key, 'lat', 'z')
# Assign entry to Universe Multi-index column
if coords._type == 'universe':
level_dict[univ_key][offset] = coords._universe._id
level_dict[cell_key][offset] = coords._cell._id
# Allocate NumPy arrays for each CSG level and
# each Multi-index column in the DataFrame
level_dict[lat_id_key] = np.empty(num_offsets)
level_dict[lat_x_key] = np.empty(num_offsets)
level_dict[lat_y_key] = np.empty(num_offsets)
level_dict[lat_z_key] = np.empty(num_offsets)
# This level is a universe / cell (e.g., ID->ID)
else:
level_type = 'universe'
# Initialize prefix Multi-index keys
univ_key = (level_key, 'univ', 'id')
cell_key = (level_key, 'cell', 'id')
# Allocate NumPy arrays for each CSG level and
# each Multi-index column in the DataFrame
level_dict[univ_key] = np.empty(num_offsets)
level_dict[cell_key] = np.empty(num_offsets)
# Determine any levels remain in path
if '-' not in first_path:
levels_remain = False
# Populate Multi-index arrays with all distribcell paths
for i, path in enumerate(distribcell_paths):
if level_type == 'lattice':
# Extract lattice ID, indices from path
next_index = path.index('-')
lat_id_indices = path[:next_index]
# Trim lattice info from distribcell path
distribcell_paths[i] = path[next_index+2:]
# Extract the lattice cell indices from the path
i1 = lat_id_indices.index('(')
i2 = lat_id_indices.index(')')
i3 = lat_id_indices[i1+1:i2]
# Assign entry to Lattice Multi-index column
level_dict[lat_id_key][i] = path[:i1]
level_dict[lat_x_key][i] = int(i3.split(',')[0]) - 1
level_dict[lat_y_key][i] = int(i3.split(',')[1]) - 1
level_dict[lat_z_key][i] = int(i3.split(',')[2]) - 1
# Assign entry to Lattice Multi-index column
else:
# Reverse y index per lattice ordering in OpenCG
level_dict[lat_id_key][offset] = coords._lattice._id
level_dict[lat_x_key][offset] = coords._lat_x
level_dict[lat_y_key][offset] = \
coords._lattice.dimension[1] - coords._lat_y - 1
level_dict[lat_z_key][offset] = coords._lat_z
# Extract universe ID from path
next_index = path.index('-')
universe_id = int(path[:next_index])
# Move to next node in LocalCoords linked list
if coords._next is None:
offsets_to_coords[offset] = None
else:
offsets_to_coords[offset] = coords._next
levels_remain = True
# Trim universe info from distribcell path
path = path[next_index+2:]
# Extract cell ID from path
if '-' in path:
next_index = path.index('-')
cell_id = int(path[:next_index])
distribcell_paths[i] = path[next_index+2:]
else:
cell_id = int(path)
distribcell_paths[i] = ''
# Assign entry to Universe, Cell Multi-index columns
level_dict[univ_key][i] = universe_id
level_dict[cell_key][i] = cell_id
# Tile the Multi-index columns
for level_key, level_bins in level_dict.items():
@ -733,7 +751,7 @@ class Filter(object):
else:
level_df = pd.concat([level_df, pd.DataFrame(level_dict)], axis=1)
# Create DataFrame column for distribcell instances IDs
# Create DataFrame column for distribcell instance IDs
# NOTE: This is performed regardless of whether the user
# requests Summary geometric information
filter_bins = np.arange(self.num_bins)

View file

@ -15,17 +15,23 @@ def reset_auto_ids():
class Geometry(object):
"""Geometry representing a collection of surfaces, cells, and universes.
Parameters
----------
root_universe : openmc.Universe, optional
Root universe which contains all others
Attributes
----------
root_universe : openmc.universe.Universe
root_universe : openmc.Universe
Root universe which contains all others
"""
def __init__(self):
# Initialize Geometry class attributes
def __init__(self, root_universe=None):
self._root_universe = None
self._offsets = {}
if root_universe is not None:
self.root_universe = root_universe
@property
def root_universe(self):
@ -42,6 +48,44 @@ class Geometry(object):
self._root_universe = root_universe
def export_to_xml(self):
"""Create a geometry.xml file that can be used for a simulation.
"""
# Clear OpenMC written IDs used to optimize XML generation
openmc.universe.WRITTEN_IDS = {}
# Create XML representation
geometry_file = ET.Element("geometry")
self.root_universe.create_xml_subelement(geometry_file)
# Clean the indentation in the file to be user-readable
sort_xml_elements(geometry_file)
clean_xml_indentation(geometry_file)
# Write the XML Tree to the geometry.xml file
tree = ET.ElementTree(geometry_file)
tree.write("geometry.xml", xml_declaration=True, encoding='utf-8',
method="xml")
def find(self, point):
"""Find cells/universes/lattices which contain a given point
Parameters
----------
point : 3-tuple of float
Cartesian coordinates of the point
Returns
-------
list
Sequence of universes, cells, and lattices which are traversed to
find the given point
"""
return self.root_universe.find(point)
def get_cell_instance(self, path):
"""Return the instance number for the final cell in a geometry path.
@ -63,15 +107,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()
for cell in cells:
if cell.id == path[-1]:
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:
@ -91,19 +139,13 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Cell
list of openmc.Cell
Cells in the geometry
"""
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)
all_cells = self.root_universe.get_all_cells()
cells = list(set(all_cells.values()))
cells.sort(key=lambda x: x.id)
return cells
@ -112,18 +154,13 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Universe
list of openmc.Universe
Universes in the geometry
"""
all_universes = self._root_universe.get_all_universes()
universes = set()
for universe in all_universes.values():
universes.add(universe)
universes = list(universes)
universes = list(set(all_universes.values()))
universes.sort(key=lambda x: x.id)
return universes
@ -132,7 +169,7 @@ class Geometry(object):
Returns
-------
list of openmc.nuclide.Nuclide
list of openmc.Nuclide
Nuclides in the geometry
"""
@ -150,21 +187,23 @@ class Geometry(object):
Returns
-------
list of openmc.material.Material
list of openmc.Material
Materials in the geometry
"""
material_cells = self.get_all_material_cells()
materials = set()
materials = []
for cell in material_cells:
if isinstance(cell.fill, Iterable):
for m in cell.fill: materials.add(m)
else:
materials.add(cell.fill)
if cell.fill_type == 'distribmat':
for m in cell.fill:
if m is not None and m not in materials:
materials.append(m)
elif cell.fill_type == 'material':
if cell.fill not in materials:
materials.append(cell.fill)
materials = list(materials)
materials.sort(key=lambda x: x.id)
return materials
@ -173,19 +212,19 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Cell
list of openmc.Cell
Cells filled by Materials in the geometry
"""
all_cells = self.get_all_cells()
material_cells = set()
material_cells = []
for cell in all_cells:
if cell._type == 'normal':
material_cells.add(cell)
if cell.fill_type in ('material', 'distribmat'):
if cell not in material_cells:
material_cells.append(cell)
material_cells = list(material_cells)
material_cells.sort(key=lambda x: x.id)
return material_cells
@ -194,21 +233,21 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Universe
list of openmc.Universe
Universes with non-fill cells
"""
all_universes = self.get_all_universes()
material_universes = set()
material_universes = []
for universe in all_universes:
cells = universe.cells
for cell in cells:
if cell._type == 'normal':
material_universes.add(universe)
if cell.fill_type in ('material', 'distribmat', 'void'):
if universe not in material_universes:
material_universes.append(universe)
material_universes = list(material_universes)
material_universes.sort(key=lambda x: x.id)
return material_universes
@ -217,19 +256,19 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Lattice
list of openmc.Lattice
Lattices in the geometry
"""
cells = self.get_all_cells()
lattices = set()
lattices = []
for cell in cells:
if isinstance(cell.fill, openmc.Lattice):
lattices.add(cell.fill)
if cell.fill_type == 'lattice':
if cell.fill not in lattices:
lattices.append(cell.fill)
lattices = list(lattices)
lattices.sort(key=lambda x: x.id)
return lattices
@ -248,7 +287,7 @@ class Geometry(object):
Returns
-------
list of openmc.material.Material
list of openmc.Material
Materials matching the queried name
"""
@ -288,7 +327,7 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Cell
list of openmc.Cell
Cells matching the queried name
"""
@ -328,7 +367,7 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Cell
list of openmc.Cell
Cells with fills matching the queried name
"""
@ -368,7 +407,7 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Universe
list of openmc.Universe
Universes matching the queried name
"""
@ -408,7 +447,7 @@ class Geometry(object):
Returns
-------
list of openmc.universe.Lattice
list of openmc.Lattice
Lattices matching the queried name
"""
@ -432,52 +471,3 @@ class Geometry(object):
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
geometry.xml input file.
Attributes
----------
geometry : Geometry
The geometry to be used
"""
def __init__(self):
# Initialize GeometryFile class attributes
self._geometry = None
self._geometry_file = ET.Element("geometry")
@property
def geometry(self):
return self._geometry
@geometry.setter
def geometry(self, geometry):
check_type('the geometry', geometry, Geometry)
self._geometry = geometry
def export_to_xml(self):
"""Create a geometry.xml file that can be used for a simulation.
"""
# Clear OpenMC written IDs used to optimize XML generation
openmc.universe.WRITTEN_IDS = {}
# Reset xml element tree
self._geometry_file.clear()
root_universe = self.geometry.root_universe
root_universe.create_xml_subelement(self._geometry_file)
# Clean the indentation in the file to be user-readable
sort_xml_elements(self._geometry_file)
clean_xml_indentation(self._geometry_file)
# Write the XML Tree to the geometry.xml file
tree = ET.ElementTree(self._geometry_file)
tree.write("geometry.xml", xml_declaration=True,
encoding='utf-8', method="xml")

1328
openmc/lattice.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -8,8 +8,9 @@ if sys.version_info[0] >= 3:
basestring = str
import openmc
from openmc.checkvalue import check_type, check_value, check_greater_than
import openmc.checkvalue as cv
from openmc.clean_xml import *
from openmc.data import natural_abundance
# A static variable for auto-generated Material IDs
@ -25,9 +26,6 @@ def reset_auto_material_id():
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
@ -52,6 +50,14 @@ class Material(object):
Units used for `density`. Can be one of 'g/cm3', 'g/cc', 'kg/cm3',
'atom/b-cm', 'atom/cm3', 'sum', or 'macro'. The 'macro' unit only
applies in the case of a multi-group calculation.
elements : collections.OrderedDict
Dictionary whose keys are element names and values are 3-tuples
consisting of an :class:`openmc.Element` instance, the percent density,
and the percent type (atom or weight fraction).
nuclides : collections.OrderedDict
Dictionary whose keys are nuclide names and values are 3-tuples
consisting of an :class:`openmc.Nuclide` instance, the percent density,
and the percent type (atom or weight fraction).
"""
@ -141,9 +147,9 @@ class Material(object):
string += '{0: <16}\n'.format('\tElements')
for element in self._elements:
percent = self._nuclides[element][1]
percent_type = self._nuclides[element][2]
string += '{0: >16}'.format('\t{0}'.format(element))
percent = self._elements[element][1]
percent_type = self._elements[element][2]
string += '{0: <16}'.format('\t{0}'.format(element))
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
return string
@ -189,6 +195,14 @@ class Material(object):
def density_units(self):
return self._density_units
@property
def elements(self):
return self._elements
@property
def nuclides(self):
return self._nuclides
@property
def convert_to_distrib_comps(self):
return self._convert_to_distrib_comps
@ -205,45 +219,51 @@ class Material(object):
self._id = AUTO_MATERIAL_ID
AUTO_MATERIAL_ID += 1
else:
check_type('material ID', material_id, Integral)
check_greater_than('material ID', material_id, 0, equality=True)
cv.check_type('material ID', material_id, Integral)
cv.check_greater_than('material ID', material_id, 0, equality=True)
self._id = material_id
@name.setter
def name(self, name):
if name is not None:
check_type('name for Material ID="{0}"'.format(self._id),
name, basestring)
cv.check_type('name for Material ID="{0}"'.format(self._id),
name, basestring)
self._name = name
else:
self._name = ''
def set_density(self, units, density=NO_DENSITY):
def set_density(self, units, density=None):
"""Set the density of the material
Parameters
----------
units : str
Physical units of density
units : {'g/cm3', 'g/cc', 'km/cm3', 'atom/b-cm', 'atom/cm3', 'sum', 'macro'}
Physical units of density.
density : float, optional
Value of the density. Must be specified unless units is given as
'sum'.
"""
check_type('the density for Material ID="{0}"'.format(self._id),
density, Real)
check_value('density units', units, DENSITY_UNITS)
if density == NO_DENSITY and units is not 'sum':
msg = 'Unable to set the density Material ID="{0}" ' \
'because a density must be set when not using ' \
'sum unit'.format(self._id)
raise ValueError(msg)
self._density = density
cv.check_value('density units', units, DENSITY_UNITS)
self._density_units = units
if units is 'sum':
if density is not None:
msg = 'Density "{0}" for Material ID="{1}" is ignored ' \
'because the unit is "sum"'.format(density, self.id)
warnings.warn(msg)
else:
if density is None:
msg = 'Unable to set the density for Material ID="{0}" ' \
'because a density value must be given when not using ' \
'"sum" unit'.format(self.id)
raise ValueError(msg)
cv.check_type('the density for Material ID="{0}"'.format(self.id),
density, Real)
self._density = density
@distrib_otf_file.setter
def distrib_otf_file(self, filename):
# TODO: remove this when distributed materials are merged
@ -270,11 +290,11 @@ class Material(object):
Parameters
----------
nuclide : str or openmc.nuclide.Nuclide
nuclide : str or openmc.Nuclide
Nuclide to add
percent : float
Atom or weight percent
percent_type : str
percent_type : {'ao', 'wo'}
'ao' for atom percent and 'wo' for weight percent
"""
@ -284,7 +304,7 @@ class Material(object):
'macroscopic data-set has already been added'.format(self._id)
raise ValueError(msg)
if not isinstance(nuclide, (openmc.Nuclide, str)):
if not isinstance(nuclide, (openmc.Nuclide, basestring)):
msg = 'Unable to add a Nuclide to Material ID="{0}" with a ' \
'non-Nuclide value "{1}"'.format(self._id, nuclide)
raise ValueError(msg)
@ -313,7 +333,7 @@ class Material(object):
Parameters
----------
nuclide : openmc.nuclide.Nuclide
nuclide : openmc.Nuclide
Nuclide to remove
"""
@ -328,11 +348,13 @@ class Material(object):
del self._nuclides[nuclide._name]
def add_macroscopic(self, macroscopic):
"""Add a macroscopic to the material
"""Add a macroscopic to the material. This will also set the
density of the material to 1.0, unless it has been otherwise set,
as a default for Macroscopic cross sections.
Parameters
----------
macroscopic : str or Macroscopic
macroscopic : str or openmc.Macroscopic
Macroscopic to add
"""
@ -366,12 +388,20 @@ class Material(object):
'Material!'.format(self._id, macroscopic)
raise ValueError(msg)
# Generally speaking, the density for a macroscopic object will
# be 1.0. Therefore, lets set density to 1.0 so that the user
# doesnt need to set it unless its needed.
# Of course, if the user has already set a value of density,
# then we will not override it.
if self._density is None:
self.set_density('macro', 1.0)
def remove_macroscopic(self, macroscopic):
"""Remove a macroscopic from the material
Parameters
----------
macroscopic : Macroscopic
macroscopic : openmc.Macroscopic
Macroscopic to remove
"""
@ -385,17 +415,21 @@ class Material(object):
if macroscopic._name == self._macroscopic.name:
self._macroscopic = None
def add_element(self, element, percent, percent_type='ao'):
def add_element(self, element, percent, percent_type='ao', expand=False):
"""Add a natural element to the material
Parameters
----------
element : openmc.element.Element
element : openmc.Element or str
Element to add
percent : float
Atom or weight percent
percent_type : str
'ao' for atom percent and 'wo' for weight percent
percent_type : {'ao', 'wo'}, optional
'ao' for atom percent and 'wo' for weight percent. Defaults to atom
percent.
expand : bool, optional
Whether to expand the natural element into its naturally-occurring
isotopes. Defaults to False.
"""
@ -404,7 +438,7 @@ class Material(object):
'macroscopic data-set has already been added'.format(self._id)
raise ValueError(msg)
if not isinstance(element, openmc.Element):
if not isinstance(element, (openmc.Element, basestring)):
msg = 'Unable to add an Element to Material ID="{0}" with a ' \
'non-Element value "{1}"'.format(self._id, element)
raise ValueError(msg)
@ -420,16 +454,27 @@ class Material(object):
raise ValueError(msg)
# Copy this Element to separate it from same Element in other Materials
element = deepcopy(element)
if isinstance(element, openmc.Element):
element = deepcopy(element)
else:
element = openmc.Element(element)
self._elements[element._name] = (element, percent, percent_type)
if expand:
if percent_type == 'wo':
raise NotImplementedError('Expanding natural element based on '
'weight percent is not yet supported.')
for isotope, abundance in element.expand():
self._nuclides[isotope.name] = (
isotope, percent*abundance, percent_type)
else:
self._elements[element.name] = (element, percent, percent_type)
def remove_element(self, element):
"""Remove a natural element from the material
Parameters
----------
element : openmc.element.Element
element : openmc.Element
Element to remove
"""
@ -471,7 +516,7 @@ class Material(object):
for nuclide_name in self._nuclides:
self._nuclides[nuclide_name][0].scattering = 'iso-in-lab'
for element_name in self._elements:
self._element[element_name][0].scattering = 'iso-in-lab'
self._elements[element_name][0].scattering = 'iso-in-lab'
def get_all_nuclides(self):
"""Returns all nuclides in the material
@ -491,6 +536,14 @@ class Material(object):
density = nuclide_tuple[1]
nuclides[nuclide._name] = (nuclide, density)
for element_name, element_tuple in self._elements.items():
element = element_tuple[0]
density = element_tuple[1]
# Expand natural element into isotopes
for isotope, abundance in element.expand():
nuclides[isotope.name] = (isotope, density*abundance)
return nuclides
def _get_nuclide_xml(self, nuclide, distrib=False):
@ -498,7 +551,7 @@ class Material(object):
xml_element.set("name", nuclide[0]._name)
if not distrib:
if nuclide[2] is 'ao':
if nuclide[2] == 'ao':
xml_element.set("ao", str(nuclide[1]))
else:
xml_element.set("wo", str(nuclide[1]))
@ -525,11 +578,14 @@ class Material(object):
xml_element.set("name", str(element[0]._name))
if not distrib:
if element[2] is 'ao':
if element[2] == 'ao':
xml_element.set("ao", str(element[1]))
else:
xml_element.set("wo", str(element[1]))
if element[0].xs is not None:
xml_element.set("xs", element[0].xs)
if not element[0].scattering is None:
xml_element.set("scattering", element[0].scattering)
@ -639,9 +695,25 @@ class Material(object):
return element
class MaterialsFile(object):
"""Materials file used for an OpenMC simulation. Corresponds directly to the
materials.xml input file.
class Materials(cv.CheckedList):
"""Collection of Materials used for an OpenMC simulation.
This class corresponds directly to the materials.xml input file. It can be
thought of as a normal Python list where each member is a
:class:`Material`. It behaves like a list as the following example
demonstrates:
>>> fuel = openmc.Material()
>>> clad = openmc.Material()
>>> water = openmc.Material()
>>> m = openmc.Materials([fuel])
>>> m.append(water)
>>> m += [clad]
Parameters
----------
materials : Iterable of openmc.Material
Materials to add to the collection
Attributes
----------
@ -651,11 +723,12 @@ class MaterialsFile(object):
"""
def __init__(self):
# Initialize MaterialsFile class attributes
self._materials = []
def __init__(self, materials=None):
super(Materials, self).__init__(Material, 'materials collection')
self._default_xs = None
self._materials_file = ET.Element("materials")
if materials is not None:
self += materials
@property
def default_xs(self):
@ -663,72 +736,95 @@ class MaterialsFile(object):
@default_xs.setter
def default_xs(self, xs):
check_type('default xs', xs, basestring)
cv.check_type('default xs', xs, basestring)
self._default_xs = xs
def add_material(self, material):
"""Add a material to the file.
"""Append material to collection
.. deprecated:: 0.8
Use :meth:`Materials.append` instead.
Parameters
----------
material : Material
material : openmc.Material
Material to add
"""
if not isinstance(material, Material):
msg = 'Unable to add a non-Material "{0}" to the ' \
'MaterialsFile'.format(material)
raise ValueError(msg)
self._materials.append(material)
warnings.warn("Materials.add_material(...) has been deprecated and may be "
"removed in a future version. Use Material.append(...) "
"instead.", DeprecationWarning)
self.append(material)
def add_materials(self, materials):
"""Add multiple materials to the file.
"""Add multiple materials to the collection
.. deprecated:: 0.8
Use compound assignment instead.
Parameters
----------
materials : tuple or list of Material
materials : Iterable of openmc.Material
Materials to add
"""
if not isinstance(materials, Iterable):
msg = 'Unable to create OpenMC materials.xml file from "{0}" which ' \
'is not iterable'.format(materials)
raise ValueError(msg)
warnings.warn("Materials.add_materials(...) has been deprecated and may be "
"removed in a future version. Use compound assignment "
"instead.", DeprecationWarning)
for material in materials:
self.add_material(material)
self.append(material)
def append(self, material):
"""Append material to collection
Parameters
----------
material : openmc.Material
Material to append
"""
super(Materials, self).append(material)
def insert(self, index, material):
"""Insert material before index
Parameters
----------
index : int
Index in list
material : openmc.Material
Material to insert
"""
super(Materials, self).insert(index, material)
def remove_material(self, material):
"""Remove a material from the file
.. deprecated:: 0.8
Use :meth:`Materials.remove` instead.
Parameters
----------
material : Material
material : openmc.Material
Material to remove
"""
if not isinstance(material, Material):
msg = 'Unable to remove a non-Material "{0}" from the ' \
'MaterialsFile'.format(material)
raise ValueError(msg)
self._materials.remove(material)
warnings.warn("Materials.remove_material(...) has been deprecated and "
"may be removed in a future version. Use "
"Materials.remove(...) instead.", DeprecationWarning)
self.remove(material)
def make_isotropic_in_lab(self):
for material in self._materials:
for material in self:
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:
for material in self:
xml_element = material.get_material_xml()
self._materials_file.append(xml_element)

View file

@ -24,7 +24,7 @@ class EnergyGroups(object):
----------
group_edges : Iterable of Real
The energy group boundaries [MeV]
num_groups : Integral
num_groups : int
The number of energy groups
"""
@ -86,7 +86,7 @@ class EnergyGroups(object):
Parameters
----------
energy : Real
energy : float
The energy of interest in MeV
Returns
@ -115,7 +115,7 @@ class EnergyGroups(object):
Parameters
----------
group : Integral
group : int
The energy group index, starting at 1 for the highest energies
Returns
@ -153,7 +153,7 @@ class EnergyGroups(object):
Returns
-------
ndarray
numpy.ndarray
The ndarray array indices for each energy group of interest
Raises
@ -200,7 +200,7 @@ class EnergyGroups(object):
Returns
-------
EnergyGroups
openmc.mgxs.EnergyGroups
A coarsened version of this EnergyGroups object.
Raises
@ -244,7 +244,7 @@ class EnergyGroups(object):
Parameters
----------
other : EnergyGroups
other : openmc.mgxs.EnergyGroups
EnergyGroups to compare with
Returns
@ -275,12 +275,12 @@ class EnergyGroups(object):
Parameters
----------
other : EnergyGroups
other : openmc.mgxs.EnergyGroups
EnergyGroups to merge with
Returns
-------
merged_groups : EnergyGroups
merged_groups : openmc.mgxs.EnergyGroups
EnergyGroups resulting from the merge
"""

View file

@ -2,8 +2,12 @@ import sys
import os
import copy
import pickle
import warnings
from numbers import Integral
from collections import OrderedDict
from warnings import warn
import numpy as np
import openmc
import openmc.mgxs
@ -30,7 +34,7 @@ class Library(object):
Parameters
----------
openmc_geometry : openmc.Geometry
An geometry which has been initialized with a root universe
A geometry which has been initialized with a root universe
by_nuclide : bool
If true, computes cross sections for each nuclide in each domain
mgxs_types : Iterable of str
@ -53,22 +57,24 @@ 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
legendre_order : int
The highest legendre moment in the scattering matrices (default is 0)
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
@ -89,8 +95,9 @@ class Library(object):
self._mgxs_types = []
self._domain_type = None
self._domains = 'all'
self._correction = 'P0'
self._energy_groups = None
self._correction = 'P0'
self._legendre_order = 0
self._tally_trigger = None
self._all_mgxs = OrderedDict()
self._sp_filename = None
@ -118,6 +125,7 @@ class Library(object):
clone._domain_type = self.domain_type
clone._domains = copy.deepcopy(self.domains)
clone._correction = self.correction
clone._legendre_order = self.legendre_order
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
clone._tally_trigger = copy.deepcopy(self.tally_trigger, memo)
clone._all_mgxs = copy.deepcopy(self.all_mgxs)
@ -185,13 +193,17 @@ class Library(object):
else:
return self._domains
@property
def energy_groups(self):
return self._energy_groups
@property
def correction(self):
return self._correction
@property
def energy_groups(self):
return self._energy_groups
def legendre_order(self):
return self._legendre_order
@property
def tally_trigger(self):
@ -245,7 +257,7 @@ class Library(object):
@domain_type.setter
def domain_type(self, domain_type):
cv.check_value('domain type', domain_type, tuple(openmc.mgxs.DOMAIN_TYPES))
cv.check_value('domain type', domain_type, openmc.mgxs.DOMAIN_TYPES)
self._domain_type = domain_type
@domains.setter
@ -280,16 +292,36 @@ class Library(object):
self._domains = domains
@correction.setter
def correction(self, correction):
cv.check_value('correction', correction, ('P0', None))
self._correction = correction
@energy_groups.setter
def energy_groups(self, energy_groups):
cv.check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups)
self._energy_groups = energy_groups
@correction.setter
def correction(self, correction):
cv.check_value('correction', correction, ('P0', None))
if correction == 'P0' and self.legendre_order > 0:
msg = 'The P0 correction will be ignored since the scattering ' \
'order {} is greater than zero'.format(self.legendre_order)
warnings.warn(msg)
self._correction = correction
@legendre_order.setter
def legendre_order(self, legendre_order):
cv.check_type('legendre_order', legendre_order, Integral)
cv.check_greater_than('legendre_order', legendre_order, 0, equality=True)
cv.check_less_than('legendre_order', legendre_order, 10, equality=True)
if self.correction == 'P0' and legendre_order > 0:
msg = 'The P0 correction will be ignored since the scattering ' \
'order {} is greater than zero'.format(self.legendre_order)
warnings.warn(msg, RuntimeWarning)
self.correction = None
self._legendre_order = legendre_order
@tally_trigger.setter
def tally_trigger(self, tally_trigger):
cv.check_type('tally trigger', tally_trigger, openmc.Trigger)
@ -308,7 +340,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:
@ -344,33 +376,34 @@ class Library(object):
# Specify whether to use a transport ('P0') correction
if isinstance(mgxs, openmc.mgxs.ScatterMatrixXS):
mgxs.correction = self.correction
mgxs.legendre_order = self.legendre_order
self.all_mgxs[domain.id][mgxs_type] = mgxs
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
----------
tallies_file : openmc.TalliesFile
A TalliesFile object to add each MGXS' tallies to generate a
"tallies.xml" input file for OpenMC
tallies_file : openmc.Tallies
A Tallies collection to add each MGXS' tallies to generate a
'tallies.xml' input file for OpenMC
merge : bool
Indicate whether tallies should be merged when possible. Defaults
to True.
"""
cv.check_type('tallies_file', tallies_file, openmc.TalliesFile)
cv.check_type('tallies_file', tallies_file, openmc.Tallies)
# Add tallies from each MGXS for each domain and mgxs type
for domain in self.domains:
for mgxs_type in self.mgxs_types:
mgxs = self.get_mgxs(domain, mgxs_type)
for tally_id, tally in mgxs.tallies.items():
tallies_file.add_tally(tally, merge=merge)
tallies_file.append(tally, merge=merge)
def load_from_statepoint(self, statepoint):
"""Extracts tallies in an OpenMC StatePoint with the data needed to
@ -403,6 +436,7 @@ class Library(object):
self._sp_filename = statepoint._f.filename
self._openmc_geometry = statepoint.summary.openmc_geometry
self._nuclides = statepoint.summary.nuclides
if statepoint.run_mode == 'k-eigenvalue':
self._keff = statepoint.k_combined[0]
@ -426,7 +460,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', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', chi'}
The type of multi-group cross section object to return
Returns
@ -450,14 +484,14 @@ class Library(object):
cv.check_type('domain', domain, (openmc.Universe, Integral))
# Check that requested domain is included in library
if cv._isinstance(domain, Integral):
if isinstance(domain, Integral):
domain_id = domain
for domain in self.domains:
if domain_id == domain.id:
break
else:
msg = 'Unable to find MGXS for {0} "{1}" in ' \
'library'.format(self.domain_type, domain)
msg = 'Unable to find MGXS for "{0}" "{1}" in ' \
'library'.format(self.domain_type, domain_id)
raise ValueError(msg)
else:
domain_id = domain.id
@ -537,7 +571,7 @@ class Library(object):
Returns
-------
Library
openmc.mgxs.Library
A new multi-group cross section library averaged across subdomains
Raises
@ -575,7 +609,8 @@ class Library(object):
return subdomain_avg_library
def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs',
subdomains='all', nuclides='all', xs_type='macro'):
subdomains='all', nuclides='all', xs_type='macro',
row_column='inout'):
"""Export the multi-group cross section library to an HDF5 binary file.
This method constructs an HDF5 file which stores the library's
@ -605,6 +640,10 @@ class Library(object):
xs_type: {'macro', 'micro'}
Store the macro or micro cross section in units of cm^-1 or barns.
Defaults to 'macro'.
row_column: {'inout', 'outin'}
Store scattering matrices indexed first by incoming group and
second by outgoing group ('inout'), or vice versa ('outin').
Defaults to 'inout'.
Raises
------
@ -635,7 +674,7 @@ class Library(object):
full_filename = os.path.join(directory, filename)
full_filename = full_filename.replace(' ', '-')
f = h5py.File(full_filename, 'w')
f.attrs["# groups"] = self.num_groups
f.attrs['# groups'] = self.num_groups
f.close()
# Export MGXS for each domain and mgxs type to an HDF5 file
@ -646,8 +685,8 @@ class Library(object):
if subdomains == 'avg':
mgxs = mgxs.get_subdomain_avg_xs()
mgxs.build_hdf5_store(filename, directory,
xs_type=xs_type, nuclides=nuclides)
mgxs.build_hdf5_store(filename, directory, xs_type=xs_type,
nuclides=nuclides, row_column=row_column)
def dump_to_file(self, filename='mgxs', directory='mgxs'):
"""Store this Library object in a pickle binary file.
@ -712,3 +751,419 @@ class Library(object):
# Load and return pickled Library object
return pickle.load(open(full_filename, 'rb'))
def get_xsdata(self, domain, xsdata_name, nuclide='total', xs_type='macro',
xs_id='1m', order=None):
"""Generates an openmc.XSdata object describing a multi-group cross section
data set for eventual combination in to an openmc.MGXSLibrary object
(i.e., the library).
Parameters
----------
domain : openmc.Material or openmc.Cell or openmc.Universe
The domain for spatial homogenization
xsdata_name : str
Name to apply to the "xsdata" entry produced by this method
nuclide : str
A nuclide name string (e.g., 'U-235'). Defaults to 'total' to
obtain a material-wise macroscopic cross section.
xs_type: {'macro', 'micro'}
Provide the macro or micro cross section in units of cm^-1 or
barns. Defaults to 'macro'. If the Library object is not tallied by
nuclide this will be set to 'macro' regardless.
xs_ids : str
Cross section set identifier. Defaults to '1m'.
order : Scattering order for this data entry. Default is None,
which will set the XSdata object to use the order of the
Library.
Returns
-------
xsdata : openmc.XSdata
Multi-Group Cross Section data set object.
Raises
------
ValueError
When the Library object is initialized with insufficient types of
cross sections for the Library.
See also
--------
Library.create_mg_library()
"""
cv.check_type('domain', domain, (openmc.Material, openmc.Cell,
openmc.Cell))
cv.check_type('xsdata_name', xsdata_name, basestring)
cv.check_type('nuclide', nuclide, basestring)
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
cv.check_type('xs_id', xs_id, basestring)
cv.check_type('order', order, (type(None), Integral))
if order is not None:
cv.check_greater_than('order', order, 0, equality=True)
cv.check_less_than('order', order, 10, equality=True)
# Make sure statepoint has been loaded
if self._sp_filename is None:
msg = 'A StatePoint must be loaded before calling ' \
'the create_mg_library() function'
raise ValueError(msg)
# If gathering material-specific data, set the xs_type to macro
if not self.by_nuclide:
xs_type = 'macro'
# Build & add metadata to XSdata object
name = xsdata_name
if nuclide is not 'total':
name += '_' + nuclide
name += '.' + xs_id
xsdata = openmc.XSdata(name, self.energy_groups)
if order is None:
# Set the order to the Library's order (the defualt behavior)
xsdata.order = self.legendre_order
else:
# Set the order of the xsdata object to the minimum of
# the provided order or the Library's order.
xsdata.order = min(order, self.legendre_order)
if nuclide is not 'total':
xsdata.zaid = self._nuclides[nuclide][0]
xsdata.awr = self._nuclides[nuclide][1]
# Now get xs data itself
if 'nu-transport' in self.mgxs_types and self.correction == 'P0':
mymgxs = self.get_mgxs(domain, 'nu-transport')
xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide])
elif 'total' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'total')
xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide])
if 'absorption' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'absorption')
xsdata.set_absorption_mgxs(mymgxs, xs_type=xs_type,
nuclide=[nuclide])
if 'fission' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'fission')
xsdata.set_fission_mgxs(mymgxs, xs_type=xs_type,
nuclide=[nuclide])
if 'kappa-fission' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'kappa-fission')
xsdata.set_kappa_fission_mgxs(mymgxs, xs_type=xs_type,
nuclide=[nuclide])
# For chi and nu-fission we can either have only a nu-fission matrix
# provided, or vectors of chi and nu-fission provided
if 'nu-fission matrix' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'nu-fission matrix')
xsdata.set_nu_fission_mgxs(mymgxs, xs_type=xs_type,
nuclide=[nuclide])
else:
if 'chi' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'chi')
xsdata.set_chi_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide])
if 'nu-fission' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'nu-fission')
xsdata.set_nu_fission_mgxs(mymgxs, xs_type=xs_type,
nuclide=[nuclide])
# If multiplicity matrix is available, prefer that
if 'multiplicity matrix' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'multiplicity matrix')
xsdata.set_multiplicity_mgxs(mymgxs, xs_type=xs_type,
nuclide=[nuclide])
using_multiplicity = True
# multiplicity wil fall back to using scatter and nu-scatter
elif ((('scatter matrix' in self.mgxs_types) and
('nu-scatter matrix' in self.mgxs_types))):
scatt_mgxs = self.get_mgxs(domain, 'scatter matrix')
nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix')
xsdata.set_multiplicity_mgxs(nuscatt_mgxs, scatt_mgxs,
xs_type=xs_type, nuclide=[nuclide])
using_multiplicity = True
else:
using_multiplicity = False
if using_multiplicity:
nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix')
xsdata.set_scatter_mgxs(nuscatt_mgxs, xs_type=xs_type,
nuclide=[nuclide])
else:
if 'nu-scatter matrix' in self.mgxs_types:
nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix')
xsdata.set_scatter_mgxs(nuscatt_mgxs, xs_type=xs_type,
nuclide=[nuclide])
# Since we are not using multiplicity, then
# scattering multiplication (nu-scatter) must be
# accounted for approximately by using an adjusted
# absorption cross section.
if 'total' in self.mgxs_types:
xsdata._absorption = \
np.subtract(xsdata.total,
np.sum(xsdata.scatter[0, :, :], axis=1))
return xsdata
def create_mg_library(self, xs_type='macro', xsdata_names=None,
xs_ids=None):
"""Creates an openmc.MGXSLibrary object to contain the MGXS data for the
Multi-Group mode of OpenMC.
Parameters
----------
xs_type: {'macro', 'micro'}
Provide the macro or micro cross section in units of cm^-1 or
barns. Defaults to 'macro'. If the Library object is not tallied by
nuclide this will be set to 'macro' regardless.
xsdata_names : Iterable of str
List of names to apply to the "xsdata" entries in the
resultant mgxs data file. Defaults to 'set1', 'set2', ...
xs_ids : str or Iterable of str
Cross section set identifier (i.e., '71c') for all
data sets (if only str) or for each individual one
(if iterable of str). Defaults to '1m'.
Returns
-------
mgxs_file : openmc.MGXSLibrary
Multi-Group Cross Section File that is ready to be printed to the
file of choice by the user.
Raises
------
ValueError
When the Library object is initialized with insufficient types of
cross sections for the Library.
See also
--------
Library.dump_to_file()
Library.create_mg_mode()
"""
# Check to ensure the Library contains the correct
# multi-group cross section types
self.check_library_for_openmc_mgxs()
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
if xsdata_names is not None:
cv.check_iterable_type('xsdata_names', xsdata_names, basestring)
if xs_ids is not None:
if isinstance(xs_ids, basestring):
# If we only have a string lets convert it now to a list
# of strings.
xs_ids = [xs_ids for i in range(len(self.domains))]
else:
cv.check_iterable_type('xs_ids', xs_ids, basestring)
else:
xs_ids = ['1m' for i in range(len(self.domains))]
# If gathering material-specific data, set the xs_type to macro
if not self.by_nuclide:
xs_type = 'macro'
# Initialize file
mgxs_file = openmc.MGXSLibrary(self.energy_groups)
# Create the xsdata object and add it to the mgxs_file
for i, domain in enumerate(self.domains):
if self.by_nuclide:
nuclides = list(domain.get_all_nuclides().keys())
else:
nuclides = ['total']
for nuclide in nuclides:
# Build & add metadata to XSdata object
if xsdata_names is None:
xsdata_name = 'set' + str(i + 1)
else:
xsdata_name = xsdata_names[i]
if nuclide is not 'total':
xsdata_name += '_' + nuclide
xsdata = self.get_xsdata(domain, xsdata_name, nuclide=nuclide,
xs_type=xs_type, xs_id=xs_ids[i])
mgxs_file.add_xsdata(xsdata)
return mgxs_file
def create_mg_mode(self, xsdata_names=None, xs_ids=None):
"""Creates an openmc.MGXSLibrary object to contain the MGXS data for the
Multi-Group mode of OpenMC as well as the associated openmc.Materials
and openmc.Geometry objects. The created Geometry is the same as that
used to generate the MGXS data, with the only differences being
modifications to point to newly-created Materials which point to the
multi-group data. This method only creates a macroscopic
MGXS Library even if nuclidic tallies are specified in the Library.
Parameters
----------
xsdata_names : Iterable of str
List of names to apply to the "xsdata" entries in the
resultant mgxs data file. Defaults to 'set1', 'set2', ...
xs_ids : str or Iterable of str
Cross section set identifier (i.e., '71c') for all
data sets (if only str) or for each individual one
(if iterable of str). Defaults to '1m'.
Returns
-------
mgxs_file : openmc.MGXSLibrary
Multi-Group Cross Section File that is ready to be printed to the
file of choice by the user.
materials : openmc.Materials
Materials file ready to be printed with all the macroscopic data
present within this Library.
geometry : openmc.Geometry
Materials file ready to be printed with all the macroscopic data
present within this Library.
Raises
------
ValueError
When the Library object is initialized with insufficient types of
cross sections for the Library.
See also
--------
Library.create_mg_library()
Library.dump_to_file()
"""
# Check to ensure the Library contains the correct
# multi-group cross section types
self.check_library_for_openmc_mgxs()
if xsdata_names is not None:
cv.check_iterable_type('xsdata_names', xsdata_names, basestring)
if xs_ids is not None:
if isinstance(xs_ids, basestring):
# If we only have a string lets convert it now to a list
# of strings.
xs_ids = [xs_ids for i in range(len(self.domains))]
else:
cv.check_iterable_type('xs_ids', xs_ids, basestring)
else:
xs_ids = ['1m' for i in range(len(self.domains))]
xs_type = 'macro'
# Initialize MGXS File
mgxs_file = openmc.MGXSLibrary(self.energy_groups)
# Create a copy of the Geometry to differentiate for these Macroscopics
geometry = copy.deepcopy(self.openmc_geometry)
materials = openmc.Materials()
# Get all Cells from the Geometry for differentiation
all_cells = geometry.get_all_material_cells()
# Create the xsdata object and add it to the mgxs_file
for i, domain in enumerate(self.domains):
# Build & add metadata to XSdata object
if xsdata_names is None:
xsdata_name = 'set' + str(i + 1)
else:
xsdata_name = xsdata_names[i]
# Create XSdata and Macroscopic for this domain
xsdata = self.get_xsdata(domain, xsdata_name, nuclide='total',
xs_type=xs_type, xs_id=xs_ids[i])
mgxs_file.add_xsdata(xsdata)
macroscopic = openmc.Macroscopic(name=xsdata_name, xs=xs_ids[i])
# Create Material and add to collection
material = openmc.Material(name=xsdata_name + '.' + xs_ids[i])
material.add_macroscopic(macroscopic)
materials.append(material)
# Differentiate Geometry with new Material
if self.domain_type == 'material':
# Fill all appropriate Cells with new Material
for cell in all_cells:
if cell.fill.id == domain.id:
cell.fill = material
elif self.domain_type == 'cell':
for cell in all_cells:
if cell.id == domain.id:
cell.fill = material
return mgxs_file, materials, geometry
def check_library_for_openmc_mgxs(self):
"""This routine will check the MGXS Types within a Library
to ensure the MGXS types provided can be used to create
a MGXS Library for OpenMC's Multi-Group mode.
The rules to check include:
- Either total or transport should be present.
- Both can be available if one wants, but we should
use whatever corresponds to Library.correction (if P0: transport)
- Absorption and total (or transport) are required.
- A nu-fission cross section and chi values are not required as a
fixed source problem could be the target.
- Fission and kappa-fission are not required as they are only
needed to support tallies the user may wish to request.
- A nu-scatter matrix is required.
- Having a multiplicity matrix is preferred.
- Having both nu-scatter (of any order) and scatter
(at least isotropic) matrices is the second choice.
- If only nu-scatter, need total (not transport), to
be used in adjusting absorption
(i.e., reduced_abs = tot - nuscatt)
See also
--------
Library.create_mg_library()
Library.create_mg_mode()
"""
error_flag = False
# Ensure absorption is present
if 'absorption' not in self.mgxs_types:
error_flag = True
msg = '"absorption" MGXS type is required but not provided.'
warn(msg)
# Ensure nu-scattering matrix is required
if 'nu-scatter matrix' not in self.mgxs_types:
error_flag = True
msg = '"nu-scatter matrix" MGXS type is required but not provided.'
warn(msg)
else:
# Ok, now see the status of scatter and/or multiplicity
if ((('scatter matrix' not in self.mgxs_types) and
('multiplicity matrix' not in self.mgxs_types))):
# We dont have data needed for multiplicity matrix, therefore
# we need total, and not transport.
if 'total' not in self.mgxs_types:
error_flag = True
msg = '"total" MGXS type is required if a ' \
'scattering matrix is not provided.'
warn(msg)
# Total or transport can be present, but if using
# self.correction=="P0", then we should use transport.
if (((self.correction is "P0") and
('nu-transport' not in self.mgxs_types))):
error_flag = True
msg = 'A "nu-transport" MGXS type is required since a "P0" ' \
'correction is applied, but a "nu-transport" MGXS is ' \
'not provided.'
warn(msg)
elif (((self.correction is None) and
('total' not in self.mgxs_types))):
error_flag = True
msg = '"total" MGXS type is required, but not provided.'
warn(msg)
if error_flag:
msg = 'Invalid MGXS configuration encountered.'
raise ValueError(msg)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
import copy
import operator
import numpy as np
@ -9,8 +10,7 @@ except ImportError:
raise ImportError(msg)
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 +79,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 +116,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 +159,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 +183,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 +266,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 +354,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
@ -410,9 +392,9 @@ def get_compatible_opencg_surfaces(opencg_surface):
surfaces = [left, right, bottom, top]
elif opencg_surface.type == 'z-squareprism':
x0 = opencg_surface.x0['x0']
y0 = opencg_surface.y0['y0']
R = opencg_surface.r['R']
x0 = opencg_surface.x0
y0 = opencg_surface.y0
R = opencg_surface.r
# Create a list of the four planes we need
left = opencg.XPlane(name=name, boundary=boundary, x0=x0-R)
@ -451,10 +433,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 +448,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)
@ -487,13 +466,13 @@ def get_opencg_cell(openmc_cell):
# half-spaces, i.e., no complex cells.
region = openmc_cell.region
if region is not None:
if isinstance(region, Halfspace):
if isinstance(region, openmc.Halfspace):
surface = region.surface
halfspace = -1 if region.side == '-' else 1
opencg_cell.add_surface(get_opencg_surface(surface), halfspace)
elif isinstance(region, Intersection):
elif isinstance(region, openmc.Intersection):
for node in region.nodes:
if not isinstance(node, Halfspace):
if not isinstance(node, openmc.Halfspace):
raise NotImplementedError("Complex cells not yet "
"supported in OpenCG.")
surface = node.surface
@ -533,20 +512,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 = []
@ -558,7 +527,7 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
# Get the compatible Surfaces (XPlanes and YPlanes)
compatible_surfaces = get_compatible_opencg_surfaces(opencg_surface)
opencg_cell.removeSurface(opencg_surface)
opencg_cell.remove_surface(opencg_surface)
# If Cell is inside SquarePrism, add "inside" of Surface halfspaces
if halfspace == -1:
@ -575,7 +544,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)
@ -625,7 +594,7 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
# Remove redundant Surfaces from the Cells
for cell in compatible_cells:
cell.removeRedundantSurfaces()
cell.remove_redundant_surfaces()
# Return the list of OpenMC compatible OpenCG Cells
return compatible_cells
@ -641,10 +610,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
@ -672,7 +638,7 @@ def make_opencg_cells_compatible(opencg_universe):
surface, halfspace)
# Remove the non-compatible OpenCG Cell from the Universe
opencg_universe.removeCell(opencg_cell)
opencg_universe.remove_cell(opencg_cell)
# Add the compatible OpenCG Cells to the Universe
opencg_universe.add_cells(cells)
@ -700,10 +666,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 +681,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)
@ -733,12 +696,13 @@ def get_openmc_cell(opencg_cell):
translation = np.asarray(opencg_cell.translation, dtype=np.float64)
openmc_cell.translation = translation
surfaces = opencg_cell.surfaces
for surface_id in surfaces:
surface = surfaces[surface_id][0]
halfspace = surfaces[surface_id][1]
openmc_cell.add_surface(get_openmc_surface(surface), halfspace)
surfaces = []
operators = []
for surface, halfspace in opencg_cell.surfaces.values():
surfaces.append(get_openmc_surface(surface))
operators.append(operator.neg if halfspace == -1 else operator.pos)
openmc_cell.region = openmc.Intersection(
*[op(s) for op, s in zip(operators, surfaces)])
# Add the OpenMC Cell to the global collection of all OpenMC Cells
OPENMC_CELLS[cell_id] = openmc_cell
@ -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
@ -906,8 +861,8 @@ def get_opencg_lattice(openmc_lattice):
universes = new_universes
# Initialize an empty array for the OpenCG nested Universes in this Lattice
universe_array = np.ndarray(tuple(np.array(dimension)[::-1]),
dtype=opencg.Universe)
universe_array = np.empty(tuple(np.array(dimension)[::-1]),
dtype=opencg.Universe)
# Create OpenCG Universes for each unique nested Universe in this Lattice
unique_universes = openmc_lattice.get_unique_universes()
@ -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
@ -977,8 +929,8 @@ def get_openmc_lattice(opencg_lattice):
outer = opencg_lattice.outside
# Initialize an empty array for the OpenMC nested Universes in this Lattice
universe_array = np.ndarray(tuple(np.array(dimension)[::-1]),
dtype=openmc.Universe)
universe_array = np.empty(tuple(np.array(dimension)[::-1]),
dtype=openmc.Universe)
# Create OpenMC Universes for each unique nested Universe in this Lattice
unique_universes = opencg_lattice.get_unique_universes()
@ -1001,7 +953,6 @@ def get_openmc_lattice(opencg_lattice):
np.array(dimension, dtype=np.float64))) / -2.0
openmc_lattice = openmc.RectLattice(lattice_id=lattice_id)
openmc_lattice.dimension = dimension
openmc_lattice.pitch = width
openmc_lattice.universes = universe_array
openmc_lattice.lower_left = lower_left
@ -1032,10 +983,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 +1001,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 +1021,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

@ -37,6 +37,11 @@ class Particle(object):
def __init__(self, filename):
import h5py
if h5py.__version__ == '2.6.0':
raise ImportError("h5py 2.6.0 has a known bug which makes it "
"incompatible with OpenMC's HDF5 files. "
"Please switch to a different version.")
self._f = h5py.File(filename, 'r')
# Ensure filetype and revision are correct

View file

@ -2,6 +2,7 @@ from collections import Iterable
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
import warnings
import numpy as np
@ -125,7 +126,7 @@ class Plot(object):
return self._background
@property
def mask_componenets(self):
def mask_components(self):
return self._mask_components
@property
@ -227,7 +228,7 @@ class Plot(object):
self._col_spec = col_spec
@mask_componenets.setter
@mask_components.setter
def mask_components(self, mask_components):
cv.check_type('plot mask_components', mask_components, Iterable, Integral)
for component in mask_components:
@ -275,7 +276,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)
@ -401,44 +402,90 @@ class Plot(object):
return element
class PlotsFile(object):
"""Plots file used for an OpenMC simulation. Corresponds directly to the
plots.xml input file.
class Plots(cv.CheckedList):
"""Collection of Plots used for an OpenMC simulation.
This class corresponds directly to the plots.xml input file. It can be
thought of as a normal Python list where each member is a :class:`Plot`. It
behaves like a list as the following example demonstrates:
>>> xz_plot = openmc.Plot()
>>> big_plot = openmc.Plot()
>>> small_plot = openmc.Plot()
>>> p = openmc.Plots((xz_plot, big_plot))
>>> p.append(small_plot)
>>> small_plot = p.pop()
Parameters
----------
plots : Iterable of openmc.Plot
Plots to add to the collection
"""
def __init__(self):
# Initialize PlotsFile class attributes
self._plots = []
def __init__(self, plots=None):
super(Plots, self).__init__(Plot, 'plots collection')
self._plots_file = ET.Element("plots")
if plots is not None:
self += plots
def add_plot(self, plot):
"""Add a plot to the file.
.. deprecated:: 0.8
Use :meth:`Plots.append` instead.
Parameters
----------
plot : Plot
plot : openmc.Plot
Plot to add
"""
warnings.warn("Plots.add_plot(...) has been deprecated and may be "
"removed in a future version. Use Plots.append(...) "
"instead.", DeprecationWarning)
self.append(plot)
if not isinstance(plot, Plot):
msg = 'Unable to add a non-Plot "{0}" to the PlotsFile'.format(plot)
raise ValueError(msg)
def append(self, plot):
"""Append plot to collection
self._plots.append(plot)
Parameters
----------
plot : openmc.Plot
Plot to append
"""
super(Plots, self).append(plot)
def insert(self, index, plot):
"""Insert plot before index
Parameters
----------
index : int
Index in list
plot : openmc.Plot
Plot to insert
"""
super(Plots, self).insert(index, plot)
def remove_plot(self, plot):
"""Remove a plot from the file.
.. deprecated:: 0.8
Use :meth:`Plots.remove` instead.
Parameters
----------
plot : Plot
plot : openmc.Plot
Plot to remove
"""
self._plots.remove(plot)
warnings.warn("Plots.remove_plot(...) has been deprecated and may be "
"removed in a future version. Use Plots.remove(...) "
"instead.", DeprecationWarning)
self.remove(plot)
def colorize(self, geometry, seed=1):
"""Generate a consistent color scheme for each domain in each plot.
@ -456,7 +503,7 @@ class PlotsFile(object):
"""
for plot in self._plots:
for plot in self:
plot.colorize(geometry, seed)
@ -482,11 +529,11 @@ class PlotsFile(object):
"""
for plot in self._plots:
for plot in self:
plot.highlight_domains(geometry, domains, seed, alpha, background)
def _create_plot_subelements(self):
for plot in self._plots:
for plot in self:
xml_element = plot.get_plot_xml()
if len(plot._name) > 0:

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.
"""
@ -27,6 +28,10 @@ class Region(object):
def __invert__(self):
return Complement(self)
@abstractmethod
def __contains__(self, point):
return False
@abstractmethod
def __str__(self):
return ''
@ -201,11 +206,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 +218,12 @@ class Intersection(Region):
Parameters
----------
*nodes
\*nodes
Regions to take the intersection of
Attributes
----------
nodes : tuple of Region
nodes : list 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
@ -228,6 +233,26 @@ class Intersection(Region):
def __init__(self, *nodes):
self.nodes = list(nodes)
def __iter__(self):
for n in self.nodes:
yield n
def __contains__(self, point):
"""Check whether a point is contained in the region.
Parameters
----------
point : 3-tuple of float
Cartesian coordinates, :math:`(x',y',z')`, of the point
Returns
-------
bool
Whether the point is in the region
"""
return all(point in n for n in self.nodes)
def __str__(self):
return '(' + ' '.join(map(str, self.nodes)) + ')'
@ -255,21 +280,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
@ -279,6 +305,26 @@ class Union(Region):
def __init__(self, *nodes):
self.nodes = list(nodes)
def __iter__(self):
for n in self.nodes:
yield n
def __contains__(self, point):
"""Check whether a point is contained in the region.
Parameters
----------
point : 3-tuple of float
Cartesian coordinates, :math:`(x',y',z')`, of the point
Returns
-------
bool
Whether the point is in the region
"""
return any(point in n for n in self.nodes)
def __str__(self):
return '(' + ' | '.join(map(str, self.nodes)) + ')'
@ -305,13 +351,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 +365,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
@ -334,6 +380,22 @@ class Complement(Region):
def __init__(self, node):
self.node = node
def __contains__(self, point):
"""Check whether a point is contained in the region.
Parameters
----------
point : 3-tuple of float
Cartesian coordinates, :math:`(x',y',z')`, of the point
Returns
-------
bool
Whether the point is in the region
"""
return point not in self.node
def __str__(self):
return '~' + str(self.node)

View file

@ -9,13 +9,14 @@ 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:
basestring = str
class SettingsFile(object):
class Settings(object):
"""Settings file used for an OpenMC simulation. Corresponds directly to the
settings.xml input file.
@ -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',
@ -69,9 +70,10 @@ class SettingsFile(object):
deviation.
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 for continuous-energy calculations
and :envvar:`MG_CROSS_SECTIONS` will be used for multi-group
cross_sections.xml). If it is not set, the
:envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used for
continuous-energy calculations and
:envvar:`OPENMC_MG_CROSS_SECTIONS` will be used for multi-group
calculations to find the path to the XML cross section file.
energy_grid : {'nuclide', 'logarithm', 'material-union'}
Set the method used to search energy grids.
@ -125,6 +127,8 @@ class SettingsFile(object):
Coordinates of the lower-left point of the UFS mesh
ufs_upper_right : tuple or list
Coordinates of the upper-right point of the UFS mesh
resonance_scattering : ResonanceScattering or iterable of ResonanceScattering
The elastic scattering model to use for resonant isotopes
"""
@ -205,6 +209,8 @@ class SettingsFile(object):
self._run_mode_subelement = None
self._source_element = None
self._resonance_scattering = None
@property
def run_mode(self):
return self._run_mode
@ -393,9 +399,13 @@ class SettingsFile(object):
def dd_count_interactions(self):
return self._dd_count_interactions
@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)
@ -764,6 +774,16 @@ class SettingsFile(object):
self._dd_count_interactions = interactions
@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':
@ -1043,6 +1063,17 @@ class SettingsFile(object):
subelement = ET.SubElement(element, "count_interactions")
subelement.text = str(self._dd_count_interactions).lower()
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.
@ -1079,6 +1110,7 @@ class SettingsFile(object):
self._create_track_subelement()
self._create_ufs_subelement()
self._create_dd_subelement()
self._create_resonance_scattering_element()
# Clean the indentation in the file to be user-readable
clean_xml_indentation(self._settings_file)
@ -1087,3 +1119,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

@ -1,5 +1,8 @@
import sys
import re
import os
import warnings
import numpy as np
import openmc
@ -14,63 +17,74 @@ class StatePoint(object):
of a given batch). Statepoints can be used to analyze tally results as well
as restart a simulation.
Parameters
----------
filename : str
Path to file to load
autolink : bool, optional
Whether to automatically link in metadata from a summary.h5
file. Defaults to True.
Attributes
----------
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.
@ -85,13 +99,18 @@ class StatePoint(object):
Indicate whether user-defined tallies are present
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
"""
def __init__(self, filename):
def __init__(self, filename, autolink=True):
import h5py
if h5py.__version__ == '2.6.0':
raise ImportError("h5py 2.6.0 has a known bug which makes it "
"incompatible with OpenMC's HDF5 files. "
"Please switch to a different version.")
self._f = h5py.File(filename, 'r')
# Ensure filetype and revision are correct
@ -101,8 +120,9 @@ 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.')
'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 '
@ -112,10 +132,17 @@ class StatePoint(object):
# Set flags for what data has been read
self._meshes_read = False
self._tallies_read = False
self._summary = False
self._summary = None
self._global_tallies = None
self._sparse = False
# Automatically link in a summary file if one exists
if autolink:
path_summary = os.path.join(os.path.dirname(filename), 'summary.h5')
if os.path.exists(path_summary):
su = openmc.Summary(path_summary)
self.link_with_summary(su)
def close(self):
self._f.close()
@ -311,6 +338,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
@ -389,7 +421,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 = \
@ -398,7 +430,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
@ -425,7 +457,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
@ -470,13 +502,18 @@ class StatePoint(object):
self.tallies[tally_id].sparse = self.sparse
def get_tally(self, scores=[], filters=[], nuclides=[],
name=None, id=None, estimator=None):
name=None, id=None, estimator=None, exact_filters=False,
exact_nuclides=False, exact_scores=False):
"""Finds and returns a Tally object with certain properties.
This routine searches the list of Tallies and returns the first Tally
found which satisfies all of the input parameters.
NOTE: The input parameters do not need to match the complete Tally
specification and may only represent a subset of the Tally's properties.
NOTE: If any of the "exact" parameters are False (default), the input
parameters do not need to match the complete Tally specification and
may only represent a subset of the Tally's properties. If an "exact"
parameter is True then number of scores, filters, or nuclides in the
parameters must precisely match those of any matching Tally.
Parameters
----------
@ -492,10 +529,22 @@ class StatePoint(object):
The id specified for the Tally (default is None).
estimator: str, optional
The type of estimator ('tracklength', 'analog'; default is None).
exact_filters : bool
If True, the number of filters in the parameters must be identical
to those in the matching Tally. If False (default), the filters in
the parameters may be a subset of those in the matching Tally.
exact_nuclides : bool
If True, the number of nuclides in the parameters must be identical
to those in the matching Tally. If False (default), the nuclides in
the parameters may be a subset of those in the matching Tally.
exact_scores : bool
If True, the number of scores in the parameters must be identical
to those in the matching Tally. If False (default), the scores
in the parameters may be a subset of those in the matching Tally.
Returns
-------
tally : Tally
tally : openmc.Tally
A tally matching the specified criteria
Raises
@ -520,7 +569,15 @@ class StatePoint(object):
continue
# Determine if Tally has queried estimator
if estimator and not estimator == test_tally.estimator:
if estimator and estimator != test_tally.estimator:
continue
# The number of filters, nuclides and scores must exactly match
if exact_scores and len(scores) != test_tally.num_scores:
continue
if exact_nuclides and len(nuclides) != test_tally.num_nuclides:
continue
if exact_filters and len(filters) != test_tally.num_filters:
continue
# Determine if Tally has the queried score(s)
@ -592,7 +649,7 @@ class StatePoint(object):
Parameters
----------
summary : Summary
summary : openmc.Summary
A Summary object.
Raises
@ -603,17 +660,24 @@ class StatePoint(object):
"""
if self.summary is not None:
warnings.warn('A Summary object has already been linked.',
RuntimeWarning)
return
if not isinstance(summary, openmc.summary.Summary):
msg = 'Unable to link statepoint with "{0}" which ' \
'is not a Summary object'.format(summary)
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:
@ -626,6 +690,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,17 +328,17 @@ class Point(Spatial):
Parameters
----------
xyz : Iterable of Real
Cartesian coordinates of location
xyz : Iterable of float, optional
Cartesian coordinates of location. Defaults to (0., 0., 0.).
Attributes
----------
xyz : Iterable of Real
xyz : Iterable of float
Cartesian coordinates of location
"""
def __init__(self, xyz):
def __init__(self, xyz=(0., 0., 0.)):
super(Point, self).__init__()
self.xyz = xyz

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
"""
@ -66,14 +66,14 @@ class Discrete(Univariate):
@x.setter
def x(self, x):
if cv._isinstance(x, Real):
if isinstance(x, Real):
x = [x]
cv.check_type('discrete values', x, Iterable, Real)
self._x = x
@p.setter
def p(self, p):
if cv._isinstance(p, Real):
if isinstance(p, Real):
p = [p]
cv.check_type('discrete probabilities', p, Iterable, Real)
for pk in p:
@ -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

@ -26,6 +26,10 @@ class Summary(object):
# Python API so we'll only try to import h5py if the user actually inits
# a Summary object.
import h5py
if h5py.__version__ == '2.6.0':
raise ImportError("h5py 2.6.0 has a known bug which makes it "
"incompatible with OpenMC's HDF5 files. "
"Please switch to a different version.")
openmc.reset_auto_ids()
@ -38,8 +42,10 @@ class Summary(object):
self._opencg_geometry = None
self._read_metadata()
self._read_nuclides()
self._read_geometry()
self._read_tallies()
self._f.close()
@property
def openmc_geometry(self):
@ -55,8 +61,8 @@ class Summary(object):
def _read_metadata(self):
# Read OpenMC version
self.version = [self._f['version_major'].value,
self._f['version_minor'].value,
self._f['version_release'].value]
self._f['version_minor'].value,
self._f['version_release'].value]
# Read date and time
self.date_and_time = self._f['date_and_time'][...]
@ -65,11 +71,23 @@ class Summary(object):
self.n_batches = self._f['n_batches'].value
self.n_particles = self._f['n_particles'].value
self.n_active = self._f['n_active'].value
self.n_inactive = self._f['n_inactive'].value
self.gen_per_batch = self._f['gen_per_batch'].value
if 'n_inactive' in self._f:
self.n_active = self._f['n_active'].value
self.n_inactive = self._f['n_inactive'].value
self.gen_per_batch = self._f['gen_per_batch'].value
self.n_procs = self._f['n_procs'].value
def _read_nuclides(self):
self.nuclides = {}
n_nuclides = self._f['nuclides/n_nuclides_total'].value
names = self._f['nuclides/names'].value
awrs = self._f['nuclides/awrs'].value
zaids = self._f['nuclides/zaids'].value
for n in range(n_nuclides):
name = names[n].decode()
name = name[:name.find('.')]
self.nuclides[name] = (zaids[n], awrs[n])
def _read_geometry(self):
# Read in and initialize the Materials and Geometry
self._read_materials()
@ -266,7 +284,7 @@ class Summary(object):
rotation = \
self._f['geometry/cells'][key]['rotation'][...]
rotation = np.asarray(rotation, dtype=np.int)
cell.rotation = rotation
cell._rotation = rotation
# Store Cell fill information for after Universe/Lattice creation
self._cell_fills[index] = (fill_type, fill)
@ -344,7 +362,6 @@ class Summary(object):
# Create the Lattice
lattice = openmc.RectLattice(lattice_id=lattice_id, name=name)
lattice.dimension = tuple(dimension)
lattice.lower_left = lower_left
lattice.pitch = pitch
@ -354,7 +371,7 @@ class Summary(object):
# Build array of Universe pointers for the Lattice
universes = \
np.ndarray(tuple(universe_ids.shape), dtype=openmc.Universe)
np.empty(tuple(universe_ids.shape), dtype=openmc.Universe)
for z in range(universe_ids.shape[0]):
for y in range(universe_ids.shape[1]):
@ -378,19 +395,17 @@ class Summary(object):
self.lattices[index] = lattice
if lattice_type == 'hexagonal':
n_rings = self._f['geometry/lattices'][key]['n_rings'][0]
n_axial = self._f['geometry/lattices'][key]['n_axial'][0]
n_rings = self._f['geometry/lattices'][key]['n_rings'].value
n_axial = self._f['geometry/lattices'][key]['n_axial'].value
center = self._f['geometry/lattices'][key]['center'][...]
pitch = self._f['geometry/lattices'][key]['pitch'][...]
outer = self._f['geometry/lattices'][key]['outer'][0]
outer = self._f['geometry/lattices'][key]['outer'].value
universe_ids = self._f[
'geometry/lattices'][key]['universes'][...]
# Create the Lattice
lattice = openmc.HexLattice(lattice_id=lattice_id, name=name)
lattice.num_rings = n_rings
lattice.num_axial = n_axial
lattice.center = center
lattice.pitch = pitch
@ -403,12 +418,12 @@ class Summary(object):
# (x, alpha, z) to the Python API's format of a ragged nested
# list of (z, ring, theta).
universes = []
for z in range(lattice.num_axial):
for z in range(n_axial):
# Add a list for this axial level.
universes.append([])
x = lattice.num_rings - 1
a = 2*lattice.num_rings - 2
for r in range(lattice.num_rings - 1, 0, -1):
x = n_rings - 1
a = 2*n_rings - 2
for r in range(n_rings - 1, 0, -1):
# Add a list for this ring.
universes[-1].append([])
@ -482,13 +497,13 @@ class Summary(object):
# Retrieve the object corresponding to the fill type and ID
if fill_type == 'normal':
if isinstance(fill_id, Iterable):
fill = [self.get_material_by_id(mat) if mat > 0 else 'void'
fill = [self.get_material_by_id(mat) if mat > 0 else None
for mat in fill_id]
else:
if fill_id > 0:
fill = self.get_material_by_id(fill_id)
else:
fill = 'void'
fill = None
elif fill_type == 'universe':
fill = self.get_universe_by_id(fill_id)
else:
@ -542,7 +557,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 +577,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 +599,7 @@ class Summary(object):
Returns
-------
material : openmc.material.Material
material : openmc.Material
Material with given id
"""
@ -599,7 +620,7 @@ class Summary(object):
Returns
-------
surface : openmc.surface.Surface
surface : openmc.Surface
Surface with given id
"""
@ -620,7 +641,7 @@ class Summary(object):
Returns
-------
cell : openmc.universe.Cell
cell : openmc.Cell
Cell with given id
"""
@ -641,7 +662,7 @@ class Summary(object):
Returns
-------
universe : openmc.universe.Universe
universe : openmc.Universe
Universe with given id
"""
@ -662,7 +683,7 @@ class Summary(object):
Returns
-------
lattice : openmc.universe.Lattice
lattice : openmc.Lattice
Lattice with given id
"""

File diff suppressed because it is too large Load diff

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

@ -11,7 +11,7 @@ except ImportError:
kwargs = {'name': 'openmc',
'version': '0.7.1',
'packages': ['openmc', 'openmc.mgxs', 'openmc.stats'],
'packages': ['openmc', 'openmc.data', 'openmc.mgxs', 'openmc.stats'],
'scripts': glob.glob('scripts/openmc-*'),
# Metadata
@ -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,58 +0,0 @@
module ace_header
use constants, only: MAX_FILE_LEN, ZERO
use dict_header, only: DictIntInt
use endf_header, only: Tab1
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
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
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
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

@ -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

View file

@ -37,7 +37,7 @@ module constants
real(8), parameter :: FP_COINCIDENT = 1e-12_8
! Maximum number of collisions/crossings
integer, parameter :: MAX_EVENTS = 10000
integer, parameter :: MAX_EVENTS = 1000000
integer, parameter :: MAX_SAMPLE = 100000
! Maximum number of secondary particles created
@ -223,6 +223,12 @@ module constants
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
@ -273,7 +279,7 @@ module constants
EVENT_ABSORB = 2
! Tally score type
integer, parameter :: N_SCORE_TYPES = 22
integer, parameter :: N_SCORE_TYPES = 20
integer, parameter :: &
SCORE_FLUX = -1, & ! flux
SCORE_TOTAL = -2, & ! total reaction rate
@ -283,20 +289,18 @@ module constants
SCORE_SCATTER_PN = -6, & ! system for scoring 0th through nth moment
SCORE_NU_SCATTER_N = -7, & ! arbitrary nu-scattering moment
SCORE_NU_SCATTER_PN = -8, & ! system for scoring 0th through nth nu-scatter moment
SCORE_TRANSPORT = -9, & ! transport reaction rate
SCORE_N_1N = -10, & ! (n,1n) rate
SCORE_ABSORPTION = -11, & ! absorption rate
SCORE_FISSION = -12, & ! fission rate
SCORE_NU_FISSION = -13, & ! neutron production rate
SCORE_KAPPA_FISSION = -14, & ! fission energy production rate
SCORE_CURRENT = -15, & ! partial current
SCORE_FLUX_YN = -16, & ! angular moment of flux
SCORE_TOTAL_YN = -17, & ! angular moment of total reaction rate
SCORE_SCATTER_YN = -18, & ! angular flux-weighted scattering moment (0:N)
SCORE_NU_SCATTER_YN = -19, & ! angular flux-weighted nu-scattering moment (0:N)
SCORE_EVENTS = -20, & ! number of events
SCORE_DELAYED_NU_FISSION = -21, & ! delayed neutron production rate
SCORE_INVERSE_VELOCITY = -22 ! flux-weighted inverse velocity
SCORE_ABSORPTION = -9, & ! absorption rate
SCORE_FISSION = -10, & ! fission rate
SCORE_NU_FISSION = -11, & ! neutron production rate
SCORE_KAPPA_FISSION = -12, & ! fission energy production rate
SCORE_CURRENT = -13, & ! partial current
SCORE_FLUX_YN = -14, & ! angular moment of flux
SCORE_TOTAL_YN = -15, & ! angular moment of total reaction rate
SCORE_SCATTER_YN = -16, & ! angular flux-weighted scattering moment (0:N)
SCORE_NU_SCATTER_YN = -17, & ! angular flux-weighted nu-scattering moment (0:N)
SCORE_EVENTS = -18, & ! number of events
SCORE_DELAYED_NU_FISSION = -19, & ! delayed neutron production rate
SCORE_INVERSE_VELOCITY = -20 ! flux-weighted inverse velocity
! Maximum scattering order supported
integer, parameter :: MAX_ANG_ORDER = 10
@ -365,10 +369,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

View file

@ -1,16 +1,14 @@
module cross_section
use ace_header, only: Reaction, UrrData
use constants
use energy_grid, only: grid_method, log_spacing
use error, only: fatal_error
use fission, only: nu_total
use global
use list_header, only: ListElemInt
use material_header, only: Material
use nuclide_header
use particle_header, only: Particle
use random_lcg, only: prn
use random_lcg, only: prn, future_prn, prn_set_stream
use sab_header, only: SAlphaBeta
use search, only: binary_search
@ -148,7 +146,7 @@ contains
integer :: i_low ! lower logarithmic mapping index
integer :: i_high ! upper logarithmic mapping index
real(8) :: f ! interp factor on nuclide energy grid
type(NuclideCE), pointer :: nuc
type(Nuclide), pointer :: nuc
type(Material), pointer :: mat
! Set pointer to nuclide and material
@ -354,154 +352,136 @@ contains
integer, intent(in) :: i_nuclide ! index into nuclides array
real(8), intent(in) :: E ! energy
integer :: i ! loop index
integer :: i_energy ! index for energy
integer :: i_low ! band index at lower bounding energy
integer :: i_up ! band index at upper bounding energy
integer :: same_nuc_idx ! index of same nuclide
real(8) :: f ! interpolation factor
real(8) :: r ! pseudo-random number
real(8) :: elastic ! elastic cross section
real(8) :: capture ! (n,gamma) cross section
real(8) :: fission ! fission cross section
real(8) :: inelastic ! inelastic cross section
logical :: same_nuc ! do we know the xs for this nuclide at this energy?
type(UrrData), pointer :: urr
type(NuclideCE), pointer :: nuc
micro_xs(i_nuclide) % use_ptable = .true.
! get pointer to probability table
nuc => nuclides(i_nuclide)
urr => nuc % urr_data
associate (nuc => nuclides(i_nuclide), urr => nuclides(i_nuclide) % urr_data)
! determine energy table
i_energy = 1
do
if (E < urr % energy(i_energy + 1)) exit
i_energy = i_energy + 1
end do
! determine energy table
i_energy = 1
do
if (E < urr % energy(i_energy + 1)) exit
i_energy = i_energy + 1
end do
! determine interpolation factor on table
f = (E - urr % energy(i_energy)) / &
(urr % energy(i_energy + 1) - urr % energy(i_energy))
! determine interpolation factor on table
f = (E - urr % energy(i_energy)) / &
(urr % energy(i_energy + 1) - urr % energy(i_energy))
! sample probability table using the cumulative distribution
! sample probability table using the cumulative distribution
! Random numbers for xs calculation are sampled from a separated stream.
! This guarantees the randomness and, at the same time, makes sure we reuse
! random number for the same nuclide at different temperatures, therefore
! preserving correlation of temperature in probability tables.
call prn_set_stream(STREAM_URR_PTABLE)
r = future_prn(int(nuc_zaid_dict % get_key(nuc % zaid), 8))
call prn_set_stream(STREAM_TRACKING)
! if we're dealing with a nuclide that we've previously encountered at
! this energy but a different temperature, use the original random number to
! preserve correlation of temperature in probability tables
same_nuc = .false.
do i = 1, nuc % nuc_list % size()
if (E /= ZERO .and. E == micro_xs(nuc % nuc_list % data(i)) % last_E) then
same_nuc = .true.
same_nuc_idx = i
exit
end if
end do
i_low = 1
do
if (urr % prob(i_energy, URR_CUM_PROB, i_low) > r) exit
i_low = i_low + 1
end do
i_up = 1
do
if (urr % prob(i_energy + 1, URR_CUM_PROB, i_up) > r) exit
i_up = i_up + 1
end do
if (same_nuc) then
r = micro_xs(nuc % nuc_list % data(same_nuc_idx)) % last_prn
else
r = prn()
micro_xs(i_nuclide) % last_prn = r
end if
! determine elastic, fission, and capture cross sections from probability
! table
if (urr % interp == LINEAR_LINEAR) then
elastic = (ONE - f) * urr % prob(i_energy, URR_ELASTIC, i_low) + &
f * urr % prob(i_energy + 1, URR_ELASTIC, i_up)
fission = (ONE - f) * urr % prob(i_energy, URR_FISSION, i_low) + &
f * urr % prob(i_energy + 1, URR_FISSION, i_up)
capture = (ONE - f) * urr % prob(i_energy, URR_N_GAMMA, i_low) + &
f * urr % prob(i_energy + 1, URR_N_GAMMA, i_up)
elseif (urr % interp == LOG_LOG) then
! Get logarithmic interpolation factor
f = log(E / urr % energy(i_energy)) / &
log(urr % energy(i_energy + 1) / urr % energy(i_energy))
i_low = 1
do
if (urr % prob(i_energy, URR_CUM_PROB, i_low) > r) exit
i_low = i_low + 1
end do
i_up = 1
do
if (urr % prob(i_energy + 1, URR_CUM_PROB, i_up) > r) exit
i_up = i_up + 1
end do
! determine elastic, fission, and capture cross sections from probability
! table
if (urr % interp == LINEAR_LINEAR) then
elastic = (ONE - f) * urr % prob(i_energy, URR_ELASTIC, i_low) + &
f * urr % prob(i_energy + 1, URR_ELASTIC, i_up)
fission = (ONE - f) * urr % prob(i_energy, URR_FISSION, i_low) + &
f * urr % prob(i_energy + 1, URR_FISSION, i_up)
capture = (ONE - f) * urr % prob(i_energy, URR_N_GAMMA, i_low) + &
f * urr % prob(i_energy + 1, URR_N_GAMMA, i_up)
elseif (urr % interp == LOG_LOG) then
! Get logarithmic interpolation factor
f = log(E / urr % energy(i_energy)) / &
log(urr % energy(i_energy + 1) / urr % energy(i_energy))
! Calculate elastic cross section/factor
elastic = ZERO
if (urr % prob(i_energy, URR_ELASTIC, i_low) > ZERO .and. &
urr % prob(i_energy + 1, URR_ELASTIC, i_up) > ZERO) then
elastic = exp((ONE - f) * log(urr % prob(i_energy, URR_ELASTIC, &
i_low)) + f * log(urr % prob(i_energy + 1, URR_ELASTIC, &
i_up)))
end if
! Calculate fission cross section/factor
fission = ZERO
if (urr % prob(i_energy, URR_FISSION, i_low) > ZERO .and. &
urr % prob(i_energy + 1, URR_FISSION, i_up) > ZERO) then
fission = exp((ONE - f) * log(urr % prob(i_energy, URR_FISSION, &
i_low)) + f * log(urr % prob(i_energy + 1, URR_FISSION, &
i_up)))
end if
! Calculate capture cross section/factor
capture = ZERO
if (urr % prob(i_energy, URR_N_GAMMA, i_low) > ZERO .and. &
urr % prob(i_energy + 1, URR_N_GAMMA, i_up) > ZERO) then
capture = exp((ONE - f) * log(urr % prob(i_energy, URR_N_GAMMA, &
i_low)) + f * log(urr % prob(i_energy + 1, URR_N_GAMMA, &
i_up)))
end if
end if
! Determine treatment of inelastic scattering
inelastic = ZERO
if (urr % inelastic_flag > 0) then
! Get index on energy grid and interpolation factor
i_energy = micro_xs(i_nuclide) % index_grid
f = micro_xs(i_nuclide) % interp_factor
! Determine inelastic scattering cross section
associate (rxn => nuc % reactions(nuc % urr_inelastic))
if (i_energy >= rxn % threshold) then
inelastic = (ONE - f) * rxn % sigma(i_energy - rxn%threshold + 1) + &
f * rxn % sigma(i_energy - rxn%threshold + 2)
! Calculate elastic cross section/factor
elastic = ZERO
if (urr % prob(i_energy, URR_ELASTIC, i_low) > ZERO .and. &
urr % prob(i_energy + 1, URR_ELASTIC, i_up) > ZERO) then
elastic = exp((ONE - f) * log(urr % prob(i_energy, URR_ELASTIC, &
i_low)) + f * log(urr % prob(i_energy + 1, URR_ELASTIC, &
i_up)))
end if
end associate
end if
! Multiply by smooth cross-section if needed
if (urr % multiply_smooth) then
elastic = elastic * micro_xs(i_nuclide) % elastic
capture = capture * (micro_xs(i_nuclide) % absorption - &
micro_xs(i_nuclide) % fission)
fission = fission * micro_xs(i_nuclide) % fission
end if
! Calculate fission cross section/factor
fission = ZERO
if (urr % prob(i_energy, URR_FISSION, i_low) > ZERO .and. &
urr % prob(i_energy + 1, URR_FISSION, i_up) > ZERO) then
fission = exp((ONE - f) * log(urr % prob(i_energy, URR_FISSION, &
i_low)) + f * log(urr % prob(i_energy + 1, URR_FISSION, &
i_up)))
end if
! Check for negative values
if (elastic < ZERO) elastic = ZERO
if (fission < ZERO) fission = ZERO
if (capture < ZERO) capture = ZERO
! Calculate capture cross section/factor
capture = ZERO
if (urr % prob(i_energy, URR_N_GAMMA, i_low) > ZERO .and. &
urr % prob(i_energy + 1, URR_N_GAMMA, i_up) > ZERO) then
capture = exp((ONE - f) * log(urr % prob(i_energy, URR_N_GAMMA, &
i_low)) + f * log(urr % prob(i_energy + 1, URR_N_GAMMA, &
i_up)))
end if
end if
! Set elastic, absorption, fission, and total cross sections. Note that the
! total cross section is calculated as sum of partials rather than using the
! table-provided value
micro_xs(i_nuclide) % elastic = elastic
micro_xs(i_nuclide) % absorption = capture + fission
micro_xs(i_nuclide) % fission = fission
micro_xs(i_nuclide) % total = elastic + inelastic + capture + fission
! Determine treatment of inelastic scattering
inelastic = ZERO
if (urr % inelastic_flag > 0) then
! Get index on energy grid and interpolation factor
i_energy = micro_xs(i_nuclide) % index_grid
f = micro_xs(i_nuclide) % interp_factor
! Determine nu-fission cross section
if (nuc % fissionable) then
micro_xs(i_nuclide) % nu_fission = nu_total(nuc, E) * &
micro_xs(i_nuclide) % fission
end if
! Determine inelastic scattering cross section
associate (rxn => nuc % reactions(nuc % urr_inelastic))
if (i_energy >= rxn % threshold) then
inelastic = (ONE - f) * rxn % sigma(i_energy - rxn%threshold + 1) + &
f * rxn % sigma(i_energy - rxn%threshold + 2)
end if
end associate
end if
! Multiply by smooth cross-section if needed
if (urr % multiply_smooth) then
elastic = elastic * micro_xs(i_nuclide) % elastic
capture = capture * (micro_xs(i_nuclide) % absorption - &
micro_xs(i_nuclide) % fission)
fission = fission * micro_xs(i_nuclide) % fission
end if
! Check for negative values
if (elastic < ZERO) elastic = ZERO
if (fission < ZERO) fission = ZERO
if (capture < ZERO) capture = ZERO
! Set elastic, absorption, fission, and total cross sections. Note that the
! total cross section is calculated as sum of partials rather than using the
! table-provided value
micro_xs(i_nuclide) % elastic = elastic
micro_xs(i_nuclide) % absorption = capture + fission
micro_xs(i_nuclide) % fission = fission
micro_xs(i_nuclide) % total = elastic + inelastic + capture + fission
! Determine nu-fission cross section
if (nuc % fissionable) then
micro_xs(i_nuclide) % nu_fission = nuc % nu(E, EMISSION_TOTAL) * &
micro_xs(i_nuclide) % fission
end if
end associate
end subroutine calculate_urr_xs
@ -533,9 +513,9 @@ contains
!===============================================================================
pure function elastic_xs_0K(E, nuc) result(xs_out)
real(8), intent(in) :: E ! trial energy
type(NuclideCE), intent(in) :: nuc ! target nuclide at temperature
real(8) :: xs_out ! 0K xs at trial energy
real(8), intent(in) :: E ! trial energy
type(Nuclide), intent(in) :: nuc ! target nuclide at temperature
real(8) :: xs_out ! 0K xs at trial energy
integer :: i_grid ! index on nuclide energy grid
real(8) :: f ! interp factor on nuclide energy grid

View file

@ -10,7 +10,7 @@ module eigenvalue
use math, only: t_percentile
use mesh, only: count_bank_sites
use mesh_header, only: RegularMesh
use random_lcg, only: prn, set_particle_seed, prn_skip
use random_lcg, only: prn, set_particle_seed, advance_prn_seed
use search, only: binary_search
use string, only: to_str
@ -99,7 +99,7 @@ contains
call set_particle_seed(int((current_batch - 1)*gen_per_batch + &
current_gen,8))
call prn_skip(start)
call advance_prn_seed(start)
! Determine how many fission sites we need to sample from the source bank
! and the probability for selecting a site.

View file

@ -34,10 +34,6 @@ contains
string = "nu-scatter-n"
case (SCORE_NU_SCATTER_PN)
string = "nu-scatter-pn"
case (SCORE_TRANSPORT)
string = "transport"
case (SCORE_N_1N)
string = "n1n"
case (SCORE_ABSORPTION)
string = "absorption"
case (SCORE_FISSION)

View file

@ -1,12 +1,51 @@
module endf_header
implicit none
use constants, only: ZERO, HISTOGRAM, LINEAR_LINEAR, LINEAR_LOG, &
LOG_LINEAR, LOG_LOG
use search, only: binary_search
implicit none
type, abstract :: Function1D
contains
procedure(function1d_evaluate_), deferred :: evaluate
end type Function1D
abstract interface
pure function function1d_evaluate_(this, x) result(y)
import Function1D
class(Function1D), intent(in) :: this
real(8), intent(in) :: x
real(8) :: y
end function function1d_evaluate_
end interface
!===============================================================================
! TAB1 represents a one-dimensional interpolable function
! CONSTANT1D represents a constant one-dimensional function
!===============================================================================
type Tab1
type, extends(Function1D) :: Constant1D
real(8) :: y
contains
procedure :: evaluate => constant1d_evaluate
end type Constant1D
!===============================================================================
! POLYNOMIAL represents a one-dimensional function expressed as a polynomial
!===============================================================================
type, extends(Function1D) :: Polynomial
real(8), allocatable :: coef(:) ! coefficients
contains
procedure :: evaluate => polynomial_evaluate
procedure :: from_ace => polynomial_from_ace
end type Polynomial
!===============================================================================
! TABULATED1D represents a one-dimensional interpolable function
!===============================================================================
type, extends(Function1D) :: Tabulated1D
integer :: n_regions = 0 ! # of interpolation regions
integer, allocatable :: nbt(:) ! values separating interpolation regions
integer, allocatable :: int(:) ! interpolation scheme
@ -14,18 +53,78 @@ module endf_header
real(8), allocatable :: x(:) ! values of abscissa
real(8), allocatable :: y(:) ! values of ordinate
contains
procedure :: from_ace
end type Tab1
procedure :: from_ace => tabulated1d_from_ace
procedure :: evaluate => tabulated1d_evaluate
end type Tabulated1D
contains
subroutine from_ace(this, xss, idx)
class(Tab1), intent(inout) :: this
!===============================================================================
! Constant1D implementation
!===============================================================================
pure function constant1d_evaluate(this, x) result(y)
class(Constant1D), intent(in) :: this
real(8), intent(in) :: x
real(8) :: y
y = this % y
end function constant1d_evaluate
!===============================================================================
! Polynomial implementation
!===============================================================================
subroutine polynomial_from_ace(this, xss, idx)
class(Polynomial), intent(inout) :: this
real(8), intent(in) :: xss(:)
integer, intent(in) :: idx
integer :: nc ! number of coefficients (order - 1)
! Clear space
if (allocated(this % coef)) deallocate(this % coef)
! Determine number of coefficients
nc = nint(xss(idx))
! Allocate space for and read coefficients
allocate(this % coef(nc))
this % coef(:) = xss(idx + 1 : idx + nc)
end subroutine polynomial_from_ace
pure function polynomial_evaluate(this, x) result(y)
class(Polynomial), intent(in) :: this
real(8), intent(in) :: x
real(8) :: y
integer :: i
! Use Horner's rule to evaluate polynomial. Note that coefficients are
! ordered in increasing powers of x.
y = ZERO
do i = size(this % coef), 1, -1
y = y*x + this % coef(i)
end do
end function polynomial_evaluate
!===============================================================================
! Tabulated1D implementation
!===============================================================================
subroutine tabulated1d_from_ace(this, xss, idx)
class(Tabulated1D), intent(inout) :: this
real(8), intent(in) :: xss(:)
integer, intent(in) :: idx
integer :: nr, ne
! Clear space
if (allocated(this % nbt)) deallocate(this % nbt)
if (allocated(this % int)) deallocate(this % int)
if (allocated(this % x)) deallocate(this % x)
if (allocated(this % y)) deallocate(this % y)
! Determine number of regions
nr = nint(xss(idx))
this%n_regions = nr
@ -47,6 +146,81 @@ contains
allocate(this%y(ne))
this%x(:) = xss(idx + 2*nr + 2 : idx + 2*nr + 1 + ne)
this%y(:) = xss(idx + 2*nr + 2 + ne : idx + 2*nr + 1 + 2*ne)
end subroutine from_ace
end subroutine tabulated1d_from_ace
pure function tabulated1d_evaluate(this, x) result(y)
class(Tabulated1D), intent(in) :: this
real(8), intent(in) :: x ! x value to find y at
real(8) :: y ! y(x)
integer :: i ! bin in which to interpolate
integer :: j ! index for interpolation region
integer :: n_regions ! number of interpolation regions
integer :: n_pairs ! number of tabulated values
integer :: interp ! ENDF interpolation scheme
real(8) :: r ! interpolation factor
real(8) :: x0, x1 ! bounding x values
real(8) :: y0, y1 ! bounding y values
! determine number of interpolation regions and pairs
n_regions = this % n_regions
n_pairs = this % n_pairs
! find which bin the abscissa is in -- if the abscissa is outside the
! tabulated range, the first or last point is chosen, i.e. no interpolation
! is done outside the energy range
if (x < this % x(1)) then
y = this % y(1)
return
elseif (x > this % x(n_pairs)) then
y = this % y(n_pairs)
return
else
i = binary_search(this % x, n_pairs, x)
end if
! determine interpolation scheme
if (n_regions == 0) then
interp = LINEAR_LINEAR
elseif (n_regions == 1) then
interp = this % int(1)
elseif (n_regions > 1) then
do j = 1, n_regions
if (i < this % nbt(j)) then
interp = this % int(j)
exit
end if
end do
end if
! handle special case of histogram interpolation
if (interp == HISTOGRAM) then
y = this % y(i)
return
end if
! determine bounding values
x0 = this % x(i)
x1 = this % x(i + 1)
y0 = this % y(i)
y1 = this % y(i + 1)
! determine interpolation factor and interpolated value
select case (interp)
case (LINEAR_LINEAR)
r = (x - x0)/(x1 - x0)
y = y0 + r*(y1 - y0)
case (LINEAR_LOG)
r = log(x/x0)/log(x1/x0)
y = y0 + r*(y1 - y0)
case (LOG_LINEAR)
r = (x - x0)/(x1 - x0)
y = y0*exp(r*log(y1/y0))
case (LOG_LOG)
r = log(x/x0)/log(x1/x0)
y = y0*exp(r*log(y1/y0))
end select
end function tabulated1d_evaluate
end module endf_header

View file

@ -1,8 +1,7 @@
module energy_distribution
use constants, only: ZERO, ONE, TWO, PI, HISTOGRAM, LINEAR_LINEAR
use endf_header, only: Tab1
use interpolation, only: interpolate_tab1
use endf_header, only: Tabulated1D
use math, only: maxwell_spectrum, watt_spectrum
use random_lcg, only: prn
use search, only: binary_search
@ -83,8 +82,8 @@ module energy_distribution
integer :: n_region
integer, allocatable :: breakpoints(:)
integer, allocatable :: interpolation(:)
real(8), allocatable :: energy_in(:)
type(CTTable), allocatable :: energy_out(:)
real(8), allocatable :: energy(:)
type(CTTable), allocatable :: distribution(:)
contains
procedure :: sample => continuous_sample
end type ContinuousTabular
@ -95,7 +94,7 @@ module energy_distribution
!===============================================================================
type, extends(EnergyDistribution) :: MaxwellEnergy
type(Tab1) :: theta ! incoming-energy-dependent parameter
type(Tabulated1D) :: theta ! incoming-energy-dependent parameter
real(8) :: u ! restriction energy
contains
procedure :: sample => maxwellenergy_sample
@ -107,7 +106,7 @@ module energy_distribution
!===============================================================================
type, extends(EnergyDistribution) :: Evaporation
type(Tab1) :: theta
type(Tabulated1D) :: theta
real(8) :: u
contains
procedure :: sample => evaporation_sample
@ -119,28 +118,13 @@ module energy_distribution
!===============================================================================
type, extends(EnergyDistribution) :: WattEnergy
type(Tab1) :: a
type(Tab1) :: b
type(Tabulated1D) :: a
type(Tabulated1D) :: b
real(8) :: u
contains
procedure :: sample => watt_sample
end type WattEnergy
!===============================================================================
! NBODYPHASESPACE gives the energy distribution for particles emitted from
! neutron and charged-particle reactions. This corresponds to ACE law 66 and
! ENDF File 6, LAW=6.
!===============================================================================
type, extends(EnergyDistribution) :: NBodyPhaseSpace
integer :: n_bodies
real(8) :: mass_ratio
real(8) :: A
real(8) :: Q
contains
procedure :: sample => nbody_sample
end type NBodyPhaseSpace
contains
function equiprobable_sample(this, E_in) result(E_out)
@ -202,6 +186,7 @@ contains
end if
end function equiprobable_sample
function level_inelastic_sample(this, E_in) result(E_out)
class(LevelInelastic), intent(in) :: this
real(8), intent(in) :: E_in
@ -210,6 +195,7 @@ contains
E_out = this%mass_ratio*(E_in - this%threshold)
end function level_inelastic_sample
function continuous_sample(this, E_in) result(E_out)
class(ContinuousTabular), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
@ -238,17 +224,17 @@ contains
! Find energy bin and calculate interpolation factor -- if the energy is
! outside the range of the tabulated energies, choose the first or last bins
n_energy_in = size(this%energy_in)
if (E_in < this%energy_in(1)) then
n_energy_in = size(this%energy)
if (E_in < this%energy(1)) then
i = 1
r = ZERO
elseif (E_in > this%energy_in(n_energy_in)) then
elseif (E_in > this%energy(n_energy_in)) then
i = n_energy_in - 1
r = ONE
else
i = binary_search(this%energy_in, n_energy_in, E_in)
r = (E_in - this%energy_in(i)) / &
(this%energy_in(i+1) - this%energy_in(i))
i = binary_search(this%energy, n_energy_in, E_in)
r = (E_in - this%energy(i)) / &
(this%energy(i+1) - this%energy(i))
end if
! Sample between the ith and (i+1)th bin
@ -263,23 +249,23 @@ contains
end if
! Interpolation for energy E1 and EK
n_energy_out = size(this%energy_out(i)%e_out)
E_i_1 = this%energy_out(i)%e_out(1)
E_i_K = this%energy_out(i)%e_out(n_energy_out)
n_energy_out = size(this%distribution(i)%e_out)
E_i_1 = this%distribution(i)%e_out(1)
E_i_K = this%distribution(i)%e_out(n_energy_out)
n_energy_out = size(this%energy_out(i+1)%e_out)
E_i1_1 = this%energy_out(i+1)%e_out(1)
E_i1_K = this%energy_out(i+1)%e_out(n_energy_out)
n_energy_out = size(this%distribution(i+1)%e_out)
E_i1_1 = this%distribution(i+1)%e_out(1)
E_i1_K = this%distribution(i+1)%e_out(n_energy_out)
E_1 = E_i_1 + r*(E_i1_1 - E_i_1)
E_K = E_i_K + r*(E_i1_K - E_i_K)
! Determine outgoing energy bin
n_energy_out = size(this%energy_out(l)%e_out)
n_energy_out = size(this%distribution(l)%e_out)
r1 = prn()
c_k = this%energy_out(l)%c(1)
c_k = this%distribution(l)%c(1)
do k = 1, n_energy_out - 1
c_k1 = this%energy_out(l)%c(k+1)
c_k1 = this%distribution(l)%c(k+1)
if (r1 < c_k1) exit
c_k = c_k1
end do
@ -287,9 +273,9 @@ contains
! Check to make sure k is <= NP - 1
k = min(k, n_energy_out - 1)
E_l_k = this%energy_out(l)%e_out(k)
p_l_k = this%energy_out(l)%p(k)
if (this%energy_out(l)%interpolation == HISTOGRAM) then
E_l_k = this%distribution(l)%e_out(k)
p_l_k = this%distribution(l)%p(k)
if (this%distribution(l)%interpolation == HISTOGRAM) then
! Histogram interpolation
if (p_l_k > ZERO) then
E_out = E_l_k + (r1 - c_k)/p_l_k
@ -297,10 +283,10 @@ contains
E_out = E_l_k
end if
elseif (this%energy_out(l)%interpolation == LINEAR_LINEAR) then
elseif (this%distribution(l)%interpolation == LINEAR_LINEAR) then
! Linear-linear interpolation
E_l_k1 = this%energy_out(l)%e_out(k+1)
p_l_k1 = this%energy_out(l)%p(k+1)
E_l_k1 = this%distribution(l)%e_out(k+1)
p_l_k1 = this%distribution(l)%p(k+1)
frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k)
if (frac == ZERO) then
@ -321,6 +307,7 @@ contains
end if
end function continuous_sample
function maxwellenergy_sample(this, E_in) result(E_out)
class(MaxwellEnergy), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
@ -329,7 +316,7 @@ contains
real(8) :: theta ! Maxwell distribution parameter
! Get temperature corresponding to incoming energy
theta = interpolate_tab1(this%theta, E_in)
theta = this % theta % evaluate(E_in)
do
! Sample maxwell fission spectrum
@ -349,9 +336,9 @@ contains
real(8) :: x, y, v
! Get temperature corresponding to incoming energy
theta = interpolate_tab1(this%theta, E_in)
theta = this % theta % evaluate(E_in)
y = (E_in - this%U)/theta
y = (E_in - this%u)/theta
v = 1 - exp(-y)
! Sample outgoing energy based on evaporation spectrum probability
@ -372,10 +359,10 @@ contains
real(8) :: a, b ! Watt spectrum parameters
! Determine Watt parameter 'a' from tabulated function
a = interpolate_tab1(this%a, E_in)
a = this % a % evaluate(E_in)
! Determine Watt parameter 'b' from tabulated function
b = interpolate_tab1(this%b, E_in)
b = this % b % evaluate(E_in)
do
! Sample energy-dependent Watt fission spectrum
@ -386,44 +373,4 @@ contains
end do
end function watt_sample
function nbody_sample(this, E_in) result(E_out)
class(NBodyPhaseSpace), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
real(8) :: E_out ! sampled outgoing energy
real(8) :: Ap ! total mass of particles in neutron masses
real(8) :: E_max ! maximum possible COM energy
real(8) :: x, y, v
real(8) :: r1, r2, r3, r4, r5, r6
! Determine E_max parameter
Ap = this%mass_ratio
E_max = (Ap - ONE)/Ap * (this%A/(this%A + ONE)*E_in + this%Q)
! x is essentially a Maxwellian distribution
x = maxwell_spectrum(ONE)
select case (this%n_bodies)
case (3)
y = maxwell_spectrum(ONE)
case (4)
r1 = prn()
r2 = prn()
r3 = prn()
y = -log(r1*r2*r3)
case (5)
r1 = prn()
r2 = prn()
r3 = prn()
r4 = prn()
r5 = prn()
r6 = prn()
y = -log(r1*r2*r3*r4) - log(r5) * cos(PI/TWO*r6)**2
end select
! Now determine v and E_out
v = x/(x+y)
E_out = E_max * v
end function nbody_sample
end module energy_distribution

View file

@ -27,7 +27,7 @@ contains
integer :: i ! index in nuclides array
integer :: j ! index in materials array
type(ListReal) :: list
type(NuclideCE), pointer :: nuc
type(Nuclide), pointer :: nuc
type(Material), pointer :: mat
call write_message("Creating unionized energy grid...", 5)
@ -70,7 +70,7 @@ contains
real(8) :: E_max ! Maximum energy in MeV
real(8) :: E_min ! Minimum energy in MeV
real(8), allocatable :: umesh(:) ! Equally log-spaced energy grid
type(NuclideCE), pointer :: nuc
type(Nuclide), pointer :: nuc
! Set minimum/maximum energies
E_max = energy_max_neutron
@ -179,7 +179,7 @@ contains
integer :: index_e ! index on union energy grid
real(8) :: union_energy ! energy on union grid
real(8) :: energy ! energy on nuclide grid
type(NuclideCE), pointer :: nuc
type(Nuclide), pointer :: nuc
type(Material), pointer :: mat
do k = 1, n_materials

View file

@ -1,161 +0,0 @@
module fission
use nuclide_header, only: NuclideCE
use constants
use error, only: fatal_error
use interpolation, only: interpolate_tab1
use search, only: binary_search
implicit none
contains
!===============================================================================
! NU_TOTAL calculates the total number of neutrons emitted per fission for a
! given nuclide and incoming neutron energy
!===============================================================================
pure function nu_total(nuc, E) result(nu)
type(NuclideCE), intent(in) :: nuc ! nuclide from which to find nu
real(8), intent(in) :: E ! energy of incoming neutron
real(8) :: nu ! number of total neutrons emitted per fission
integer :: i ! loop index
integer :: NC ! number of polynomial coefficients
real(8) :: c ! polynomial coefficient
if (nuc % nu_t_type == NU_NONE) then
nu = ERROR_REAL
elseif (nuc % nu_t_type == NU_POLYNOMIAL) then
! determine number of coefficients
NC = int(nuc % nu_t_data(1))
! sum up polynomial in energy
nu = ZERO
do i = 0, NC - 1
c = nuc % nu_t_data(i+2)
nu = nu + c * E**i
end do
elseif (nuc % nu_t_type == NU_TABULAR) then
! use ENDF interpolation laws to determine nu
nu = interpolate_tab1(nuc % nu_t_data, E)
end if
end function nu_total
!===============================================================================
! NU_PROMPT calculates the total number of prompt neutrons emitted per fission
! for a given nuclide and incoming neutron energy
!===============================================================================
pure function nu_prompt(nuc, E) result(nu)
type(NuclideCE), intent(in) :: nuc ! nuclide from which to find nu
real(8), intent(in) :: E ! energy of incoming neutron
real(8) :: nu ! number of prompt neutrons emitted per fission
integer :: i ! loop index
integer :: NC ! number of polynomial coefficients
real(8) :: c ! polynomial coefficient
if (nuc % nu_p_type == NU_NONE) then
! since no prompt or delayed data is present, this means all neutron
! emission is prompt -- WARNING: This currently returns zero. The calling
! routine needs to know this situation is occurring since we don't want
! to call nu_total unnecessarily if it has already been called.
nu = ZERO
elseif (nuc % nu_p_type == NU_POLYNOMIAL) then
! determine number of coefficients
NC = int(nuc % nu_p_data(1))
! sum up polynomial in energy
nu = ZERO
do i = 0, NC - 1
c = nuc % nu_p_data(i+2)
nu = nu + c * E**i
end do
elseif (nuc % nu_p_type == NU_TABULAR) then
! use ENDF interpolation laws to determine nu
nu = interpolate_tab1(nuc % nu_p_data, E)
end if
end function nu_prompt
!===============================================================================
! NU_DELAYED calculates the total number of delayed neutrons emitted per fission
! for a given nuclide and incoming neutron energy
!===============================================================================
pure function nu_delayed(nuc, E) result(nu)
type(NuclideCE), intent(in) :: nuc ! nuclide from which to find nu
real(8), intent(in) :: E ! energy of incoming neutron
real(8) :: nu ! number of delayed neutrons emitted per fission
if (nuc % nu_d_type == NU_NONE) then
! since no prompt or delayed data is present, this means all neutron
! emission is prompt -- WARNING: This currently returns zero. The calling
! routine needs to know this situation is occurring since we don't want
! to call nu_delayed unnecessarily if it has already been called.
nu = ZERO
elseif (nuc % nu_d_type == NU_TABULAR) then
! use ENDF interpolation laws to determine nu
nu = interpolate_tab1(nuc % nu_d_data, E)
end if
end function nu_delayed
!===============================================================================
! YIELD_DELAYED calculates the fractional yield of delayed neutrons emitted for
! a given nuclide and incoming neutron energy in a given delayed group.
!===============================================================================
pure function yield_delayed(nuc, E, g) result(yield)
type(NuclideCE), intent(in) :: nuc ! nuclide from which to find nu
real(8), intent(in) :: E ! energy of incoming neutron
real(8) :: yield ! delayed neutron precursor yield
integer, intent(in) :: g ! the delayed neutron precursor group
integer :: d ! precursor group
integer :: lc ! index before start of energies/nu values
integer :: NR ! number of interpolation regions
integer :: NE ! number of energies tabulated
yield = ZERO
if (g > nuc % n_precursor .or. g < 1) then
! if the precursor group is outside the range of precursor groups for
! the input nuclide, return ZERO.
yield = ZERO
else if (nuc % nu_d_type == NU_NONE) then
! since no prompt or delayed data is present, this means all neutron
! emission is prompt -- WARNING: This currently returns zero. The calling
! routine needs to know this situation is occurring since we don't want
! to call yield_delayed unnecessarily if it has already been called.
yield = ZERO
else if (nuc % nu_d_type == NU_TABULAR) then
lc = 1
! loop over delayed groups and determine the yield for the desired group
do d = 1, nuc % n_precursor
! determine number of interpolation regions and energies
NR = int(nuc % nu_d_precursor_data(lc + 1))
NE = int(nuc % nu_d_precursor_data(lc + 2 + 2*NR))
! check if this is the desired group
if (d == g) then
! determine delayed neutron precursor yield for group g
yield = interpolate_tab1(nuc % nu_d_precursor_data( &
lc+1:lc+2+2*NR+2*NE), E)
exit
end if
! advance pointer
lc = lc + 2 + 2*NR + 2*NE + 1
end do
end if
end function yield_delayed
end module fission

View file

@ -378,6 +378,7 @@ contains
real(8) :: v ! y-component of direction
real(8) :: w ! z-component of direction
real(8) :: norm ! "norm" of surface normal
real(8) :: xyz(3) ! Saved global coordinate
integer :: i_surface ! index in surfaces
logical :: found ! particle found in universe?
class(Surface), pointer :: surf
@ -432,12 +433,13 @@ contains
! Score surface currents since reflection causes the direction of the
! particle to change -- artificially move the particle slightly back in
! case the surface crossing in coincident with a mesh boundary
! case the surface crossing is coincident with a mesh boundary
if (active_current_tallies % size() > 0) then
xyz = p % coord(1) % xyz
p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw
call score_surface_current(p)
p % coord(1) % xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
p % coord(1) % xyz = xyz
end if
! Reflect particle off surface
@ -475,6 +477,70 @@ contains
&// trim(to_str(surf%id)))
end if
return
elseif (surf % bc == BC_PERIODIC .and. run_mode /= MODE_PLOTTING) then
! =======================================================================
! PERIODIC BOUNDARY
! Do not handle periodic boundary conditions on lower universes
if (p % n_coord /= 1) then
call handle_lost_particle(p, "Cannot transfer particle " &
// trim(to_str(p % id)) // " across surface in a lower universe.&
& Boundary conditions must be applied to universe 0.")
return
end if
! Score surface currents since reflection causes the direction of the
! particle to change -- artificially move the particle slightly back in
! case the surface crossing is coincident with a mesh boundary
if (active_current_tallies % size() > 0) then
xyz = p % coord(1) % xyz
p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw
call score_surface_current(p)
p % coord(1) % xyz = xyz
end if
select type (surf)
type is (SurfaceXPlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfaceXPlane)
p % coord(1) % xyz(1) = opposite % x0
end select
type is (SurfaceYPlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfaceYPlane)
p % coord(1) % xyz(2) = opposite % y0
end select
type is (SurfaceZPlane)
select type (opposite => surfaces(surf % i_periodic) % obj)
type is (SurfaceZPlane)
p % coord(1) % xyz(3) = opposite % z0
end select
end select
! Reassign particle's surface
p % surface = sign(surf % i_periodic, p % surface)
! Figure out what cell particle is in now
p % n_coord = 1
call find_cell(p, found)
if (.not. found) then
call handle_lost_particle(p, "Couldn't find particle after hitting &
&periodic boundary on surface " // trim(to_str(surf%id)) // ".")
return
end if
! Set previous coordinate going slightly past surface crossing
p % last_xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
! Diagnostic message
if (verbosity >= 10 .or. trace) then
call write_message(" Hit periodic boundary on surface " &
// trim(to_str(surf%id)))
end if
return
end if
! ==========================================================================

View file

@ -1,6 +1,6 @@
module geometry_header
use constants, only: HALF, TWO, THREE
use constants, only: HALF, TWO, THREE, INFINITY
implicit none
@ -201,20 +201,22 @@ contains
real(8), intent(in) :: global_xyz(3)
integer :: i_xyz(3)
real(8) :: xyz(3) ! global_xyz alias
real(8) :: xyz(3) ! global xyz relative to the center
real(8) :: alpha ! Skewed coord axis
real(8) :: xyz_t(3) ! Local xyz
real(8) :: dists(4) ! Squared distances from cell centers
real(8) :: d, d_min ! Squared distance from cell centers
integer :: i, j, k ! Iterators
integer :: loc(1) ! Minimum distance index
integer :: k_min ! Minimum distance index
xyz = global_xyz
xyz(1) = global_xyz(1) - this % center(1)
xyz(2) = global_xyz(2) - this % center(2)
! Index z direction.
if (this % is_3d) then
i_xyz(3) = ceiling((xyz(3) - this % center(3))/this % pitch(2) + HALF)&
+ this % n_axial/2
xyz(3) = global_xyz(3) - this % center(3)
i_xyz(3) = ceiling(xyz(3)/this % pitch(2) + HALF*this % n_axial)
else
xyz(3) = global_xyz(3)
i_xyz(3) = 1
end if
@ -233,28 +235,33 @@ contains
! the four possible cells. Regular hexagonal tiles form a centroidal
! Voronoi tessellation so the global xyz should be in the hexagonal cell
! that it is closest to the center of. This method is used over a
! method that uses the remainders of the floor divisions above becasue it
! method that uses the remainders of the floor divisions above because it
! provides better finite precision performance. Squared distances are
! used becasue they are more computationally efficient than normal
! distances.
k = 1
do i=0,1
do j=0,1
xyz_t = this % get_local_xyz(xyz, i_xyz + (/j, i, 0/))
dists(k) = xyz_t(1)**2 + xyz_t(2)**2
d_min = INFINITY
do i = 0, 1
do j = 0, 1
xyz_t = this % get_local_xyz(global_xyz, i_xyz + [j, i, 0])
d = xyz_t(1)**2 + xyz_t(2)**2
if (d < d_min) then
d_min = d
k_min = k
end if
k = k + 1
end do
end do
! Select the minimum squared distance which corresponds to the cell the
! coordinates are in.
loc = minloc(dists)
if (loc(1) == 2) then
i_xyz = i_xyz + (/1, 0, 0/)
else if (loc(1) == 3) then
i_xyz = i_xyz + (/0, 1, 0/)
else if (loc(1) == 4) then
i_xyz = i_xyz + (/1, 1, 0/)
if (k_min == 2) then
i_xyz(1) = i_xyz(1) + 1
else if (k_min == 3) then
i_xyz(2) = i_xyz(2) + 1
else if (k_min == 4) then
i_xyz(1) = i_xyz(1) + 1
i_xyz(2) = i_xyz(2) + 1
end if
end function get_inds_hex
@ -303,7 +310,7 @@ contains
(i_xyz(1) - this % n_rings) * this % pitch(1) / TWO)
if (this % is_3d) then
local_xyz(3) = xyz(3) - this % center(3) &
+ (this % n_axial/2 - i_xyz(3) + 1) * this % pitch(2)
+ (HALF*this % n_axial - i_xyz(3) + HALF) * this % pitch(2)
else
local_xyz(3) = xyz(3)
end if

View file

@ -5,9 +5,9 @@ module global
use constants
use dict_header, only: DictCharInt, DictIntInt
use geometry_header, only: Cell, Universe, Lattice, LatticeContainer
use macroxs_header, only: MacroXSContainer
use material_header, only: Material
use mesh_header, only: RegularMesh
use mgxs_header, only: Mgxs, MgxsContainer
use nuclide_header
use plot_header, only: ObjectPlot
use sab_header, only: SAlphaBeta
@ -86,7 +86,7 @@ module global
! CONTINUOUS-ENERGY CROSS SECTION RELATED VARIABLES
! Cross section arrays
type(NuclideCE), allocatable, target :: nuclides(:) ! Nuclide cross-sections
type(Nuclide), allocatable, target :: nuclides(:) ! Nuclide cross-sections
type(SAlphaBeta), allocatable, target :: sab_tables(:) ! S(a,b) tables
integer :: n_sab_tables ! Number of S(a,b) thermal scattering tables
@ -104,14 +104,18 @@ module global
! What to assume for expanding natural elements
integer :: default_expand = ENDF_BVII1
! Total amount of nuclide ZAID and dictionary of nuclide ZAID and index
integer(8) :: n_nuc_zaid_total
type(DictIntInt) :: nuc_zaid_dict
! ============================================================================
! MULTI-GROUP CROSS SECTION RELATED VARIABLES
! Cross section arrays
type(NuclideMGContainer), allocatable, target :: nuclides_MG(:)
type(MgxsContainer), allocatable, target :: nuclides_MG(:)
! Cross section caches
type(MacroXSContainer), target, allocatable :: macro_xs(:)
type(MgxsContainer), target, allocatable :: macro_xs(:)
! Number of energy groups
integer :: energy_groups

File diff suppressed because it is too large Load diff

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