mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-25 20:45:35 -04:00
Merge branch 'multipole' into diff_tally5
This commit is contained in:
commit
68e6cf1d67
256 changed files with 36876 additions and 15417 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -26,6 +26,7 @@ examples/python/**/*.xml
|
|||
docs/build
|
||||
docs/source/_images/*.pdf
|
||||
docs/source/_images/*.aux
|
||||
docs/source/pythonapi/generated/
|
||||
|
||||
# Source build
|
||||
build
|
||||
|
|
|
|||
10
.travis.yml
10
.travis.yml
|
|
@ -27,7 +27,7 @@ before_install:
|
|||
- conda config --set always_yes yes --set changeps1 no
|
||||
- conda update -q conda
|
||||
- conda info -a
|
||||
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy h5py pandas
|
||||
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy h5py=2.5 pandas
|
||||
- source activate test-environment
|
||||
|
||||
# Install GCC, MPICH, HDF5, PHDF5
|
||||
|
|
@ -44,10 +44,10 @@ before_script:
|
|||
- git clone --branch=master git://github.com/bhermanmit/nndc_xs nndc_xs
|
||||
- cat nndc_xs/nndc.tar.gza* | tar xzvf -
|
||||
- rm -rf nndc_xs
|
||||
- export CROSS_SECTIONS=$PWD/nndc/cross_sections.xml
|
||||
- wget http://web.mit.edu/smharper/Public/multipole_lib.tar.gz
|
||||
- tar -xzf multipole_lib.tar.gz
|
||||
- export MULTIPOLE_LIBRARY=$PWD/multipole_lib
|
||||
- export OPENMC_CROSS_SECTIONS=$PWD/nndc/cross_sections.xml
|
||||
- git clone --branch=master git://github.com/smharper/windowed_multipole_library.git wmp_lib
|
||||
- tar xzvf wmp_lib/multipole_lib.tar.gz
|
||||
- export OPENMC_MULTIPOLE_LIBRARY=$PWD/multipole_lib
|
||||
- cd ..
|
||||
|
||||
script:
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ except ImportError:
|
|||
cwd = os.getcwd()
|
||||
sys.path.insert(0, os.path.join(cwd, '..'))
|
||||
|
||||
baseUrl = 'http://web.mit.edu/smharper/Public/'
|
||||
files = ['multipole_lib.tar.gz']
|
||||
baseUrl = 'https://github.com/smharper/windowed_multipole_library/blob/master/'
|
||||
files = ['multipole_lib.tar.gz?raw=true']
|
||||
checksums = ['9f0307132fe5beca78b8fc7a01fb401c']
|
||||
block_size = 16384
|
||||
|
||||
# ==============================================================================
|
||||
# DOWNLOAD FILES FROM ATHENA LOCKER
|
||||
# DOWNLOAD FILES FROM GITHUB REPO
|
||||
|
||||
filesComplete = []
|
||||
for f in files:
|
||||
|
|
@ -44,23 +44,26 @@ for f in files:
|
|||
file_size = req.length
|
||||
downloaded = 0
|
||||
|
||||
# Remove GitHub junk from the file name.
|
||||
fname = f[:-9] if f.endswith('?raw=true') else f
|
||||
|
||||
# Check if file already downloaded
|
||||
if os.path.exists(f):
|
||||
if os.path.getsize(f) == file_size:
|
||||
print('Skipping ' + f)
|
||||
filesComplete.append(f)
|
||||
if os.path.exists(fname):
|
||||
if os.path.getsize(fname) == file_size:
|
||||
print('Skipping ' + fname)
|
||||
filesComplete.append(fname)
|
||||
continue
|
||||
else:
|
||||
if sys.version_info[0] < 3:
|
||||
overwrite = raw_input('Overwrite {0}? ([y]/n) '.format(f))
|
||||
overwrite = raw_input('Overwrite {0}? ([y]/n) '.format(fname))
|
||||
else:
|
||||
overwrite = input('Overwrite {0}? ([y]/n) '.format(f))
|
||||
overwrite = input('Overwrite {0}? ([y]/n) '.format(fname))
|
||||
if overwrite.lower().startswith('n'):
|
||||
continue
|
||||
|
||||
# Copy file to disk
|
||||
print('Downloading {0}... '.format(f), end='')
|
||||
with open(f, 'wb') as fh:
|
||||
with open(fname, 'wb') as fh:
|
||||
while True:
|
||||
chunk = req.read(block_size)
|
||||
if not chunk: break
|
||||
|
|
@ -69,14 +72,15 @@ for f in files:
|
|||
status = '{0:10} [{1:3.2f}%]'.format(downloaded, downloaded * 100. / file_size)
|
||||
print(status + chr(8)*len(status), end='')
|
||||
print('')
|
||||
filesComplete.append(f)
|
||||
filesComplete.append(fname)
|
||||
|
||||
# ==============================================================================
|
||||
# VERIFY MD5 CHECKSUMS
|
||||
|
||||
print('Verifying MD5 checksums...')
|
||||
for f, checksum in zip(files, checksums):
|
||||
downloadsum = hashlib.md5(open(f, 'rb').read()).hexdigest()
|
||||
fname = f[:-9] if f.endswith('?raw=true') else f
|
||||
downloadsum = hashlib.md5(open(fname, 'rb').read()).hexdigest()
|
||||
if downloadsum != checksum:
|
||||
raise IOError("MD5 checksum for {} does not match. If this is your first "
|
||||
"time receiving this message, please re-run the script. "
|
||||
|
|
@ -87,12 +91,13 @@ for f, checksum in zip(files, checksums):
|
|||
# EXTRACT FILES FROM TGZ
|
||||
|
||||
for f in files:
|
||||
if not f in filesComplete:
|
||||
fname = f[:-9] if f.endswith('?raw=true') else f
|
||||
if not fname in filesComplete:
|
||||
continue
|
||||
|
||||
# Extract files
|
||||
with tarfile.open(f, 'r') as tgz:
|
||||
print('Extracting {0}...'.format(f))
|
||||
with tarfile.open(fname, 'r') as tgz:
|
||||
print('Extracting {0}...'.format(fname))
|
||||
tgz.extractall(path='wmp/')
|
||||
|
||||
# Move data files down one level
|
||||
|
|
|
|||
7
docs/source/_templates/myclass.rst
Normal file
7
docs/source/_templates/myclass.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{{ fullname }}
|
||||
{{ underline }}
|
||||
|
||||
.. currentmodule:: {{ module }}
|
||||
|
||||
.. autoclass:: {{ objname }}
|
||||
:members:
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ The OpenMC Monte Carlo Code
|
|||
|
||||
OpenMC is a Monte Carlo particle transport simulation code focused on neutron
|
||||
criticality calculations. It is capable of simulating 3D models based on
|
||||
constructive solid geometry with second-order surfaces. The particle interaction
|
||||
data is based on ACE format cross sections, also used in the MCNP and Serpent
|
||||
Monte Carlo codes.
|
||||
constructive solid geometry with second-order surfaces. OpenMC supports either
|
||||
continuous-energy or multi-group transport. The continuous-energy
|
||||
particle interaction data is based on ACE format cross sections, also used
|
||||
in the MCNP and Serpent Monte Carlo codes.
|
||||
|
||||
OpenMC was originally developed by members of the `Computational Reactor Physics
|
||||
Group`_ at the `Massachusetts Institute of Technology`_ starting
|
||||
|
|
|
|||
|
|
@ -63,6 +63,79 @@ Other Methods
|
|||
A good survey of other energy grid techniques, including unionized energy grids,
|
||||
can be found in a paper by Leppanen_.
|
||||
|
||||
---------------------------------
|
||||
Windowed Multipole Representation
|
||||
---------------------------------
|
||||
|
||||
In addition to the usual pointwise representation of cross sections, OpenMC
|
||||
offers support for an experimental data format called windowed multipole (WMP).
|
||||
This data format requires less memory than pointwise cross sections, and it
|
||||
allows on-the-fly Doppler broadening to arbitrary temperature.
|
||||
|
||||
The multipole method was introduced by [Hwang]_ and the faster windowed
|
||||
multipole method by [Josey]_. In the multipole format, cross section resonances
|
||||
are represented by poles, :math:`p_j`, and residues, :math:`r_j`, in the complex
|
||||
plane. The 0K cross sections in the resolved resonance region can be computed
|
||||
by summing up a contribution from each pole:
|
||||
|
||||
.. math::
|
||||
\sigma(E, T=0\text{K}) = \frac{1}{E} \sum_j \text{Re} \left[
|
||||
\frac{i r_j}{\sqrt{E} - p_j} \right]
|
||||
|
||||
Assuming free-gas thermal motion, cross sections in the multipole form can be
|
||||
analytically Doppler broadened to give the form:
|
||||
|
||||
.. math::
|
||||
\sigma(E, T) = \frac{1}{2 E \sqrt{\xi}} \sum_j \text{Re} \left[i r_j
|
||||
\sqrt{\pi} W_i(z) - \frac{r_j}{\sqrt{\pi}} C \left(\frac{p_j}{\sqrt{\xi}},
|
||||
\frac{u}{2 \sqrt{\xi}}\right)\right]
|
||||
.. math::
|
||||
W_i(z) = \frac{i}{\pi} \int_{-\infty}^\infty dt \frac{e^{-t^2}}{z - t}
|
||||
.. math::
|
||||
C \left(\frac{p_j}{\sqrt{\xi}},\frac{u}{2 \sqrt{\xi}}\right) =
|
||||
2p_j \int_0^\infty du' \frac{e^{-(u + u')^2/4\xi}}{p_j^2 - u'^2}
|
||||
.. math::
|
||||
z = \frac{\sqrt{E} - p_j}{2 \sqrt{\xi}}
|
||||
.. math::
|
||||
\xi = \frac{k_B T}{4 A}
|
||||
.. math::
|
||||
u = \sqrt{E}
|
||||
|
||||
where :math:`T` is the temperature of the resonant scatterer, :math:`k_B` is the
|
||||
Boltzmann constant, :math:`A` is the mass of the target nucleus. For
|
||||
:math:`E \gg k_b T/A`, the :math:`C` integral is approximately zero, simplifying
|
||||
the cross section to:
|
||||
|
||||
.. math::
|
||||
\sigma(E, T) = \frac{1}{2 E \sqrt{\xi}} \sum_j \text{Re} \left[i r_j
|
||||
\sqrt{\pi} W_i(z)\right]
|
||||
|
||||
The :math:`W_i` integral simplifies down to an analytic form. We define the
|
||||
Faddeeva function, :math:`W` as:
|
||||
|
||||
.. math::
|
||||
W(z) = e^{-z^2} \text{Erfc}(-iz)
|
||||
|
||||
Through this, the integral transforms as follows:
|
||||
|
||||
.. math::
|
||||
\text{Im} (z) > 0 : W_i(z) = W(z)
|
||||
.. math::
|
||||
\text{Im} (z) < 0 : W_i(z) = -W(z^*)^*
|
||||
|
||||
There are freely available algorithms_ to evaluate the Faddeeva function. For
|
||||
many nuclides, the Faddeeva function needs to be evaluated thousands of times to
|
||||
calculate a cross section. To mitigate that computational cost, the WMP method
|
||||
only evaluates poles within a certain energy "window" around the incident
|
||||
neutron energy and accounts for the effect of resonances outside that window
|
||||
with a polynomial fit. This polynomial fit is then broadened exactly. This
|
||||
exact broadening can make up for the removal of the :math:`C` integral, as
|
||||
typically at low energies, only curve fits are used.
|
||||
|
||||
Note that the implementation of WMP in OpenMC currently assumes that inelastic
|
||||
scattering does not occur in the resolved resonance region. This is usually,
|
||||
but not always the case. Future library versions may eliminate this issue.
|
||||
|
||||
.. only:: html
|
||||
|
||||
.. rubric:: References
|
||||
|
|
@ -70,8 +143,17 @@ can be found in a paper by Leppanen_.
|
|||
.. [Brown] Forrest B. Brown, "New Hash-based Energy Lookup Algorithm for Monte
|
||||
Carlo codes," LA-UR-14-24530, Los Alamos National Laboratory (2014).
|
||||
|
||||
.. [Hwang] R. N. Hwang, "A Rigorous Pole Representation of Multilevel Cross
|
||||
Sections and Its Practical Application," *Nucl. Sci. Eng.*, **96**,
|
||||
192-209 (1987).
|
||||
|
||||
.. [Josey] Colin Josey, Pablo Ducru, Benoit Forget, and Kord Smith, "Windowed
|
||||
Multipole for Cross Section Doppler Broadening," *J. Comp. Phys*,
|
||||
**307**, 715-727 (2016). http://dx.doi.org/10.1016/j.jcp.2015.08.013
|
||||
|
||||
.. _MCNP: http://mcnp.lanl.gov
|
||||
.. _Serpent: http://montecarlo.vtt.fi
|
||||
.. _NJOY: http://t2.lanl.gov/codes.shtml
|
||||
.. _ENDF/B data: http://www.nndc.bnl.gov/endf
|
||||
.. _Leppanen: http://dx.doi.org/10.1016/j.anucene.2009.03.019
|
||||
.. _algorithms: http://ab-initio.mit.edu/wiki/index.php/Faddeeva_Package
|
||||
|
|
|
|||
|
|
@ -84,10 +84,10 @@ to fully define the surface.
|
|||
| Plane perpendicular | x-plane | :math:`x - x_0 = 0` | :math:`x_0` |
|
||||
| to :math:`x`-axis | | | |
|
||||
+----------------------+------------+------------------------------+-------------------------+
|
||||
| Plane perpendicular | y-plane | :math:`x - x_0 = 0` | :math:`y_0` |
|
||||
| Plane perpendicular | y-plane | :math:`y - y_0 = 0` | :math:`y_0` |
|
||||
| to :math:`y`-axis | | | |
|
||||
+----------------------+------------+------------------------------+-------------------------+
|
||||
| Plane perpendicular | z-plane | :math:`x - x_0 = 0` | :math:`z_0` |
|
||||
| Plane perpendicular | z-plane | :math:`z - z_0 = 0` | :math:`z_0` |
|
||||
| to :math:`z`-axis | | | |
|
||||
+----------------------+------------+------------------------------+-------------------------+
|
||||
| Arbitrary plane | plane | :math:`Ax + By + Cz = D` | :math:`A\;B\;C\;D` |
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_ace:
|
||||
|
||||
==========
|
||||
ACE Format
|
||||
==========
|
||||
|
||||
.. automodule:: openmc.ace
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_cmfd:
|
||||
|
||||
====
|
||||
CMFD
|
||||
====
|
||||
|
||||
.. automodule:: openmc.cmfd
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_element:
|
||||
|
||||
=======
|
||||
Element
|
||||
=======
|
||||
|
||||
.. automodule:: openmc.element
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_energy_groups:
|
||||
|
||||
=============
|
||||
Energy Groups
|
||||
=============
|
||||
|
||||
.. automodule:: openmc.mgxs.groups
|
||||
:members:
|
||||
|
|
@ -141,15 +141,12 @@
|
|||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%matplotlib inline\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"import openmc\n",
|
||||
"import openmc.mgxs as mgxs\n",
|
||||
"from openmc.source import Source\n",
|
||||
"from openmc.stats import Box\n",
|
||||
"\n",
|
||||
"%matplotlib inline"
|
||||
"import openmc.mgxs as mgxs"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -341,10 +338,12 @@
|
|||
"settings_file.batches = batches\n",
|
||||
"settings_file.inactive = inactive\n",
|
||||
"settings_file.particles = particles\n",
|
||||
"settings_file.output = {'tallies': True, 'summary': True}\n",
|
||||
"settings_file.output = {'tallies': True}\n",
|
||||
"\n",
|
||||
"# Create an initial uniform spatial source distribution over fissionable zones\n",
|
||||
"bounds = [-0.63, -0.63, -0.63, 0.63, 0.63, 0.63]\n",
|
||||
"settings_file.source = Source(space=Box(\n",
|
||||
" bounds[:3], bounds[3:], only_fissionable=True))\n",
|
||||
"uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)\n",
|
||||
"settings_file.source = openmc.source.Source(space=uniform_dist)\n",
|
||||
"\n",
|
||||
"# Export to \"settings.xml\"\n",
|
||||
"settings_file.export_to_xml()"
|
||||
|
|
@ -423,22 +422,24 @@
|
|||
"data": {
|
||||
"text/plain": [
|
||||
"OrderedDict([('flux', Tally\n",
|
||||
" \tID =\t10000\n",
|
||||
" \tName =\t\n",
|
||||
" \tFilters =\t\n",
|
||||
" \t\tcell\t[1]\n",
|
||||
" \t\tenergy\t[ 0.00000000e+00 6.25000000e-07 2.00000000e+01]\n",
|
||||
" \tNuclides =\ttotal \n",
|
||||
" \tScores =\t['flux']\n",
|
||||
" \tEstimator =\ttracklength), ('absorption', Tally\n",
|
||||
" \tID =\t10001\n",
|
||||
" \tName =\t\n",
|
||||
" \tFilters =\t\n",
|
||||
" \t\tcell\t[1]\n",
|
||||
" \t\tenergy\t[ 0.00000000e+00 6.25000000e-07 2.00000000e+01]\n",
|
||||
" \tNuclides =\ttotal \n",
|
||||
" \tScores =\t['absorption']\n",
|
||||
" \tEstimator =\ttracklength)])"
|
||||
"\tID =\t10000\n",
|
||||
"\tName =\t\n",
|
||||
"\tFilters =\t\n",
|
||||
" \t\tcell\t[1]\n",
|
||||
" \t\tenergy\t[ 0.00000000e+00 6.25000000e-07 2.00000000e+01]\n",
|
||||
"\tNuclides =\ttotal \n",
|
||||
"\tScores =\t['flux']\n",
|
||||
"\tEstimator =\ttracklength\n",
|
||||
"), ('absorption', Tally\n",
|
||||
"\tID =\t10001\n",
|
||||
"\tName =\t\n",
|
||||
"\tFilters =\t\n",
|
||||
" \t\tcell\t[1]\n",
|
||||
" \t\tenergy\t[ 0.00000000e+00 6.25000000e-07 2.00000000e+01]\n",
|
||||
"\tNuclides =\ttotal \n",
|
||||
"\tScores =\t['absorption']\n",
|
||||
"\tEstimator =\ttracklength\n",
|
||||
")])"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
|
|
@ -518,8 +519,9 @@
|
|||
" Copyright: 2011-2015 Massachusetts Institute of Technology\n",
|
||||
" License: http://mit-crpg.github.io/openmc/license.html\n",
|
||||
" Version: 0.7.1\n",
|
||||
" Git SHA1: ea9fb637f63f9374c7436456141afa850b84acf9\n",
|
||||
" Date/Time: 2016-01-14 07:16:05\n",
|
||||
" Git SHA1: eeb5091ca3a34cc85df73a3318cae2b6c7097413\n",
|
||||
" Date/Time: 2016-04-13 11:24:09\n",
|
||||
" MPI Processes: 1\n",
|
||||
"\n",
|
||||
" ===========================================================================\n",
|
||||
" ========================> INITIALIZATION <=========================\n",
|
||||
|
|
@ -545,56 +547,56 @@
|
|||
"\n",
|
||||
" Bat./Gen. k Average k \n",
|
||||
" ========= ======== ==================== \n",
|
||||
" 1/1 1.19804 \n",
|
||||
" 2/1 1.12945 \n",
|
||||
" 3/1 1.15573 \n",
|
||||
" 4/1 1.13929 \n",
|
||||
" 5/1 1.16300 \n",
|
||||
" 6/1 1.22117 \n",
|
||||
" 7/1 1.19012 \n",
|
||||
" 8/1 1.11299 \n",
|
||||
" 9/1 1.16066 \n",
|
||||
" 10/1 1.12566 \n",
|
||||
" 11/1 1.20854 \n",
|
||||
" 12/1 1.14691 1.17773 +/- 0.03082\n",
|
||||
" 13/1 1.17204 1.17583 +/- 0.01789\n",
|
||||
" 14/1 1.14148 1.16724 +/- 0.01529\n",
|
||||
" 15/1 1.17272 1.16834 +/- 0.01189\n",
|
||||
" 16/1 1.18575 1.17124 +/- 0.01014\n",
|
||||
" 17/1 1.20498 1.17606 +/- 0.00983\n",
|
||||
" 18/1 1.14754 1.17249 +/- 0.00923\n",
|
||||
" 19/1 1.18141 1.17348 +/- 0.00820\n",
|
||||
" 20/1 1.15074 1.17121 +/- 0.00768\n",
|
||||
" 21/1 1.15914 1.17011 +/- 0.00703\n",
|
||||
" 22/1 1.14586 1.16809 +/- 0.00673\n",
|
||||
" 23/1 1.18999 1.16978 +/- 0.00642\n",
|
||||
" 24/1 1.15101 1.16844 +/- 0.00609\n",
|
||||
" 25/1 1.13791 1.16640 +/- 0.00602\n",
|
||||
" 26/1 1.19791 1.16837 +/- 0.00597\n",
|
||||
" 27/1 1.19818 1.17012 +/- 0.00587\n",
|
||||
" 28/1 1.14160 1.16854 +/- 0.00576\n",
|
||||
" 29/1 1.11487 1.16571 +/- 0.00614\n",
|
||||
" 30/1 1.17538 1.16620 +/- 0.00584\n",
|
||||
" 31/1 1.20210 1.16791 +/- 0.00581\n",
|
||||
" 32/1 1.20078 1.16940 +/- 0.00574\n",
|
||||
" 33/1 1.14624 1.16839 +/- 0.00558\n",
|
||||
" 34/1 1.14618 1.16747 +/- 0.00542\n",
|
||||
" 35/1 1.16866 1.16752 +/- 0.00520\n",
|
||||
" 36/1 1.18565 1.16821 +/- 0.00504\n",
|
||||
" 37/1 1.16824 1.16821 +/- 0.00485\n",
|
||||
" 38/1 1.18299 1.16874 +/- 0.00471\n",
|
||||
" 39/1 1.21418 1.17031 +/- 0.00480\n",
|
||||
" 40/1 1.11167 1.16835 +/- 0.00504\n",
|
||||
" 41/1 1.11545 1.16665 +/- 0.00516\n",
|
||||
" 42/1 1.11114 1.16491 +/- 0.00529\n",
|
||||
" 43/1 1.14227 1.16423 +/- 0.00517\n",
|
||||
" 44/1 1.14104 1.16355 +/- 0.00506\n",
|
||||
" 45/1 1.16756 1.16366 +/- 0.00492\n",
|
||||
" 46/1 1.13065 1.16274 +/- 0.00487\n",
|
||||
" 47/1 1.11251 1.16139 +/- 0.00492\n",
|
||||
" 48/1 1.14731 1.16101 +/- 0.00481\n",
|
||||
" 49/1 1.16691 1.16117 +/- 0.00469\n",
|
||||
" 50/1 1.19679 1.16206 +/- 0.00465\n",
|
||||
" 1/1 1.11184 \n",
|
||||
" 2/1 1.15820 \n",
|
||||
" 3/1 1.18468 \n",
|
||||
" 4/1 1.17492 \n",
|
||||
" 5/1 1.19645 \n",
|
||||
" 6/1 1.18436 \n",
|
||||
" 7/1 1.14070 \n",
|
||||
" 8/1 1.15150 \n",
|
||||
" 9/1 1.19202 \n",
|
||||
" 10/1 1.17677 \n",
|
||||
" 11/1 1.20272 \n",
|
||||
" 12/1 1.21366 1.20819 +/- 0.00547\n",
|
||||
" 13/1 1.15906 1.19181 +/- 0.01668\n",
|
||||
" 14/1 1.14687 1.18058 +/- 0.01629\n",
|
||||
" 15/1 1.14570 1.17360 +/- 0.01442\n",
|
||||
" 16/1 1.13480 1.16713 +/- 0.01343\n",
|
||||
" 17/1 1.17680 1.16852 +/- 0.01144\n",
|
||||
" 18/1 1.16866 1.16853 +/- 0.00990\n",
|
||||
" 19/1 1.19253 1.17120 +/- 0.00913\n",
|
||||
" 20/1 1.18124 1.17220 +/- 0.00823\n",
|
||||
" 21/1 1.19206 1.17401 +/- 0.00766\n",
|
||||
" 22/1 1.17681 1.17424 +/- 0.00700\n",
|
||||
" 23/1 1.17634 1.17440 +/- 0.00644\n",
|
||||
" 24/1 1.13659 1.17170 +/- 0.00654\n",
|
||||
" 25/1 1.17144 1.17169 +/- 0.00609\n",
|
||||
" 26/1 1.20649 1.17386 +/- 0.00610\n",
|
||||
" 27/1 1.11238 1.17024 +/- 0.00678\n",
|
||||
" 28/1 1.18911 1.17129 +/- 0.00647\n",
|
||||
" 29/1 1.14681 1.17000 +/- 0.00626\n",
|
||||
" 30/1 1.12152 1.16758 +/- 0.00641\n",
|
||||
" 31/1 1.12729 1.16566 +/- 0.00639\n",
|
||||
" 32/1 1.15399 1.16513 +/- 0.00612\n",
|
||||
" 33/1 1.13547 1.16384 +/- 0.00599\n",
|
||||
" 34/1 1.17723 1.16440 +/- 0.00576\n",
|
||||
" 35/1 1.09296 1.16154 +/- 0.00622\n",
|
||||
" 36/1 1.19621 1.16287 +/- 0.00612\n",
|
||||
" 37/1 1.12560 1.16149 +/- 0.00605\n",
|
||||
" 38/1 1.17872 1.16211 +/- 0.00586\n",
|
||||
" 39/1 1.17721 1.16263 +/- 0.00568\n",
|
||||
" 40/1 1.13724 1.16178 +/- 0.00555\n",
|
||||
" 41/1 1.18526 1.16254 +/- 0.00542\n",
|
||||
" 42/1 1.13779 1.16177 +/- 0.00531\n",
|
||||
" 43/1 1.15066 1.16143 +/- 0.00516\n",
|
||||
" 44/1 1.12174 1.16026 +/- 0.00514\n",
|
||||
" 45/1 1.17479 1.16068 +/- 0.00501\n",
|
||||
" 46/1 1.14146 1.16014 +/- 0.00489\n",
|
||||
" 47/1 1.20464 1.16135 +/- 0.00491\n",
|
||||
" 48/1 1.15119 1.16108 +/- 0.00479\n",
|
||||
" 49/1 1.17938 1.16155 +/- 0.00468\n",
|
||||
" 50/1 1.15798 1.16146 +/- 0.00457\n",
|
||||
" Creating state point statepoint.50.h5...\n",
|
||||
"\n",
|
||||
" ===========================================================================\n",
|
||||
|
|
@ -604,27 +606,27 @@
|
|||
"\n",
|
||||
" =======================> TIMING STATISTICS <=======================\n",
|
||||
"\n",
|
||||
" Total time for initialization = 1.1720E+00 seconds\n",
|
||||
" Reading cross sections = 9.0300E-01 seconds\n",
|
||||
" Total time in simulation = 1.7319E+01 seconds\n",
|
||||
" Time in transport only = 1.7310E+01 seconds\n",
|
||||
" Time in inactive batches = 1.9120E+00 seconds\n",
|
||||
" Time in active batches = 1.5407E+01 seconds\n",
|
||||
" Time synchronizing fission bank = 2.0000E-03 seconds\n",
|
||||
" Sampling source sites = 2.0000E-03 seconds\n",
|
||||
" SEND/RECV source sites = 0.0000E+00 seconds\n",
|
||||
" Total time for initialization = 4.6300E-01 seconds\n",
|
||||
" Reading cross sections = 1.2100E-01 seconds\n",
|
||||
" Total time in simulation = 1.6504E+01 seconds\n",
|
||||
" Time in transport only = 1.6479E+01 seconds\n",
|
||||
" Time in inactive batches = 1.9620E+00 seconds\n",
|
||||
" Time in active batches = 1.4542E+01 seconds\n",
|
||||
" Time synchronizing fission bank = 1.0000E-02 seconds\n",
|
||||
" Sampling source sites = 4.0000E-03 seconds\n",
|
||||
" SEND/RECV source sites = 3.0000E-03 seconds\n",
|
||||
" Time accumulating tallies = 0.0000E+00 seconds\n",
|
||||
" Total time for finalization = 1.0000E-03 seconds\n",
|
||||
" Total time elapsed = 1.8507E+01 seconds\n",
|
||||
" Calculation Rate (inactive) = 13075.3 neutrons/second\n",
|
||||
" Calculation Rate (active) = 6490.56 neutrons/second\n",
|
||||
" Total time for finalization = 0.0000E+00 seconds\n",
|
||||
" Total time elapsed = 1.6977E+01 seconds\n",
|
||||
" Calculation Rate (inactive) = 12742.1 neutrons/second\n",
|
||||
" Calculation Rate (active) = 6876.63 neutrons/second\n",
|
||||
"\n",
|
||||
" ============================> RESULTS <============================\n",
|
||||
"\n",
|
||||
" k-effective (Collision) = 1.16131 +/- 0.00453\n",
|
||||
" k-effective (Track-length) = 1.16206 +/- 0.00465\n",
|
||||
" k-effective (Absorption) = 1.16096 +/- 0.00364\n",
|
||||
" Combined k-effective = 1.16120 +/- 0.00325\n",
|
||||
" k-effective (Collision) = 1.15984 +/- 0.00411\n",
|
||||
" k-effective (Track-length) = 1.16146 +/- 0.00457\n",
|
||||
" k-effective (Absorption) = 1.16177 +/- 0.00380\n",
|
||||
" Combined k-effective = 1.16105 +/- 0.00364\n",
|
||||
" Leakage Fraction = 0.00000 +/- 0.00000\n",
|
||||
"\n"
|
||||
]
|
||||
|
|
@ -750,8 +752,8 @@
|
|||
"\tDomain Type =\tcell\n",
|
||||
"\tDomain ID =\t1\n",
|
||||
"\tCross Sections [cm^-1]:\n",
|
||||
" Group 1 [6.25e-07 - 20.0 MeV]:\t6.81e-01 +/- 1.88e-01%\n",
|
||||
" Group 2 [0.0 - 6.25e-07 MeV]:\t1.40e+00 +/- 5.91e-01%\n",
|
||||
" Group 1 [6.25e-07 - 20.0 MeV]:\t6.81e-01 +/- 2.69e-01%\n",
|
||||
" Group 2 [0.0 - 6.25e-07 MeV]:\t1.40e+00 +/- 5.93e-01%\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
|
|
@ -779,7 +781,7 @@
|
|||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
|
||||
"<div>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
|
|
@ -797,16 +799,16 @@
|
|||
" <td>1</td>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>0.668323</td>\n",
|
||||
" <td>0.001264</td>\n",
|
||||
" <td>0.667787</td>\n",
|
||||
" <td>0.001802</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>2</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>1.293258</td>\n",
|
||||
" <td>0.007624</td>\n",
|
||||
" <td>1.292013</td>\n",
|
||||
" <td>0.007642</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
|
|
@ -814,8 +816,8 @@
|
|||
],
|
||||
"text/plain": [
|
||||
" cell group in nuclide mean std. dev.\n",
|
||||
"1 1 1 total 0.668323 0.001264\n",
|
||||
"0 1 2 total 1.293258 0.007624"
|
||||
"1 1 1 total 0.667787 0.001802\n",
|
||||
"0 1 2 total 1.292013 0.007642"
|
||||
]
|
||||
},
|
||||
"execution_count": 20,
|
||||
|
|
@ -890,13 +892,14 @@
|
|||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
|
||||
"<div>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -907,33 +910,35 @@
|
|||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>0.000000e+00</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((total / flux) - (absorption / flux)) - (sca...</td>\n",
|
||||
" <td>4.884981e-15</td>\n",
|
||||
" <td>0.011274</td>\n",
|
||||
" <td>-3.774758e-15</td>\n",
|
||||
" <td>0.011292</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>2.000000e+01</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((total / flux) - (absorption / flux)) - (sca...</td>\n",
|
||||
" <td>1.221245e-15</td>\n",
|
||||
" <td>0.001802</td>\n",
|
||||
" <td>1.443290e-15</td>\n",
|
||||
" <td>0.002570</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide \\\n",
|
||||
"0 1 (0.0e+00 - 6.3e-07) total \n",
|
||||
"1 1 (6.3e-07 - 2.0e+01) total \n",
|
||||
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
|
||||
"0 1 0.00e+00 6.25e-07 total \n",
|
||||
"1 1 6.25e-07 2.00e+01 total \n",
|
||||
"\n",
|
||||
" score mean std. dev. \n",
|
||||
"0 (((total / flux) - (absorption / flux)) - (sca... 4.884981e-15 0.011274 \n",
|
||||
"1 (((total / flux) - (absorption / flux)) - (sca... 1.221245e-15 0.001802 "
|
||||
" score mean std. dev. \n",
|
||||
"0 (((total / flux) - (absorption / flux)) - (sca... -3.77e-15 1.13e-02 \n",
|
||||
"1 (((total / flux) - (absorption / flux)) - (sca... 1.44e-15 2.57e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 23,
|
||||
|
|
@ -966,13 +971,14 @@
|
|||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
|
||||
"<div>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -983,33 +989,35 @@
|
|||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>0.000000e+00</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>((absorption / flux) / (total / flux))</td>\n",
|
||||
" <td>0.076219</td>\n",
|
||||
" <td>0.000651</td>\n",
|
||||
" <td>0.076115</td>\n",
|
||||
" <td>0.000649</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>2.000000e+01</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>((absorption / flux) / (total / flux))</td>\n",
|
||||
" <td>0.019319</td>\n",
|
||||
" <td>0.000086</td>\n",
|
||||
" <td>0.019263</td>\n",
|
||||
" <td>0.000095</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide score \\\n",
|
||||
"0 1 (0.0e+00 - 6.3e-07) total ((absorption / flux) / (total / flux)) \n",
|
||||
"1 1 (6.3e-07 - 2.0e+01) total ((absorption / flux) / (total / flux)) \n",
|
||||
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
|
||||
"0 1 0.00e+00 6.25e-07 total \n",
|
||||
"1 1 6.25e-07 2.00e+01 total \n",
|
||||
"\n",
|
||||
" mean std. dev. \n",
|
||||
"0 0.076219 0.000651 \n",
|
||||
"1 0.019319 0.000086 "
|
||||
" score mean std. dev. \n",
|
||||
"0 ((absorption / flux) / (total / flux)) 7.61e-02 6.49e-04 \n",
|
||||
"1 ((absorption / flux) / (total / flux)) 1.93e-02 9.46e-05 "
|
||||
]
|
||||
},
|
||||
"execution_count": 24,
|
||||
|
|
@ -1035,13 +1043,14 @@
|
|||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
|
||||
"<div>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -1052,33 +1061,35 @@
|
|||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>0.000000e+00</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>((scatter / flux) / (total / flux))</td>\n",
|
||||
" <td>0.923781</td>\n",
|
||||
" <td>0.007714</td>\n",
|
||||
" <td>0.923885</td>\n",
|
||||
" <td>0.007736</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>2.000000e+01</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>((scatter / flux) / (total / flux))</td>\n",
|
||||
" <td>0.980681</td>\n",
|
||||
" <td>0.002617</td>\n",
|
||||
" <td>0.980737</td>\n",
|
||||
" <td>0.003737</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide score \\\n",
|
||||
"0 1 (0.0e+00 - 6.3e-07) total ((scatter / flux) / (total / flux)) \n",
|
||||
"1 1 (6.3e-07 - 2.0e+01) total ((scatter / flux) / (total / flux)) \n",
|
||||
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
|
||||
"0 1 0.00e+00 6.25e-07 total \n",
|
||||
"1 1 6.25e-07 2.00e+01 total \n",
|
||||
"\n",
|
||||
" mean std. dev. \n",
|
||||
"0 0.923781 0.007714 \n",
|
||||
"1 0.980681 0.002617 "
|
||||
" score mean std. dev. \n",
|
||||
"0 ((scatter / flux) / (total / flux)) 9.24e-01 7.74e-03 \n",
|
||||
"1 ((scatter / flux) / (total / flux)) 9.81e-01 3.74e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 25,
|
||||
|
|
@ -1111,13 +1122,14 @@
|
|||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div style=\"max-height:1000px;max-width:1500px;overflow:auto;\">\n",
|
||||
"<div>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>cell</th>\n",
|
||||
" <th>energy [MeV]</th>\n",
|
||||
" <th>energy low [MeV]</th>\n",
|
||||
" <th>energy high [MeV]</th>\n",
|
||||
" <th>nuclide</th>\n",
|
||||
" <th>score</th>\n",
|
||||
" <th>mean</th>\n",
|
||||
|
|
@ -1128,33 +1140,35 @@
|
|||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(0.0e+00 - 6.3e-07)</td>\n",
|
||||
" <td>0.000000e+00</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((absorption / flux) / (total / flux)) + ((sc...</td>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>0.007741</td>\n",
|
||||
" <td>0.007763</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>(6.3e-07 - 2.0e+01)</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>2.000000e+01</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((absorption / flux) / (total / flux)) + ((sc...</td>\n",
|
||||
" <td>1</td>\n",
|
||||
" <td>0.002619</td>\n",
|
||||
" <td>0.003739</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" cell energy [MeV] nuclide \\\n",
|
||||
"0 1 (0.0e+00 - 6.3e-07) total \n",
|
||||
"1 1 (6.3e-07 - 2.0e+01) total \n",
|
||||
" cell energy low [MeV] energy high [MeV] nuclide \\\n",
|
||||
"0 1 0.00e+00 6.25e-07 total \n",
|
||||
"1 1 6.25e-07 2.00e+01 total \n",
|
||||
"\n",
|
||||
" score mean std. dev. \n",
|
||||
"0 (((absorption / flux) / (total / flux)) + ((sc... 1 0.007741 \n",
|
||||
"1 (((absorption / flux) / (total / flux)) + ((sc... 1 0.002619 "
|
||||
" score mean std. dev. \n",
|
||||
"0 (((absorption / flux) / (total / flux)) + ((sc... 1.00e+00 7.76e-03 \n",
|
||||
"1 (((absorption / flux) / (total / flux)) + ((sc... 1.00e+00 3.74e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 26,
|
||||
|
|
@ -1187,7 +1201,7 @@
|
|||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython2",
|
||||
"version": "2.7.10"
|
||||
"version": "2.7.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_executor:
|
||||
|
||||
========
|
||||
Executor
|
||||
========
|
||||
|
||||
.. automodule:: openmc.executor
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_filter:
|
||||
|
||||
======
|
||||
Filter
|
||||
======
|
||||
|
||||
.. automodule:: openmc.filter
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_geometry:
|
||||
|
||||
========
|
||||
Geometry
|
||||
========
|
||||
|
||||
.. automodule:: openmc.geometry
|
||||
:members:
|
||||
|
|
@ -13,62 +13,267 @@ online. We recommend going through the modules from Codecademy_ and/or the
|
|||
`Scipy lectures`_. The full API documentation serves to provide more information
|
||||
on a given module or class.
|
||||
|
||||
**Handling nuclear data:**
|
||||
------------------------------------
|
||||
:mod:`openmc` -- Basic Functionality
|
||||
------------------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
Handling nuclear data
|
||||
---------------------
|
||||
|
||||
ace
|
||||
Classes
|
||||
+++++++
|
||||
|
||||
**Creating input files:**
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
openmc.XSdata
|
||||
openmc.MGXSLibraryFile
|
||||
|
||||
cmfd
|
||||
element
|
||||
filter
|
||||
geometry
|
||||
material
|
||||
mesh
|
||||
nuclide
|
||||
opencg_compatible
|
||||
plots
|
||||
settings
|
||||
source
|
||||
stats
|
||||
surface
|
||||
tallies
|
||||
trigger
|
||||
universe
|
||||
Functions
|
||||
+++++++++
|
||||
|
||||
**Running OpenMC:**
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
openmc.ace.ascii_to_binary
|
||||
|
||||
executor
|
||||
Simulation Settings
|
||||
-------------------
|
||||
|
||||
**Post-processing:**
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
openmc.Source
|
||||
openmc.ResonanceScattering
|
||||
openmc.SettingsFile
|
||||
|
||||
particle_restart
|
||||
statepoint
|
||||
summary
|
||||
tallies
|
||||
Material Specification
|
||||
----------------------
|
||||
|
||||
**Multi-Group Cross Section Generation**
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
openmc.Nuclide
|
||||
openmc.Element
|
||||
openmc.Macroscopic
|
||||
openmc.Material
|
||||
openmc.MaterialsFile
|
||||
|
||||
mgxs
|
||||
energy_groups
|
||||
mgxs_library
|
||||
Building geometry
|
||||
-----------------
|
||||
|
||||
**Example Jupyter Notebooks:**
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.Plane
|
||||
openmc.XPlane
|
||||
openmc.YPlane
|
||||
openmc.ZPlane
|
||||
openmc.XCylinder
|
||||
openmc.YCylinder
|
||||
openmc.ZCylinder
|
||||
openmc.Sphere
|
||||
openmc.Cone
|
||||
openmc.XCone
|
||||
openmc.YCone
|
||||
openmc.ZCone
|
||||
openmc.Quadric
|
||||
openmc.Halfspace
|
||||
openmc.Intersection
|
||||
openmc.Union
|
||||
openmc.Complement
|
||||
openmc.Cell
|
||||
openmc.Universe
|
||||
openmc.RectLattice
|
||||
openmc.HexLattice
|
||||
openmc.Geometry
|
||||
openmc.GeometryFile
|
||||
|
||||
Many of the above classes are derived from several abstract classes:
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.Surface
|
||||
openmc.Region
|
||||
openmc.Lattice
|
||||
|
||||
Constructing Tallies
|
||||
--------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.Filter
|
||||
openmc.Mesh
|
||||
openmc.Trigger
|
||||
openmc.Tally
|
||||
openmc.TalliesFile
|
||||
|
||||
Coarse Mesh Finite Difference Acceleration
|
||||
------------------------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.CMFDMesh
|
||||
openmc.CMFDFile
|
||||
|
||||
Plotting
|
||||
--------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.Plot
|
||||
openmc.PlotsFile
|
||||
|
||||
Running OpenMC
|
||||
--------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.Executor
|
||||
|
||||
Post-processing
|
||||
---------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.Particle
|
||||
openmc.StatePoint
|
||||
openmc.Summary
|
||||
|
||||
Various classes may be created when performing tally slicing and/or arithmetic:
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.arithmetic.CrossScore
|
||||
openmc.arithmetic.CrossNuclide
|
||||
openmc.arithmetic.CrossFilter
|
||||
openmc.arithmetic.AggregateScore
|
||||
openmc.arithmetic.AggregateNuclide
|
||||
openmc.arithmetic.AggregateFilter
|
||||
|
||||
---------------------------------
|
||||
:mod:`openmc.stats` -- Statistics
|
||||
---------------------------------
|
||||
|
||||
Univariate Probability Distributions
|
||||
------------------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.stats.Univariate
|
||||
openmc.stats.Discrete
|
||||
openmc.stats.Uniform
|
||||
openmc.stats.Maxwell
|
||||
openmc.stats.Watt
|
||||
openmc.stats.Tabular
|
||||
|
||||
Angular Distributions
|
||||
---------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.stats.UnitSphere
|
||||
openmc.stats.PolarAzimuthal
|
||||
openmc.stats.Isotropic
|
||||
openmc.stats.Monodirectional
|
||||
|
||||
Spatial Distributions
|
||||
---------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.stats.Spatial
|
||||
openmc.stats.CartesianIndependent
|
||||
openmc.stats.Box
|
||||
openmc.stats.Point
|
||||
|
||||
----------------------------------------------------------
|
||||
:mod:`openmc.mgxs` -- Multi-Group Cross Section Generation
|
||||
----------------------------------------------------------
|
||||
|
||||
Energy Groups
|
||||
-------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.mgxs.EnergyGroups
|
||||
|
||||
Multi-group Cross Sections
|
||||
--------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.mgxs.MGXS
|
||||
openmc.mgxs.AbsorptionXS
|
||||
openmc.mgxs.CaptureXS
|
||||
openmc.mgxs.Chi
|
||||
openmc.mgxs.FissionXS
|
||||
openmc.mgxs.NuFissionXS
|
||||
openmc.mgxs.NuScatterXS
|
||||
openmc.mgxs.NuScatterMatrixXS
|
||||
openmc.mgxs.ScatterXS
|
||||
openmc.mgxs.ScatterMatrixXS
|
||||
openmc.mgxs.TotalXS
|
||||
openmc.mgxs.TransportXS
|
||||
|
||||
Multi-group Cross Section Libraries
|
||||
-----------------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.mgxs.Library
|
||||
|
||||
-------------------------
|
||||
Example Jupyter Notebooks
|
||||
-------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_material:
|
||||
|
||||
=========
|
||||
Materials
|
||||
=========
|
||||
|
||||
.. automodule:: openmc.material
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_mesh:
|
||||
|
||||
====
|
||||
Mesh
|
||||
====
|
||||
|
||||
.. automodule:: openmc.mesh
|
||||
:members:
|
||||
|
|
@ -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:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_mgxs_library:
|
||||
|
||||
============
|
||||
MGXS Library
|
||||
============
|
||||
|
||||
.. automodule:: openmc.mgxs.library
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_nuclide:
|
||||
|
||||
=======
|
||||
Nuclide
|
||||
=======
|
||||
|
||||
.. automodule:: openmc.nuclide
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_particle_restart:
|
||||
|
||||
================
|
||||
Particle Restart
|
||||
================
|
||||
|
||||
.. automodule:: openmc.particle_restart
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_plots:
|
||||
|
||||
=====
|
||||
Plots
|
||||
=====
|
||||
|
||||
.. automodule:: openmc.plots
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_settings:
|
||||
|
||||
========
|
||||
Settings
|
||||
========
|
||||
|
||||
.. automodule:: openmc.settings
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_source:
|
||||
|
||||
======
|
||||
Source
|
||||
======
|
||||
|
||||
.. automodule:: openmc.source
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_statepoint:
|
||||
|
||||
==========
|
||||
Statepoint
|
||||
==========
|
||||
|
||||
.. automodule:: openmc.statepoint
|
||||
:members:
|
||||
|
|
@ -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:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_summary:
|
||||
|
||||
=======
|
||||
Summary
|
||||
=======
|
||||
|
||||
.. automodule:: openmc.summary
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_surface:
|
||||
|
||||
=======
|
||||
Surface
|
||||
=======
|
||||
|
||||
.. automodule:: openmc.surface
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_tallies:
|
||||
|
||||
=======
|
||||
Tallies
|
||||
=======
|
||||
|
||||
.. automodule:: openmc.tallies
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_trigger:
|
||||
|
||||
=======
|
||||
Trigger
|
||||
=======
|
||||
|
||||
.. automodule:: openmc.trigger
|
||||
:members:
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
.. _pythonapi_universe:
|
||||
|
||||
========
|
||||
Universe
|
||||
========
|
||||
|
||||
.. automodule:: openmc.universe
|
||||
:members:
|
||||
|
|
@ -12,7 +12,7 @@ In a nutshell, OpenMC simulates neutrons moving around randomly in a `nuclear
|
|||
reactor`_ (or other fissile system). This is what's known as `Monte Carlo`_
|
||||
simulation. Neutrons are important in nuclear reactors because they are the
|
||||
particles that induce `fission`_ in uranium and other nuclides. Knowing the
|
||||
behavior of neutrons allows you to figure out how often and where fission
|
||||
behavior of neutrons allows you to determine how often and where fission
|
||||
occurs. The amount of energy released is then directly proportional to the
|
||||
fission reaction rate since most heat is produced by fission. By simulating many
|
||||
neutrons (millions or billions), it is possible to determine the average
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ essential aspects of using OpenMC to perform simulations.
|
|||
beginners
|
||||
install
|
||||
input
|
||||
mgxs_library
|
||||
output/index
|
||||
processing
|
||||
troubleshoot
|
||||
|
|
|
|||
|
|
@ -112,9 +112,11 @@ standard deviation.
|
|||
|
||||
The ``<cross_sections>`` element has no attributes and simply indicates the path
|
||||
to an XML cross section listing file (usually named cross_sections.xml). If this
|
||||
element is absent from the settings.xml file, the :envvar:`CROSS_SECTIONS`
|
||||
environment variable will be used to find the path to the XML cross section
|
||||
listing.
|
||||
element is absent from the settings.xml file, the
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used to find the
|
||||
path to the XML cross section listing when in continuous-energy mode, and the
|
||||
:envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable will be used in
|
||||
multi-group mode.
|
||||
|
||||
``<cutoff>`` Element
|
||||
--------------------
|
||||
|
|
@ -212,8 +214,21 @@ cross section values between.
|
|||
|
||||
*Default*: logarithm
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
.. _LA-UR-14-24530: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf
|
||||
|
||||
.. _energy_mode:
|
||||
|
||||
``<energy_mode>`` Element
|
||||
-------------------------
|
||||
|
||||
The ``<energy_mode>`` element tells OpenMC if the run-mode should be
|
||||
continuous-energy or multi-group. Options for entry are: ``continuous-energy``
|
||||
or ``multi-group``.
|
||||
|
||||
*Default*: continuous-energy
|
||||
|
||||
``<entropy>`` Element
|
||||
---------------------
|
||||
|
||||
|
|
@ -264,6 +279,8 @@ based on the recommended value in LA-UR-14-24530_.
|
|||
|
||||
*Default*: 8000
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
``<multipole_library>`` Element
|
||||
-------------------------------
|
||||
|
||||
|
|
@ -271,11 +288,24 @@ The ``<multipole_library>`` element indicates the directory containing a
|
|||
windowed multipole library. If a windowed multipole library is available,
|
||||
OpenMC can use it for on-the-fly Doppler-broadening of resolved resonance range
|
||||
cross sections. If this element is absent from the settings.xml file, the
|
||||
:envvar:`MULTIPOLE_LIBRARY` environment variable will be used.
|
||||
:envvar:`OPENMC_MULTIPOLE_LIBRARY` environment variable will be used.
|
||||
|
||||
.. note:: The <use_windowed_multipole> element must also be set to "True"
|
||||
.. note:: The <use_windowed_multipole> element must also be set to "true"
|
||||
for windowed multipole functionality.
|
||||
|
||||
``<max_order>`` Element
|
||||
---------------------------
|
||||
|
||||
The ``<max_order>`` element allows the user to set a maximum scattering order
|
||||
to apply to every nuclide/material in the problem. That is, if the data
|
||||
library has :math:`P_3` data available, but ``<max_order>`` was set to ``1``,
|
||||
then, OpenMC will only use up to the :math:`P_1` data.
|
||||
|
||||
*Default*: Use the maximum order in the data library
|
||||
|
||||
.. note:: This element is not used in the continuous-energy
|
||||
:ref:`energy_mode`.
|
||||
|
||||
.. _natural_elements:
|
||||
|
||||
``<natural_elements>`` Element
|
||||
|
|
@ -324,10 +354,10 @@ out the file and "false" will not.
|
|||
*Default*: false
|
||||
|
||||
:summary:
|
||||
Writes out an ASCII summary file describing all of the user input files that
|
||||
Writes out an HDF5 summary file describing all of the user input files that
|
||||
were read in.
|
||||
|
||||
*Default*: false
|
||||
*Default*: true
|
||||
|
||||
:tallies:
|
||||
Write out an ASCII file of tally results.
|
||||
|
|
@ -355,6 +385,8 @@ or sub-elements and can be set to either "false" or "true".
|
|||
|
||||
*Default*: true
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
``<resonance_scattering>`` Element
|
||||
----------------------------------
|
||||
|
||||
|
|
@ -414,6 +446,8 @@ attributes or sub-elements:
|
|||
|
||||
*Defaults*: None (scatterer), ARES (method), 0.01 eV (E_min), 1.0 keV (E_max)
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
``<run_cmfd>`` Element
|
||||
----------------------
|
||||
|
||||
|
|
@ -591,6 +625,8 @@ variable and whose sub-elements/attributes are as follows:
|
|||
number :math:`a` that parameterizes the distribution :math:`p(x) dx = c x
|
||||
e^{-x/a} dx`.
|
||||
|
||||
.. note:: The above format should be used even when using the multi-group
|
||||
:ref:`energy_mode`.
|
||||
:interpolation:
|
||||
For a "tabular" distribution, ``interpolation`` can be set to "histogram" or
|
||||
"linear-linear" thereby specifying how tabular points are to be interpolated.
|
||||
|
|
@ -1216,10 +1252,18 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
``<element>`` sub-elements are to be interpreted as nuclide/element
|
||||
densities in atom/b-cm, and the total density of the material is taken as
|
||||
the sum of all nuclides/elements. The "sum" option cannot be used in
|
||||
conjunction with weight percents.
|
||||
conjunction with weight percents. The "macro" unit is used with
|
||||
a ``macroscopic`` quantity to indicate that the density is already included
|
||||
in the library and thus not needed here. However, if a value is provided
|
||||
for the ``value``, then this is treated as a number density multiplier on
|
||||
the macroscopic cross sections in the multi-group data. This can be used,
|
||||
for example, when perturbing the density slightly.
|
||||
|
||||
*Default*: None
|
||||
|
||||
.. note:: A ``macroscopic`` quantity can not be used in conjunction with a
|
||||
``nuclide``, ``element``, or ``sab`` quantity.
|
||||
|
||||
:nuclide:
|
||||
An element with attributes/sub-elements called ``name``, ``xs``, and ``ao``
|
||||
or ``wo``. The ``name`` attribute is the name of the cross-section for a
|
||||
|
|
@ -1247,6 +1291,9 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
|
||||
*Default*: None
|
||||
|
||||
.. note:: The ``scattering`` attribute/sub-element is not used in the
|
||||
multi-group :ref:`energy_mode`.
|
||||
|
||||
:element:
|
||||
|
||||
Specifies that a natural element is present in the material. The natural
|
||||
|
|
@ -1282,6 +1329,9 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
|
||||
*Default*: None
|
||||
|
||||
.. note:: The ``scattering`` attribute/sub-element is not used in the
|
||||
multi-group :ref:`energy_mode`.
|
||||
|
||||
:sab:
|
||||
Associates an S(a,b) table with the material. This element has
|
||||
attributes/sub-elements called ``name`` and ``xs``. The ``name`` attribute
|
||||
|
|
@ -1290,6 +1340,26 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
|
||||
*Default*: None
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
:macroscopic:
|
||||
The ``macroscopic`` element is similar to the ``nuclide`` element, but,
|
||||
recognizes that some multi-group libraries may be providing material
|
||||
specific macroscopic cross sections instead of always providing nuclide
|
||||
specific data like in the continuous-energy case. To that end, the
|
||||
macroscopic element has attributes/sub-elements called ``name``, and ``xs``.
|
||||
The ``name`` attribute is the name of the cross-section for a
|
||||
desired nuclide while the ``xs`` attribute is the cross-section
|
||||
identifier. One example would be as follows:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<macroscopic name="UO2" xs="71c" />
|
||||
|
||||
.. note:: This element is only used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
*Default*: None
|
||||
|
||||
.. _IUPAC Isotopic Compositions of the Elements 2009:
|
||||
http://pac.iupac.org/publications/pac/pdf/2011/pdf/8302x0397.pdf
|
||||
|
||||
|
|
@ -1376,7 +1446,8 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
A list of universes for which the tally should be accumulated.
|
||||
|
||||
:energy:
|
||||
A monotonically increasing list of bounding **pre-collision** energies
|
||||
In continuous-energy mode, this filter should be provided as a
|
||||
monotonically increasing list of bounding **pre-collision** energies
|
||||
for a number of groups. For example, if this filter is specified as
|
||||
|
||||
.. code-block:: xml
|
||||
|
|
@ -1386,17 +1457,24 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
then two energy bins will be created, one with energies between 0 and
|
||||
1 MeV and the other with energies between 1 and 20 MeV.
|
||||
|
||||
In multi-group mode the bins provided must match group edges
|
||||
defined in the multi-group library.
|
||||
|
||||
:energyout:
|
||||
A monotonically increasing list of bounding **post-collision**
|
||||
energies for a number of groups. For example, if this filter is
|
||||
specified as
|
||||
In continuous-energy mode, this filter should be provided as a
|
||||
monotonically increasing list of bounding **post-collision** energies
|
||||
for a number of groups. For example, if this filter is specified as
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<filter type="energyout" bins="0.0 1.0 20.0" />
|
||||
|
||||
then two post-collision energy bins will be created, one with energies
|
||||
between 0 and 1 MeV and the other with energies between 1 and 20 MeV.
|
||||
then two post-collision energy bins will be created, one with
|
||||
energies between 0 and 1 MeV and the other with energies between
|
||||
1 and 20 MeV.
|
||||
|
||||
In multi-group mode the bins provided must match group edges
|
||||
defined in the multi-group library.
|
||||
|
||||
:mu:
|
||||
A monotonically increasing list of bounding **post-collision** cosines
|
||||
|
|
@ -1480,6 +1558,8 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
|
||||
<filter type="delayedgroup" bins="1 2 3 4 5 6" />
|
||||
|
||||
.. note:: This filter type is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
:nuclides:
|
||||
If specified, the scores listed will be for particular nuclides, not the
|
||||
summation of reactions from all nuclides. The format for nuclides should be
|
||||
|
|
@ -1659,7 +1739,8 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
|Score | Description |
|
||||
+======================+===================================================+
|
||||
|delayed-nu-fission |Total production of delayed neutrons due to |
|
||||
| |fission. |
|
||||
| |fission. This score type is not used in the |
|
||||
| |multi-group :ref:`energy_mode`. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|nu-fission |Total production of neutrons due to fission. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|
|
@ -1688,6 +1769,8 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
+----------------------+---------------------------------------------------+
|
||||
|inverse-velocity |The flux-weighted inverse velocity where the |
|
||||
| |velocity is in units of centimeters per second. |
|
||||
| |This score type is not used in the |
|
||||
| |multi-group :ref:`energy_mode`. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|kappa-fission |The recoverable energy production rate due to |
|
||||
| |fission. The recoverable energy is defined as the |
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ your PATH environment variable and subsequently uses it to determine library
|
|||
locations and compile flags. If you have multiple installations of HDF5 or one
|
||||
that does not appear on your PATH, you can set the HDF5_ROOT environment
|
||||
variable to the root directory of the HDF5 installation, e.g.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
export HDF5_ROOT=/opt/hdf5/1.8.15
|
||||
|
|
@ -355,7 +356,7 @@ Testing Build
|
|||
-------------
|
||||
|
||||
If you have ENDF/B-VII.1 cross sections from NNDC_ you can test your build.
|
||||
Make sure the **CROSS_SECTIONS** environmental variable is set to the
|
||||
Make sure the **OPENMC_CROSS_SECTIONS** environmental variable is set to the
|
||||
*cross_sections.xml* file in the *data/nndc* directory.
|
||||
There are two ways to run tests. The first is to use the Makefile present in
|
||||
the source directory and run the following:
|
||||
|
|
@ -380,11 +381,17 @@ Cross Section Configuration
|
|||
---------------------------
|
||||
|
||||
In order to run a simulation with OpenMC, you will need cross section data for
|
||||
each nuclide in your problem. Since OpenMC uses ACE format cross sections, you
|
||||
can use nuclear data that was processed with NJOY_, such as that distributed
|
||||
with MCNP_ or Serpent_. Several sources provide free processed ACE data as
|
||||
described below. The TALYS-based evaluated nuclear data library, TENDL_, is also
|
||||
openly available in ACE format.
|
||||
each nuclide or material in your problem. OpenMC can be run in
|
||||
continuous-energy or multi-group mode.
|
||||
|
||||
In continuous-energy mode OpenMC uses ACE format cross sections; in this case
|
||||
you can use nuclear data that was processed with NJOY_, such as that
|
||||
distributed with MCNP_ or Serpent_. Several sources provide free processed
|
||||
ACE data as described below. The TALYS-based evaluated nuclear data library,
|
||||
TENDL_, is also openly available in ACE format.
|
||||
|
||||
In multi-group mode, OpenMC utilizes an XML-based library format which can be
|
||||
used to describe nuclide- or material-specific quantities.
|
||||
|
||||
Using ENDF/B-VII.1 Cross Sections from NNDC
|
||||
-------------------------------------------
|
||||
|
|
@ -399,9 +406,10 @@ extract, and set up a confiuration file:
|
|||
cd openmc/data
|
||||
python get_nndc_data.py
|
||||
|
||||
At this point, you should set the :envvar:`CROSS_SECTIONS` environment variable
|
||||
to the absolute path of the file ``openmc/data/nndc/cross_sections.xml``. This
|
||||
cross section set is used by the test suite.
|
||||
At this point, you should set the :envvar:`OPENMC_CROSS_SECTIONS` environment
|
||||
variable to the absolute path of the file
|
||||
``openmc/data/nndc/cross_sections.xml``. This cross section set is used by the
|
||||
test suite.
|
||||
|
||||
Using JEFF Cross Sections from OECD/NEA
|
||||
---------------------------------------
|
||||
|
|
@ -427,8 +435,8 @@ the following steps must be taken:
|
|||
4. Additionally, you may need to change any occurrences of upper-case "ACE"
|
||||
within the ``cross_sections.xml`` file to lower-case.
|
||||
5. Either set the :ref:`cross_sections` in a settings.xml file or the
|
||||
:envvar:`CROSS_SECTIONS` environment variable to the absolute path of the
|
||||
``cross_sections.xml`` file.
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
|
||||
the ``cross_sections.xml`` file.
|
||||
|
||||
Using Cross Sections from MCNP
|
||||
------------------------------
|
||||
|
|
@ -436,8 +444,9 @@ Using Cross Sections from MCNP
|
|||
To use cross sections distributed with MCNP, change the <directory> element in
|
||||
the ``cross_sections.xml`` file in the root directory of the OpenMC distribution
|
||||
to the location of the MCNP cross sections. Then, either set the
|
||||
:ref:`cross_sections` in a settings.xml file or the :envvar:`CROSS_SECTIONS`
|
||||
environment variable to the absolute path of the ``cross_sections.xml`` file.
|
||||
:ref:`cross_sections` in a settings.xml file or the
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
|
||||
the ``cross_sections.xml`` file.
|
||||
|
||||
Using Cross Sections from Serpent
|
||||
---------------------------------
|
||||
|
|
@ -445,10 +454,21 @@ Using Cross Sections from Serpent
|
|||
To use cross sections distributed with Serpent, change the <directory> element
|
||||
in the ``cross_sections_serpent.xml`` file in the root directory of the OpenMC
|
||||
distribution to the location of the Serpent cross sections. Then, either set the
|
||||
:ref:`cross_sections` in a settings.xml file or the :envvar:`CROSS_SECTIONS`
|
||||
environment variable to the absolute path of the ``cross_sections_serpent.xml``
|
||||
:ref:`cross_sections` in a settings.xml file or the
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable to the absolute path of
|
||||
the ``cross_sections_serpent.xml``
|
||||
file.
|
||||
|
||||
Using Multi-Group Cross Sections
|
||||
--------------------------------
|
||||
|
||||
Multi-group cross section libraries are generally tailored to the specific
|
||||
calculation to be performed. Therefore, at this point in time, OpenMC is not
|
||||
distributed with any pre-existing multi-group cross section libraries.
|
||||
However, if the user has obtained or generated their own library, the user
|
||||
should set the :envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable
|
||||
to the absolute path of the file library expected to used most frequently.
|
||||
|
||||
.. _NJOY: http://t2.lanl.gov/nis/codes.shtml
|
||||
.. _NNDC: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html
|
||||
.. _NEA: http://www.oecd-nea.org
|
||||
|
|
|
|||
302
docs/source/usersguide/mgxs_library.rst
Normal file
302
docs/source/usersguide/mgxs_library.rst
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
.. _usersguide_mgxs_library:
|
||||
|
||||
========================================
|
||||
Multi-Group Cross Section Library Format
|
||||
========================================
|
||||
|
||||
OpenMC can be run in continuous-energy mode or multi-group mode, provided the
|
||||
nuclear data is available. In continuous-energy mode, the
|
||||
``cross_sections.xml`` file contains necessary meta-data for each data set,
|
||||
including the name and a file system location where the complete library
|
||||
can be found. In multi-group mode, this ``cross_sections.xml`` file contains
|
||||
this same meta-data describing the nuclide or material, but also contains the
|
||||
group-wise nuclear data. This portion of the manual describes the format of
|
||||
the multi-group data library required to be used in the ``cross_sections.xml``
|
||||
file.
|
||||
|
||||
Similar to the other input file types, the multi-group library is provided in
|
||||
the XML_ format. This library must provide some meta-data about the library
|
||||
itself (such as the number of groups and the group structure, etc.) as well as
|
||||
the actual cross section data itself for each of the necessary nuclides or
|
||||
materials.
|
||||
|
||||
.. _XML: http://www.w3.org/XML/
|
||||
|
||||
------------------------------------------------
|
||||
MGXS Library Specification -- cross_sections.xml
|
||||
------------------------------------------------
|
||||
|
||||
The multi-group library meta-data is contained within the groups_,
|
||||
group_structure_, and inverse_velocities_ elements.
|
||||
The actual multi-group data itself is contained within the xsdata_ element.
|
||||
|
||||
.. _groups:
|
||||
|
||||
``<groups>`` Element
|
||||
----------------------------------
|
||||
|
||||
The ``<groups>`` element has no attributes and simply provides the number of
|
||||
energy groups contained within the library.
|
||||
|
||||
*Default*: None, this must be provided.
|
||||
|
||||
.. _group_structure:
|
||||
|
||||
``<group_structure>`` Element
|
||||
-----------------------------
|
||||
|
||||
The ``<group_structure>`` element has no attributes and should be provided as a
|
||||
monotonically increasing list of bounding energies, in MeV, for a number of
|
||||
groups. To provide proper energy boundaries, the length of the data within the
|
||||
``<group_structure>`` element should be one more than the number of groups in
|
||||
the problem. For example, a two-group problem could be specified as:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<group_structure> 0.0 0.625E-6 20.0 </group_structure>
|
||||
|
||||
*Default*: None, this must be provided.
|
||||
|
||||
.. _inverse_velocities:
|
||||
|
||||
``<inverse_velocities>`` Element
|
||||
--------------------------------
|
||||
|
||||
The ``<inverse_velocities>`` element optionally indicates the average
|
||||
inverse velocity corresponding to each of the groups in the problem.
|
||||
This element should therefore be an array with a length which matches the
|
||||
number of groups set in the groups_ element.
|
||||
|
||||
*Default*: Should this be needed by the presence of an ``inverse-velocity``
|
||||
score in the ``tallies.xml`` file and not provided in this element, OpenMC
|
||||
will simply convert the group mid-point energy to an inverse of the velocity
|
||||
and use this information for tallying.
|
||||
|
||||
.. _xsdata:
|
||||
|
||||
``<xsdata>`` Element
|
||||
--------------------
|
||||
|
||||
The ``<xsdata>`` element contains the nuclide or material-specific meta-data as
|
||||
well as the actual cross section data. The following are the
|
||||
attributes/sub-elements required to describe the meta-data:
|
||||
|
||||
:name:
|
||||
The name of the microscopic or macroscopic data set. An extension to the
|
||||
name must be provided (e.g., the ``.300K`` in ``UO2.300K``). The name and
|
||||
extension together must be twelve or less characters in length. This
|
||||
extension must follow a period and be five characters or less in length.
|
||||
similar to the equivalent in the continuous-energy ``cross_sections.xml``
|
||||
file, is used to denote variants of the particular nuclide or material of
|
||||
interest (i.e. the ``UO2`` data in this example could have been generated
|
||||
at a temperature of 300K).
|
||||
|
||||
*Default*: None, this must be provided.
|
||||
|
||||
:alias:
|
||||
An alternative name to use for the microscopic or macroscopic data set.
|
||||
|
||||
*Default*: If no alias is provided, it will adopt the value of ``name``.
|
||||
|
||||
:kT:
|
||||
The temperature times Boltzmann's constant (in units of MeV) at which the
|
||||
data was generated.
|
||||
|
||||
*Default*: Room temperature, 2.53E-8 MeV
|
||||
|
||||
:fissionable:
|
||||
This element states whether or not the data in question is fissionable.
|
||||
Accepted values are "true" or "false".
|
||||
|
||||
*Default*: None, this element must be provided.
|
||||
|
||||
:representation:
|
||||
This element provides the method used to generate and represent the
|
||||
multi-group cross sections. That is, whether they were generated with
|
||||
scalar flux weighting (or reduced to an equivalent representation)
|
||||
and thus are angle-independent, or if the data was generated with angular
|
||||
dependent fluxes and thus the data is angle-dependent. The options are
|
||||
either "isotropic" or "angle".
|
||||
|
||||
*Default*: "isotropic"
|
||||
|
||||
:num_azimuthal:
|
||||
This element provides the number of equal width angular bins that the
|
||||
azimuthal angular domain is subdivided in the case of angle-dependent
|
||||
cross sections (i.e., "angle" is passed to the ``representation`` element).
|
||||
Note that these bins are equal in azimuthal angle widths, not equal in the
|
||||
cosine of the azimuthal angle widths.
|
||||
|
||||
*Default*: If ``representation`` is "angle", this must be provided. This
|
||||
parameter is not used for other ``representation`` types.
|
||||
|
||||
:num_polar:
|
||||
This element provides the number of equal width angular bins that the
|
||||
polar angular domain is subdivided in the case of angle-dependent
|
||||
cross sections (i.e., "angle" is passed to the ``representation`` element).
|
||||
Note that these bins are equal in polar angle widths, not equal in the
|
||||
cosine of the polar angle widths.
|
||||
|
||||
|
||||
*Default*: If ``representation`` is "angle", this must be provided. This
|
||||
parameter is not used for other ``representation`` types.
|
||||
|
||||
:scatt_type:
|
||||
This element provides the representation of the angular distribution
|
||||
associated with each group-to-group transfer probability. The options are
|
||||
either "legendre", "histogram", or "tabular".
|
||||
The "legendre" option means the angular distribution has been
|
||||
expanded via Legendre polynomials of the order provided in the "order"
|
||||
element.
|
||||
The "histogram" option means the angular distribution is provided in
|
||||
an equi-width histogram format with a number of bins as provided in the
|
||||
"order" element. This is useful when the angular distribution was
|
||||
obtained from a Monte Carlo tally and thus is natively in the histogram
|
||||
format.
|
||||
The "tabular" option means the angular distribution is provided in an
|
||||
equi-spaced point-wise representation.
|
||||
|
||||
*Default*: "legendre"
|
||||
|
||||
:order:
|
||||
This element provides either the Legendre order, number of bins, or number
|
||||
of points used to describe the angular distribution associated with each
|
||||
group-to-group transfer probability. The specific meaning of this bin
|
||||
depends upon the value of ``scatt_type`` as discussed above.
|
||||
|
||||
*Default*: None, this element must be provided.
|
||||
|
||||
:tabular_legendre:
|
||||
This optional element is used to set how the Legendre scattering kernel, if
|
||||
provided via the ``scatt_type`` element above, is represented and thus used
|
||||
during the scattering process. Specifically, the options are to either
|
||||
convert the Legendre expansion to a tabular representation or leave it as
|
||||
a set of Legendre coefficients. Converting to a tabular representation will
|
||||
cost memory but is likely to decrease runtime compared to leaving as a
|
||||
set of Legendre coefficients. This element has the following
|
||||
attributes/sub-elements:
|
||||
|
||||
:enable:
|
||||
This attribute/sub-element denotes whether or not the conversion to the
|
||||
tabular format should be performed or not. A value of "true" means
|
||||
the conversion should be performed, "false" means it should not.
|
||||
|
||||
*Default*: "true"
|
||||
|
||||
:num_points:
|
||||
If the conversion is to take place the number of tabular points is
|
||||
required. This attribute/sub-element allows the user to set the desired
|
||||
number of points.
|
||||
|
||||
*Default*: 33
|
||||
|
||||
The following attributes/sub-elements are the cross section values to
|
||||
be used during the transport process.
|
||||
|
||||
:total:
|
||||
This element requires the group-wise total cross section ordered by
|
||||
increasing group index (i.e., fast to thermal). If ``representation`` is
|
||||
"isotropic", then the length of this list should equal the number of
|
||||
groups described in the ``groups`` element. If ``representation`` is
|
||||
"angle", then the length of this list should equal the number of groups
|
||||
times the number of azimuthal angles times the number of polar angles,
|
||||
with the inner-dimension being groups, intermediate-dimension being
|
||||
azimuthal angles and outer-dimension being the polar angles.
|
||||
|
||||
*Default*: If not provided, it will be determined by summing the
|
||||
absorption and scattering cross sections.
|
||||
|
||||
:absorption:
|
||||
This element requires the group-wise absorption cross section ordered by
|
||||
increasing group index (i.e., fast to thermal). If ``representation`` is
|
||||
"isotropic", then the length of this list should equal the number of
|
||||
groups described in the ``groups`` element. If ``representation`` is
|
||||
"angle", then the length of this list should equal the number of groups
|
||||
times the number of azimuthal angles times the number of polar angles,
|
||||
with the inner-dimension being groups, intermediate-dimension being
|
||||
azimuthal angles and outer-dimension being the polar angles.
|
||||
|
||||
*Default*: None, this must be provided.
|
||||
|
||||
:scatter:
|
||||
This element requires the scattering moment matrices presented with the
|
||||
columns representing incoming group and rows representing the outgoing
|
||||
group. That is, down-scatter will be above the diagonal of the resultant
|
||||
matrix. This matrix is repeated for every Legendre order (in order of
|
||||
increasing orders) if ``scatt_type`` is "legendre"; otherwise, this
|
||||
matrix is repeated for every bin of the histogram or tabular
|
||||
representation. Finally, if ``representation`` is "angle", the above
|
||||
is repeated for every azimuthal angle and every polar angle, in that
|
||||
order.
|
||||
|
||||
*Default*: None, this must be provided.
|
||||
|
||||
:multiplicity:
|
||||
This element provides the ratio of neutrons produced in scattering
|
||||
collisions to the neutrons which undergo scattering collisions; that is,
|
||||
the multiplicity provides the code with a scaling factor to account for
|
||||
neutrons being produced in (n,xn) reactions. This information is assumed
|
||||
isotropic and therefore does not need to be repeated for every Legendre
|
||||
moment or histogram/tabular bin. This matrix follows the same arrangement
|
||||
as described for the ``scatter`` element, with the exception of the
|
||||
data needed to provide the scattering type information.
|
||||
|
||||
*Default*: Multiplicities of 1.0 are assumed (i.e., (n,xn) reactions are
|
||||
neglected).
|
||||
|
||||
The following fission-specific data are only needed should ``fissionable``
|
||||
be "true".
|
||||
|
||||
:fission:
|
||||
This element requires the group-wise fission cross section ordered by
|
||||
increasing group index (i.e., fast to thermal). If ``representation`` is
|
||||
"isotropic", then the length of this list should equal the number of
|
||||
groups described in the ``groups`` element. If ``representation`` is
|
||||
"angle", then the length of this list should equal the number of groups
|
||||
times the number of azimuthal angles times the number of polar angles,
|
||||
with the inner-dimension being groups, intermediate-dimension being
|
||||
azimuthal angles and outer-dimension being the polar angles.
|
||||
|
||||
*Default*: None, this is required only if fission tallies are
|
||||
requested and the material is fissionable.
|
||||
|
||||
:kappa_fission:
|
||||
This element requires the group-wise kappa-fission cross section ordered by
|
||||
increasing group index (i.e., fast to thermal). If ``representation`` is
|
||||
"isotropic", then the length of this list should equal the number of
|
||||
groups described in the ``groups`` element. If ``representation`` is
|
||||
"angle", then the length of this list should equal the number of groups
|
||||
times the number of azimuthal angles times the number of polar angles,
|
||||
with the inner-dimension being groups, intermediate-dimension being
|
||||
azimuthal angles and outer-dimension being the polar angles.
|
||||
|
||||
*Default*: None, this is required only if kappa_fission tallies are
|
||||
requested and the material is fissionable.
|
||||
|
||||
:chi:
|
||||
This element requires the group-wise fission spectra ordered by
|
||||
increasing group index (i.e., fast to thermal). This element should be
|
||||
used if making the common approximation that the fission spectra does
|
||||
not depend on incoming energy. If the user does not wish to make this
|
||||
approximation, then this should not be provided and this information
|
||||
included in the ``nu_fission`` element instead. If ``representation`` is
|
||||
"isotropic", then the length of this list should equal the number of
|
||||
groups described in the ``groups`` element. If ``representation`` is
|
||||
"angle", then the length of this list should equal the number of groups
|
||||
times the number of azimuthal angles times the number of polar angles,
|
||||
with the inner-dimension being groups, intermediate-dimension being
|
||||
azimuthal angles and outer-dimension being the polar angles.
|
||||
|
||||
*Default*: None, either this element is provided or ``nu_fission`` is
|
||||
provided in fission matrix form, or the material is not fissionable.
|
||||
|
||||
:nu_fission:
|
||||
This element provides either the group-wise fission production cross
|
||||
section vector (i.e., if ``chi`` is provided), or is the group-wise fission
|
||||
production matrix. If providing the vector, it should be ordered the same
|
||||
as the ``fission`` data. If providing the matrix, it should be ordered
|
||||
the same as the ``multiplicity`` matrix.
|
||||
|
||||
*Default*: None, either this element must be provided if the material
|
||||
is fissionable.
|
||||
|
||||
|
|
@ -46,7 +46,8 @@ The current revision of the particle restart file format is 1.
|
|||
|
||||
**/energy** (*double*)
|
||||
|
||||
Energy of the particle in MeV.
|
||||
Energy of the particle in MeV for continuous-energy mode, or the energy
|
||||
group of the particle for multi-group mode.
|
||||
|
||||
**/xyz** (*double[3]*)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,5 +15,6 @@ is that documented here.
|
|||
**/source_bank** (Compound type)
|
||||
|
||||
Source bank information for each particle. The compound type has fields
|
||||
``wgt``, ``xyz``, ``uvw``, and ``E`` which represent the weight, position,
|
||||
direction, and energy of the source particle, respectively.
|
||||
``wgt``, ``xyz``, ``uvw``, ``E``, and ``delayed_group``, which
|
||||
represent the weight, position, direction, energy, energy group, and
|
||||
delayed_group of the source particle, respectively.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
State Point File Format
|
||||
=======================
|
||||
|
||||
The current revision of the statepoint file format is 14.
|
||||
The current revision of the statepoint file format is 15.
|
||||
|
||||
**/filetype** (*char[]*)
|
||||
|
||||
|
|
@ -39,6 +39,12 @@ The current revision of the statepoint file format is 14.
|
|||
|
||||
Pseudo-random number generator seed.
|
||||
|
||||
**/run_CE** (*int*)
|
||||
|
||||
Flag to denote continuous-energy or multi-group mode. A value of 1
|
||||
indicates a continuous-energy run while a value of 0 indicates a
|
||||
multi-group run.
|
||||
|
||||
**/run_mode** (*char[]*)
|
||||
|
||||
Run mode used. A value of 1 indicates a fixed-source run and a value of 2
|
||||
|
|
@ -258,7 +264,7 @@ if run_mode == 'k-eigenvalue':
|
|||
Accumulated sum and sum-of-squares for each global tally. The compound type
|
||||
has fields named ``sum`` and ``sum_sq``.
|
||||
|
||||
**tallies_present** (*int*)
|
||||
**/tallies_present** (*int*)
|
||||
|
||||
Flag indicated if tallies are present in the file.
|
||||
|
||||
|
|
@ -267,5 +273,72 @@ if (run_mode == 'k-eigenvalue' and source_present > 0)
|
|||
**/source_bank** (Compound type)
|
||||
|
||||
Source bank information for each particle. The compound type has fields
|
||||
``wgt``, ``xyz``, ``uvw``, and ``E`` which represent the weight,
|
||||
position, direction, and energy of the source particle, respectively.
|
||||
``wgt``, ``xyz``, ``uvw``, ``E``, ``g``, and ``delayed_group``, which
|
||||
represent the weight, position, direction, energy, energy group, and
|
||||
delayed_group of the source particle, respectively.
|
||||
|
||||
**/runtime/total initialization** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent reading inputs, allocating
|
||||
arrays, etc.
|
||||
|
||||
**/runtime/reading cross sections** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent loading cross section
|
||||
libraries (this is a subset of initialization).
|
||||
|
||||
**/runtime/simulation** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent between initialization and
|
||||
finalization.
|
||||
|
||||
**/runtime/transport** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent transporting particles.
|
||||
|
||||
**/runtime/inactive batches** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent in the inactive batches
|
||||
(including non-transport activities like communcating sites).
|
||||
|
||||
**/runtime/active batches** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent in the active batches
|
||||
(including non-transport activities like communicating sites).
|
||||
|
||||
**/runtime/synchronizing fission bank** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent sampling source particles
|
||||
from fission sites and communicating them to other processes for load
|
||||
balancing.
|
||||
|
||||
**/runtime/sampling source sites** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent sampling source particles
|
||||
from fission sites.
|
||||
|
||||
**/runtime/SEND-RECV source sites** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent communicating source sites
|
||||
between processes for load balancing.
|
||||
|
||||
**/runtime/accumulating tallies** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent communicating tally results
|
||||
and evaluating their statistics.
|
||||
|
||||
**/runtime/CMFD** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent evaluating CMFD.
|
||||
|
||||
**/runtime/CMFD building matrices** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent buliding CMFD matrices.
|
||||
|
||||
**/runtime/CMFD solving matrices** (*double*)
|
||||
|
||||
Time (in seconds on the master process) spent solving CMFD matrices.
|
||||
|
||||
**/runtime/total** (*double*)
|
||||
|
||||
Total time spent (in seconds on the master process) in the program.
|
||||
|
|
|
|||
|
|
@ -297,6 +297,13 @@ The current revision of the summary file format is 1.
|
|||
|
||||
Filter offset (used for distribcell filter).
|
||||
|
||||
**/tallies/tally <uid>/filter <j>/paths** (*char[][]*)
|
||||
|
||||
The paths traversed through the CSG tree to reach each distribcell
|
||||
instance (for 'distribcell' filters only). This consists of the integer
|
||||
IDs for each universe, cell and lattice delimited by '->'. Each lattice
|
||||
cell is specified by its (x,y) or (x,y,z) indices.
|
||||
|
||||
**/tallies/tally <uid>/filter <j>/n_bins** (*int*)
|
||||
|
||||
Number of bins for the j-th filter.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import openmc
|
||||
from openmc.source import Source
|
||||
from openmc.stats import Box
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
|
|
@ -94,7 +93,12 @@ settings_file = openmc.SettingsFile()
|
|||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
settings_file.source = Source(space=Box([-4, -4, -4], [4, 4, 4]))
|
||||
|
||||
# Create an initial uniform spatial source distribution over fissionable zones
|
||||
bounds = [-4., -4., -4., 4., 4., 4.]
|
||||
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
|
||||
settings_file.source = openmc.source.Source(space=uniform_dist)
|
||||
|
||||
settings_file.export_to_xml()
|
||||
|
||||
|
||||
|
|
@ -109,29 +113,20 @@ energyout_filter = openmc.Filter(type='energyout', bins=[0., 20.])
|
|||
|
||||
# Instantiate the first Tally
|
||||
first_tally = openmc.Tally(tally_id=1, name='first tally')
|
||||
first_tally.add_filter(cell_filter)
|
||||
scores = ['total', 'scatter', 'nu-scatter', \
|
||||
first_tally.filters = [cell_filter]
|
||||
scores = ['total', 'scatter', 'nu-scatter',
|
||||
'absorption', 'fission', 'nu-fission']
|
||||
for score in scores:
|
||||
first_tally.add_score(score)
|
||||
first_tally.scores = scores
|
||||
|
||||
# Instantiate the second Tally
|
||||
second_tally = openmc.Tally(tally_id=2, name='second tally')
|
||||
second_tally.add_filter(cell_filter)
|
||||
second_tally.add_filter(energy_filter)
|
||||
scores = ['total', 'scatter', 'nu-scatter', \
|
||||
'absorption', 'fission', 'nu-fission']
|
||||
for score in scores:
|
||||
second_tally.add_score(score)
|
||||
second_tally.filters = [cell_filter, energy_filter]
|
||||
second_tally.scores = scores
|
||||
|
||||
# Instantiate the third Tally
|
||||
third_tally = openmc.Tally(tally_id=3, name='third tally')
|
||||
third_tally.add_filter(cell_filter)
|
||||
third_tally.add_filter(energy_filter)
|
||||
third_tally.add_filter(energyout_filter)
|
||||
scores = ['scatter', 'nu-scatter', 'nu-fission']
|
||||
for score in scores:
|
||||
third_tally.add_score(score)
|
||||
third_tally.filters = [cell_filter, energy_filter, energyout_filter]
|
||||
third_tally.scores = ['scatter', 'nu-scatter', 'nu-fission']
|
||||
|
||||
# Instantiate a TalliesFile, register all Tallies, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import numpy as np
|
||||
|
||||
import openmc
|
||||
from openmc.source import Source
|
||||
from openmc.stats import Box
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
|
|
@ -119,7 +116,11 @@ settings_file = openmc.SettingsFile()
|
|||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
settings_file.source = Source(space=Box(*outer_cube.bounding_box))
|
||||
|
||||
# Create an initial uniform spatial source distribution over fissionable zones
|
||||
uniform_dist = openmc.stats.Box(*outer_cube.bounding_box, only_fissionable=True)
|
||||
settings_file.source = openmc.source.Source(space=uniform_dist)
|
||||
|
||||
settings_file.export_to_xml()
|
||||
|
||||
###############################################################################
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import openmc
|
||||
from openmc.source import Source
|
||||
from openmc.stats import Box
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
|
|
@ -126,8 +124,12 @@ settings_file = openmc.SettingsFile()
|
|||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
settings_file.source = Source(space=Box(
|
||||
[-1, -1, -1], [1, 1, 1]))
|
||||
|
||||
# Create an initial uniform spatial source distribution over fissionable zones
|
||||
bounds = [-1, -1, -1, 1, 1, 1]
|
||||
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
|
||||
settings_file.source = openmc.source.Source(space=uniform_dist)
|
||||
|
||||
settings_file.keff_trigger = {'type' : 'std_dev', 'threshold' : 5E-4}
|
||||
settings_file.trigger_active = True
|
||||
settings_file.trigger_max_batches = 100
|
||||
|
|
@ -166,8 +168,8 @@ plot_file.export_to_xml()
|
|||
|
||||
# Instantiate a distribcell Tally
|
||||
tally = openmc.Tally(tally_id=1)
|
||||
tally.add_filter(openmc.Filter(type='distribcell', bins=[cell2.id]))
|
||||
tally.add_score('total')
|
||||
tally.filters = [openmc.Filter(type='distribcell', bins=[cell2.id])]
|
||||
tally.scores = ['total']
|
||||
|
||||
# Instantiate a TalliesFile, register Tally/Mesh, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import openmc
|
||||
from openmc.source import Source
|
||||
from openmc.stats import Box
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
|
|
@ -137,8 +135,12 @@ settings_file = openmc.SettingsFile()
|
|||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
settings_file.source = Source(space=Box(
|
||||
[-1, -1, -1], [1, 1, 1]))
|
||||
|
||||
# Create an initial uniform spatial source distribution over fissionable zones
|
||||
bounds = [-1, -1, -1, 1, 1, 1]
|
||||
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
|
||||
settings_file.source = openmc.source.Source(space=uniform_dist)
|
||||
|
||||
settings_file.export_to_xml()
|
||||
|
||||
|
||||
|
|
@ -175,8 +177,8 @@ mesh_filter.mesh = mesh
|
|||
|
||||
# Instantiate the Tally
|
||||
tally = openmc.Tally(tally_id=1)
|
||||
tally.add_filter(mesh_filter)
|
||||
tally.add_score('total')
|
||||
tally.filters = [mesh_filter]
|
||||
tally.scores = ['total']
|
||||
|
||||
# Instantiate a TalliesFile, register Tally/Mesh, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import openmc
|
||||
from openmc.source import Source
|
||||
from openmc.stats import Box
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
|
|
@ -127,8 +125,12 @@ settings_file = openmc.SettingsFile()
|
|||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
settings_file.source = Source(space=Box(
|
||||
[-1, -1, -1], [1, 1, 1]))
|
||||
|
||||
# Create an initial uniform spatial source distribution over fissionable zones
|
||||
bounds = [-1, -1, -1, 1, 1, 1]
|
||||
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
|
||||
settings_file.source = openmc.source.Source(space=uniform_dist)
|
||||
|
||||
settings_file.trigger_active = True
|
||||
settings_file.trigger_max_batches = 100
|
||||
settings_file.export_to_xml()
|
||||
|
|
@ -167,13 +169,13 @@ mesh_filter.mesh = mesh
|
|||
|
||||
# Instantiate tally Trigger
|
||||
trigger = openmc.Trigger(trigger_type='rel_err', threshold=1E-2)
|
||||
trigger.add_score('all')
|
||||
trigger.scores = ['all']
|
||||
|
||||
# Instantiate the Tally
|
||||
tally = openmc.Tally(tally_id=1)
|
||||
tally.add_filter(mesh_filter)
|
||||
tally.add_score('total')
|
||||
tally.add_trigger(trigger)
|
||||
tally.filters = [mesh_filter]
|
||||
tally.scores = ['total']
|
||||
tally.triggers = [trigger]
|
||||
|
||||
# Instantiate a TalliesFile, register Tally/Mesh, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import openmc
|
||||
from openmc.source import Source
|
||||
from openmc.stats import Box
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
|
|
@ -170,8 +168,12 @@ settings_file = openmc.SettingsFile()
|
|||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
settings_file.source = Source(space=Box(
|
||||
[-0.62992, -0.62992, -1], [0.62992, 0.62992, 1]))
|
||||
|
||||
# Create an initial uniform spatial source distribution over fissionable zones
|
||||
bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1]
|
||||
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
|
||||
settings_file.source = openmc.source.Source(space=uniform_dist)
|
||||
|
||||
settings_file.entropy_lower_left = [-0.39218, -0.39218, -1.e50]
|
||||
settings_file.entropy_upper_right = [0.39218, 0.39218, 1.e50]
|
||||
settings_file.entropy_dimension = [10, 10, 1]
|
||||
|
|
@ -196,11 +198,8 @@ mesh_filter.mesh = mesh
|
|||
|
||||
# Instantiate the Tally
|
||||
tally = openmc.Tally(tally_id=1, name='tally 1')
|
||||
tally.add_filter(energy_filter)
|
||||
tally.add_filter(mesh_filter)
|
||||
tally.add_score('flux')
|
||||
tally.add_score('fission')
|
||||
tally.add_score('nu-fission')
|
||||
tally.filters = [energy_filter, mesh_filter]
|
||||
tally.scores = ['flux', 'fission', 'nu-fission']
|
||||
|
||||
# Instantiate a TalliesFile, register all Tallies, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
|
|
|
|||
184
examples/python/pincell_multigroup/build-xml.py
Normal file
184
examples/python/pincell_multigroup/build-xml.py
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
import numpy as np
|
||||
import openmc
|
||||
import openmc.mgxs
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
###############################################################################
|
||||
|
||||
# OpenMC simulation parameters
|
||||
batches = 100
|
||||
inactive = 10
|
||||
particles = 1000
|
||||
|
||||
###############################################################################
|
||||
# Exporting to OpenMC mg_cross_sections.xml File
|
||||
###############################################################################
|
||||
|
||||
# Instantiate the energy group data
|
||||
groups = openmc.mgxs.EnergyGroups(group_edges=[1E-11, 0.0635E-6, 10.0E-6,
|
||||
1.0E-4, 1.0E-3, 0.5, 1.0, 20.0])
|
||||
|
||||
# Instantiate the 7-group (C5G7) cross section data
|
||||
uo2_xsdata = openmc.XSdata('UO2.300K', groups)
|
||||
uo2_xsdata.order = 0
|
||||
uo2_xsdata.total = np.array([0.1779492, 0.3298048, 0.4803882, 0.5543674,
|
||||
0.3118013, 0.3951678, 0.5644058])
|
||||
uo2_xsdata.absorption = np.array([8.0248E-03, 3.7174E-03, 2.6769E-02, 9.6236E-02,
|
||||
3.0020E-02, 1.1126E-01, 2.8278E-01])
|
||||
scatter = [[[0.1275370, 0.0423780, 0.0000094, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
|
||||
[0.0000000, 0.3244560, 0.0016314, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
|
||||
[0.0000000, 0.0000000, 0.4509400, 0.0026792, 0.0000000, 0.0000000, 0.0000000],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.4525650, 0.0055664, 0.0000000, 0.0000000],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0001253, 0.2714010, 0.0102550, 0.0000000],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0012968, 0.2658020, 0.0168090],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0085458, 0.2730800]]]
|
||||
uo2_xsdata.scatter = np.array(scatter[:][:])
|
||||
uo2_xsdata.fission = np.array([7.21206E-03, 8.19301E-04, 6.45320E-03,
|
||||
1.85648E-02, 1.78084E-02, 8.30348E-02,
|
||||
2.16004E-01])
|
||||
uo2_xsdata.nu_fission = np.array([2.005998E-02, 2.027303E-03, 1.570599E-02,
|
||||
4.518301E-02, 4.334208E-02, 2.020901E-01,
|
||||
5.257105E-01])
|
||||
uo2_xsdata.chi = np.array([5.8791E-01, 4.1176E-01, 3.3906E-04, 1.1761E-07,
|
||||
0.0000E+00, 0.0000E+00, 0.0000E+00])
|
||||
|
||||
h2o_xsdata = openmc.XSdata('LWTR.300K', groups)
|
||||
h2o_xsdata.order = 0
|
||||
h2o_xsdata.total = np.array([0.15920605, 0.412969593, 0.59030986, 0.58435,
|
||||
0.718, 1.2544497, 2.650379])
|
||||
h2o_xsdata.absorption = np.array([6.0105E-04, 1.5793E-05, 3.3716E-04,
|
||||
1.9406E-03, 5.7416E-03, 1.5001E-02,
|
||||
3.7239E-02])
|
||||
scatter = [[[0.0444777, 0.1134000, 0.0007235, 0.0000037, 0.0000001, 0.0000000, 0.0000000],
|
||||
[0.0000000, 0.2823340, 0.1299400, 0.0006234, 0.0000480, 0.0000074, 0.0000010],
|
||||
[0.0000000, 0.0000000, 0.3452560, 0.2245700, 0.0169990, 0.0026443, 0.0005034],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0910284, 0.4155100, 0.0637320, 0.0121390],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0000714, 0.1391380, 0.5118200, 0.0612290],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0022157, 0.6999130, 0.5373200],
|
||||
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.1324400, 2.4807000]]]
|
||||
h2o_xsdata.scatter = np.array(scatter)
|
||||
|
||||
mg_cross_sections_file = openmc.MGXSLibraryFile(groups)
|
||||
mg_cross_sections_file.add_xsdatas([uo2_xsdata,h2o_xsdata])
|
||||
mg_cross_sections_file.export_to_xml()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Exporting to OpenMC materials.xml File
|
||||
###############################################################################
|
||||
|
||||
# Instantiate some Macroscopic Data
|
||||
uo2_data = openmc.Macroscopic('UO2', '300K')
|
||||
h2o_data = openmc.Macroscopic('LWTR', '300K')
|
||||
|
||||
# Instantiate some Materials and register the appropriate Macroscopic objects
|
||||
uo2 = openmc.Material(material_id=1, name='UO2 fuel')
|
||||
uo2.set_density('macro', 1.0)
|
||||
uo2.add_macroscopic(uo2_data)
|
||||
|
||||
water = openmc.Material(material_id=2, name='Water')
|
||||
water.set_density('macro', 1.0)
|
||||
water.add_macroscopic(h2o_data)
|
||||
|
||||
# Instantiate a MaterialsFile, register all Materials, and export to XML
|
||||
materials_file = openmc.MaterialsFile()
|
||||
materials_file.default_xs = '300K'
|
||||
materials_file.add_materials([uo2, water])
|
||||
materials_file.export_to_xml()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Exporting to OpenMC geometry.xml File
|
||||
###############################################################################
|
||||
|
||||
# Instantiate ZCylinder surfaces
|
||||
fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, R=0.54, name='Fuel OR')
|
||||
left = openmc.XPlane(surface_id=4, x0=-0.63, name='left')
|
||||
right = openmc.XPlane(surface_id=5, x0=0.63, name='right')
|
||||
bottom = openmc.YPlane(surface_id=6, y0=-0.63, name='bottom')
|
||||
top = openmc.YPlane(surface_id=7, y0=0.63, name='top')
|
||||
|
||||
left.boundary_type = 'reflective'
|
||||
right.boundary_type = 'reflective'
|
||||
top.boundary_type = 'reflective'
|
||||
bottom.boundary_type = 'reflective'
|
||||
|
||||
# Instantiate Cells
|
||||
fuel = openmc.Cell(cell_id=1, name='cell 1')
|
||||
moderator = openmc.Cell(cell_id=2, name='cell 2')
|
||||
|
||||
# Use surface half-spaces to define regions
|
||||
fuel.region = -fuel_or
|
||||
moderator.region = +fuel_or & +left & -right & +bottom & -top
|
||||
|
||||
# Register Materials with Cells
|
||||
fuel.fill = uo2
|
||||
moderator.fill = water
|
||||
|
||||
# Instantiate Universe
|
||||
root = openmc.Universe(universe_id=0, name='root universe')
|
||||
|
||||
# Register Cells with Universe
|
||||
root.add_cells([fuel, moderator])
|
||||
|
||||
# Instantiate a Geometry and register the root Universe
|
||||
geometry = openmc.Geometry()
|
||||
geometry.root_universe = root
|
||||
|
||||
# Instantiate a GeometryFile, register Geometry, and export to XML
|
||||
geometry_file = openmc.GeometryFile()
|
||||
geometry_file.geometry = geometry
|
||||
geometry_file.export_to_xml()
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Exporting to OpenMC settings.xml File
|
||||
###############################################################################
|
||||
|
||||
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
|
||||
settings_file = openmc.SettingsFile()
|
||||
settings_file.energy_mode = "multi-group"
|
||||
settings_file.cross_sections = "./mg_cross_sections.xml"
|
||||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
|
||||
# Create an initial uniform spatial source distribution over fissionable zones
|
||||
bounds = [-0.63, -0.63, -1, 0.63, 0.63, 1]
|
||||
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:])
|
||||
settings_file.source = openmc.source.Source(space=uniform_dist)
|
||||
|
||||
settings_file.export_to_xml()
|
||||
|
||||
###############################################################################
|
||||
# Exporting to OpenMC tallies.xml File
|
||||
###############################################################################
|
||||
|
||||
# Instantiate a tally mesh
|
||||
mesh = openmc.Mesh(mesh_id=1)
|
||||
mesh.type = 'regular'
|
||||
mesh.dimension = [100, 100, 1]
|
||||
mesh.lower_left = [-0.63, -0.63, -1.e50]
|
||||
mesh.upper_right = [0.63, 0.63, 1.e50]
|
||||
|
||||
# Instantiate some tally Filters
|
||||
energy_filter = openmc.Filter(type='energy',
|
||||
bins=[1E-11, 0.0635E-6, 10.0E-6, 1.0E-4, 1.0E-3,
|
||||
0.5, 1.0, 20.0])
|
||||
mesh_filter = openmc.Filter()
|
||||
mesh_filter.mesh = mesh
|
||||
|
||||
# Instantiate the Tally
|
||||
tally = openmc.Tally(tally_id=1, name='tally 1')
|
||||
tally.add_filter(energy_filter)
|
||||
tally.add_filter(mesh_filter)
|
||||
tally.add_score('flux')
|
||||
tally.add_score('fission')
|
||||
tally.add_score('nu-fission')
|
||||
|
||||
# Instantiate a TalliesFile, register all Tallies, and export to XML
|
||||
tallies_file = openmc.TalliesFile()
|
||||
tallies_file.add_mesh(mesh)
|
||||
tallies_file.add_tally(tally)
|
||||
tallies_file.export_to_xml()
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
import numpy as np
|
||||
|
||||
import openmc
|
||||
from openmc.stats import Box
|
||||
from openmc.source import Source
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
|
|
@ -86,5 +83,10 @@ settings_file = openmc.SettingsFile()
|
|||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
settings_file.source = Source(space=Box(*cell.region.bounding_box))
|
||||
|
||||
# Create an initial uniform spatial source distribution over fissionable zones
|
||||
uniform_dist = openmc.stats.Box(*cell.region.bounding_box,
|
||||
only_fissionable=True)
|
||||
settings_file.source = openmc.source.Source(space=uniform_dist)
|
||||
|
||||
settings_file.export_to_xml()
|
||||
|
|
|
|||
16
examples/xml/pincell_multigroup/geometry.xml
Normal file
16
examples/xml/pincell_multigroup/geometry.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<geometry>
|
||||
|
||||
|
||||
|
||||
<surface coeffs="0. 0. 0.540" id="1" type="z-cylinder" />
|
||||
|
||||
<surface boundary="reflective" coeffs="-0.63" id="20" type="x-plane" />
|
||||
<surface boundary="reflective" coeffs=" 0.63" id="21" type="x-plane" />
|
||||
<surface boundary="reflective" coeffs="-0.63" id="22" type="y-plane" />
|
||||
<surface boundary="reflective" coeffs=" 0.63" id="23" type="y-plane" />
|
||||
|
||||
|
||||
<cell id="1" material="1" region=" -1" />
|
||||
<cell id="2" material="7" region="1 20 -21 22 -23" />
|
||||
|
||||
</geometry>
|
||||
54
examples/xml/pincell_multigroup/materials.xml
Normal file
54
examples/xml/pincell_multigroup/materials.xml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0"?>
|
||||
<materials>
|
||||
<!-- Set default xs set to use 300K data -->
|
||||
<default_xs>300K</default_xs>
|
||||
|
||||
<!-- UO2 -->
|
||||
<material id="1">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="UO2"/>
|
||||
</material>
|
||||
|
||||
<!-- 4.3% MOX -->
|
||||
<material id="2">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="MOX1"/>
|
||||
</material>
|
||||
|
||||
<!-- 7.0 MOX -->
|
||||
<material id="3">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="MOX2"/>
|
||||
</material>
|
||||
|
||||
<!-- 8.0% MOX -->
|
||||
<material id="4">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="MOX3"/>
|
||||
</material>
|
||||
|
||||
<!-- Fission Chamber -->
|
||||
<material id="5">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="FC"/>
|
||||
</material>
|
||||
|
||||
<!-- Guide Tube -->
|
||||
<material id="6">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="GT"/>
|
||||
</material>
|
||||
|
||||
<!-- Water -->
|
||||
<material id="7">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="LWTR"/>
|
||||
</material>
|
||||
|
||||
<!-- Control Rod -->
|
||||
<material id="8">
|
||||
<density units="macro" value="1.0" />
|
||||
<macroscopic name="CR"/>
|
||||
</material>
|
||||
|
||||
</materials>
|
||||
383
examples/xml/pincell_multigroup/mg_cross_sections.xml
Normal file
383
examples/xml/pincell_multigroup/mg_cross_sections.xml
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
<?xml version="1.0"?>
|
||||
<library>
|
||||
<!-- Before getting to the data, set common information -->
|
||||
<groups> 7 </groups>
|
||||
<group_structure>
|
||||
1E-11 0.0635E-6 10.0E-6 1.0E-4 1.0E-3 0.5 1.0 20.0
|
||||
</group_structure>
|
||||
|
||||
<!--
|
||||
Move on to the data. Each <xsdata> has a unique id and label.
|
||||
-->
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>UO2.300K</name>
|
||||
<alias>UO2.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
<!-- Optional (default is isotropic) -->
|
||||
<representation>isotropic</representation>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
8.0248E-03 3.7174E-03 2.6769E-02 9.6236E-02 3.0020E-02 1.1126E-01 2.8278E-01
|
||||
</absorption>
|
||||
|
||||
<nu_fission>
|
||||
2.005998E-02 2.027303E-03 1.570599E-02 4.518301E-02 4.334208E-02 2.020901E-01 5.257105E-01
|
||||
</nu_fission>
|
||||
|
||||
<chi>
|
||||
5.8791E-01 4.1176E-01 3.3906E-04 1.1761E-07 0.0000E+00 0.0000E+00 0.0000E+00
|
||||
</chi>
|
||||
<fission>
|
||||
7.21206E-03 8.19301E-04 6.45320E-03 1.85648E-02 1.78084E-02 8.30348E-02 2.16004E-01
|
||||
</fission>
|
||||
|
||||
<!-- units of MeV/cm -->
|
||||
<!-- If no kappa fission tallies, this is not needed; it will not be loaded
|
||||
if there is no kappa fission scores anyways -->
|
||||
<k_fission>
|
||||
1.0 1.0 1.0 1.0 1.0 1.0 1.0
|
||||
</k_fission>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
0.1275370 0.0423780 0.0000094 0.0000000 0.0000000 0.0000000 0.0000000
|
||||
0.0000000 0.3244560 0.0016314 0.0000000 0.0000000 0.0000000 0.0000000
|
||||
0.0000000 0.0000000 0.4509400 0.0026792 0.0000000 0.0000000 0.0000000
|
||||
0.0000000 0.0000000 0.0000000 0.4525650 0.0055664 0.0000000 0.0000000
|
||||
0.0000000 0.0000000 0.0000000 0.0001253 0.2714010 0.0102550 0.0000000
|
||||
0.0000000 0.0000000 0.0000000 0.0000000 0.0012968 0.2658020 0.0168090
|
||||
0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0085458 0.2730800
|
||||
</scatter>
|
||||
|
||||
<!-- If total is not provided, it will be calculated.
|
||||
However, in the C5G7 problems, we want to use a transport-corrected value
|
||||
so we dont want to have it be calculated -->
|
||||
<total>
|
||||
0.1779492 0.3298048 0.4803882 0.5543674000000001 0.3118013 0.39516779999999996 0.5644058
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>MOX1.300K</name>
|
||||
<alias>MOX1.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
8.4339E-03 3.7577E-03 2.7970E-02 1.0421E-01 1.3994E-01 4.0918E-01 4.0935E-01
|
||||
</absorption>
|
||||
<!--
|
||||
Since chi_vector is false, this will be a matrix
|
||||
Matrix is g_in, g_out.
|
||||
This is to show that you can either use a chi vector + nu_fission vector,
|
||||
like in the UO2 data, or a nu_fission matrix like here.
|
||||
-->
|
||||
<nu_fission>
|
||||
1.27888062E-02 8.95701528E-03 7.37557218E-06 2.55837033E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
1.49041240E-03 1.04385401E-03 8.59552023E-07 2.98153464E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
9.56411400E-03 6.69850756E-03 5.51582469E-06 1.91327830E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
3.84928781E-02 2.69596154E-02 2.21996483E-05 7.70040890E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
1.80629998E-02 1.26509513E-02 1.04173100E-05 3.61346022E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
3.91930789E-01 2.74500216E-01 2.26034688E-04 7.84048241E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
4.19762096E-01 2.93992687E-01 2.42085585E-04 8.39724109E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
</nu_fission>
|
||||
|
||||
<fission>
|
||||
7.62704E-03 8.76898E-04 5.69835E-03 2.28872E-02 1.07635E-02 2.32757E-01 2.48968E-01
|
||||
</fission>
|
||||
|
||||
<!-- units of MeV/cm -->
|
||||
<k_fission>
|
||||
1.0 1.0 1.0 1.0 1.0 1.0 1.0
|
||||
</k_fission>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
1.27537000E-01 4.23780000E-02 9.43740000E-06 5.51630000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 3.24456000E-01 1.63140000E-03 3.14270000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 4.50940000E-01 2.67920000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 4.52565000E-01 5.56640000E-03 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 1.25250000E-04 2.71401000E-01 1.02550000E-02 1.00210000E-08
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 1.29680000E-03 2.65802000E-01 1.68090000E-02
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 8.54580000E-03 2.73080000E-01
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
0.1783583429163 0.3298451031427 0.4815892 0.5623414 0.421721260021 0.6930878 0.6909757999999999
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>MOX2.300K</name>
|
||||
<alias>MOX2.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
0.0090657 0.0042967 0.032881 0.12203 0.18298 0.56846 0.58521
|
||||
</absorption>
|
||||
<!--
|
||||
Since chi_vector is false, this will be a matrix
|
||||
Matrix is g_in, g_out !!! Need to get these values looking right to match
|
||||
output of a nu-fission tally with <energy> filter above <energyout> filter
|
||||
-->
|
||||
<nu_fission>
|
||||
1.40004593E-02 9.80563205E-03 8.07435789E-06 2.80075866E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
2.26856185E-03 1.58885378E-03 1.30832709E-06 4.53820413E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
1.41886199E-02 9.93741584E-03 8.18287404E-06 2.83839974E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
5.54788444E-02 3.88562347E-02 3.19958106E-05 1.10984111E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
2.69085702E-02 1.88462058E-02 1.55187355E-05 5.38299559E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
5.45687127E-01 3.82187973E-01 3.14709185E-04 1.09163414E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
6.13307712E-01 4.29548032E-01 3.53707392E-04 1.22690752E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
</nu_fission>
|
||||
|
||||
<fission>
|
||||
0.00825446 0.00132565 0.00842156 0.032873 0.0159636 0.323794 0.362803
|
||||
</fission>
|
||||
|
||||
<!-- units of MeV/cm -->
|
||||
<k_fission>
|
||||
1.0 1.0 1.0 1.0 1.0 1.0 1.0
|
||||
</k_fission>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
1.30457000E-01 4.17920000E-02 8.51050000E-06 5.13290000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 3.28428000E-01 1.64360000E-03 2.20170000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 4.58371000E-01 2.53310000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 4.63709000E-01 5.47660000E-03 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 1.76190000E-04 2.82313000E-01 8.72890000E-03 9.00160000E-09
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 2.27600000E-03 2.49751000E-01 1.31140000E-02
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 8.86450000E-03 2.59529000E-01
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
0.1813232156329 0.3343683022017 0.4937851 0.5912156 0.47419809900160004 0.833601 0.8536035
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>MOX3.300K</name>
|
||||
<alias>MOX3.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
9.48620000E-03 4.65560000E-03 3.62400000E-02 1.32720000E-01 2.08400000E-01 6.58700000E-01 6.90170000E-01
|
||||
</absorption>
|
||||
<!--
|
||||
Since chi_vector is false, this will be a matrix
|
||||
Matrix is g_in, g_out !!! Need to get these values looking right to match
|
||||
output of a nu-fission tally with <energy> filter above <energyout> filter
|
||||
-->
|
||||
<nu_fission>
|
||||
1.48071013E-02 1.03705874E-02 8.53956516E-06 2.96212546E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
2.78640474E-03 1.95154023E-03 1.60697792E-06 5.57413653E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
1.73304404E-02 1.21378819E-02 9.99482763E-06 3.46691346E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
6.59928975E-02 4.62200600E-02 3.80594850E-05 1.32017225E-08 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
3.25131926E-02 2.27715674E-02 1.87510386E-05 6.50418701E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
6.32002662E-01 4.42641588E-01 3.64489161E-04 1.26430632E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
7.28595687E-01 5.10293344E-01 4.20196380E-04 1.45753838E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
</nu_fission>
|
||||
|
||||
<fission>
|
||||
8.67209000E-03 1.62426000E-03 1.02716000E-02 3.90447000E-02 1.92576000E-02 3.74888000E-01 4.30599000E-01
|
||||
</fission>
|
||||
|
||||
<!-- units of MeV/cm -->
|
||||
<k_fission>
|
||||
1.0 1.0 1.0 1.0 1.0 1.0 1.0
|
||||
</k_fission>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
1.31504000E-01 4.20460000E-02 8.69720000E-06 5.19380000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 3.30403000E-01 1.64630000E-03 2.60060000E-09 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 4.61792000E-01 2.47490000E-03 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 4.68021000E-01 5.43300000E-03 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 1.85970000E-04 2.85771000E-01 8.39730000E-03 8.92800000E-09
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 2.39160000E-03 2.47614000E-01 1.23220000E-02
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 8.96810000E-03 2.56093000E-01
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
1.83044902E-01 3.36704903E-01 5.00506900E-01 6.06174000E-01 5.02754279E-01 9.21027600E-01 9.55231100E-01
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>FC.300K</name>
|
||||
<alias>FC.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
5.1132E-04 7.5813E-05 3.1643E-04 1.1675E-03 3.3977E-03 9.1886E-03 2.3244E-02
|
||||
</absorption>
|
||||
|
||||
<nu_fission>
|
||||
1.323401E-08 1.434500E-08 1.128599E-06 1.276299E-05 3.538502E-07 1.740099E-06 5.063302E-06
|
||||
</nu_fission>
|
||||
|
||||
<chi>
|
||||
5.8791E-01 4.1176E-01 3.3906E-04 1.1761E-07 0.0000E+00 0.0000E+00 0.0000E+00
|
||||
</chi>
|
||||
|
||||
<fission>
|
||||
4.79002E-09 5.82564E-09 4.63719E-07 5.24406E-06 1.45390E-07 7.14972E-07 2.08041E-06
|
||||
</fission>
|
||||
|
||||
<!-- units of MeV/cm -->
|
||||
<k_fission>
|
||||
1.0 1.0 1.0 1.0 1.0 1.0 1.0
|
||||
</k_fission>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
6.61659000E-02 5.90700000E-02 2.83340000E-04 1.46220000E-06 2.06420000E-08 0.00000000E00 0.00000000E00
|
||||
0.00000000E00 2.40377000E-01 5.24350000E-02 2.49900000E-04 1.92390000E-05 2.98750000E-06 4.21400000E-07
|
||||
0.00000000E00 0.00000000E00 1.83425000E-01 9.22880000E-02 6.93650000E-03 1.07900000E-03 2.05430000E-04
|
||||
0.00000000E00 0.00000000E00 0.00000000E00 7.90769000E-02 1.69990000E-01 2.58600000E-02 4.92560000E-03
|
||||
0.00000000E00 0.00000000E00 0.00000000E00 3.73400000E-05 9.97570000E-02 2.06790000E-01 2.44780000E-02
|
||||
0.00000000E00 0.00000000E00 0.00000000E00 0.00000000E00 9.17420000E-04 3.16774000E-01 2.38760000E-01
|
||||
0.00000000E00 0.00000000E00 0.00000000E00 0.00000000E00 0.00000000E00 4.97930000E-02 1.09910000E00
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
1.26032048E-01 2.93160367E-01 2.84250824E-01 2.81025244E-01 3.34460185E-01 5.65640735E-01 1.17213908E00
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>GT.300K</name>
|
||||
<alias>GT.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>false</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
5.11320000E-04 7.58010000E-05 3.15720000E-04 1.15820000E-03 3.39750000E-03 9.18780000E-03 2.32420000E-02
|
||||
</absorption>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
6.61659000E-02 5.90700000E-02 2.83340000E-04 1.46220000E-06 2.06420000E-08 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 2.40377000E-01 5.24350000E-02 2.49900000E-04 1.92390000E-05 2.98750000E-06 4.21400000E-07
|
||||
0.00000000E+00 0.00000000E+00 1.83297000E-01 9.23970000E-02 6.94460000E-03 1.08030000E-03 2.05670000E-04
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 7.88511000E-02 1.70140000E-01 2.58810000E-02 4.92970000E-03
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 3.73330000E-05 9.97372000E-02 2.06790000E-01 2.44780000E-02
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 9.17260000E-04 3.16765000E-01 2.38770000E-01
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 4.97920000E-02 1.09912000E+00
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
1.26032043E-01 2.93160349E-01 2.84240290E-01 2.80960000E-01 3.34440033E-01 5.65640060E-01 1.17215400E+00
|
||||
</total>
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>LWTR.300K</name>
|
||||
<alias>LWTR.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>false</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
6.0105E-04 1.5793E-05 3.3716E-04 1.9406E-03 5.7416E-03 1.5001E-02 3.7239E-02
|
||||
</absorption>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
0.0444777 0.1134000 0.0007235 0.0000037 0.0000001 0.0000000 0.0000000
|
||||
0.0000000 0.2823340 0.1299400 0.0006234 0.0000480 0.0000074 0.0000010
|
||||
0.0000000 0.0000000 0.3452560 0.2245700 0.0169990 0.0026443 0.0005034
|
||||
0.0000000 0.0000000 0.0000000 0.0910284 0.4155100 0.0637320 0.0121390
|
||||
0.0000000 0.0000000 0.0000000 0.0000714 0.1391380 0.5118200 0.0612290
|
||||
0.0000000 0.0000000 0.0000000 0.0000000 0.0022157 0.6999130 0.5373200
|
||||
0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.1324400 2.4807000
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
0.15920605 0.41296959299999997 0.59030986 0.5843499999999999 0.7180000000000001 1.2544497000000001 2.650379
|
||||
</total>
|
||||
|
||||
</xsdata>
|
||||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>CR.300K</name>
|
||||
<alias>CR.300K</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>false</fissionable>
|
||||
|
||||
<!-- The data itself, like tallies,
|
||||
goes from low energies (groups) to high energies
|
||||
-->
|
||||
<absorption>
|
||||
1.70490000E-03 8.36224000E-03 8.37901000E-02 3.97797000E-01 6.98763000E-01 9.29508000E-01 1.17836000E+00
|
||||
</absorption>
|
||||
|
||||
<!-- for consistency must include nu-scatter -->
|
||||
<!-- will be a matrix of (order+1) x g_in x g_out -->
|
||||
<scatter>
|
||||
1.70563000E-01 4.44012000E-02 9.83670000E-05 1.27786000E-07 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 4.71050000E-01 6.85480000E-04 3.91395000E-10 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 8.01859000E-01 7.20132000E-04 0.00000000E+00 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 5.70752000E-01 1.46015000E-03 0.00000000E+00 0.00000000E+00
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 6.55562000E-05 2.07838000E-01 3.81486000E-03 3.69760000E-09
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 1.02427000E-03 2.02465000E-01 4.75290000E-03
|
||||
0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 3.53043000E-03 6.58597000E-01
|
||||
</scatter>
|
||||
|
||||
<total>
|
||||
2.16767595E-01 4.80097720E-01 8.86369232E-01 9.70009150E-01 9.10481420E-01 1.13775017E+00 1.84048743E+00
|
||||
</total>
|
||||
</xsdata>
|
||||
</library>
|
||||
28
examples/xml/pincell_multigroup/plots.xml
Normal file
28
examples/xml/pincell_multigroup/plots.xml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0"?>
|
||||
<plots>
|
||||
|
||||
<plot>
|
||||
<id>1</id>
|
||||
<filename>mat</filename>
|
||||
<color>material</color>
|
||||
<origin>0 0 0</origin>
|
||||
<width>1.26 1.26</width>
|
||||
<type>slice</type>
|
||||
<pixels>1000 1000 </pixels>
|
||||
<col_spec id="1" rgb="255 0 0" />
|
||||
<col_spec id="2" rgb="0 0 0" />
|
||||
<col_spec id="3" rgb="0 255 0" />
|
||||
<col_spec id="4" rgb="0 0 255" />
|
||||
</plot>
|
||||
|
||||
<plot>
|
||||
<id>2</id>
|
||||
<filename>cell</filename>
|
||||
<color>cell</color>
|
||||
<origin>0 0 0</origin>
|
||||
<width>1.26 1.26</width>
|
||||
<type>slice</type>
|
||||
<pixels>1000 1000 </pixels>
|
||||
</plot>
|
||||
|
||||
</plots>
|
||||
40
examples/xml/pincell_multigroup/settings.xml
Normal file
40
examples/xml/pincell_multigroup/settings.xml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0"?>
|
||||
<settings>
|
||||
|
||||
<energy_mode>multi-group</energy_mode>
|
||||
|
||||
<!--
|
||||
Define how many particles to run and for how many batches
|
||||
in an eigenvalue calculation mode
|
||||
-->
|
||||
<eigenvalue>
|
||||
<batches>100</batches>
|
||||
<inactive>10</inactive>
|
||||
<particles>1000</particles>
|
||||
</eigenvalue>
|
||||
|
||||
<!--
|
||||
Start with uniformally distributed neutron source
|
||||
with the default energy spectrum of a Maxwellian
|
||||
and isotropic distribution.
|
||||
-->
|
||||
<source>
|
||||
<space type="box">
|
||||
<parameters>
|
||||
-0.63 -0.63 -1E50
|
||||
0.63 0.63 1E50
|
||||
</parameters>
|
||||
</space>
|
||||
</source>
|
||||
|
||||
<output>
|
||||
<cross_sections>true</cross_sections>
|
||||
<summary>true</summary>
|
||||
<tallies>true</tallies>
|
||||
</output>
|
||||
|
||||
<survival_biasing>false</survival_biasing>
|
||||
|
||||
<cross_sections>./mg_cross_sections.xml</cross_sections>
|
||||
|
||||
</settings>
|
||||
13
examples/xml/pincell_multigroup/tallies.xml
Normal file
13
examples/xml/pincell_multigroup/tallies.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0"?>
|
||||
<tallies>
|
||||
<mesh id="1" type="regular">
|
||||
<dimension>100 100 1</dimension>
|
||||
<lower_left>-0.63 -0.63 -1e+50</lower_left>
|
||||
<upper_right>0.63 0.63 1e+50</upper_right>
|
||||
</mesh>
|
||||
<tally id="1" name="tally 1">
|
||||
<filter bins="1e-11 6.35e-08 1e-05 0.0001 0.001 0.5 1.0 20.0" type="energy" />
|
||||
<filter bins="1" type="mesh" />
|
||||
<scores>flux fission nu-fission</scores>
|
||||
</tally>
|
||||
</tallies>
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
from openmc.cell import *
|
||||
from openmc.lattice import *
|
||||
from openmc.element import *
|
||||
from openmc.geometry import *
|
||||
from openmc.nuclide import *
|
||||
from openmc.macroscopic import *
|
||||
from openmc.material import *
|
||||
from openmc.plots import *
|
||||
from openmc.settings import *
|
||||
from openmc.surface import *
|
||||
from openmc.universe import *
|
||||
from openmc.mgxs_library import *
|
||||
from openmc.mesh import *
|
||||
from openmc.filter import *
|
||||
from openmc.trigger import *
|
||||
|
|
@ -15,6 +19,9 @@ from openmc.cmfd import *
|
|||
from openmc.executor import *
|
||||
from openmc.statepoint import *
|
||||
from openmc.summary import *
|
||||
from openmc.region import *
|
||||
from openmc.source import *
|
||||
from openmc.particle_restart import *
|
||||
|
||||
try:
|
||||
from openmc.opencg_compatible import *
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import sys
|
||||
import copy
|
||||
from numbers import Integral
|
||||
from collections import Iterable
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
|
@ -14,7 +16,7 @@ if sys.version_info[0] >= 3:
|
|||
_TALLY_ARITHMETIC_OPS = ['+', '-', '*', '/', '^']
|
||||
|
||||
# Acceptable tally aggregation operations
|
||||
_TALLY_AGGREGATE_OPS = ['sum', 'mean']
|
||||
_TALLY_AGGREGATE_OPS = ['sum', 'avg']
|
||||
|
||||
|
||||
class CrossScore(object):
|
||||
|
|
@ -186,6 +188,23 @@ class CrossNuclide(object):
|
|||
return existing
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@property
|
||||
def left_nuclide(self):
|
||||
return self._left_nuclide
|
||||
|
||||
@property
|
||||
def right_nuclide(self):
|
||||
return self._right_nuclide
|
||||
|
||||
@property
|
||||
def binary_op(self):
|
||||
return self._binary_op
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
||||
string = ''
|
||||
|
||||
|
|
@ -207,18 +226,6 @@ class CrossNuclide(object):
|
|||
|
||||
return string
|
||||
|
||||
@property
|
||||
def left_nuclide(self):
|
||||
return self._left_nuclide
|
||||
|
||||
@property
|
||||
def right_nuclide(self):
|
||||
return self._right_nuclide
|
||||
|
||||
@property
|
||||
def binary_op(self):
|
||||
return self._binary_op
|
||||
|
||||
@left_nuclide.setter
|
||||
def left_nuclide(self, left_nuclide):
|
||||
cv.check_type('left_nuclide', left_nuclide,
|
||||
|
|
@ -430,7 +437,7 @@ class CrossFilter(object):
|
|||
filter_index = left_index * self.right_filter.num_bins + right_index
|
||||
return filter_index
|
||||
|
||||
def get_pandas_dataframe(self, datasize, summary=None):
|
||||
def get_pandas_dataframe(self, data_size, summary=None):
|
||||
"""Builds a Pandas DataFrame for the CrossFilter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the CrossFilter
|
||||
|
|
@ -445,7 +452,7 @@ class CrossFilter(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
datasize : Integral
|
||||
data_size : Integral
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
summary : None or Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
|
|
@ -472,19 +479,18 @@ class CrossFilter(object):
|
|||
|
||||
# If left and right filters are identical, do not combine bins
|
||||
if self.left_filter == self.right_filter:
|
||||
df = self.left_filter.get_pandas_dataframe(datasize, summary)
|
||||
df = self.left_filter.get_pandas_dataframe(data_size, summary)
|
||||
|
||||
# If left and right filters are different, combine their bins
|
||||
else:
|
||||
left_df = self.left_filter.get_pandas_dataframe(datasize, summary)
|
||||
right_df = self.right_filter.get_pandas_dataframe(datasize, summary)
|
||||
left_df = self.left_filter.get_pandas_dataframe(data_size, summary)
|
||||
right_df = self.right_filter.get_pandas_dataframe(data_size, summary)
|
||||
left_df = left_df.astype(str)
|
||||
right_df = right_df.astype(str)
|
||||
df = '(' + left_df + ' ' + self.binary_op + ' ' + right_df + ')'
|
||||
|
||||
return df
|
||||
|
||||
|
||||
class AggregateScore(object):
|
||||
"""A special-purpose tally score used to encapsulate an aggregate of a
|
||||
subset or all of tally's scores for tally aggregation.
|
||||
|
|
@ -494,7 +500,7 @@ class AggregateScore(object):
|
|||
scores : Iterable of str or CrossScore
|
||||
The scores included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally's scores with this AggregateScore
|
||||
|
||||
Attributes
|
||||
|
|
@ -502,7 +508,7 @@ class AggregateScore(object):
|
|||
scores : Iterable of str or CrossScore
|
||||
The scores included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally's scores with this AggregateScore
|
||||
|
||||
"""
|
||||
|
|
@ -556,10 +562,16 @@ class AggregateScore(object):
|
|||
def aggregate_op(self):
|
||||
return self._aggregate_op
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
||||
# Append each score in the aggregate to the string
|
||||
string = '(' + ', '.join(self.scores) + ')'
|
||||
return string
|
||||
|
||||
@scores.setter
|
||||
def scores(self, scores):
|
||||
cv.check_iterable_type('scores', scores,
|
||||
(basestring, CrossScore, AggregateScore))
|
||||
cv.check_iterable_type('scores', scores, basestring)
|
||||
self._scores = scores
|
||||
|
||||
@aggregate_op.setter
|
||||
|
|
@ -578,7 +590,7 @@ class AggregateNuclide(object):
|
|||
nuclides : Iterable of str or Nuclide or CrossNuclide
|
||||
The nuclides included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally's nuclides with this AggregateNuclide
|
||||
|
||||
Attributes
|
||||
|
|
@ -586,7 +598,7 @@ class AggregateNuclide(object):
|
|||
nuclides : Iterable of str or Nuclide or CrossNuclide
|
||||
The nuclides included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally's nuclides with this AggregateNuclide
|
||||
|
||||
"""
|
||||
|
|
@ -644,10 +656,19 @@ class AggregateNuclide(object):
|
|||
def aggregate_op(self):
|
||||
return self._aggregate_op
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
||||
# Append each nuclide in the aggregate to the string
|
||||
names = [nuclide.name if isinstance(nuclide, Nuclide) else str(nuclide)
|
||||
for nuclide in self.nuclides]
|
||||
string = '(' + ', '.join(map(str, names)) + ')'
|
||||
return string
|
||||
|
||||
@nuclides.setter
|
||||
def nuclides(self, nuclides):
|
||||
cv.check_iterable_type('nuclides', nuclides,
|
||||
(basestring, Nuclide, CrossNuclide, AggregateNuclide))
|
||||
(basestring, Nuclide, CrossNuclide))
|
||||
self._nuclides = nuclides
|
||||
|
||||
@aggregate_op.setter
|
||||
|
|
@ -668,7 +689,7 @@ class AggregateFilter(object):
|
|||
bins : Iterable of tuple
|
||||
The filter bins included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally filter's bins with this AggregateFilter
|
||||
|
||||
Attributes
|
||||
|
|
@ -678,7 +699,7 @@ class AggregateFilter(object):
|
|||
aggregate_filter : filter
|
||||
The filter included in the aggregation
|
||||
aggregate_op : str
|
||||
The tally aggregation operator (e.g., 'sum', 'mean', etc.) used
|
||||
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
|
||||
to aggregate across a tally filter's bins with this AggregateFilter
|
||||
bins : Iterable of tuple
|
||||
The filter bins included in the aggregation
|
||||
|
|
@ -715,6 +736,21 @@ class AggregateFilter(object):
|
|||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __gt__(self, other):
|
||||
if self.type != other.type:
|
||||
if self.aggregate_filter.type in _FILTER_TYPES and \
|
||||
other.aggregate_filter.type in _FILTER_TYPES:
|
||||
delta = _FILTER_TYPES.index(self.aggregate_filter.type) - \
|
||||
_FILTER_TYPES.index(other.aggregate_filter.type)
|
||||
return delta > 0
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def __lt__(self, other):
|
||||
return not self > other
|
||||
|
||||
def __repr__(self):
|
||||
string = 'AggregateFilter\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.type)
|
||||
|
|
@ -759,7 +795,7 @@ class AggregateFilter(object):
|
|||
|
||||
@property
|
||||
def num_bins(self):
|
||||
return 1 if self.aggregate_filter else 0
|
||||
return len(self.bins) if self.aggregate_filter else 0
|
||||
|
||||
@property
|
||||
def stride(self):
|
||||
|
|
@ -776,14 +812,13 @@ class AggregateFilter(object):
|
|||
|
||||
@aggregate_filter.setter
|
||||
def aggregate_filter(self, aggregate_filter):
|
||||
cv.check_type('aggregate_filter', aggregate_filter,
|
||||
(Filter, CrossFilter, AggregateFilter))
|
||||
cv.check_type('aggregate_filter', aggregate_filter, (Filter, CrossFilter))
|
||||
self._aggregate_filter = aggregate_filter
|
||||
|
||||
@bins.setter
|
||||
def bins(self, bins):
|
||||
cv.check_iterable_type('bins', bins, (Integral, tuple))
|
||||
self._bins = bins
|
||||
cv.check_iterable_type('bins', bins, Iterable)
|
||||
self._bins = list(map(tuple, bins))
|
||||
|
||||
@aggregate_op.setter
|
||||
def aggregate_op(self, aggregate_op):
|
||||
|
|
@ -823,15 +858,14 @@ class AggregateFilter(object):
|
|||
|
||||
"""
|
||||
|
||||
if filter_bin not in self.bins and \
|
||||
filter_bin != self._aggregate_filter.bins:
|
||||
if filter_bin not in self.bins:
|
||||
msg = 'Unable to get the bin index for AggregateFilter since ' \
|
||||
'"{0}" is not one of the bins'.format(filter_bin)
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
return 0
|
||||
return self.bins.index(filter_bin)
|
||||
|
||||
def get_pandas_dataframe(self, datasize, summary=None):
|
||||
def get_pandas_dataframe(self, data_size, summary=None):
|
||||
"""Builds a Pandas DataFrame for the AggregateFilter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the AggregateFilter
|
||||
|
|
@ -840,7 +874,7 @@ class AggregateFilter(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
datasize : Integral
|
||||
data_size : Integral
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
summary : None or Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
|
|
@ -868,14 +902,80 @@ class AggregateFilter(object):
|
|||
|
||||
import pandas as pd
|
||||
|
||||
# Construct a sring representing the filter aggregation
|
||||
aggregate_bin = '{0}('.format(self.aggregate_op)
|
||||
aggregate_bin += ', '.join(map(str, self.bins)) + ')'
|
||||
# Create NumPy array of the bin tuples for repeating / tiling
|
||||
filter_bins = np.empty(self.num_bins, dtype=tuple)
|
||||
for i, bin in enumerate(self.bins):
|
||||
filter_bins[i] = bin
|
||||
|
||||
# Construct NumPy array of bin repeated for each element in dataframe
|
||||
aggregate_bin_array = np.array([aggregate_bin])
|
||||
aggregate_bin_array = np.repeat(aggregate_bin_array, datasize)
|
||||
# Repeat and tile bins as needed for DataFrame
|
||||
filter_bins = np.repeat(filter_bins, self.stride)
|
||||
tile_factor = data_size / len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
|
||||
# Construct Pandas DataFrame for the AggregateFilter
|
||||
df = pd.DataFrame({self.type: aggregate_bin_array})
|
||||
# Create DataFrame with aggregated bins
|
||||
df = pd.DataFrame({self.type: filter_bins})
|
||||
return df
|
||||
|
||||
def can_merge(self, other):
|
||||
"""Determine if AggregateFilter can be merged with another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : AggregateFilter
|
||||
Filter to compare with
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
Whether the filter can be merged
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(other, AggregateFilter):
|
||||
return False
|
||||
|
||||
# Filters must be of the same type
|
||||
elif self.type != other.type:
|
||||
return False
|
||||
|
||||
# None of the bins in this filter should match in the other filter
|
||||
for bin in self.bins:
|
||||
if bin in other.bins:
|
||||
return False
|
||||
|
||||
# If all conditional checks passed then filters are mergeable
|
||||
return True
|
||||
|
||||
def merge(self, other):
|
||||
"""Merge this aggregatefilter with another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : AggregateFilter
|
||||
Filter to merge with
|
||||
|
||||
Returns
|
||||
-------
|
||||
merged_filter : AggregateFilter
|
||||
Filter resulting from the merge
|
||||
|
||||
"""
|
||||
|
||||
if not self.can_merge(other):
|
||||
msg = 'Unable to merge "{0}" with "{1}" ' \
|
||||
'filters'.format(self.type, other.type)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Create deep copy of filter to return as merged filter
|
||||
merged_filter = copy.deepcopy(self)
|
||||
|
||||
# Merge unique filter bins
|
||||
merged_bins = self.bins + other.bins
|
||||
|
||||
# Sort energy bin edges
|
||||
if 'energy' in self.type:
|
||||
merged_bins = sorted(merged_bins)
|
||||
|
||||
# Assign merged bins to merged filter
|
||||
merged_filter.bins = list(merged_bins)
|
||||
return merged_filter
|
||||
|
|
|
|||
478
openmc/cell.py
Normal file
478
openmc/cell.py
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
from collections import OrderedDict, Iterable
|
||||
from numbers import Real, Integral
|
||||
from xml.etree import ElementTree as ET
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import openmc
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.surface import Halfspace
|
||||
from openmc.region import Region, Intersection, Complement
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
|
||||
# A static variable for auto-generated Cell IDs
|
||||
AUTO_CELL_ID = 10000
|
||||
|
||||
|
||||
def reset_auto_cell_id():
|
||||
global AUTO_CELL_ID
|
||||
AUTO_CELL_ID = 10000
|
||||
|
||||
|
||||
class Cell(object):
|
||||
"""A region of space defined as the intersection of half-space created by
|
||||
quadric surfaces.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cell_id : int, optional
|
||||
Unique identifier for the cell. If not specified, an identifier will
|
||||
automatically be assigned.
|
||||
name : str, optional
|
||||
Name of the cell. If not specified, the name is the empty string.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id : int
|
||||
Unique identifier for the cell
|
||||
name : str
|
||||
Name of the cell
|
||||
fill : openmc.Material or openmc.Universe or openmc.Lattice or 'void' or iterable of openmc.Material
|
||||
Indicates what the region of space is filled with
|
||||
region : openmc.Region
|
||||
Region of space that is assigned to the cell.
|
||||
rotation : numpy.ndarray
|
||||
If the cell is filled with a universe, this array specifies the angles
|
||||
in degrees about the x, y, and z axes that the filled universe should be
|
||||
rotated.
|
||||
temperature : float or iterable of float
|
||||
Temperature of the cell in Kelvin. Multiple temperatures can be given
|
||||
to give each distributed cell instance a unique temperature.
|
||||
translation : numpy.ndarray
|
||||
If the cell is filled with a universe, this array specifies a vector
|
||||
that is used to translate (shift) the universe.
|
||||
offsets : ndarray
|
||||
Array of offsets used for distributed cell searches
|
||||
distribcell_index : int
|
||||
Index of this cell in distribcell arrays
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, cell_id=None, name=''):
|
||||
# Initialize Cell class attributes
|
||||
self.id = cell_id
|
||||
self.name = name
|
||||
self._fill = None
|
||||
self._type = None
|
||||
self._region = None
|
||||
self._temperature = None
|
||||
self._rotation = None
|
||||
self._translation = None
|
||||
self._offsets = None
|
||||
self._distribcell_index = None
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Cell):
|
||||
return False
|
||||
elif self.id != other.id:
|
||||
return False
|
||||
elif self.name != other.name:
|
||||
return False
|
||||
elif self.fill != other.fill:
|
||||
return False
|
||||
elif self.region != other.region:
|
||||
return False
|
||||
elif self.rotation != other.rotation:
|
||||
return False
|
||||
elif self.temperature != other.temperature:
|
||||
return False
|
||||
elif self.translation != other.translation:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
def __repr__(self):
|
||||
string = 'Cell\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
|
||||
|
||||
if isinstance(self._fill, openmc.Material):
|
||||
string += '{0: <16}{1}{2}\n'.format('\tMaterial', '=\t',
|
||||
self._fill._id)
|
||||
elif isinstance(self._fill, Iterable):
|
||||
string += '{0: <16}{1}'.format('\tMaterial', '=\t')
|
||||
string += '['
|
||||
string += ', '.join(['void' if m == 'void' else str(m.id)
|
||||
for m in self.fill])
|
||||
string += ']\n'
|
||||
elif isinstance(self._fill, (openmc.Universe, openmc.Lattice)):
|
||||
string += '{0: <16}{1}{2}\n'.format('\tFill', '=\t',
|
||||
self._fill._id)
|
||||
else:
|
||||
string += '{0: <16}{1}{2}\n'.format('\tFill', '=\t', self._fill)
|
||||
|
||||
string += '{0: <16}{1}{2}\n'.format('\tRegion', '=\t', self._region)
|
||||
|
||||
string += '{0: <16}{1}{2}\n'.format('\tRotation', '=\t',
|
||||
self._rotation)
|
||||
if self.fill_type == 'material':
|
||||
string += '\t{0: <15}=\t{1}\n'.format('Temperature',
|
||||
self.temperature)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tTranslation', '=\t',
|
||||
self._translation)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tOffset', '=\t', self._offsets)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tDistribcell index', '=\t',
|
||||
self._distribcell_index)
|
||||
|
||||
return string
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def fill(self):
|
||||
return self._fill
|
||||
|
||||
@property
|
||||
def fill_type(self):
|
||||
if isinstance(self.fill, openmc.Material):
|
||||
return 'material'
|
||||
elif isinstance(self.fill, openmc.Universe):
|
||||
return 'universe'
|
||||
elif isinstance(self.fill, openmc.Lattice):
|
||||
return 'lattice'
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def region(self):
|
||||
return self._region
|
||||
|
||||
@property
|
||||
def rotation(self):
|
||||
return self._rotation
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
return self._temperature
|
||||
|
||||
@property
|
||||
def translation(self):
|
||||
return self._translation
|
||||
|
||||
@property
|
||||
def offsets(self):
|
||||
return self._offsets
|
||||
|
||||
@property
|
||||
def distribcell_index(self):
|
||||
return self._distribcell_index
|
||||
|
||||
@id.setter
|
||||
def id(self, cell_id):
|
||||
if cell_id is None:
|
||||
global AUTO_CELL_ID
|
||||
self._id = AUTO_CELL_ID
|
||||
AUTO_CELL_ID += 1
|
||||
else:
|
||||
cv.check_type('cell ID', cell_id, Integral)
|
||||
cv.check_greater_than('cell ID', cell_id, 0, equality=True)
|
||||
self._id = cell_id
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
if name is not None:
|
||||
cv.check_type('cell name', name, basestring)
|
||||
self._name = name
|
||||
else:
|
||||
self._name = ''
|
||||
|
||||
@fill.setter
|
||||
def fill(self, fill):
|
||||
if isinstance(fill, basestring):
|
||||
if fill.strip().lower() == 'void':
|
||||
self._type = 'void'
|
||||
else:
|
||||
msg = 'Unable to set Cell ID="{0}" to use a non-Material or ' \
|
||||
'Universe fill "{1}"'.format(self._id, fill)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif isinstance(fill, openmc.Material):
|
||||
self._type = 'normal'
|
||||
|
||||
elif isinstance(fill, Iterable):
|
||||
cv.check_type('cell.fill', fill, Iterable,
|
||||
(openmc.Material, basestring))
|
||||
self._type = 'normal'
|
||||
|
||||
elif isinstance(fill, openmc.Universe):
|
||||
self._type = 'fill'
|
||||
|
||||
elif isinstance(fill, openmc.Lattice):
|
||||
self._type = 'lattice'
|
||||
|
||||
else:
|
||||
msg = 'Unable to set Cell ID="{0}" to use a non-Material or ' \
|
||||
'Universe fill "{1}"'.format(self._id, fill)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._fill = fill
|
||||
|
||||
@rotation.setter
|
||||
def rotation(self, rotation):
|
||||
cv.check_type('cell rotation', rotation, Iterable, Real)
|
||||
cv.check_length('cell rotation', rotation, 3)
|
||||
self._rotation = rotation
|
||||
|
||||
@translation.setter
|
||||
def translation(self, translation):
|
||||
cv.check_type('cell translation', translation, Iterable, Real)
|
||||
cv.check_length('cell translation', translation, 3)
|
||||
self._translation = translation
|
||||
|
||||
@temperature.setter
|
||||
def temperature(self, temperature):
|
||||
cv.check_type('cell temperature', temperature, (Iterable, Real))
|
||||
if isinstance(temperature, Iterable):
|
||||
cv.check_type('cell temperature', temperature, Iterable, Real)
|
||||
for T in temperature:
|
||||
cv.check_greater_than('cell temperature', T, 0.0, True)
|
||||
else:
|
||||
cv.check_greater_than('cell temperature', temperature, 0.0, True)
|
||||
self._temperature = temperature
|
||||
|
||||
@offsets.setter
|
||||
def offsets(self, offsets):
|
||||
cv.check_type('cell offsets', offsets, Iterable)
|
||||
self._offsets = offsets
|
||||
|
||||
@region.setter
|
||||
def region(self, region):
|
||||
cv.check_type('cell region', region, Region)
|
||||
self._region = region
|
||||
|
||||
@distribcell_index.setter
|
||||
def distribcell_index(self, ind):
|
||||
cv.check_type('distribcell index', ind, Integral)
|
||||
self._distribcell_index = ind
|
||||
|
||||
def add_surface(self, surface, halfspace):
|
||||
"""Add a half-space to the list of half-spaces whose intersection defines the
|
||||
cell.
|
||||
|
||||
.. deprecated:: 0.7.1
|
||||
Use the :attr:`Cell.region` property to directly specify a Region
|
||||
expression.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
surface : openmc.Surface
|
||||
Quadric surface dividing space
|
||||
halfspace : {-1, 1}
|
||||
Indicate whether the negative or positive half-space is to be used
|
||||
|
||||
"""
|
||||
|
||||
warnings.warn("Cell.add_surface(...) has been deprecated and may be "
|
||||
"removed in a future version. The region for a Cell "
|
||||
"should be defined using the region property directly.",
|
||||
DeprecationWarning)
|
||||
|
||||
if not isinstance(surface, openmc.Surface):
|
||||
msg = 'Unable to add Surface "{0}" to Cell ID="{1}" since it is ' \
|
||||
'not a Surface object'.format(surface, self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
if halfspace not in [-1, +1]:
|
||||
msg = 'Unable to add Surface "{0}" to Cell ID="{1}" with halfspace ' \
|
||||
'"{2}" since it is not +/-1'.format(surface, self._id, halfspace)
|
||||
raise ValueError(msg)
|
||||
|
||||
# If no region has been assigned, simply use the half-space. Otherwise,
|
||||
# take the intersection of the current region and the half-space
|
||||
# specified
|
||||
region = +surface if halfspace == 1 else -surface
|
||||
if self.region is None:
|
||||
self.region = region
|
||||
else:
|
||||
if isinstance(self.region, Intersection):
|
||||
self.region.nodes.append(region)
|
||||
else:
|
||||
self.region = Intersection(self.region, region)
|
||||
|
||||
def get_cell_instance(self, path, distribcell_index):
|
||||
|
||||
# If the Cell is filled by a Material
|
||||
if self._type == 'normal' or self._type == 'void':
|
||||
offset = 0
|
||||
|
||||
# If the Cell is filled by a Universe
|
||||
elif self._type == 'fill':
|
||||
offset = self.offsets[distribcell_index-1]
|
||||
offset += self.fill.get_cell_instance(path, distribcell_index)
|
||||
|
||||
# If the Cell is filled by a Lattice
|
||||
else:
|
||||
offset = self.fill.get_cell_instance(path, distribcell_index)
|
||||
|
||||
return offset
|
||||
|
||||
def get_all_nuclides(self):
|
||||
"""Return all nuclides contained in the cell
|
||||
|
||||
Returns
|
||||
-------
|
||||
nuclides : dict
|
||||
Dictionary whose keys are nuclide names and values are 2-tuples of
|
||||
(nuclide, density)
|
||||
|
||||
"""
|
||||
|
||||
nuclides = OrderedDict()
|
||||
|
||||
if self._type != 'void':
|
||||
nuclides.update(self._fill.get_all_nuclides())
|
||||
|
||||
return nuclides
|
||||
|
||||
def get_all_cells(self):
|
||||
"""Return all cells that are contained within this one if it is filled with a
|
||||
universe or lattice
|
||||
|
||||
Returns
|
||||
-------
|
||||
cells : dict
|
||||
Dictionary whose keys are cell IDs and values are :class:`Cell`
|
||||
instances
|
||||
|
||||
"""
|
||||
|
||||
cells = OrderedDict()
|
||||
|
||||
if self._type == 'fill' or self._type == 'lattice':
|
||||
cells.update(self._fill.get_all_cells())
|
||||
|
||||
return cells
|
||||
|
||||
def get_all_materials(self):
|
||||
"""Return all materials that are contained within the cell
|
||||
|
||||
Returns
|
||||
-------
|
||||
materials : dict
|
||||
Dictionary whose keys are material IDs and values are
|
||||
:class:`Material` instances
|
||||
|
||||
"""
|
||||
|
||||
materials = OrderedDict()
|
||||
if self.fill_type == 'material':
|
||||
materials[self.fill.id] = self.fill
|
||||
|
||||
# Append all Cells in each Cell in the Universe to the dictionary
|
||||
cells = self.get_all_cells()
|
||||
for cell_id, cell in cells.items():
|
||||
materials.update(cell.get_all_materials())
|
||||
|
||||
return materials
|
||||
|
||||
def get_all_universes(self):
|
||||
"""Return all universes that are contained within this one if any of
|
||||
its cells are filled with a universe or lattice.
|
||||
|
||||
Returns
|
||||
-------
|
||||
universes : dict
|
||||
Dictionary whose keys are universe IDs and values are
|
||||
:class:`Universe` instances
|
||||
|
||||
"""
|
||||
|
||||
universes = OrderedDict()
|
||||
|
||||
if self._type == 'fill':
|
||||
universes[self._fill._id] = self._fill
|
||||
universes.update(self._fill.get_all_universes())
|
||||
elif self._type == 'lattice':
|
||||
universes.update(self._fill.get_all_universes())
|
||||
|
||||
return universes
|
||||
|
||||
def create_xml_subelement(self, xml_element):
|
||||
element = ET.Element("cell")
|
||||
element.set("id", str(self.id))
|
||||
|
||||
if len(self._name) > 0:
|
||||
element.set("name", str(self.name))
|
||||
|
||||
if isinstance(self.fill, basestring):
|
||||
element.set("material", "void")
|
||||
|
||||
elif isinstance(self.fill, openmc.Material):
|
||||
element.set("material", str(self.fill.id))
|
||||
|
||||
elif isinstance(self.fill, Iterable):
|
||||
element.set("material", ' '.join([m if m == 'void' else str(m.id)
|
||||
for m in self.fill]))
|
||||
|
||||
elif isinstance(self.fill, (openmc.Universe, openmc.Lattice)):
|
||||
element.set("fill", str(self.fill.id))
|
||||
self.fill.create_xml_subelement(xml_element)
|
||||
|
||||
else:
|
||||
element.set("fill", str(self.fill))
|
||||
self.fill.create_xml_subelement(xml_element)
|
||||
|
||||
if self.region is not None:
|
||||
# Set the region attribute with the region specification
|
||||
element.set("region", str(self.region))
|
||||
|
||||
# Only surfaces that appear in a region are added to the geometry
|
||||
# file, so the appropriate check is performed here. First we create
|
||||
# a function which is called recursively to navigate through the CSG
|
||||
# tree. When it reaches a leaf (a Halfspace), it creates a <surface>
|
||||
# element for the corresponding surface if none has been created
|
||||
# thus far.
|
||||
def create_surface_elements(node, element):
|
||||
if isinstance(node, Halfspace):
|
||||
path = './surface[@id=\'{0}\']'.format(node.surface.id)
|
||||
if xml_element.find(path) is None:
|
||||
surface_subelement = node.surface.create_xml_subelement()
|
||||
xml_element.append(surface_subelement)
|
||||
elif isinstance(node, Complement):
|
||||
create_surface_elements(node.node, element)
|
||||
else:
|
||||
for subnode in node.nodes:
|
||||
create_surface_elements(subnode, element)
|
||||
|
||||
# Call the recursive function from the top node
|
||||
create_surface_elements(self.region, xml_element)
|
||||
|
||||
if self.temperature is not None:
|
||||
if isinstance(self.temperature, Iterable):
|
||||
element.set("temperature", ' '.join(
|
||||
str(t) for t in self.temperature))
|
||||
else:
|
||||
element.set("temperature", str(self.temperature))
|
||||
|
||||
if self.translation is not None:
|
||||
element.set("translation", ' '.join(map(str, self.translation)))
|
||||
|
||||
if self.rotation is not None:
|
||||
element.set("rotation", ' '.join(map(str, self.rotation)))
|
||||
|
||||
return element
|
||||
|
|
@ -41,25 +41,36 @@ def check_type(name, value, expected_type, expected_iter_type=None):
|
|||
Description of value being checked
|
||||
value : object
|
||||
Object to check type of
|
||||
expected_type : type
|
||||
expected_type : type or Iterable of type
|
||||
type to check object against
|
||||
expected_iter_type : type or None, optional
|
||||
expected_iter_type : type or Iterable of type or None, optional
|
||||
Expected type of each element in value, assuming it is iterable. If
|
||||
None, no check will be performed.
|
||||
|
||||
"""
|
||||
|
||||
if not _isinstance(value, expected_type):
|
||||
msg = 'Unable to set "{0}" to "{1}" which is not of type "{2}"'.format(
|
||||
name, value, expected_type.__name__)
|
||||
if isinstance(expected_type, Iterable):
|
||||
msg = 'Unable to set "{0}" to "{1}" which is not one of the ' \
|
||||
'following types: "{2}"'.format(name, value, ', '.join(
|
||||
[t.__name__ for t in expected_type]))
|
||||
else:
|
||||
msg = 'Unable to set "{0}" to "{1}" which is not of type "{2}"'.format(
|
||||
name, value, expected_type.__name__)
|
||||
raise ValueError(msg)
|
||||
|
||||
if expected_iter_type:
|
||||
for item in value:
|
||||
if not _isinstance(item, expected_iter_type):
|
||||
msg = 'Unable to set "{0}" to "{1}" since each item must be ' \
|
||||
'of type "{2}"'.format(name, value,
|
||||
expected_iter_type.__name__)
|
||||
if isinstance(expected_iter_type, Iterable):
|
||||
msg = 'Unable to set "{0}" to "{1}" since each item must be ' \
|
||||
'one of the following types: "{2}"'.format(
|
||||
name, value, ', '.join([t.__name__ for t in
|
||||
expected_iter_type]))
|
||||
else:
|
||||
msg = 'Unable to set "{0}" to "{1}" since each item must be ' \
|
||||
'of type "{2}"'.format(name, value,
|
||||
expected_iter_type.__name__)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
|
|
@ -127,7 +138,7 @@ def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1):
|
|||
# But first, have we exceeded the max depth?
|
||||
if len(tree) > max_depth:
|
||||
msg = 'Error setting {0}: Found an iterable at {1}, items '\
|
||||
'in that iterable excceed the maximum depth of {2}' \
|
||||
'in that iterable exceed the maximum depth of {2}' \
|
||||
.format(name, ind_str, max_depth)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
|
@ -245,3 +256,50 @@ def check_greater_than(name, value, minimum, equality=False):
|
|||
msg = 'Unable to set "{0}" to "{1}" since it is less than ' \
|
||||
'or equal to "{2}"'.format(name, value, minimum)
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
class CheckedList(list):
|
||||
"""A list for which each element is type-checked as it's added
|
||||
|
||||
Parameters
|
||||
----------
|
||||
expected_type : type or Iterable of type
|
||||
Type(s) which each element should be
|
||||
name : str
|
||||
Name of data being checked
|
||||
items : Iterable, optional
|
||||
Items to initialize the list with
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, expected_type, name, items=[]):
|
||||
self.expected_type = expected_type
|
||||
self.name = name
|
||||
for item in items:
|
||||
self.append(item)
|
||||
|
||||
def append(self, item):
|
||||
"""Append item to list
|
||||
|
||||
Parameters
|
||||
----------
|
||||
item : object
|
||||
Item to append
|
||||
|
||||
"""
|
||||
check_type(self.name, item, self.expected_type)
|
||||
super(CheckedList, self).append(item)
|
||||
|
||||
def insert(self, index, item):
|
||||
"""Insert item before index
|
||||
|
||||
Parameters
|
||||
----------
|
||||
index : int
|
||||
Index in list
|
||||
item : object
|
||||
Item to insert
|
||||
|
||||
"""
|
||||
check_type(self.name, item, self.expected_type)
|
||||
super(CheckedList, self).insert(index, item)
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class CMFDMesh(object):
|
|||
to any tallies far away from fission source neutron regions. A ``2``
|
||||
must be used to identify any fission source region.
|
||||
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._lower_left = None
|
||||
|
|
@ -219,7 +219,7 @@ class CMFDFile(object):
|
|||
inner tolerance for Gauss-Seidel iterations when performing CMFD.
|
||||
ktol : float
|
||||
Tolerance on the eigenvalue when performing CMFD power iteration
|
||||
cmfd_mesh : CMFDMesh
|
||||
cmfd_mesh : openmc.CMFDMesh
|
||||
Structured mesh to be used for acceleration
|
||||
norm : float
|
||||
Normalization factor applied to the CMFD fission source distribution
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class Element(object):
|
|||
Chemical symbol of the element, e.g. Pu
|
||||
xs : str
|
||||
Cross section identifier, e.g. 71c
|
||||
scattering : 'data' or 'iso-in-lab' or None
|
||||
scattering : {'data', 'iso-in-lab', None}
|
||||
The type of angular scattering distribution to use
|
||||
|
||||
"""
|
||||
|
|
@ -57,6 +57,15 @@ class Element(object):
|
|||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __gt__(self, other):
|
||||
return repr(self) > repr(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return not self > other
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ class Executor(object):
|
|||
# Launch a subprocess to run OpenMC
|
||||
p = subprocess.Popen(command, shell=True,
|
||||
cwd=self._working_directory,
|
||||
stdout=subprocess.PIPE)
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
|
||||
# Capture and re-print OpenMC output in real-time
|
||||
while True:
|
||||
|
|
|
|||
147
openmc/filter.py
147
openmc/filter.py
|
|
@ -40,11 +40,14 @@ class Filter(object):
|
|||
The bins for the filter
|
||||
num_bins : Integral
|
||||
The number of filter bins
|
||||
mesh : Mesh or None
|
||||
mesh : openmc.Mesh or None
|
||||
A Mesh object for 'mesh' type filters.
|
||||
stride : Integral
|
||||
The number of filter, nuclide and score bins within each of this
|
||||
filter's bins.
|
||||
distribcell_paths : list of str
|
||||
The paths traversed through the CSG tree to reach each distribcell
|
||||
instance (for 'distribcell' filters only)
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -56,6 +59,7 @@ class Filter(object):
|
|||
self._bins = None
|
||||
self._mesh = None
|
||||
self._stride = None
|
||||
self._distribcell_paths = None
|
||||
|
||||
if type is not None:
|
||||
self.type = type
|
||||
|
|
@ -77,6 +81,25 @@ class Filter(object):
|
|||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __gt__(self, other):
|
||||
if self.type != other.type:
|
||||
if self.type in _FILTER_TYPES and other.type in _FILTER_TYPES:
|
||||
delta = _FILTER_TYPES.index(self.type) - \
|
||||
_FILTER_TYPES.index(other.type)
|
||||
return delta > 0
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
# Compare largest/smallest energy bin edges in energy filters
|
||||
# This logic is used when merging tallies with energy filters
|
||||
if 'energy' in self.type and 'energy' in other.type:
|
||||
return self.bins[0] >= other.bins[-1]
|
||||
else:
|
||||
return max(self.bins) > max(other.bins)
|
||||
|
||||
def __lt__(self, other):
|
||||
return not self > other
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
|
|
@ -91,6 +114,7 @@ class Filter(object):
|
|||
clone._num_bins = self.num_bins
|
||||
clone._mesh = copy.deepcopy(self.mesh, memo)
|
||||
clone._stride = self.stride
|
||||
clone._distribcell_paths = copy.deepcopy(self.distribcell_paths)
|
||||
|
||||
memo[id(self)] = clone
|
||||
|
||||
|
|
@ -133,6 +157,10 @@ class Filter(object):
|
|||
def stride(self):
|
||||
return self._stride
|
||||
|
||||
@property
|
||||
def distribcell_paths(self):
|
||||
return self._distribcell_paths
|
||||
|
||||
@type.setter
|
||||
def type(self, type):
|
||||
if type is None:
|
||||
|
|
@ -227,12 +255,17 @@ class Filter(object):
|
|||
|
||||
self._stride = stride
|
||||
|
||||
@distribcell_paths.setter
|
||||
def distribcell_paths(self, distribcell_paths):
|
||||
cv.check_iterable_type('distribcell_paths', distribcell_paths, str)
|
||||
self._distribcell_paths = distribcell_paths
|
||||
|
||||
def can_merge(self, other):
|
||||
"""Determine if filter can be merged with another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Filter
|
||||
other : openmc.Filter
|
||||
Filter to compare with
|
||||
|
||||
Returns
|
||||
|
|
@ -246,20 +279,28 @@ class Filter(object):
|
|||
return False
|
||||
|
||||
# Filters must be of the same type
|
||||
elif self.type != other.type:
|
||||
if self.type != other.type:
|
||||
return False
|
||||
|
||||
# Distribcell filters cannot have more than one bin
|
||||
elif self.type == 'distribcell':
|
||||
if self.type == 'distribcell':
|
||||
return False
|
||||
|
||||
# Mesh filters cannot have more than one bin
|
||||
elif self.type == 'mesh':
|
||||
return False
|
||||
|
||||
# Different energy bins are not mergeable
|
||||
# Different energy bins structures must be mutually exclusive and
|
||||
# share only one shared bin edge at the minimum or maximum energy
|
||||
elif 'energy' in self.type:
|
||||
return False
|
||||
# This low energy edge coincides with other's high energy edge
|
||||
if self.bins[0] == other.bins[-1]:
|
||||
return True
|
||||
# This high energy edge coincides with other's low energy edge
|
||||
elif self.bins[-1] == other.bins[0]:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
else:
|
||||
return True
|
||||
|
|
@ -269,12 +310,12 @@ class Filter(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
other : Filter
|
||||
other : openmc.Filter
|
||||
Filter to merge with
|
||||
|
||||
Returns
|
||||
-------
|
||||
merged_filter : Filter
|
||||
merged_filter : openmc.Filter
|
||||
Filter resulting from the merge
|
||||
|
||||
"""
|
||||
|
|
@ -288,9 +329,21 @@ class Filter(object):
|
|||
merged_filter = copy.deepcopy(self)
|
||||
|
||||
# Merge unique filter bins
|
||||
merged_bins = list(set(np.concatenate((self.bins, other.bins))))
|
||||
merged_filter.bins = merged_bins
|
||||
merged_filter.num_bins = len(merged_bins)
|
||||
merged_bins = np.concatenate((self.bins, other.bins))
|
||||
merged_bins = np.unique(merged_bins)
|
||||
|
||||
# Sort energy bin edges
|
||||
if 'energy' in self.type:
|
||||
merged_bins = sorted(merged_bins)
|
||||
|
||||
# Assign merged bins to merged filter
|
||||
merged_filter.bins = list(merged_bins)
|
||||
|
||||
# Count bins in the merged filter
|
||||
if 'energy' in merged_filter.type:
|
||||
merged_filter.num_bins = len(merged_bins) - 1
|
||||
else:
|
||||
merged_filter.num_bins = len(merged_bins)
|
||||
|
||||
return merged_filter
|
||||
|
||||
|
|
@ -302,7 +355,7 @@ class Filter(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
other : Filter
|
||||
other : openmc.Filter
|
||||
The filter to query as a subset of this filter
|
||||
|
||||
Returns
|
||||
|
|
@ -466,8 +519,8 @@ class Filter(object):
|
|||
"""Builds a Pandas DataFrame for the Filter's bins.
|
||||
|
||||
This method constructs a Pandas DataFrame object for the filter with
|
||||
columns annotated by filter bin information. This is a helper method
|
||||
for the Tally.get_pandas_dataframe(...) method.
|
||||
columns annotated by filter bin information. This is a helper method for
|
||||
:meth:`Tally.get_pandas_dataframe`.
|
||||
|
||||
This capability has been tested for Pandas >=0.13.1. However, it is
|
||||
recommended to use v0.16 or newer versions of Pandas since this method
|
||||
|
|
@ -477,7 +530,7 @@ class Filter(object):
|
|||
----------
|
||||
data_size : Integral
|
||||
The total number of bins in the tally corresponding to this filter
|
||||
summary : None or Summary
|
||||
summary : None or openmc.Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
distribcell tally filters (default is None). The geometric
|
||||
information in the Summary object is embedded into a Multi-index
|
||||
|
|
@ -502,9 +555,9 @@ class Filter(object):
|
|||
2. separate columns for the cell IDs, universe IDs, and lattice IDs
|
||||
and x,y,z cell indices corresponding to each (with summary info).
|
||||
|
||||
For 'energy' and 'energyout' filters, the DataFrame include a single
|
||||
column with each element comprising a string with the lower, upper
|
||||
energy bounds for each filter bin.
|
||||
For 'energy' and 'energyout' filters, the DataFrame includes one
|
||||
column for the lower energy bound and one column for the upper
|
||||
energy bound for each filter bin.
|
||||
|
||||
For 'mesh' filters, the DataFrame includes three columns for the
|
||||
x,y,z mesh cell indices corresponding to each filter bin.
|
||||
|
|
@ -521,14 +574,8 @@ class Filter(object):
|
|||
|
||||
"""
|
||||
|
||||
# Attempt to import Pandas
|
||||
try:
|
||||
import pandas as pd
|
||||
except ImportError:
|
||||
msg = 'The Pandas Python package must be installed on your system'
|
||||
raise ImportError(msg)
|
||||
|
||||
# Initialize Pandas DataFrame
|
||||
import pandas as pd
|
||||
df = pd.DataFrame()
|
||||
|
||||
# mesh filters
|
||||
|
|
@ -599,18 +646,10 @@ class Filter(object):
|
|||
# offsets to OpenCG LocalCoords linked lists
|
||||
offsets_to_coords = {}
|
||||
|
||||
# Use OpenCG to compute LocalCoords linked list for
|
||||
# each region and store in dictionary
|
||||
for region in range(num_regions):
|
||||
for offset, path in enumerate(self.distribcell_paths):
|
||||
region = opencg_geometry.get_region_from_path(path)
|
||||
coords = opencg_geometry.find_region(region)
|
||||
path = opencg.get_path(coords)
|
||||
cell_id = path[-1]
|
||||
|
||||
# If this region is in Cell corresponding to the
|
||||
# distribcell filter bin, store it in dictionary
|
||||
if cell_id == self.bins[0]:
|
||||
offset = openmc_geometry.get_cell_instance(path)
|
||||
offsets_to_coords[offset] = coords
|
||||
offsets_to_coords[offset] = coords
|
||||
|
||||
# Each distribcell offset is a DataFrame bin
|
||||
# Unravel the paths into DataFrame columns
|
||||
|
|
@ -707,7 +746,6 @@ class Filter(object):
|
|||
filter_bins = np.repeat(filter_bins, self.stride)
|
||||
tile_factor = data_size / len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
filter_bins = filter_bins
|
||||
df = pd.DataFrame({self.type : filter_bins})
|
||||
|
||||
# If OpenCG level info DataFrame was created, concatenate
|
||||
|
|
@ -719,21 +757,30 @@ class Filter(object):
|
|||
|
||||
# energy, energyout filters
|
||||
elif 'energy' in self.type:
|
||||
bins = self.bins
|
||||
num_bins = self.num_bins
|
||||
# Extract the lower and upper energy bounds, then repeat and tile
|
||||
# them as necessary to account for other filters.
|
||||
lo_bins = np.repeat(self.bins[:-1], self.stride)
|
||||
hi_bins = np.repeat(self.bins[1:], self.stride)
|
||||
tile_factor = data_size / len(lo_bins)
|
||||
lo_bins = np.tile(lo_bins, tile_factor)
|
||||
hi_bins = np.tile(hi_bins, tile_factor)
|
||||
|
||||
# Create strings for
|
||||
template = '({0:.1e} - {1:.1e})'
|
||||
filter_bins = []
|
||||
for i in range(num_bins):
|
||||
filter_bins.append(template.format(bins[i], bins[i+1]))
|
||||
# Add the new energy columns to the DataFrame.
|
||||
df.loc[:, self.type + ' low [MeV]'] = lo_bins
|
||||
df.loc[:, self.type + ' high [MeV]'] = hi_bins
|
||||
|
||||
# Tile the energy bins into a DataFrame column
|
||||
filter_bins = np.repeat(filter_bins, self.stride)
|
||||
tile_factor = data_size / len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
filter_bins = filter_bins
|
||||
df = pd.concat([df, pd.DataFrame({self.type + ' [MeV]' : filter_bins})])
|
||||
elif self.type in ('azimuthal', 'polar'):
|
||||
# Extract the lower and upper angle bounds, then repeat and tile
|
||||
# them as necessary to account for other filters.
|
||||
lo_bins = np.repeat(self.bins[:-1], self.stride)
|
||||
hi_bins = np.repeat(self.bins[1:], self.stride)
|
||||
tile_factor = data_size / len(lo_bins)
|
||||
lo_bins = np.tile(lo_bins, tile_factor)
|
||||
hi_bins = np.tile(hi_bins, tile_factor)
|
||||
|
||||
# Add the new angle columns to the DataFrame.
|
||||
df.loc[:, self.type + ' low'] = lo_bins
|
||||
df.loc[:, self.type + ' high'] = hi_bins
|
||||
|
||||
# universe, material, surface, cell, and cellborn filters
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class Geometry(object):
|
|||
|
||||
Attributes
|
||||
----------
|
||||
root_universe : openmc.universe.Universe
|
||||
root_universe : openmc.Universe
|
||||
Root universe which contains all others
|
||||
|
||||
"""
|
||||
|
|
@ -63,13 +63,19 @@ class Geometry(object):
|
|||
|
||||
"""
|
||||
|
||||
# Extract the cell id from the path
|
||||
last_index = path.rfind('>')
|
||||
cell_id = int(path[last_index+1:])
|
||||
|
||||
# Find the distribcell index of the cell.
|
||||
cells = self.get_all_cells()
|
||||
if path[-1] in cells:
|
||||
distribcell_index = cells[path[-1]].distribcell_index
|
||||
for cell in cells:
|
||||
if cell.id == cell_id:
|
||||
distribcell_index = cell.distribcell_index
|
||||
break
|
||||
else:
|
||||
raise RuntimeError('Could not find cell {} specified in a \
|
||||
distribcell filter'.format(path[-1]))
|
||||
distribcell filter'.format(cell_id))
|
||||
|
||||
# Return memoize'd offset if possible
|
||||
if (path, distribcell_index) in self._offsets:
|
||||
|
|
@ -89,31 +95,48 @@ class Geometry(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.universe.Cell
|
||||
list of openmc.Cell
|
||||
Cells in the geometry
|
||||
|
||||
"""
|
||||
|
||||
return self._root_universe.get_all_cells()
|
||||
all_cells = self._root_universe.get_all_cells()
|
||||
cells = set()
|
||||
|
||||
for cell in all_cells.values():
|
||||
if cell._type == 'normal':
|
||||
cells.add(cell)
|
||||
|
||||
cells = list(cells)
|
||||
cells.sort(key=lambda x: x.id)
|
||||
return cells
|
||||
|
||||
def get_all_universes(self):
|
||||
"""Return all universes defined
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.universe.Universe
|
||||
list of openmc.Universe
|
||||
Universes in the geometry
|
||||
|
||||
"""
|
||||
|
||||
return self._root_universe.get_all_universes()
|
||||
all_universes = self._root_universe.get_all_universes()
|
||||
universes = set()
|
||||
|
||||
for universe in all_universes.values():
|
||||
universes.add(universe)
|
||||
|
||||
universes = list(universes)
|
||||
universes.sort(key=lambda x: x.id)
|
||||
return universes
|
||||
|
||||
def get_all_nuclides(self):
|
||||
"""Return all nuclides assigned to a material in the geometry
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.nuclide.Nuclide
|
||||
list of openmc.Nuclide
|
||||
Nuclides in the geometry
|
||||
|
||||
"""
|
||||
|
|
@ -131,7 +154,7 @@ class Geometry(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.material.Material
|
||||
list of openmc.Material
|
||||
Materials in the geometry
|
||||
|
||||
"""
|
||||
|
|
@ -150,10 +173,19 @@ class Geometry(object):
|
|||
return materials
|
||||
|
||||
def get_all_material_cells(self):
|
||||
"""Return all cells filled by a material
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.Cell
|
||||
Cells filled by Materials in the geometry
|
||||
|
||||
"""
|
||||
|
||||
all_cells = self.get_all_cells()
|
||||
material_cells = set()
|
||||
|
||||
for cell_id, cell in all_cells.items():
|
||||
for cell in all_cells:
|
||||
if cell._type == 'normal':
|
||||
material_cells.add(cell)
|
||||
|
||||
|
|
@ -166,7 +198,7 @@ class Geometry(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.universe.Universe
|
||||
list of openmc.Universe
|
||||
Universes with non-fill cells
|
||||
|
||||
"""
|
||||
|
|
@ -174,9 +206,9 @@ class Geometry(object):
|
|||
all_universes = self.get_all_universes()
|
||||
material_universes = set()
|
||||
|
||||
for universe_id, universe in all_universes.items():
|
||||
cells = universe._cells
|
||||
for cell_id, cell in cells.items():
|
||||
for universe in all_universes:
|
||||
cells = universe.cells
|
||||
for cell in cells:
|
||||
if cell._type == 'normal':
|
||||
material_universes.add(universe)
|
||||
|
||||
|
|
@ -184,6 +216,227 @@ class Geometry(object):
|
|||
material_universes.sort(key=lambda x: x.id)
|
||||
return material_universes
|
||||
|
||||
def get_all_lattices(self):
|
||||
"""Return all lattices defined
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.Lattice
|
||||
Lattices in the geometry
|
||||
|
||||
"""
|
||||
|
||||
cells = self.get_all_cells()
|
||||
lattices = set()
|
||||
|
||||
for cell in cells:
|
||||
if isinstance(cell.fill, openmc.Lattice):
|
||||
lattices.add(cell.fill)
|
||||
|
||||
lattices = list(lattices)
|
||||
lattices.sort(key=lambda x: x.id)
|
||||
return lattices
|
||||
|
||||
def get_materials_by_name(self, name, case_sensitive=False, matching=False):
|
||||
"""Return a list of materials with matching names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name to match
|
||||
case_sensitive : bool
|
||||
Whether to distinguish upper and lower case letters in each
|
||||
material's name (default is True)
|
||||
matching : bool
|
||||
Whether the names must match completely (default is True)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.Material
|
||||
Materials matching the queried name
|
||||
|
||||
"""
|
||||
|
||||
if not case_sensitive:
|
||||
name = name.lower()
|
||||
|
||||
all_materials = self.get_all_materials()
|
||||
materials = set()
|
||||
|
||||
for material in all_materials:
|
||||
material_name = material.name
|
||||
if not case_sensitive:
|
||||
material_name = material_name.lower()
|
||||
|
||||
if material_name == name:
|
||||
materials.add(material)
|
||||
elif not matching and name in material_name:
|
||||
materials.add(material)
|
||||
|
||||
materials = list(materials)
|
||||
materials.sort(key=lambda x: x.id)
|
||||
return materials
|
||||
|
||||
def get_cells_by_name(self, name, case_sensitive=False, matching=False):
|
||||
"""Return a list of cells with matching names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name to search match
|
||||
case_sensitive : bool
|
||||
Whether to distinguish upper and lower case letters in each
|
||||
cell's name (default is True)
|
||||
matching : bool
|
||||
Whether the names must match completely (default is True)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.Cell
|
||||
Cells matching the queried name
|
||||
|
||||
"""
|
||||
|
||||
if not case_sensitive:
|
||||
name = name.lower()
|
||||
|
||||
all_cells = self.get_all_cells()
|
||||
cells = set()
|
||||
|
||||
for cell in all_cells:
|
||||
cell_name = cell.name
|
||||
if not case_sensitive:
|
||||
cell_name = cell_name.lower()
|
||||
|
||||
if cell_name == name:
|
||||
cells.add(cell)
|
||||
elif not matching and name in cell_name:
|
||||
cells.add(cell)
|
||||
|
||||
cells = list(cells)
|
||||
cells.sort(key=lambda x: x.id)
|
||||
return cells
|
||||
|
||||
def get_cells_by_fill_name(self, name, case_sensitive=False, matching=False):
|
||||
"""Return a list of cells with fills with matching names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name to match
|
||||
case_sensitive : bool
|
||||
Whether to distinguish upper and lower case letters in each
|
||||
cell's name (default is True)
|
||||
matching : bool
|
||||
Whether the names must match completely (default is True)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.Cell
|
||||
Cells with fills matching the queried name
|
||||
|
||||
"""
|
||||
|
||||
if not case_sensitive:
|
||||
name = name.lower()
|
||||
|
||||
all_cells = self.get_all_cells()
|
||||
cells = set()
|
||||
|
||||
for cell in all_cells:
|
||||
cell_fill_name = cell.fill.name
|
||||
if not case_sensitive:
|
||||
cell_fill_name = cell_fill_name.lower()
|
||||
|
||||
if cell_fill_name == name:
|
||||
cells.add(cell)
|
||||
elif not matching and name in cell_fill_name:
|
||||
cells.add(cell)
|
||||
|
||||
cells = list(cells)
|
||||
cells.sort(key=lambda x: x.id)
|
||||
return cells
|
||||
|
||||
def get_universes_by_name(self, name, case_sensitive=False, matching=False):
|
||||
"""Return a list of universes with matching names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name to match
|
||||
case_sensitive : bool
|
||||
Whether to distinguish upper and lower case letters in each
|
||||
universe's name (default is True)
|
||||
matching : bool
|
||||
Whether the names must match completely (default is True)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.Universe
|
||||
Universes matching the queried name
|
||||
|
||||
"""
|
||||
|
||||
if not case_sensitive:
|
||||
name = name.lower()
|
||||
|
||||
all_universes = self.get_all_universes()
|
||||
universes = set()
|
||||
|
||||
for universe in all_universes:
|
||||
universe_name = universe.name
|
||||
if not case_sensitive:
|
||||
universe_name = universe_name.lower()
|
||||
|
||||
if universe_name == name:
|
||||
universes.add(universe)
|
||||
elif not matching and name in universe_name:
|
||||
universes.add(universe)
|
||||
|
||||
universes = list(universes)
|
||||
universes.sort(key=lambda x: x.id)
|
||||
return universes
|
||||
|
||||
def get_lattices_by_name(self, name, case_sensitive=False, matching=False):
|
||||
"""Return a list of lattices with matching names.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name to match
|
||||
case_sensitive : bool
|
||||
Whether to distinguish upper and lower case letters in each
|
||||
lattice's name (default is True)
|
||||
matching : bool
|
||||
Whether the names must match completely (default is True)
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of openmc.Lattice
|
||||
Lattices matching the queried name
|
||||
|
||||
"""
|
||||
|
||||
if not case_sensitive:
|
||||
name = name.lower()
|
||||
|
||||
all_lattices = self.get_all_lattices()
|
||||
lattices = set()
|
||||
|
||||
for lattice in all_lattices:
|
||||
lattice_name = lattice.name
|
||||
if not case_sensitive:
|
||||
lattice_name = lattice_name.lower()
|
||||
|
||||
if lattice_name == name:
|
||||
lattices.add(lattice)
|
||||
elif not matching and name in lattice_name:
|
||||
lattices.add(lattice)
|
||||
|
||||
lattices = list(lattices)
|
||||
lattices.sort(key=lambda x: x.id)
|
||||
return lattices
|
||||
|
||||
|
||||
class GeometryFile(object):
|
||||
"""Geometry file used for an OpenMC simulation. Corresponds directly to the
|
||||
|
|
@ -191,7 +444,7 @@ class GeometryFile(object):
|
|||
|
||||
Attributes
|
||||
----------
|
||||
geometry : Geometry
|
||||
geometry : openmc.Geometry
|
||||
The geometry to be used
|
||||
|
||||
"""
|
||||
|
|
|
|||
871
openmc/lattice.py
Normal file
871
openmc/lattice.py
Normal file
|
|
@ -0,0 +1,871 @@
|
|||
import abc
|
||||
from collections import OrderedDict, Iterable
|
||||
from numbers import Real, Integral
|
||||
from xml.etree import ElementTree as ET
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.universe import Universe, AUTO_UNIVERSE_ID
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
|
||||
class Lattice(object):
|
||||
"""A repeating structure wherein each element is a universe.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lattice_id : int, optional
|
||||
Unique identifier for the lattice. If not specified, an identifier will
|
||||
automatically be assigned.
|
||||
name : str, optional
|
||||
Name of the lattice. If not specified, the name is the empty string.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id : int
|
||||
Unique identifier for the lattice
|
||||
name : str
|
||||
Name of the lattice
|
||||
pitch : float
|
||||
Pitch of the lattice in cm
|
||||
outer : int
|
||||
The unique identifier of a universe to fill all space outside the
|
||||
lattice
|
||||
universes : numpy.ndarray of openmc.Universe
|
||||
An array of universes filling each element of the lattice
|
||||
|
||||
"""
|
||||
|
||||
# This is an abstract class which cannot be instantiated
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, lattice_id=None, name=''):
|
||||
# Initialize Lattice class attributes
|
||||
self.id = lattice_id
|
||||
self.name = name
|
||||
self._pitch = None
|
||||
self._outer = None
|
||||
self._universes = None
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Lattice):
|
||||
return False
|
||||
elif self.id != other.id:
|
||||
return False
|
||||
elif self.name != other.name:
|
||||
return False
|
||||
elif self.pitch != other.pitch:
|
||||
return False
|
||||
elif self.outer != other.outer:
|
||||
return False
|
||||
elif self.universes != other.universes:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def pitch(self):
|
||||
return self._pitch
|
||||
|
||||
@property
|
||||
def outer(self):
|
||||
return self._outer
|
||||
|
||||
@property
|
||||
def universes(self):
|
||||
return self._universes
|
||||
|
||||
@id.setter
|
||||
def id(self, lattice_id):
|
||||
if lattice_id is None:
|
||||
global AUTO_UNIVERSE_ID
|
||||
self._id = AUTO_UNIVERSE_ID
|
||||
AUTO_UNIVERSE_ID += 1
|
||||
else:
|
||||
cv.check_type('lattice ID', lattice_id, Integral)
|
||||
cv.check_greater_than('lattice ID', lattice_id, 0, equality=True)
|
||||
self._id = lattice_id
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
if name is not None:
|
||||
cv.check_type('lattice name', name, basestring)
|
||||
self._name = name
|
||||
else:
|
||||
self._name = ''
|
||||
|
||||
@outer.setter
|
||||
def outer(self, outer):
|
||||
cv.check_type('outer universe', outer, Universe)
|
||||
self._outer = outer
|
||||
|
||||
@universes.setter
|
||||
def universes(self, universes):
|
||||
cv.check_iterable_type('lattice universes', universes, Universe,
|
||||
min_depth=2, max_depth=3)
|
||||
self._universes = np.asarray(universes)
|
||||
|
||||
def get_unique_universes(self):
|
||||
"""Determine all unique universes in the lattice
|
||||
|
||||
Returns
|
||||
-------
|
||||
universes : collections.OrderedDict
|
||||
Dictionary whose keys are universe IDs and values are
|
||||
:class:`Universe` instances
|
||||
|
||||
"""
|
||||
|
||||
univs = OrderedDict()
|
||||
for k in range(len(self._universes)):
|
||||
for j in range(len(self._universes[k])):
|
||||
if isinstance(self._universes[k][j], Universe):
|
||||
u = self._universes[k][j]
|
||||
univs[u._id] = u
|
||||
else:
|
||||
for i in range(len(self._universes[k][j])):
|
||||
u = self._universes[k][j][i]
|
||||
assert isinstance(u, Universe)
|
||||
univs[u._id] = u
|
||||
|
||||
if self.outer is not None:
|
||||
univs[self.outer._id] = self.outer
|
||||
|
||||
return univs
|
||||
|
||||
def get_all_nuclides(self):
|
||||
"""Return all nuclides contained in the lattice
|
||||
|
||||
Returns
|
||||
-------
|
||||
nuclides : collections.OrderedDict
|
||||
Dictionary whose keys are nuclide names and values are 2-tuples of
|
||||
(nuclide, density)
|
||||
|
||||
"""
|
||||
|
||||
nuclides = OrderedDict()
|
||||
|
||||
# Get all unique Universes contained in each of the lattice cells
|
||||
unique_universes = self.get_unique_universes()
|
||||
|
||||
# Append all Universes containing each cell to the dictionary
|
||||
for universe_id, universe in unique_universes.items():
|
||||
nuclides.update(universe.get_all_nuclides())
|
||||
|
||||
return nuclides
|
||||
|
||||
def get_all_cells(self):
|
||||
"""Return all cells that are contained within the lattice
|
||||
|
||||
Returns
|
||||
-------
|
||||
cells : collections.OrderedDict
|
||||
Dictionary whose keys are cell IDs and values are :class:`Cell`
|
||||
instances
|
||||
|
||||
"""
|
||||
|
||||
cells = OrderedDict()
|
||||
unique_universes = self.get_unique_universes()
|
||||
|
||||
for universe_id, universe in unique_universes.items():
|
||||
cells.update(universe.get_all_cells())
|
||||
|
||||
return cells
|
||||
|
||||
def get_all_materials(self):
|
||||
"""Return all materials that are contained within the lattice
|
||||
|
||||
Returns
|
||||
-------
|
||||
materials : collections.OrderedDict
|
||||
Dictionary whose keys are material IDs and values are
|
||||
:class:`Material` instances
|
||||
|
||||
"""
|
||||
|
||||
materials = OrderedDict()
|
||||
|
||||
# Append all Cells in each Cell in the Universe to the dictionary
|
||||
cells = self.get_all_cells()
|
||||
for cell_id, cell in cells.items():
|
||||
materials.update(cell.get_all_materials())
|
||||
|
||||
return materials
|
||||
|
||||
def get_all_universes(self):
|
||||
"""Return all universes that are contained within the lattice
|
||||
|
||||
Returns
|
||||
-------
|
||||
universes : collections.OrderedDict
|
||||
Dictionary whose keys are universe IDs and values are
|
||||
:class:`Universe` instances
|
||||
|
||||
"""
|
||||
|
||||
# Initialize a dictionary of all Universes contained by the Lattice
|
||||
# in each nested Universe level
|
||||
all_universes = OrderedDict()
|
||||
|
||||
# Get all unique Universes contained in each of the lattice cells
|
||||
unique_universes = self.get_unique_universes()
|
||||
|
||||
# Add the unique Universes filling each Lattice cell
|
||||
all_universes.update(unique_universes)
|
||||
|
||||
# Append all Universes containing each cell to the dictionary
|
||||
for universe_id, universe in unique_universes.items():
|
||||
all_universes.update(universe.get_all_universes())
|
||||
|
||||
return all_universes
|
||||
|
||||
|
||||
class RectLattice(Lattice):
|
||||
"""A lattice consisting of rectangular prisms.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lattice_id : int, optional
|
||||
Unique identifier for the lattice. If not specified, an identifier will
|
||||
automatically be assigned.
|
||||
name : str, optional
|
||||
Name of the lattice. If not specified, the name is the empty string.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id : int
|
||||
Unique identifier for the lattice
|
||||
name : str
|
||||
Name of the lattice
|
||||
dimension : Iterable of int
|
||||
An array of two or three integers representing the number of lattice
|
||||
cells in the x- and y- (and z-) directions, respectively.
|
||||
lower_left : Iterable of float
|
||||
The coordinates of the lower-left corner of the lattice. If the lattice
|
||||
is two-dimensional, only the x- and y-coordinates are specified.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, lattice_id=None, name=''):
|
||||
super(RectLattice, self).__init__(lattice_id, name)
|
||||
|
||||
# Initialize Lattice class attributes
|
||||
self._dimension = None
|
||||
self._lower_left = None
|
||||
self._offsets = None
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, RectLattice):
|
||||
return False
|
||||
elif not super(RectLattice, self).__eq__(other):
|
||||
return False
|
||||
elif self.dimension != other.dimension:
|
||||
return False
|
||||
elif self.lower_left != other.lower_left:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
def __repr__(self):
|
||||
string = 'RectLattice\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tDimension', '=\t',
|
||||
self._dimension)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tLower Left', '=\t',
|
||||
self._lower_left)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tPitch', '=\t', self._pitch)
|
||||
|
||||
if self._outer is not None:
|
||||
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
|
||||
self._outer._id)
|
||||
else:
|
||||
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
|
||||
self._outer)
|
||||
|
||||
string += '{0: <16}\n'.format('\tUniverses')
|
||||
|
||||
# Lattice nested Universe IDs - column major for Fortran
|
||||
for i, universe in enumerate(np.ravel(self._universes)):
|
||||
string += '{0} '.format(universe._id)
|
||||
|
||||
# Add a newline character every time we reach end of row of cells
|
||||
if (i+1) % self._dimension[-1] == 0:
|
||||
string += '\n'
|
||||
|
||||
string = string.rstrip('\n')
|
||||
|
||||
if self._offsets is not None:
|
||||
string += '{0: <16}\n'.format('\tOffsets')
|
||||
|
||||
# Lattice cell offsets
|
||||
for i, offset in enumerate(np.ravel(self._offsets)):
|
||||
string += '{0} '.format(offset)
|
||||
|
||||
# Add a newline character when we reach end of row of cells
|
||||
if (i+1) % self._dimension[-1] == 0:
|
||||
string += '\n'
|
||||
|
||||
string = string.rstrip('\n')
|
||||
|
||||
return string
|
||||
|
||||
@property
|
||||
def dimension(self):
|
||||
return self._dimension
|
||||
|
||||
@property
|
||||
def lower_left(self):
|
||||
return self._lower_left
|
||||
|
||||
@property
|
||||
def offsets(self):
|
||||
return self._offsets
|
||||
|
||||
@dimension.setter
|
||||
def dimension(self, dimension):
|
||||
cv.check_type('lattice dimension', dimension, Iterable, Integral)
|
||||
cv.check_length('lattice dimension', dimension, 2, 3)
|
||||
for dim in dimension:
|
||||
cv.check_greater_than('lattice dimension', dim, 0)
|
||||
self._dimension = dimension
|
||||
|
||||
@lower_left.setter
|
||||
def lower_left(self, lower_left):
|
||||
cv.check_type('lattice lower left corner', lower_left, Iterable, Real)
|
||||
cv.check_length('lattice lower left corner', lower_left, 2, 3)
|
||||
self._lower_left = lower_left
|
||||
|
||||
@offsets.setter
|
||||
def offsets(self, offsets):
|
||||
cv.check_type('lattice offsets', offsets, Iterable)
|
||||
self._offsets = offsets
|
||||
|
||||
@Lattice.pitch.setter
|
||||
def pitch(self, pitch):
|
||||
cv.check_type('lattice pitch', pitch, Iterable, Real)
|
||||
cv.check_length('lattice pitch', pitch, 2, 3)
|
||||
for dim in pitch:
|
||||
cv.check_greater_than('lattice pitch', dim, 0.0)
|
||||
self._pitch = pitch
|
||||
|
||||
def get_cell_instance(self, path, distribcell_index):
|
||||
|
||||
# Extract the lattice element from the path
|
||||
next_index = path.index('-')
|
||||
lat_id_indices = path[:next_index]
|
||||
path = path[next_index+2:]
|
||||
|
||||
# Extract the lattice cell indices from the path
|
||||
i1 = lat_id_indices.index('(')
|
||||
i2 = lat_id_indices.index(')')
|
||||
i = lat_id_indices[i1+1:i2]
|
||||
lat_x = int(i.split(',')[0]) - 1
|
||||
lat_y = int(i.split(',')[1]) - 1
|
||||
lat_z = int(i.split(',')[2]) - 1
|
||||
|
||||
# For 2D Lattices
|
||||
if len(self._dimension) == 2:
|
||||
offset = self._offsets[lat_z, lat_y, lat_x, distribcell_index-1]
|
||||
offset += self._universes[lat_x][lat_y].get_cell_instance(path,
|
||||
distribcell_index)
|
||||
|
||||
# For 3D Lattices
|
||||
else:
|
||||
offset = self._offsets[lat_z, lat_y, lat_x, distribcell_index-1]
|
||||
offset += self._universes[lat_z][lat_y][lat_x].get_cell_instance(
|
||||
path, distribcell_index)
|
||||
|
||||
return offset
|
||||
|
||||
def create_xml_subelement(self, xml_element):
|
||||
|
||||
# Determine if XML element already contains subelement for this Lattice
|
||||
path = './lattice[@id=\'{0}\']'.format(self._id)
|
||||
test = xml_element.find(path)
|
||||
|
||||
# If the element does contain the Lattice subelement, then return
|
||||
if test is not None:
|
||||
return
|
||||
|
||||
lattice_subelement = ET.Element("lattice")
|
||||
lattice_subelement.set("id", str(self._id))
|
||||
|
||||
if len(self._name) > 0:
|
||||
lattice_subelement.set("name", str(self._name))
|
||||
|
||||
# Export the Lattice cell pitch
|
||||
pitch = ET.SubElement(lattice_subelement, "pitch")
|
||||
pitch.text = ' '.join(map(str, self._pitch))
|
||||
|
||||
# Export the Lattice outer Universe (if specified)
|
||||
if self._outer is not None:
|
||||
outer = ET.SubElement(lattice_subelement, "outer")
|
||||
outer.text = '{0}'.format(self._outer._id)
|
||||
self._outer.create_xml_subelement(xml_element)
|
||||
|
||||
# Export Lattice cell dimensions
|
||||
dimension = ET.SubElement(lattice_subelement, "dimension")
|
||||
dimension.text = ' '.join(map(str, self._dimension))
|
||||
|
||||
# Export Lattice lower left
|
||||
lower_left = ET.SubElement(lattice_subelement, "lower_left")
|
||||
lower_left.text = ' '.join(map(str, self._lower_left))
|
||||
|
||||
# Export the Lattice nested Universe IDs - column major for Fortran
|
||||
universe_ids = '\n'
|
||||
|
||||
# 3D Lattices
|
||||
if len(self._dimension) == 3:
|
||||
for z in range(self._dimension[2]):
|
||||
for y in range(self._dimension[1]):
|
||||
for x in range(self._dimension[0]):
|
||||
universe = self._universes[z][y][x]
|
||||
|
||||
# Append Universe ID to the Lattice XML subelement
|
||||
universe_ids += '{0} '.format(universe._id)
|
||||
|
||||
# Create XML subelement for this Universe
|
||||
universe.create_xml_subelement(xml_element)
|
||||
|
||||
# Add newline character when we reach end of row of cells
|
||||
universe_ids += '\n'
|
||||
|
||||
# Add newline character when we reach end of row of cells
|
||||
universe_ids += '\n'
|
||||
|
||||
# 2D Lattices
|
||||
else:
|
||||
for y in range(self._dimension[1]):
|
||||
for x in range(self._dimension[0]):
|
||||
universe = self._universes[y][x]
|
||||
|
||||
# Append Universe ID to Lattice XML subelement
|
||||
universe_ids += '{0} '.format(universe._id)
|
||||
|
||||
# Create XML subelement for this Universe
|
||||
universe.create_xml_subelement(xml_element)
|
||||
|
||||
# Add newline character when we reach end of row of cells
|
||||
universe_ids += '\n'
|
||||
|
||||
# Remove trailing newline character from Universe IDs string
|
||||
universe_ids = universe_ids.rstrip('\n')
|
||||
|
||||
universes = ET.SubElement(lattice_subelement, "universes")
|
||||
universes.text = universe_ids
|
||||
|
||||
# Append the XML subelement for this Lattice to the XML element
|
||||
xml_element.append(lattice_subelement)
|
||||
|
||||
|
||||
class HexLattice(Lattice):
|
||||
"""A lattice consisting of hexagonal prisms.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lattice_id : int, optional
|
||||
Unique identifier for the lattice. If not specified, an identifier will
|
||||
automatically be assigned.
|
||||
name : str, optional
|
||||
Name of the lattice. If not specified, the name is the empty string.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id : int
|
||||
Unique identifier for the lattice
|
||||
name : str
|
||||
Name of the lattice
|
||||
num_rings : int
|
||||
Number of radial ring positions in the xy-plane
|
||||
num_axial : int
|
||||
Number of positions along the z-axis.
|
||||
center : Iterable of float
|
||||
Coordinates of the center of the lattice. If the lattice does not have
|
||||
axial sections then only the x- and y-coordinates are specified
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, lattice_id=None, name=''):
|
||||
super(HexLattice, self).__init__(lattice_id, name)
|
||||
|
||||
# Initialize Lattice class attributes
|
||||
self._num_rings = None
|
||||
self._num_axial = None
|
||||
self._center = None
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, HexLattice):
|
||||
return False
|
||||
elif not super(HexLattice, self).__eq__(other):
|
||||
return False
|
||||
elif self.num_rings != other.num_rings:
|
||||
return False
|
||||
elif self.num_axial != other.num_axial:
|
||||
return False
|
||||
elif self.center != other.center:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
def __repr__(self):
|
||||
string = 'HexLattice\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
|
||||
string += '{0: <16}{1}{2}\n'.format('\t# Rings', '=\t', self._num_rings)
|
||||
string += '{0: <16}{1}{2}\n'.format('\t# Axial', '=\t', self._num_axial)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tCenter', '=\t',
|
||||
self._center)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tPitch', '=\t', self._pitch)
|
||||
|
||||
if self._outer is not None:
|
||||
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
|
||||
self._outer._id)
|
||||
else:
|
||||
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
|
||||
self._outer)
|
||||
|
||||
string += '{0: <16}\n'.format('\tUniverses')
|
||||
|
||||
if self._num_axial is not None:
|
||||
slices = [self._repr_axial_slice(x) for x in self._universes]
|
||||
string += '\n'.join(slices)
|
||||
|
||||
else:
|
||||
string += self._repr_axial_slice(self._universes)
|
||||
|
||||
return string
|
||||
|
||||
@property
|
||||
def num_rings(self):
|
||||
return self._num_rings
|
||||
|
||||
@property
|
||||
def num_axial(self):
|
||||
return self._num_axial
|
||||
|
||||
@property
|
||||
def center(self):
|
||||
return self._center
|
||||
|
||||
@num_rings.setter
|
||||
def num_rings(self, num_rings):
|
||||
cv.check_type('number of rings', num_rings, Integral)
|
||||
cv.check_greater_than('number of rings', num_rings, 0)
|
||||
self._num_rings = num_rings
|
||||
|
||||
@num_axial.setter
|
||||
def num_axial(self, num_axial):
|
||||
cv.check_type('number of axial', num_axial, Integral)
|
||||
cv.check_greater_than('number of axial', num_axial, 0)
|
||||
self._num_axial = num_axial
|
||||
|
||||
@center.setter
|
||||
def center(self, center):
|
||||
cv.check_type('lattice center', center, Iterable, Real)
|
||||
cv.check_length('lattice center', center, 2, 3)
|
||||
self._center = center
|
||||
|
||||
@Lattice.pitch.setter
|
||||
def pitch(self, pitch):
|
||||
cv.check_type('lattice pitch', pitch, Iterable, Real)
|
||||
cv.check_length('lattice pitch', pitch, 1, 2)
|
||||
for dim in pitch:
|
||||
cv.check_greater_than('lattice pitch', dim, 0)
|
||||
self._pitch = pitch
|
||||
|
||||
@Lattice.universes.setter
|
||||
def universes(self, universes):
|
||||
# Call Lattice.universes parent class setter property
|
||||
Lattice.universes.fset(self, universes)
|
||||
|
||||
# NOTE: This routine assumes that the user creates a "ragged" list of
|
||||
# lists, where each sub-list corresponds to one ring of Universes.
|
||||
# The sub-lists are ordered from outermost ring to innermost ring.
|
||||
# The Universes within each sub-list are ordered from the "top" in a
|
||||
# clockwise fashion.
|
||||
|
||||
# Check to see if the given universes look like a 2D or a 3D array.
|
||||
if isinstance(self._universes[0][0], Universe):
|
||||
n_dims = 2
|
||||
|
||||
elif isinstance(self._universes[0][0][0], Universe):
|
||||
n_dims = 3
|
||||
|
||||
else:
|
||||
msg = 'HexLattice ID={0:d} does not appear to be either 2D or ' \
|
||||
'3D. Make sure set_universes was given a two-deep or ' \
|
||||
'three-deep iterable of universes.'.format(self._id)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
# Set the number of axial positions.
|
||||
if n_dims == 3:
|
||||
self.num_axial = len(self._universes)
|
||||
else:
|
||||
self._num_axial = None
|
||||
|
||||
# Set the number of rings and make sure this number is consistent for
|
||||
# all axial positions.
|
||||
if n_dims == 3:
|
||||
self.num_rings = len(self._universes)
|
||||
for rings in self._universes:
|
||||
if len(rings) != self._num_rings:
|
||||
msg = 'HexLattice ID={0:d} has an inconsistent number of ' \
|
||||
'rings per axial positon'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
else:
|
||||
self.num_rings = len(self._universes)
|
||||
|
||||
# Make sure there are the correct number of elements in each ring.
|
||||
if n_dims == 3:
|
||||
for axial_slice in self._universes:
|
||||
# Check the center ring.
|
||||
if len(axial_slice[-1]) != 1:
|
||||
msg = 'HexLattice ID={0:d} has the wrong number of ' \
|
||||
'elements in the innermost ring. Only 1 element is ' \
|
||||
'allowed in the innermost ring.'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Check the outer rings.
|
||||
for r in range(self._num_rings-1):
|
||||
if len(axial_slice[r]) != 6*(self._num_rings - 1 - r):
|
||||
msg = 'HexLattice ID={0:d} has the wrong number of ' \
|
||||
'elements in ring number {1:d} (counting from the '\
|
||||
'outermost ring). This ring should have {2:d} ' \
|
||||
'elements.'.format(self._id, r,
|
||||
6*(self._num_rings - 1 - r))
|
||||
raise ValueError(msg)
|
||||
|
||||
else:
|
||||
axial_slice = self._universes
|
||||
# Check the center ring.
|
||||
if len(axial_slice[-1]) != 1:
|
||||
msg = 'HexLattice ID={0:d} has the wrong number of ' \
|
||||
'elements in the innermost ring. Only 1 element is ' \
|
||||
'allowed in the innermost ring.'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Check the outer rings.
|
||||
for r in range(self._num_rings-1):
|
||||
if len(axial_slice[r]) != 6*(self._num_rings - 1 - r):
|
||||
msg = 'HexLattice ID={0:d} has the wrong number of ' \
|
||||
'elements in ring number {1:d} (counting from the '\
|
||||
'outermost ring). This ring should have {2:d} ' \
|
||||
'elements.'.format(self._id, r,
|
||||
6*(self._num_rings - 1 - r))
|
||||
raise ValueError(msg)
|
||||
|
||||
def create_xml_subelement(self, xml_element):
|
||||
# Determine if XML element already contains subelement for this Lattice
|
||||
path = './hex_lattice[@id=\'{0}\']'.format(self._id)
|
||||
test = xml_element.find(path)
|
||||
|
||||
# If the element does contain the Lattice subelement, then return
|
||||
if test is not None:
|
||||
return
|
||||
|
||||
lattice_subelement = ET.Element("hex_lattice")
|
||||
lattice_subelement.set("id", str(self._id))
|
||||
|
||||
if len(self._name) > 0:
|
||||
lattice_subelement.set("name", str(self._name))
|
||||
|
||||
# Export the Lattice cell pitch
|
||||
pitch = ET.SubElement(lattice_subelement, "pitch")
|
||||
pitch.text = ' '.join(map(str, self._pitch))
|
||||
|
||||
# Export the Lattice outer Universe (if specified)
|
||||
if self._outer is not None:
|
||||
outer = ET.SubElement(lattice_subelement, "outer")
|
||||
outer.text = '{0}'.format(self._outer._id)
|
||||
self._outer.create_xml_subelement(xml_element)
|
||||
|
||||
lattice_subelement.set("n_rings", str(self._num_rings))
|
||||
|
||||
if self._num_axial is not None:
|
||||
lattice_subelement.set("n_axial", str(self._num_axial))
|
||||
|
||||
# Export Lattice cell center
|
||||
dimension = ET.SubElement(lattice_subelement, "center")
|
||||
dimension.text = ' '.join(map(str, self._center))
|
||||
|
||||
# Export the Lattice nested Universe IDs.
|
||||
|
||||
# 3D Lattices
|
||||
if self._num_axial is not None:
|
||||
slices = []
|
||||
for z in range(self._num_axial):
|
||||
# Initialize the center universe.
|
||||
universe = self._universes[z][-1][0]
|
||||
universe.create_xml_subelement(xml_element)
|
||||
|
||||
# Initialize the remaining universes.
|
||||
for r in range(self._num_rings-1):
|
||||
for theta in range(6*(self._num_rings - 1 - r)):
|
||||
universe = self._universes[z][r][theta]
|
||||
universe.create_xml_subelement(xml_element)
|
||||
|
||||
# Get a string representation of the universe IDs.
|
||||
slices.append(self._repr_axial_slice(self._universes[z]))
|
||||
|
||||
# Collapse the list of axial slices into a single string.
|
||||
universe_ids = '\n'.join(slices)
|
||||
|
||||
# 2D Lattices
|
||||
else:
|
||||
# Initialize the center universe.
|
||||
universe = self._universes[-1][0]
|
||||
universe.create_xml_subelement(xml_element)
|
||||
|
||||
# Initialize the remaining universes.
|
||||
for r in range(self._num_rings - 1):
|
||||
for theta in range(6*(self._num_rings - 1 - r)):
|
||||
universe = self._universes[r][theta]
|
||||
universe.create_xml_subelement(xml_element)
|
||||
|
||||
# Get a string representation of the universe IDs.
|
||||
universe_ids = self._repr_axial_slice(self._universes)
|
||||
|
||||
universes = ET.SubElement(lattice_subelement, "universes")
|
||||
universes.text = '\n' + universe_ids
|
||||
|
||||
# Append the XML subelement for this Lattice to the XML element
|
||||
xml_element.append(lattice_subelement)
|
||||
|
||||
def _repr_axial_slice(self, universes):
|
||||
"""Return string representation for the given 2D group of universes.
|
||||
|
||||
The 'universes' argument should be a list of lists of universes where
|
||||
each sub-list represents a single ring. The first list should be the
|
||||
outer ring.
|
||||
"""
|
||||
|
||||
# Find the largest universe ID and count the number of digits so we can
|
||||
# properly pad the output string later.
|
||||
largest_id = max([max([univ._id for univ in ring])
|
||||
for ring in universes])
|
||||
n_digits = len(str(largest_id))
|
||||
pad = ' '*n_digits
|
||||
id_form = '{: ^' + str(n_digits) + 'd}'
|
||||
|
||||
# Initialize the list for each row.
|
||||
rows = [[] for i in range(1 + 4 * (self._num_rings-1))]
|
||||
middle = 2 * (self._num_rings - 1)
|
||||
|
||||
# Start with the degenerate first ring.
|
||||
universe = universes[-1][0]
|
||||
rows[middle] = [id_form.format(universe._id)]
|
||||
|
||||
# Add universes one ring at a time.
|
||||
for r in range(1, self._num_rings):
|
||||
# r_prime increments down while r increments up.
|
||||
r_prime = self._num_rings - 1 - r
|
||||
theta = 0
|
||||
y = middle + 2*r
|
||||
|
||||
# Climb down the top-right.
|
||||
for i in range(r):
|
||||
# Add the universe.
|
||||
universe = universes[r_prime][theta]
|
||||
rows[y].append(id_form.format(universe._id))
|
||||
|
||||
# Translate the indices.
|
||||
y -= 1
|
||||
theta += 1
|
||||
|
||||
# Climb down the right.
|
||||
for i in range(r):
|
||||
# Add the universe.
|
||||
universe = universes[r_prime][theta]
|
||||
rows[y].append(id_form.format(universe._id))
|
||||
|
||||
# Translate the indices.
|
||||
y -= 2
|
||||
theta += 1
|
||||
|
||||
# Climb down the bottom-right.
|
||||
for i in range(r):
|
||||
# Add the universe.
|
||||
universe = universes[r_prime][theta]
|
||||
rows[y].append(id_form.format(universe._id))
|
||||
|
||||
# Translate the indices.
|
||||
y -= 1
|
||||
theta += 1
|
||||
|
||||
# Climb up the bottom-left.
|
||||
for i in range(r):
|
||||
# Add the universe.
|
||||
universe = universes[r_prime][theta]
|
||||
rows[y].insert(0, id_form.format(universe._id))
|
||||
|
||||
# Translate the indices.
|
||||
y += 1
|
||||
theta += 1
|
||||
|
||||
# Climb up the left.
|
||||
for i in range(r):
|
||||
# Add the universe.
|
||||
universe = universes[r_prime][theta]
|
||||
rows[y].insert(0, id_form.format(universe._id))
|
||||
|
||||
# Translate the indices.
|
||||
y += 2
|
||||
theta += 1
|
||||
|
||||
# Climb up the top-left.
|
||||
for i in range(r):
|
||||
# Add the universe.
|
||||
universe = universes[r_prime][theta]
|
||||
rows[y].insert(0, id_form.format(universe._id))
|
||||
|
||||
# Translate the indices.
|
||||
y += 1
|
||||
theta += 1
|
||||
|
||||
# Flip the rows and join each row into a single string.
|
||||
rows = [pad.join(x) for x in rows[::-1]]
|
||||
|
||||
# Pad the beginning of the rows so they line up properly.
|
||||
for y in range(self._num_rings - 1):
|
||||
rows[y] = (self._num_rings - 1 - y)*pad + rows[y]
|
||||
rows[-1 - y] = (self._num_rings - 1 - y)*pad + rows[-1 - y]
|
||||
|
||||
for y in range(self._num_rings % 2, self._num_rings, 2):
|
||||
rows[middle + y] = pad + rows[middle + y]
|
||||
if y != 0:
|
||||
rows[middle - y] = pad + rows[middle - y]
|
||||
|
||||
# Join the rows together and return the string.
|
||||
universe_ids = '\n'.join(rows)
|
||||
return universe_ids
|
||||
85
openmc/macroscopic.py
Normal file
85
openmc/macroscopic.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
from numbers import Integral
|
||||
import sys
|
||||
|
||||
from openmc.checkvalue import check_type
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
|
||||
class Macroscopic(object):
|
||||
"""A Macroscopic object that can be used in a material.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of the macroscopic data, e.g. UO2
|
||||
xs : str
|
||||
Cross section identifier, e.g. 71c
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name : str
|
||||
Name of the nuclide, e.g. UO2
|
||||
xs : str
|
||||
Cross section identifier, e.g. 71c
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name='', xs=None):
|
||||
# Initialize class attributes
|
||||
self._name = ''
|
||||
self._xs = None
|
||||
|
||||
# Set the Material class attributes
|
||||
self.name = name
|
||||
|
||||
if xs is not None:
|
||||
self.xs = xs
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Macroscopic):
|
||||
if self._name != other._name:
|
||||
return False
|
||||
elif self._xs != other._xs:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
elif isinstance(other, basestring) and other == self.name:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self._name, self._xs))
|
||||
|
||||
def __repr__(self):
|
||||
string = 'Nuclide - {0}\n'.format(self._name)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
|
||||
return string
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def xs(self):
|
||||
return self._xs
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
check_type('name', name, basestring)
|
||||
self._name = name
|
||||
|
||||
@xs.setter
|
||||
def xs(self, xs):
|
||||
check_type('cross-section identifier', xs, basestring)
|
||||
self._xs = xs
|
||||
|
||||
def __repr__(self):
|
||||
string = 'Macroscopic - {0}\n'.format(self._name)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self.xs)
|
||||
return string
|
||||
|
|
@ -22,14 +22,15 @@ def reset_auto_material_id():
|
|||
|
||||
|
||||
# Units for density supported by OpenMC
|
||||
DENSITY_UNITS = ['g/cm3', 'g/cc', 'kg/cm3', 'atom/b-cm', 'atom/cm3', 'sum']
|
||||
DENSITY_UNITS = ['g/cm3', 'g/cc', 'kg/cm3', 'atom/b-cm', 'atom/cm3', 'sum',
|
||||
'macro']
|
||||
|
||||
# Constant for density when not needed
|
||||
NO_DENSITY = 99999.
|
||||
|
||||
|
||||
class Material(object):
|
||||
"""A material composed of a collection of nuclides/elements that can be
|
||||
"""A material composed of a collection of nuclides/elements that can be
|
||||
assigned to a region of space.
|
||||
|
||||
Parameters
|
||||
|
|
@ -49,7 +50,8 @@ class Material(object):
|
|||
Density of the material (units defined separately)
|
||||
density_units : str
|
||||
Units used for `density`. Can be one of 'g/cm3', 'g/cc', 'kg/cm3',
|
||||
'atom/b-cm', 'atom/cm3', or 'sum'.
|
||||
'atom/b-cm', 'atom/cm3', 'sum', or 'macro'. The 'macro' unit only
|
||||
applies in the case of a multi-group calculation.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -65,6 +67,10 @@ class Material(object):
|
|||
# Values - tuple (nuclide, percent, percent type)
|
||||
self._nuclides = OrderedDict()
|
||||
|
||||
# The single instance of Macroscopic data present in this material
|
||||
# (only one is allowed, hence this is different than _nuclides, etc)
|
||||
self._macroscopic = None
|
||||
|
||||
# An ordered dictionary of Elements (order affects OpenMC results)
|
||||
# Keys - Element names
|
||||
# Values - tuple (element, percent, percent type)
|
||||
|
|
@ -128,6 +134,10 @@ class Material(object):
|
|||
string += '{0: <16}'.format('\t{0}'.format(nuclide))
|
||||
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
|
||||
|
||||
if self._macroscopic is not None:
|
||||
string += '{0: <16}\n'.format('\tMacroscopic Data')
|
||||
string += '{0: <16}'.format('\t{0}'.format(self._macroscopic))
|
||||
|
||||
string += '{0: <16}\n'.format('\tElements')
|
||||
|
||||
for element in self._elements:
|
||||
|
|
@ -149,6 +159,7 @@ class Material(object):
|
|||
clone._density = self._density
|
||||
clone._density_units = self._density_units
|
||||
clone._nuclides = deepcopy(self._nuclides, memo)
|
||||
clone._macroscopic = self._macroscopic
|
||||
clone._elements = deepcopy(self._elements, memo)
|
||||
clone._sab = deepcopy(self._sab, memo)
|
||||
clone._convert_to_distrib_comps = self._convert_to_distrib_comps
|
||||
|
|
@ -259,7 +270,7 @@ class Material(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
nuclide : str or openmc.nuclide.Nuclide
|
||||
nuclide : str or openmc.Nuclide
|
||||
Nuclide to add
|
||||
percent : float
|
||||
Atom or weight percent
|
||||
|
|
@ -268,6 +279,11 @@ class Material(object):
|
|||
|
||||
"""
|
||||
|
||||
if self._macroscopic is not None:
|
||||
msg = 'Unable to add a Nuclide to Material ID="{0}" as a ' \
|
||||
'macroscopic data-set has already been added'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(nuclide, (openmc.Nuclide, str)):
|
||||
msg = 'Unable to add a Nuclide to Material ID="{0}" with a ' \
|
||||
'non-Nuclide value "{1}"'.format(self._id, nuclide)
|
||||
|
|
@ -297,7 +313,7 @@ class Material(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
nuclide : openmc.nuclide.Nuclide
|
||||
nuclide : openmc.Nuclide
|
||||
Nuclide to remove
|
||||
|
||||
"""
|
||||
|
|
@ -311,12 +327,70 @@ class Material(object):
|
|||
if nuclide._name in self._nuclides:
|
||||
del self._nuclides[nuclide._name]
|
||||
|
||||
def add_macroscopic(self, macroscopic):
|
||||
"""Add a macroscopic to the material
|
||||
|
||||
Parameters
|
||||
----------
|
||||
macroscopic : str or openmc.Macroscopic
|
||||
Macroscopic to add
|
||||
|
||||
"""
|
||||
|
||||
# Ensure no nuclides, elements, or sab are added since these would be
|
||||
# incompatible with macroscopics
|
||||
if self._nuclides or self._elements or self._sab:
|
||||
msg = 'Unable to add a Macroscopic data set to Material ID="{0}" ' \
|
||||
'with a macroscopic value "{1}" as an incompatible data ' \
|
||||
'member (i.e., nuclide, element, or S(a,b) table) ' \
|
||||
'has already been added'.format(self._id, macroscopic)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(macroscopic, (openmc.Macroscopic, basestring)):
|
||||
msg = 'Unable to add a Macroscopic to Material ID="{0}" with a ' \
|
||||
'non-Macroscopic value "{1}"'.format(self._id, macroscopic)
|
||||
raise ValueError(msg)
|
||||
|
||||
if isinstance(macroscopic, openmc.Macroscopic):
|
||||
# Copy this Macroscopic to separate it from the Macroscopic in
|
||||
# other Materials
|
||||
macroscopic = deepcopy(macroscopic)
|
||||
else:
|
||||
macroscopic = openmc.Macroscopic(macroscopic)
|
||||
|
||||
if self._macroscopic is None:
|
||||
self._macroscopic = macroscopic
|
||||
else:
|
||||
msg = 'Unable to add a Macroscopic to Material ID="{0}", ' \
|
||||
'Only One Macroscopic allowed per ' \
|
||||
'Material!'.format(self._id, macroscopic)
|
||||
raise ValueError(msg)
|
||||
|
||||
def remove_macroscopic(self, macroscopic):
|
||||
"""Remove a macroscopic from the material
|
||||
|
||||
Parameters
|
||||
----------
|
||||
macroscopic : openmc.Macroscopic
|
||||
Macroscopic to remove
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(macroscopic, openmc.Macroscopic):
|
||||
msg = 'Unable to remove a Macroscopic "{0}" in Material ID="{1}" ' \
|
||||
'since it is not a Macroscopic'.format(self._id, macroscopic)
|
||||
raise ValueError(msg)
|
||||
|
||||
# If the Material contains the Macroscopic, delete it
|
||||
if macroscopic._name == self._macroscopic.name:
|
||||
self._macroscopic = None
|
||||
|
||||
def add_element(self, element, percent, percent_type='ao'):
|
||||
"""Add a natural element to the material
|
||||
|
||||
Parameters
|
||||
----------
|
||||
element : openmc.element.Element
|
||||
element : openmc.Element
|
||||
Element to add
|
||||
percent : float
|
||||
Atom or weight percent
|
||||
|
|
@ -325,6 +399,11 @@ class Material(object):
|
|||
|
||||
"""
|
||||
|
||||
if self._macroscopic is not None:
|
||||
msg = 'Unable to add an Element to Material ID="{0}" as a ' \
|
||||
'macroscopic data-set has already been added'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(element, openmc.Element):
|
||||
msg = 'Unable to add an Element to Material ID="{0}" with a ' \
|
||||
'non-Element value "{1}"'.format(self._id, element)
|
||||
|
|
@ -350,7 +429,7 @@ class Material(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
element : openmc.element.Element
|
||||
element : openmc.Element
|
||||
Element to remove
|
||||
|
||||
"""
|
||||
|
|
@ -371,6 +450,11 @@ class Material(object):
|
|||
|
||||
"""
|
||||
|
||||
if self._macroscopic is not None:
|
||||
msg = 'Unable to add an S(a,b) table to Material ID="{0}" as a ' \
|
||||
'macroscopic data-set has already been added'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(name, basestring):
|
||||
msg = 'Unable to add an S(a,b) table to Material ID="{0}" with a ' \
|
||||
'non-string table name "{1}"'.format(self._id, name)
|
||||
|
|
@ -427,6 +511,15 @@ class Material(object):
|
|||
|
||||
return xml_element
|
||||
|
||||
def _get_macroscopic_xml(self, macroscopic):
|
||||
xml_element = ET.Element("macroscopic")
|
||||
xml_element.set("name", macroscopic._name)
|
||||
|
||||
if macroscopic.xs is not None:
|
||||
xml_element.set("xs", macroscopic.xs)
|
||||
|
||||
return xml_element
|
||||
|
||||
def _get_element_xml(self, element, distrib=False):
|
||||
xml_element = ET.Element("element")
|
||||
xml_element.set("name", str(element[0]._name))
|
||||
|
|
@ -482,14 +575,19 @@ class Material(object):
|
|||
subelement.set("units", self._density_units)
|
||||
|
||||
if not self._convert_to_distrib_comps:
|
||||
# Create nuclide XML subelements
|
||||
subelements = self._get_nuclides_xml(self._nuclides)
|
||||
for subelement in subelements:
|
||||
element.append(subelement)
|
||||
if self._macroscopic is None:
|
||||
# Create nuclide XML subelements
|
||||
subelements = self._get_nuclides_xml(self._nuclides)
|
||||
for subelement in subelements:
|
||||
element.append(subelement)
|
||||
|
||||
# Create element XML subelements
|
||||
subelements = self._get_elements_xml(self._elements)
|
||||
for subelement in subelements:
|
||||
# Create element XML subelements
|
||||
subelements = self._get_elements_xml(self._elements)
|
||||
for subelement in subelements:
|
||||
element.append(subelement)
|
||||
else:
|
||||
# Create macroscopic XML subelements
|
||||
subelement = self._get_macroscopic_xml(self._macroscopic)
|
||||
element.append(subelement)
|
||||
|
||||
else:
|
||||
|
|
@ -516,15 +614,21 @@ class Material(object):
|
|||
subsubelement = ET.SubElement(subelement, "otf_file_path")
|
||||
subsubelement.text = self._distrib_otf_file
|
||||
|
||||
# Create nuclide XML subelements
|
||||
subelements = self.get_nuclides_xml(self._nuclides, distrib=True)
|
||||
for subelement_nuc in subelements:
|
||||
subelement.append(subelement_nuc)
|
||||
if self._macroscopic is None:
|
||||
# Create nuclide XML subelements
|
||||
subelements = self.get_nuclides_xml(self._nuclides, distrib=True)
|
||||
for subelement_nuc in subelements:
|
||||
subelement.append(subelement_nuc)
|
||||
|
||||
# Create element XML subelements
|
||||
subelements = self._get_elements_xml(self._elements, distrib=True)
|
||||
for subelement_ele in subelements:
|
||||
subelement.append(subelement_ele)
|
||||
# Create element XML subelements
|
||||
subelements = self._get_elements_xml(self._elements, distrib=True)
|
||||
for subsubelement in subelements:
|
||||
subelement.append(subsubelement)
|
||||
else:
|
||||
# Create macroscopic XML subelements
|
||||
subsubelement = self._get_macroscopic_xml(self._macroscopic,
|
||||
distrib=True)
|
||||
subelement.append(subsubelement)
|
||||
|
||||
if len(self._sab) > 0:
|
||||
for sab in self._sab:
|
||||
|
|
@ -567,7 +671,7 @@ class MaterialsFile(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
material : Material
|
||||
material : openmc.Material
|
||||
Material to add
|
||||
|
||||
"""
|
||||
|
|
@ -584,7 +688,7 @@ class MaterialsFile(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
materials : tuple or list of Material
|
||||
materials : tuple or list of openmc.Material
|
||||
Materials to add
|
||||
|
||||
"""
|
||||
|
|
@ -602,7 +706,7 @@ class MaterialsFile(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
material : Material
|
||||
material : openmc.Material
|
||||
Material to remove
|
||||
|
||||
"""
|
||||
|
|
@ -619,9 +723,8 @@ class MaterialsFile(object):
|
|||
material.make_isotropic_in_lab()
|
||||
|
||||
def _create_material_subelements(self):
|
||||
subelement = ET.SubElement(self._materials_file, "default_xs")
|
||||
|
||||
if self._default_xs is not None:
|
||||
subelement = ET.SubElement(self._materials_file, "default_xs")
|
||||
subelement.text = self._default_xs
|
||||
|
||||
for material in self._materials:
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class EnergyGroups(object):
|
|||
----------
|
||||
group_edges : Iterable of Real
|
||||
The energy group boundaries [MeV]
|
||||
num_group : Integral
|
||||
num_groups : int
|
||||
The number of energy groups
|
||||
|
||||
"""
|
||||
|
|
@ -54,10 +54,12 @@ class EnergyGroups(object):
|
|||
def __eq__(self, other):
|
||||
if not isinstance(other, EnergyGroups):
|
||||
return False
|
||||
elif self.group_edges != other.group_edges:
|
||||
elif self.num_groups != other.num_groups:
|
||||
return False
|
||||
else:
|
||||
elif np.allclose(self.group_edges, other.group_edges):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
|
@ -84,7 +86,7 @@ class EnergyGroups(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
energy : Real
|
||||
energy : float
|
||||
The energy of interest in MeV
|
||||
|
||||
Returns
|
||||
|
|
@ -113,7 +115,7 @@ class EnergyGroups(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
group : Integral
|
||||
group : int
|
||||
The energy group index, starting at 1 for the highest energies
|
||||
|
||||
Returns
|
||||
|
|
@ -151,7 +153,7 @@ class EnergyGroups(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
ndarray
|
||||
numpy.ndarray
|
||||
The ndarray array indices for each energy group of interest
|
||||
|
||||
Raises
|
||||
|
|
@ -198,7 +200,7 @@ class EnergyGroups(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
EnergyGroups
|
||||
openmc.mgxs.EnergyGroups
|
||||
A coarsened version of this EnergyGroups object.
|
||||
|
||||
Raises
|
||||
|
|
@ -236,3 +238,64 @@ class EnergyGroups(object):
|
|||
condensed_groups.group_edges = group_edges
|
||||
|
||||
return condensed_groups
|
||||
|
||||
def can_merge(self, other):
|
||||
"""Determine if energy groups can be merged with another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : openmc.mgxs.EnergyGroups
|
||||
EnergyGroups to compare with
|
||||
|
||||
Returns
|
||||
-------
|
||||
bool
|
||||
Whether the energy groups can be merged
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(other, EnergyGroups):
|
||||
return False
|
||||
|
||||
# If the energy group structures match then groups are mergeable
|
||||
if self == other:
|
||||
return True
|
||||
|
||||
# This low energy edge coincides with other's high energy edge
|
||||
if self.group_edges[0] == other.group_edges[-1]:
|
||||
return True
|
||||
# This high energy edge coincides with other's low energy edge
|
||||
elif self.group_edges[-1] == other.group_edges[0]:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def merge(self, other):
|
||||
"""Merge this energy groups with another.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : openmc.mgxs.EnergyGroups
|
||||
EnergyGroups to merge with
|
||||
|
||||
Returns
|
||||
-------
|
||||
merged_groups : openmc.mgxs.EnergyGroups
|
||||
EnergyGroups resulting from the merge
|
||||
|
||||
"""
|
||||
|
||||
if not self.can_merge(other):
|
||||
raise ValueError('Unable to merge energy groups')
|
||||
|
||||
# Create deep copy to return as merged energy groups
|
||||
merged_groups = copy.deepcopy(self)
|
||||
|
||||
# Merge unique filter bins
|
||||
merged_edges = np.concatenate((self.group_edges, other.group_edges))
|
||||
merged_edges = np.unique(merged_edges)
|
||||
merged_edges = sorted(merged_edges)
|
||||
|
||||
# Assign merged edges to merged groups
|
||||
merged_groups.group_edges = list(merged_edges)
|
||||
return merged_groups
|
||||
|
|
|
|||
|
|
@ -53,22 +53,22 @@ class Library(object):
|
|||
The types of cross sections in the library (e.g., ['total', 'scatter'])
|
||||
domain_type : {'material', 'cell', 'distribcell', 'universe'}
|
||||
Domain type for spatial homogenization
|
||||
domains : Iterable of Material, Cell or Universe
|
||||
domains : Iterable of openmc.Material, openmc.Cell or openmc.Universe
|
||||
The spatial domain(s) for which MGXS in the Library are computed
|
||||
correction : 'P0' or None
|
||||
correction : {'P0', None}
|
||||
Apply the P0 correction to scattering matrices if set to 'P0'
|
||||
energy_groups : EnergyGroups
|
||||
energy_groups : openmc.mgxs.EnergyGroups
|
||||
Energy group structure for energy condensation
|
||||
tally_trigger : Trigger
|
||||
tally_trigger : openmc.Trigger
|
||||
An (optional) tally precision trigger given to each tally used to
|
||||
compute the cross section
|
||||
all_mgxs : OrderedDict
|
||||
all_mgxs : collections.OrderedDict
|
||||
MGXS objects keyed by domain ID and cross section type
|
||||
sp_filename : str
|
||||
The filename of the statepoint with tally data used to the
|
||||
compute cross sections
|
||||
keff : Real or None
|
||||
The combined keff from the statepoint file with tally data used to
|
||||
The combined keff from the statepoint file with tally data used to
|
||||
compute cross sections (for eigenvalue calculations only)
|
||||
name : str, optional
|
||||
Name of the multi-group cross section library. Used as a label to
|
||||
|
|
@ -116,11 +116,11 @@ class Library(object):
|
|||
clone._by_nuclide = self.by_nuclide
|
||||
clone._mgxs_types = self.mgxs_types
|
||||
clone._domain_type = self.domain_type
|
||||
clone._domains = self.domains
|
||||
clone._domains = copy.deepcopy(self.domains)
|
||||
clone._correction = self.correction
|
||||
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
|
||||
clone._tally_trigger = copy.deepcopy(self.tally_trigger, memo)
|
||||
clone._all_mgxs = self.all_mgxs
|
||||
clone._all_mgxs = copy.deepcopy(self.all_mgxs)
|
||||
clone._sp_filename = self._sp_filename
|
||||
clone._keff = self._keff
|
||||
clone._sparse = self.sparse
|
||||
|
|
@ -308,7 +308,7 @@ class Library(object):
|
|||
"""
|
||||
|
||||
cv.check_type('sparse', sparse, bool)
|
||||
|
||||
|
||||
# Sparsify or densify each MGXS in the Library
|
||||
for domain in self.domains:
|
||||
for mgxs_type in self.mgxs_types:
|
||||
|
|
@ -350,7 +350,7 @@ class Library(object):
|
|||
def add_to_tallies_file(self, tallies_file, merge=True):
|
||||
"""Add all tallies from all MGXS objects to a tallies file.
|
||||
|
||||
NOTE: This assumes that build_library() has been called
|
||||
NOTE: This assumes that :meth:`Library.build_library` has been called
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -426,7 +426,7 @@ class Library(object):
|
|||
----------
|
||||
domain : Material or Cell or Universe or Integral
|
||||
The material, cell, or universe object of interest (or its ID)
|
||||
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
|
||||
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
|
||||
The type of multi-group cross section object to return
|
||||
|
||||
Returns
|
||||
|
|
@ -457,7 +457,7 @@ class Library(object):
|
|||
break
|
||||
else:
|
||||
msg = 'Unable to find MGXS for {0} "{1}" in ' \
|
||||
'library'.format(self.domain_type, domain)
|
||||
'library'.format(self.domain_type, domain_id)
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
domain_id = domain.id
|
||||
|
|
@ -537,7 +537,7 @@ class Library(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
Library
|
||||
openmc.mgxs.Library
|
||||
A new multi-group cross section library averaged across subdomains
|
||||
|
||||
Raises
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ MGXS_TYPES = ['total',
|
|||
'capture',
|
||||
'fission',
|
||||
'nu-fission',
|
||||
'kappa-fission',
|
||||
'scatter',
|
||||
'nu-scatter',
|
||||
'scatter matrix',
|
||||
|
|
@ -58,11 +59,11 @@ class MGXS(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
domain : Material or Cell or Universe
|
||||
domain : openmc.Material or openmc.Cell or openmc.Universe
|
||||
The domain for spatial homogenization
|
||||
domain_type : {'material', 'cell', 'distribcell', 'universe'}
|
||||
The domain type for spatial homogenization
|
||||
energy_groups : EnergyGroups
|
||||
energy_groups : openmc.mgxs.EnergyGroups
|
||||
The energy group structure for energy condensation
|
||||
by_nuclide : bool
|
||||
If true, computes cross sections for each nuclide in domain
|
||||
|
|
@ -82,34 +83,38 @@ class MGXS(object):
|
|||
Domain for spatial homogenization
|
||||
domain_type : {'material', 'cell', 'distribcell', 'universe'}
|
||||
Domain type for spatial homogenization
|
||||
energy_groups : EnergyGroups
|
||||
energy_groups : openmc.mgxs.EnergyGroups
|
||||
Energy group structure for energy condensation
|
||||
tally_trigger : Trigger
|
||||
tally_trigger : openmc.Trigger
|
||||
An (optional) tally precision trigger given to each tally used to
|
||||
compute the cross section
|
||||
tallies : OrderedDict
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section
|
||||
rxn_rate_tally : Tally
|
||||
rxn_rate_tally : openmc.Tally
|
||||
Derived tally for the reaction rate tally used in the numerator to
|
||||
compute the multi-group cross section. This attribute is None
|
||||
unless the multi-group cross section has been computed.
|
||||
xs_tally : Tally
|
||||
xs_tally : openmc.Tally
|
||||
Derived tally for the multi-group cross section. This attribute
|
||||
is None unless the multi-group cross section has been computed.
|
||||
num_subdomains : Integral
|
||||
num_subdomains : int
|
||||
The number of subdomains is unity for 'material', 'cell' and 'universe'
|
||||
domain types. When the This is equal to the number of cell instances
|
||||
for 'distribcell' domain types (it is equal to unity prior to loading
|
||||
tally data from a statepoint file).
|
||||
num_nuclides : Integral
|
||||
num_nuclides : int
|
||||
The number of nuclides for which the multi-group cross section is
|
||||
being tracked. This is unity if the by_nuclide attribute is False.
|
||||
nuclides : list of str or 'sum'
|
||||
A list of nuclide string names (e.g., 'U-238', 'O-16') when by_nuclide
|
||||
is True and 'sum' when by_nuclide is False.
|
||||
nuclides : Iterable of str or 'sum'
|
||||
The optional user-specified nuclides for which to compute cross
|
||||
sections (e.g., 'U-238', 'O-16'). If by_nuclide is True but nuclides
|
||||
are not specified by the user, all nuclides in the spatial domain
|
||||
are included. This attribute is 'sum' if by_nuclide is false.
|
||||
sparse : bool
|
||||
Whether or not the MGXS' tallies use SciPy's LIL sparse matrix format
|
||||
for compressed data storage
|
||||
derived : bool
|
||||
Whether or not the MGXS is merged from one or more other MGXS
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -122,6 +127,7 @@ class MGXS(object):
|
|||
self._name = ''
|
||||
self._rxn_type = None
|
||||
self._by_nuclide = None
|
||||
self._nuclides = None
|
||||
self._domain = None
|
||||
self._domain_type = None
|
||||
self._energy_groups = None
|
||||
|
|
@ -130,6 +136,7 @@ class MGXS(object):
|
|||
self._rxn_rate_tally = None
|
||||
self._xs_tally = None
|
||||
self._sparse = False
|
||||
self._derived = False
|
||||
|
||||
self.name = name
|
||||
self.by_nuclide = by_nuclide
|
||||
|
|
@ -150,6 +157,7 @@ class MGXS(object):
|
|||
clone._name = self.name
|
||||
clone._rxn_type = self.rxn_type
|
||||
clone._by_nuclide = self.by_nuclide
|
||||
clone._nuclides = copy.deepcopy(self._nuclides)
|
||||
clone._domain = self.domain
|
||||
clone._domain_type = self.domain_type
|
||||
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
|
||||
|
|
@ -157,6 +165,7 @@ class MGXS(object):
|
|||
clone._rxn_rate_tally = copy.deepcopy(self._rxn_rate_tally, memo)
|
||||
clone._xs_tally = copy.deepcopy(self._xs_tally, memo)
|
||||
clone._sparse = self.sparse
|
||||
clone._derived = self.derived
|
||||
|
||||
clone._tallies = OrderedDict()
|
||||
for tally_type, tally in self.tallies.items():
|
||||
|
|
@ -231,8 +240,7 @@ class MGXS(object):
|
|||
|
||||
@property
|
||||
def num_subdomains(self):
|
||||
tally = list(self.tallies.values())[0]
|
||||
domain_filter = tally.find_filter(self.domain_type)
|
||||
domain_filter = self.xs_tally.find_filter(self.domain_type)
|
||||
return domain_filter.num_bins
|
||||
|
||||
@property
|
||||
|
|
@ -249,6 +257,10 @@ class MGXS(object):
|
|||
else:
|
||||
return 'sum'
|
||||
|
||||
@property
|
||||
def derived(self):
|
||||
return self._derived
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
cv.check_type('name', name, basestring)
|
||||
|
|
@ -259,6 +271,11 @@ class MGXS(object):
|
|||
cv.check_type('by_nuclide', by_nuclide, bool)
|
||||
self._by_nuclide = by_nuclide
|
||||
|
||||
@nuclides.setter
|
||||
def nuclides(self, nuclides):
|
||||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
self._nuclides = nuclides
|
||||
|
||||
@domain.setter
|
||||
def domain(self, domain):
|
||||
cv.check_type('domain', domain, tuple(_DOMAINS))
|
||||
|
|
@ -315,13 +332,13 @@ class MGXS(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
|
||||
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
|
||||
The type of multi-group cross section object to return
|
||||
domain : Material or Cell or Universe
|
||||
domain : openmc.Material or openmc.Cell or openmc.Universe
|
||||
The domain for spatial homogenization
|
||||
domain_type : {'material', 'cell', 'distribcell', 'universe'}
|
||||
The domain type for spatial homogenization
|
||||
energy_groups : EnergyGroups
|
||||
energy_groups : openmc.mgxs.EnergyGroups
|
||||
The energy group structure for energy condensation
|
||||
by_nuclide : bool
|
||||
If true, computes cross sections for each nuclide in domain.
|
||||
|
|
@ -332,7 +349,7 @@ class MGXS(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
MGXS
|
||||
openmc.mgxs.MGXS
|
||||
A subclass of the abstract MGXS class for the multi-group cross
|
||||
section type requested by the user
|
||||
|
||||
|
|
@ -352,6 +369,8 @@ class MGXS(object):
|
|||
mgxs = FissionXS(domain, domain_type, energy_groups)
|
||||
elif mgxs_type == 'nu-fission':
|
||||
mgxs = NuFissionXS(domain, domain_type, energy_groups)
|
||||
elif mgxs_type == 'kappa-fission':
|
||||
mgxs = KappaFissionXS(domain, domain_type, energy_groups)
|
||||
elif mgxs_type == 'scatter':
|
||||
mgxs = ScatterXS(domain, domain_type, energy_groups)
|
||||
elif mgxs_type == 'nu-scatter':
|
||||
|
|
@ -386,8 +405,14 @@ class MGXS(object):
|
|||
if self.domain is None:
|
||||
raise ValueError('Unable to get all nuclides without a domain')
|
||||
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
return nuclides.keys()
|
||||
# If the user defined nuclides, return them
|
||||
if self._nuclides:
|
||||
return self._nuclides
|
||||
|
||||
# Otherwise, return all nuclides in the spatial domain
|
||||
else:
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
return nuclides.keys()
|
||||
|
||||
def get_nuclide_density(self, nuclide):
|
||||
"""Get the atomic number density in units of atoms/b-cm for a nuclide
|
||||
|
|
@ -400,7 +425,7 @@ class MGXS(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
Real
|
||||
float
|
||||
The atomic number density (atom/b-cm) for the nuclide of interest
|
||||
|
||||
Raises
|
||||
|
|
@ -439,7 +464,7 @@ class MGXS(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
ndarray of Real
|
||||
numpy.ndarray of float
|
||||
An array of the atomic number densities (atom/b-cm) for each of the
|
||||
nuclides in the spatial domain
|
||||
|
||||
|
|
@ -487,11 +512,11 @@ class MGXS(object):
|
|||
----------
|
||||
scores : Iterable of str
|
||||
Scores for each tally
|
||||
all_filters : Iterable of tuple of Filter
|
||||
all_filters : Iterable of tuple of openmc.Filter
|
||||
Tuples of non-spatial domain filters for each tally
|
||||
keys : Iterable of str
|
||||
Key string used to store each tally in the tallies dictionary
|
||||
estimator : {'analog' or 'tracklength'}
|
||||
estimator : {'analog', 'tracklength'}
|
||||
Type of estimator to use for each tally
|
||||
|
||||
"""
|
||||
|
|
@ -510,27 +535,27 @@ class MGXS(object):
|
|||
# Create each Tally needed to compute the multi group cross section
|
||||
for score, key, filters in zip(scores, keys, all_filters):
|
||||
self.tallies[key] = openmc.Tally(name=self.name)
|
||||
self.tallies[key].add_score(score)
|
||||
self.tallies[key].scores = [score]
|
||||
self.tallies[key].estimator = estimator
|
||||
self.tallies[key].add_filter(domain_filter)
|
||||
self.tallies[key].filters = [domain_filter]
|
||||
|
||||
# If a tally trigger was specified, add it to each tally
|
||||
if self.tally_trigger:
|
||||
trigger_clone = copy.deepcopy(self.tally_trigger)
|
||||
trigger_clone.add_score(score)
|
||||
self.tallies[key].add_trigger(trigger_clone)
|
||||
trigger_clone.scores = [score]
|
||||
self.tallies[key].triggers.append(trigger_clone)
|
||||
|
||||
# Add all non-domain specific Filters (e.g., 'energy') to the Tally
|
||||
for add_filter in filters:
|
||||
self.tallies[key].add_filter(add_filter)
|
||||
self.tallies[key].filters.append(add_filter)
|
||||
|
||||
# If this is a by-nuclide cross-section, add all nuclides to Tally
|
||||
if self.by_nuclide and score != 'flux':
|
||||
all_nuclides = self.domain.get_all_nuclides()
|
||||
all_nuclides = self.get_all_nuclides()
|
||||
for nuclide in all_nuclides:
|
||||
self.tallies[key].add_nuclide(nuclide)
|
||||
self.tallies[key].nuclides.append(nuclide)
|
||||
else:
|
||||
self.tallies[key].add_nuclide('total')
|
||||
self.tallies[key].nuclides.append('total')
|
||||
|
||||
def _compute_xs(self):
|
||||
"""Performs generic cleanup after a subclass' uses tally arithmetic to
|
||||
|
|
@ -550,9 +575,9 @@ class MGXS(object):
|
|||
# If computing xs for each nuclide, replace CrossNuclides with originals
|
||||
if self.by_nuclide:
|
||||
self.xs_tally._nuclides = []
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
nuclides = self.get_all_nuclides()
|
||||
for nuclide in nuclides:
|
||||
self.xs_tally.add_nuclide(openmc.Nuclide(nuclide))
|
||||
self.xs_tally.nuclides.append(openmc.Nuclide(nuclide))
|
||||
|
||||
# Remove NaNs which may have resulted from divide-by-zero operations
|
||||
self.xs_tally._mean = np.nan_to_num(self.xs_tally.mean)
|
||||
|
|
@ -659,7 +684,7 @@ class MGXS(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
ndarray
|
||||
numpy.ndarray
|
||||
A NumPy array of the multi-group cross section indexed in the order
|
||||
each group, subdomain and nuclide is listed in the parameters.
|
||||
|
||||
|
|
@ -679,7 +704,7 @@ class MGXS(object):
|
|||
|
||||
# Construct a collection of the domain filter bins
|
||||
if not isinstance(subdomains, basestring):
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=2)
|
||||
for subdomain in subdomains:
|
||||
filters.append(self.domain_type)
|
||||
filter_bins.append((subdomain,))
|
||||
|
|
@ -734,10 +759,9 @@ class MGXS(object):
|
|||
# Reverse energies to align with increasing energy groups
|
||||
xs = xs[:, ::-1, :]
|
||||
|
||||
# Eliminate trivial dimensions
|
||||
xs = np.squeeze(xs)
|
||||
xs = np.atleast_1d(xs)
|
||||
|
||||
# Eliminate trivial dimensions
|
||||
xs = np.squeeze(xs)
|
||||
xs = np.atleast_1d(xs)
|
||||
return xs
|
||||
|
||||
def get_condensed_xs(self, coarse_groups):
|
||||
|
|
@ -831,7 +855,7 @@ class MGXS(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
MGXS
|
||||
openmc.mgxs.MGXS
|
||||
A new MGXS averaged across the subdomains of interest
|
||||
|
||||
Raises
|
||||
|
|
@ -852,19 +876,181 @@ class MGXS(object):
|
|||
|
||||
# Clone this MGXS to initialize the subdomain-averaged version
|
||||
avg_xs = copy.deepcopy(self)
|
||||
avg_xs._rxn_rate_tally = None
|
||||
avg_xs._xs_tally = None
|
||||
|
||||
# Average each of the tallies across subdomains
|
||||
for tally_type, tally in avg_xs.tallies.items():
|
||||
tally_avg = tally.summation(filter_type=self.domain_type,
|
||||
filter_bins=subdomains)
|
||||
avg_xs.tallies[tally_type] = tally_avg
|
||||
if self.derived:
|
||||
avg_xs._rxn_rate_tally = avg_xs.rxn_rate_tally.average(
|
||||
filter_type=self.domain_type, filter_bins=subdomains)
|
||||
else:
|
||||
avg_xs._rxn_rate_tally = None
|
||||
avg_xs._xs_tally = None
|
||||
|
||||
avg_xs._domain_type = 'sum({0})'.format(self.domain_type)
|
||||
# Average each of the tallies across subdomains
|
||||
for tally_type, tally in avg_xs.tallies.items():
|
||||
tally_avg = tally.average(filter_type=self.domain_type,
|
||||
filter_bins=subdomains)
|
||||
avg_xs.tallies[tally_type] = tally_avg
|
||||
|
||||
avg_xs._domain_type = 'avg({0})'.format(self.domain_type)
|
||||
avg_xs.sparse = self.sparse
|
||||
return avg_xs
|
||||
|
||||
def get_slice(self, nuclides=[], groups=[]):
|
||||
"""Build a sliced MGXS for the specified nuclides and energy groups.
|
||||
|
||||
This method constructs a new MGXS to encapsulate a subset of the data
|
||||
represented by this MGXS. The subset of data to include in the tally
|
||||
slice is determined by the nuclides and energy groups specified in
|
||||
the input parameters.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclides : list of str
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U-235', 'U-238']; default is [])
|
||||
groups : list of int
|
||||
A list of energy group indices starting at 1 for the high energies
|
||||
(e.g., [1, 2, 3]; default is [])
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.mgxs.MGXS
|
||||
A new tally which encapsulates the subset of data requested for the
|
||||
nuclide(s) and/or energy group(s) requested in the parameters.
|
||||
|
||||
"""
|
||||
|
||||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
cv.check_iterable_type('energy_groups', groups, Integral)
|
||||
|
||||
# Build lists of filters and filter bins to slice
|
||||
if len(groups) == 0:
|
||||
filters = []
|
||||
filter_bins = []
|
||||
else:
|
||||
filter_bins = []
|
||||
for group in groups:
|
||||
group_bounds = self.energy_groups.get_group_bounds(group)
|
||||
filter_bins.append(group_bounds)
|
||||
filter_bins = [tuple(filter_bins)]
|
||||
filters = ['energy']
|
||||
|
||||
# Clone this MGXS to initialize the sliced version
|
||||
slice_xs = copy.deepcopy(self)
|
||||
slice_xs._rxn_rate_tally = None
|
||||
slice_xs._xs_tally = None
|
||||
|
||||
# Slice each of the tallies across nuclides and energy groups
|
||||
for tally_type, tally in slice_xs.tallies.items():
|
||||
slice_nuclides = [nuc for nuc in nuclides if nuc in tally.nuclides]
|
||||
if len(groups) != 0 and tally.contains_filter('energy'):
|
||||
tally_slice = tally.get_slice(filters=filters,
|
||||
filter_bins=filter_bins, nuclides=slice_nuclides)
|
||||
else:
|
||||
tally_slice = tally.get_slice(nuclides=slice_nuclides)
|
||||
slice_xs.tallies[tally_type] = tally_slice
|
||||
|
||||
# Assign sliced energy group structure to sliced MGXS
|
||||
if groups:
|
||||
new_group_edges = []
|
||||
for group in groups:
|
||||
group_edges = self.energy_groups.get_group_bounds(group)
|
||||
new_group_edges.extend(group_edges)
|
||||
new_group_edges = np.unique(new_group_edges)
|
||||
slice_xs.energy_groups.group_edges = sorted(new_group_edges)
|
||||
|
||||
# Assign sliced nuclides to sliced MGXS
|
||||
if nuclides:
|
||||
slice_xs.nuclides = nuclides
|
||||
|
||||
slice_xs.sparse = self.sparse
|
||||
return slice_xs
|
||||
|
||||
def can_merge(self, other):
|
||||
"""Determine if another MGXS can be merged with this one
|
||||
|
||||
If results have been loaded from a statepoint, then MGXS are only
|
||||
mergeable along one and only one of enegy groups or nuclides.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : openmc.mgxs.MGXS
|
||||
MGXS to check for merging
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(other, type(self)):
|
||||
return False
|
||||
|
||||
# Compare reaction type, energy groups, nuclides, domain type
|
||||
if self.rxn_type != other.rxn_type:
|
||||
return False
|
||||
elif not self.energy_groups.can_merge(other.energy_groups):
|
||||
return False
|
||||
elif self.by_nuclide != other.by_nuclide:
|
||||
return False
|
||||
elif self.domain_type != other.domain_type:
|
||||
return False
|
||||
elif 'distribcell' not in self.domain_type and self.domain != other.domain:
|
||||
return False
|
||||
elif not self.xs_tally.can_merge(other.xs_tally):
|
||||
return False
|
||||
elif not self.rxn_rate_tally.can_merge(other.rxn_rate_tally):
|
||||
return False
|
||||
|
||||
# If all conditionals pass then MGXS are mergeable
|
||||
return True
|
||||
|
||||
def merge(self, other):
|
||||
"""Merge another MGXS with this one
|
||||
|
||||
MGXS are only mergeable if their energy groups and nuclides are either
|
||||
identical or mutually exclusive. If results have been loaded from a
|
||||
statepoint, then MGXS are only mergeable along one and only one of
|
||||
energy groups or nuclides.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : openmc.mgxs.MGXS
|
||||
MGXS to merge with this one
|
||||
|
||||
Returns
|
||||
-------
|
||||
merged_mgxs : openmc.mgxs.MGXS
|
||||
Merged MGXS
|
||||
|
||||
"""
|
||||
|
||||
if not self.can_merge(other):
|
||||
raise ValueError('Unable to merge MGXS')
|
||||
|
||||
# Create deep copy of tally to return as merged tally
|
||||
merged_mgxs = copy.deepcopy(self)
|
||||
merged_mgxs._derived = True
|
||||
|
||||
# Merge energy groups
|
||||
if self.energy_groups != other.energy_groups:
|
||||
merged_groups = self.energy_groups.merge(other.energy_groups)
|
||||
merged_mgxs.energy_groups = merged_groups
|
||||
|
||||
# Merge nuclides
|
||||
if self.nuclides != other.nuclides:
|
||||
|
||||
# The nuclides must be mutually exclusive
|
||||
for nuclide in self.nuclides:
|
||||
if nuclide in other.nuclides:
|
||||
msg = 'Unable to merge MGXS with shared nuclides'
|
||||
raise ValueError(msg)
|
||||
|
||||
# Concatenate lists of nuclides for the merged MGXS
|
||||
merged_mgxs.nuclides = self.nuclides + other.nuclides
|
||||
|
||||
# Null base tallies but merge reaction rate and cross section tallies
|
||||
merged_mgxs._tallies = OrderedDict()
|
||||
merged_mgxs._rxn_rate_tally = self.rxn_rate_tally.merge(other.rxn_rate_tally)
|
||||
merged_mgxs._xs_tally = self.xs_tally.merge(other.xs_tally)
|
||||
|
||||
return merged_mgxs
|
||||
|
||||
def print_xs(self, subdomains='all', nuclides='all', xs_type='macro'):
|
||||
"""Print a string representation for the multi-group cross section.
|
||||
|
||||
|
|
@ -1019,6 +1205,9 @@ class MGXS(object):
|
|||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
elif self.domain_type == 'distribcell':
|
||||
subdomains = np.arange(self.num_subdomains, dtype=np.int)
|
||||
elif self.domain_type == 'avg(distribcell)':
|
||||
domain_filter = self.xs_tally.find_filter('avg(distribcell)')
|
||||
subdomains = domain_filter.bins
|
||||
else:
|
||||
subdomains = [self.domain.id]
|
||||
|
||||
|
|
@ -1160,7 +1349,7 @@ class MGXS(object):
|
|||
xs_type='macro', summary=None):
|
||||
"""Build a Pandas DataFrame for the MGXS data.
|
||||
|
||||
This method leverages the Tally.get_pandas_dataframe(...) method, but
|
||||
This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but
|
||||
renames the columns with terminology appropriate for cross section data.
|
||||
|
||||
Parameters
|
||||
|
|
@ -1177,7 +1366,7 @@ class MGXS(object):
|
|||
xs_type: {'macro', 'micro'}
|
||||
Return macro or micro cross section in units of cm^-1 or barns.
|
||||
Defaults to 'macro'.
|
||||
summary : None or Summary
|
||||
summary : None or openmc.Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
distribcell tally filters (default is None). The geometric
|
||||
information in the Summary object is embedded into a multi-index
|
||||
|
|
@ -1232,28 +1421,34 @@ class MGXS(object):
|
|||
# Override energy groups bounds with indices
|
||||
all_groups = np.arange(self.num_groups, 0, -1, dtype=np.int)
|
||||
all_groups = np.repeat(all_groups, self.num_nuclides)
|
||||
if 'energy [MeV]' in df and 'energyout [MeV]' in df:
|
||||
df.rename(columns={'energy [MeV]': 'group in'}, inplace=True)
|
||||
if 'energy low [MeV]' in df and 'energyout low [MeV]' in df:
|
||||
df.rename(columns={'energy low [MeV]': 'group in'},
|
||||
inplace=True)
|
||||
in_groups = np.tile(all_groups, self.num_subdomains)
|
||||
in_groups = np.repeat(in_groups, self.num_groups)
|
||||
in_groups = np.repeat(in_groups, df.shape[0] / in_groups.size)
|
||||
df['group in'] = in_groups
|
||||
del df['energy high [MeV]']
|
||||
|
||||
df.rename(columns={'energyout [MeV]': 'group out'}, inplace=True)
|
||||
out_groups = \
|
||||
np.tile(all_groups, self.num_subdomains * self.num_groups)
|
||||
df.rename(columns={'energyout low [MeV]': 'group out'},
|
||||
inplace=True)
|
||||
out_groups = np.tile(all_groups, df.shape[0] / all_groups.size)
|
||||
df['group out'] = out_groups
|
||||
del df['energyout high [MeV]']
|
||||
columns = ['group in', 'group out']
|
||||
|
||||
elif 'energyout [MeV]' in df:
|
||||
df.rename(columns={'energyout [MeV]': 'group out'}, inplace=True)
|
||||
elif 'energyout low [MeV]' in df:
|
||||
df.rename(columns={'energyout low [MeV]': 'group out'},
|
||||
inplace=True)
|
||||
in_groups = np.tile(all_groups, self.num_subdomains)
|
||||
df['group out'] = in_groups
|
||||
del df['energyout high [MeV]']
|
||||
columns = ['group out']
|
||||
|
||||
elif 'energy [MeV]' in df:
|
||||
df.rename(columns={'energy [MeV]': 'group in'}, inplace=True)
|
||||
elif 'energy low [MeV]' in df:
|
||||
df.rename(columns={'energy low [MeV]': 'group in'}, inplace=True)
|
||||
in_groups = np.tile(all_groups, self.num_subdomains)
|
||||
df['group in'] = in_groups
|
||||
del df['energy high [MeV]']
|
||||
columns = ['group in']
|
||||
|
||||
# Select out those groups the user requested
|
||||
|
|
@ -1275,8 +1470,7 @@ class MGXS(object):
|
|||
|
||||
# Sort the dataframe by domain type id (e.g., distribcell id) and
|
||||
# energy groups such that data is from fast to thermal
|
||||
df.sort([self.domain_type] + columns, inplace=True)
|
||||
|
||||
df.sort_values(by=[self.domain_type] + columns, inplace=True)
|
||||
return df
|
||||
|
||||
|
||||
|
|
@ -1319,7 +1513,7 @@ class TotalXS(MGXS):
|
|||
|
||||
@property
|
||||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
if self._rxn_rate_tally is None :
|
||||
self._rxn_rate_tally = self.tallies['total']
|
||||
self._rxn_rate_tally.sparse = self.sparse
|
||||
return self._rxn_rate_tally
|
||||
|
|
@ -1477,96 +1671,80 @@ class CaptureXS(MGXS):
|
|||
self._rxn_rate_tally.sparse = self.sparse
|
||||
return self._rxn_rate_tally
|
||||
|
||||
class FissionXSBase(MGXS):
|
||||
"""A fission production multi-group cross section base class
|
||||
for NuFission and KappaFission
|
||||
"""
|
||||
|
||||
class FissionXS(MGXS):
|
||||
# This is an abstract class which cannot be instantiated
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, rxn_type, domain=None, domain_type=None,
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(FissionXSBase, self).__init__(domain, domain_type,
|
||||
groups, by_nuclide, name)
|
||||
self._rxn_type = rxn_type
|
||||
|
||||
@property
|
||||
def tallies(self):
|
||||
"""Construct the OpenMC tallies needed to compute this cross section.
|
||||
|
||||
This method constructs two tracklength tallies to compute the 'flux'
|
||||
and 'rxn_type' reaction rates in the spatial domain and energy
|
||||
groups of interest.
|
||||
|
||||
"""
|
||||
|
||||
# Instantiate tallies if they do not exist
|
||||
if self._tallies is None:
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', self._rxn_type]
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Initialize the Tallies
|
||||
self._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
return self._tallies
|
||||
|
||||
@property
|
||||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
self._rxn_rate_tally = self.tallies[self._rxn_type]
|
||||
self._rxn_rate_tally.sparse = self.sparse
|
||||
return self._rxn_rate_tally
|
||||
|
||||
|
||||
class FissionXS(FissionXSBase):
|
||||
"""A fission multi-group cross section."""
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(FissionXS, self).__init__(domain, domain_type,
|
||||
super(FissionXS, self).__init__('fission', domain, domain_type,
|
||||
groups, by_nuclide, name)
|
||||
self._rxn_type = 'fission'
|
||||
|
||||
@property
|
||||
def tallies(self):
|
||||
"""Construct the OpenMC tallies needed to compute this cross section.
|
||||
|
||||
This method constructs two tracklength tallies to compute the 'flux'
|
||||
and 'fission' reaction rates in the spatial domain and energy
|
||||
groups of interest.
|
||||
|
||||
"""
|
||||
|
||||
# Instantiate tallies if they do not exist
|
||||
if self._tallies is None:
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'fission']
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Initialize the Tallies
|
||||
self._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
return self._tallies
|
||||
|
||||
@property
|
||||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
self._rxn_rate_tally = self.tallies['fission']
|
||||
self._rxn_rate_tally.sparse = self.sparse
|
||||
return self._rxn_rate_tally
|
||||
|
||||
|
||||
class NuFissionXS(MGXS):
|
||||
class NuFissionXS(FissionXSBase):
|
||||
"""A fission production multi-group cross section."""
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(NuFissionXS, self).__init__(domain, domain_type,
|
||||
super(NuFissionXS, self).__init__('nu-fission', domain, domain_type,
|
||||
groups, by_nuclide, name)
|
||||
self._rxn_type = 'nu-fission'
|
||||
|
||||
@property
|
||||
def tallies(self):
|
||||
"""Construct the OpenMC tallies needed to compute this cross section.
|
||||
|
||||
This method constructs two tracklength tallies to compute the 'flux'
|
||||
and 'nu-fission' reaction rates in the spatial domain and energy
|
||||
groups of interest.
|
||||
|
||||
"""
|
||||
|
||||
# Instantiate tallies if they do not exist
|
||||
if self._tallies is None:
|
||||
|
||||
# Create a list of scores for each Tally to be created
|
||||
scores = ['flux', 'nu-fission']
|
||||
estimator = 'tracklength'
|
||||
keys = scores
|
||||
|
||||
# Create the non-domain specific Filters for the Tallies
|
||||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.Filter('energy', group_edges)
|
||||
filters = [[energy_filter], [energy_filter]]
|
||||
|
||||
# Initialize the Tallies
|
||||
self._create_tallies(scores, filters, keys, estimator)
|
||||
|
||||
return self._tallies
|
||||
|
||||
@property
|
||||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
self._rxn_rate_tally = self.tallies['nu-fission']
|
||||
self._rxn_rate_tally.sparse = self.sparse
|
||||
return self._rxn_rate_tally
|
||||
class KappaFissionXS(FissionXSBase):
|
||||
"""A recoverable fission energy production rate multi-group cross section."""
|
||||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
groups=None, by_nuclide=False, name=''):
|
||||
super(KappaFissionXS, self).__init__('kappa-fission', domain, domain_type,
|
||||
groups, by_nuclide, name)
|
||||
|
||||
class ScatterXS(MGXS):
|
||||
"""A scatter multi-group cross section."""
|
||||
|
|
@ -1741,6 +1919,58 @@ class ScatterMatrixXS(MGXS):
|
|||
cv.check_value('correction', correction, ('P0', None))
|
||||
self._correction = correction
|
||||
|
||||
def get_slice(self, nuclides=[], in_groups=[], out_groups=[]):
|
||||
"""Build a sliced ScatterMatrix for the specified nuclides and
|
||||
energy groups.
|
||||
|
||||
This method constructs a new MGXS to encapsulate a subset of the data
|
||||
represented by this MGXS. The subset of data to include in the tally
|
||||
slice is determined by the nuclides and energy groups specified in
|
||||
the input parameters.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclides : list of str
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U-235', 'U-238']; default is [])
|
||||
in_groups : list of int
|
||||
A list of incoming energy group indices starting at 1 for the high
|
||||
energies (e.g., [1, 2, 3]; default is [])
|
||||
out_groups : list of int
|
||||
A list of outgoing energy group indices starting at 1 for the high
|
||||
energies (e.g., [1, 2, 3]; default is [])
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.mgxs.MGXS
|
||||
A new tally which encapsulates the subset of data requested for the
|
||||
nuclide(s) and/or energy group(s) requested in the parameters.
|
||||
|
||||
"""
|
||||
|
||||
# Call super class method and null out derived tallies
|
||||
slice_xs = super(ScatterMatrixXS, self).get_slice(nuclides, in_groups)
|
||||
slice_xs._rxn_rate_tally = None
|
||||
slice_xs._xs_tally = None
|
||||
|
||||
# Slice outgoing energy groups if needed
|
||||
if len(out_groups) != 0:
|
||||
filter_bins = []
|
||||
for group in out_groups:
|
||||
group_bounds = self.energy_groups.get_group_bounds(group)
|
||||
filter_bins.append(group_bounds)
|
||||
filter_bins = [tuple(filter_bins)]
|
||||
|
||||
# Slice each of the tallies across energyout groups
|
||||
for tally_type, tally in slice_xs.tallies.items():
|
||||
if tally.contains_filter('energyout'):
|
||||
tally_slice = tally.get_slice(filters=['energyout'],
|
||||
filter_bins=filter_bins)
|
||||
slice_xs.tallies[tally_type] = tally_slice
|
||||
|
||||
slice_xs.sparse = self.sparse
|
||||
return slice_xs
|
||||
|
||||
def get_xs(self, in_groups='all', out_groups='all',
|
||||
subdomains='all', nuclides='all', xs_type='macro',
|
||||
order_groups='increasing', value='mean'):
|
||||
|
|
@ -1795,7 +2025,7 @@ class ScatterMatrixXS(MGXS):
|
|||
|
||||
# Construct a collection of the domain filter bins
|
||||
if not isinstance(subdomains, basestring):
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=2)
|
||||
for subdomain in subdomains:
|
||||
filters.append(self.domain_type)
|
||||
filter_bins.append((subdomain,))
|
||||
|
|
@ -2080,10 +2310,117 @@ class Chi(MGXS):
|
|||
super(Chi, self)._compute_xs()
|
||||
|
||||
# Add the coarse energy filter back to the nu-fission tally
|
||||
nu_fission_in.add_filter(energy_filter)
|
||||
nu_fission_in.filters.append(energy_filter)
|
||||
|
||||
return self._xs_tally
|
||||
|
||||
def get_slice(self, nuclides=[], groups=[]):
|
||||
"""Build a sliced Chi for the specified nuclides and energy groups.
|
||||
|
||||
This method constructs a new MGXS to encapsulate a subset of the data
|
||||
represented by this MGXS. The subset of data to include in the tally
|
||||
slice is determined by the nuclides and energy groups specified in
|
||||
the input parameters.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclides : list of str
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U-235', 'U-238']; default is [])
|
||||
groups : list of Integral
|
||||
A list of energy group indices starting at 1 for the high energies
|
||||
(e.g., [1, 2, 3]; default is [])
|
||||
|
||||
Returns
|
||||
-------
|
||||
MGXS
|
||||
A new tally which encapsulates the subset of data requested for the
|
||||
nuclide(s) and/or energy group(s) requested in the parameters.
|
||||
|
||||
"""
|
||||
|
||||
# Temporarily remove energy filter from nu-fission-in since its
|
||||
# group structure will work in super MGXS.get_slice(...) method
|
||||
nu_fission_in = self.tallies['nu-fission-in']
|
||||
energy_filter = nu_fission_in.find_filter('energy')
|
||||
nu_fission_in.remove_filter(energy_filter)
|
||||
|
||||
# Call super class method and null out derived tallies
|
||||
slice_xs = super(Chi, self).get_slice(nuclides, groups)
|
||||
slice_xs._rxn_rate_tally = None
|
||||
slice_xs._xs_tally = None
|
||||
|
||||
# Slice energy groups if needed
|
||||
if len(groups) != 0:
|
||||
filter_bins = []
|
||||
for group in groups:
|
||||
group_bounds = self.energy_groups.get_group_bounds(group)
|
||||
filter_bins.append(group_bounds)
|
||||
filter_bins = [tuple(filter_bins)]
|
||||
|
||||
# Slice nu-fission-out tally along energyout filter
|
||||
nu_fission_out = slice_xs.tallies['nu-fission-out']
|
||||
tally_slice = nu_fission_out.get_slice(filters=['energyout'],
|
||||
filter_bins=filter_bins)
|
||||
slice_xs._tallies['nu-fission-out'] = tally_slice
|
||||
|
||||
# Add energy filter back to nu-fission-in tallies
|
||||
self.tallies['nu-fission-in'].add_filter(energy_filter)
|
||||
slice_xs._tallies['nu-fission-in'].add_filter(energy_filter)
|
||||
|
||||
slice_xs.sparse = self.sparse
|
||||
return slice_xs
|
||||
|
||||
def merge(self, other):
|
||||
"""Merge another Chi with this one
|
||||
|
||||
If results have been loaded from a statepoint, then Chi are only
|
||||
mergeable along one and only one of energy groups or nuclides.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : openmc.mgxs.MGXS
|
||||
MGXS to merge with this one
|
||||
|
||||
Returns
|
||||
-------
|
||||
merged_mgxs : openmc.mgxs.MGXS
|
||||
Merged MGXS
|
||||
"""
|
||||
|
||||
if not self.can_merge(other):
|
||||
raise ValueError('Unable to merge Chi')
|
||||
|
||||
# Create deep copy of tally to return as merged tally
|
||||
merged_mgxs = copy.deepcopy(self)
|
||||
merged_mgxs._derived = True
|
||||
merged_mgxs._rxn_rate_tally = None
|
||||
merged_mgxs._xs_tally = None
|
||||
|
||||
# Merge energy groups
|
||||
if self.energy_groups != other.energy_groups:
|
||||
merged_groups = self.energy_groups.merge(other.energy_groups)
|
||||
merged_mgxs.energy_groups = merged_groups
|
||||
|
||||
# Merge nuclides
|
||||
if self.nuclides != other.nuclides:
|
||||
|
||||
# The nuclides must be mutually exclusive
|
||||
for nuclide in self.nuclides:
|
||||
if nuclide in other.nuclides:
|
||||
msg = 'Unable to merge Chi with shared nuclides'
|
||||
raise ValueError(msg)
|
||||
|
||||
# Concatenate lists of nuclides for the merged MGXS
|
||||
merged_mgxs.nuclides = self.nuclides + other.nuclides
|
||||
|
||||
# Merge tallies
|
||||
for tally_key in self.tallies:
|
||||
merged_tally = self.tallies[tally_key].merge(other.tallies[tally_key])
|
||||
merged_mgxs.tallies[tally_key] = merged_tally
|
||||
|
||||
return merged_mgxs
|
||||
|
||||
def get_xs(self, groups='all', subdomains='all', nuclides='all',
|
||||
xs_type='macro', order_groups='increasing', value='mean'):
|
||||
"""Returns an array of the fission spectrum.
|
||||
|
|
@ -2115,7 +2452,7 @@ class Chi(MGXS):
|
|||
|
||||
Returns
|
||||
-------
|
||||
ndarray
|
||||
numpy.ndarray
|
||||
A NumPy array of the multi-group cross section indexed in the order
|
||||
each group, subdomain and nuclide is listed in the parameters.
|
||||
|
||||
|
|
@ -2135,7 +2472,7 @@ class Chi(MGXS):
|
|||
|
||||
# Construct a collection of the domain filter bins
|
||||
if not isinstance(subdomains, basestring):
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral)
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=2)
|
||||
for subdomain in subdomains:
|
||||
filters.append(self.domain_type)
|
||||
filter_bins.append((subdomain,))
|
||||
|
|
@ -2172,7 +2509,7 @@ class Chi(MGXS):
|
|||
xs_tally = nu_fission_out / nu_fission_in
|
||||
|
||||
# Add the coarse energy filter back to the nu-fission tally
|
||||
nu_fission_in.add_filter(energy_filter)
|
||||
nu_fission_in.filters.append(energy_filter)
|
||||
|
||||
xs = xs_tally.get_values(filters=filters,
|
||||
filter_bins=filter_bins, value=value)
|
||||
|
|
@ -2223,7 +2560,7 @@ class Chi(MGXS):
|
|||
xs_type='macro', summary=None):
|
||||
"""Build a Pandas DataFrame for the MGXS data.
|
||||
|
||||
This method leverages the Tally.get_pandas_dataframe(...) method, but
|
||||
This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but
|
||||
renames the columns with terminology appropriate for cross section data.
|
||||
|
||||
Parameters
|
||||
|
|
@ -2240,7 +2577,7 @@ class Chi(MGXS):
|
|||
xs_type: {'macro', 'micro'}
|
||||
Return macro or micro cross section in units of cm^-1 or barns.
|
||||
Defaults to 'macro'.
|
||||
summary : None or Summary
|
||||
summary : None or openmc.Summary
|
||||
An optional Summary object to be used to construct columns for
|
||||
distribcell tally filters (default is None). The geometric
|
||||
information in the Summary object is embedded into a multi-index
|
||||
|
|
|
|||
797
openmc/mgxs_library.py
Normal file
797
openmc/mgxs_library.py
Normal file
|
|
@ -0,0 +1,797 @@
|
|||
from collections import Iterable
|
||||
from numbers import Real, Integral
|
||||
from xml.etree import ElementTree as ET
|
||||
import warnings
|
||||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
import numpy as np
|
||||
|
||||
import openmc
|
||||
from openmc.mgxs import EnergyGroups
|
||||
from openmc.checkvalue import check_type, check_value, check_greater_than, \
|
||||
check_iterable_type
|
||||
from openmc.clean_xml import *
|
||||
|
||||
# Supported incoming particle MGXS angular treatment representations
|
||||
_REPRESENTATIONS = ['isotropic', 'angle']
|
||||
|
||||
|
||||
def ndarray_to_string(arr):
|
||||
"""Converts a numpy ndarray in to a join with spaces between entries
|
||||
similar to ' '.join(map(str,arr)) but applied to all sub-dimensions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
arr : numpy.ndarray
|
||||
Array to combine in to a string
|
||||
|
||||
Returns
|
||||
-------
|
||||
text : str
|
||||
String representation of array in arr
|
||||
|
||||
"""
|
||||
|
||||
shape = arr.shape
|
||||
ndim = arr.ndim
|
||||
tab = ' '
|
||||
indent = '\n' + tab + tab
|
||||
text = indent
|
||||
|
||||
if ndim == 1:
|
||||
text += tab
|
||||
for i in range(shape[0]):
|
||||
text += '{:.7E} '.format(arr[i])
|
||||
text += indent
|
||||
elif ndim == 2:
|
||||
for i in range(shape[0]):
|
||||
text += tab
|
||||
for j in range(shape[1]):
|
||||
text += '{:.7E} '.format(arr[i, j])
|
||||
text += indent
|
||||
elif ndim == 3:
|
||||
for i in range(shape[0]):
|
||||
for j in range(shape[1]):
|
||||
text += tab
|
||||
for k in range(shape[2]):
|
||||
text += '{:.7E} '.format(arr[i, j, k])
|
||||
text += indent
|
||||
elif ndim == 4:
|
||||
for i in range(shape[0]):
|
||||
for j in range(shape[1]):
|
||||
for k in range(shape[2]):
|
||||
text += tab
|
||||
for l in range(shape[3]):
|
||||
text += '{:.7E} '.format(arr[i, j, k, l])
|
||||
text += indent
|
||||
elif ndim == 5:
|
||||
for i in range(shape[0]):
|
||||
for j in range(shape[1]):
|
||||
for k in range(shape[2]):
|
||||
for l in range(shape[3]):
|
||||
text += tab
|
||||
for m in range(shape[4]):
|
||||
text += '{:.7E} '.format(arr[i, j, k, l, m])
|
||||
text += indent
|
||||
|
||||
return text
|
||||
|
||||
|
||||
class XSdata(object):
|
||||
"""A multi-group cross section data set providing all the
|
||||
multi-group data necessary for a multi-group OpenMC calculation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str, optional
|
||||
Name of the mgxs data set.
|
||||
energy_groups : openmc.mgxs.EnergyGroups
|
||||
Energygroup structure
|
||||
representation : {'isotropic', 'angle'}, optional
|
||||
Method used in generating the MGXS (isotropic or angle-dependent flux
|
||||
weighting). Defaults to 'isotropic'
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name : str
|
||||
Unique identifier for the xsdata object
|
||||
alias : str
|
||||
Separate unique identifier for the xsdata object
|
||||
kT : float
|
||||
Temperature (in units of MeV).
|
||||
energy_groups : openmc.mgxs.EnergyGroups
|
||||
Energy group structure
|
||||
fissionable : bool
|
||||
Whether or not this is a fissionable data set.
|
||||
scatt_type : {'legendre', 'histogram', or 'tabular'}
|
||||
Angular distribution representation (legendre, histogram, or tabular)
|
||||
order : int
|
||||
Either the Legendre order, number of bins, or number of points used to
|
||||
describe the angular distribution associated with each group-to-group
|
||||
transfer probability.
|
||||
tabular_legendre : dict
|
||||
Set how to treat the Legendre scattering kernel (tabular or leave in
|
||||
Legendre polynomial form). Dict contains two keys: 'enable' and
|
||||
'num_points'. 'enable' is a boolean and 'num_points' is the
|
||||
number of points to use, if 'enable' is True.
|
||||
num_azimuthal : int
|
||||
Number of equal width angular bins that the azimuthal angular domain is
|
||||
subdivided into. This only applies when ``representation`` is "angle".
|
||||
num_polar : int
|
||||
Number of equal width angular bins that the polar angular domain is
|
||||
subdivided into. This only applies when ``representation`` is "angle".
|
||||
total : numpy.ndarray
|
||||
Group-wise total cross section ordered by increasing group index (i.e.,
|
||||
fast to thermal). If ``representation`` is "isotropic", then the length
|
||||
of this list should equal the number of groups described in the
|
||||
``groups`` element. If ``representation`` is "angle", then the length
|
||||
of this list should equal the number of groups times the number of
|
||||
azimuthal angles times the number of polar angles, with the
|
||||
inner-dimension being groups, intermediate-dimension being azimuthal
|
||||
angles and outer-dimension being the polar angles.
|
||||
absorption : numpy.ndarray
|
||||
Group-wise absorption cross section ordered by increasing group index
|
||||
(i.e., fast to thermal). If ``representation`` is "isotropic", then the
|
||||
length of this list should equal the number of groups described in the
|
||||
``groups`` attribute. If ``representation`` is "angle", then the length
|
||||
of this list should equal the number of groups times the number of
|
||||
azimuthal angles times the number of polar angles, with the
|
||||
inner-dimension being groups, intermediate-dimension being azimuthal
|
||||
angles and outer-dimension being the polar angles.
|
||||
scatter : numpy.ndarray
|
||||
Scattering moment matrices presented with the columns representing
|
||||
incoming group and rows representing the outgoing group. That is,
|
||||
down-scatter will be above the diagonal of the resultant matrix. This
|
||||
matrix is repeated for every Legendre order (in order of increasing
|
||||
orders) if ``scatt_type`` is "legendre"; otherwise, this matrix is
|
||||
repeated for every bin of the histogram or tabular representation.
|
||||
Finally, if ``representation`` is "angle", the above is repeated for
|
||||
every azimuthal angle and every polar angle, in that order.
|
||||
multiplicity : numpy.ndarray
|
||||
Ratio of neutrons produced in scattering collisions to the neutrons
|
||||
which undergo scattering collisions; that is, the multiplicity provides
|
||||
the code with a scaling factor to account for neutrons being produced in
|
||||
(n,xn) reactions. This information is assumed isotropic and therefore
|
||||
does not need to be repeated for every Legendre moment or
|
||||
histogram/tabular bin. This matrix follows the same arrangement as
|
||||
described for the ``scatter`` attribute, with the exception of the data
|
||||
needed to provide the scattering type information.
|
||||
fission : numpy.ndarray
|
||||
Group-wise fission cross section ordered by increasing group index
|
||||
(i.e., fast to thermal). If ``representation`` is "isotropic", then the
|
||||
length of this list should equal the number of groups described in the
|
||||
``groups`` attribute. If ``representation`` is "angle", then the length
|
||||
of this list should equal the number of groups times the number of
|
||||
azimuthal angles times the number of polar angles, with the
|
||||
inner-dimension being groups, intermediate-dimension being azimuthal
|
||||
angles and outer-dimension being the polar angles.
|
||||
k_fission : numpy.ndarray
|
||||
Group-wise kappa-fission cross section ordered by increasing group index
|
||||
(i.e., fast to thermal). If ``representation`` is "isotropic", then the
|
||||
length of this list should equal the number of groups described in the
|
||||
``groups`` attribute. If ``representation`` is "angle", then the length
|
||||
of this list should equal the number of groups times the number of
|
||||
azimuthal angles times the number of polar angles, with the
|
||||
inner-dimension being groups, intermediate-dimension being azimuthal
|
||||
angles and outer-dimension being the polar angles.
|
||||
chi : numpy.ndarray
|
||||
Group-wise fission spectra ordered by increasing group index (i.e., fast
|
||||
to thermal). This attribute should be used if making the common
|
||||
approximation that the fission spectra does not depend on incoming
|
||||
energy. If the user does not wish to make this approximation, then this
|
||||
should not be provided and this information included in the
|
||||
``nu_fission`` element instead. If ``representation`` is "isotropic",
|
||||
then the length of this list should equal the number of groups described
|
||||
in the ``groups`` element. If ``representation`` is "angle", then the
|
||||
length of this list should equal the number of groups times the number
|
||||
of azimuthal angles times the number of polar angles, with the
|
||||
inner-dimension being groups, intermediate-dimension being azimuthal
|
||||
angles and outer-dimension being the polar angles.
|
||||
nu_fission : numpy.ndarray
|
||||
Group-wise fission production cross section vector (i.e., if ``chi`` is
|
||||
provided), or is the group-wise fission production matrix. If providing
|
||||
the vector, it should be ordered the same as the ``fission`` data. If
|
||||
providing the matrix, it should be ordered the same as the
|
||||
``multiplicity`` matrix.
|
||||
|
||||
"""
|
||||
def __init__(self, name, energy_groups, representation="isotropic"):
|
||||
# Initialize class attributes
|
||||
self._name = name
|
||||
self._energy_groups = energy_groups
|
||||
self._representation = representation
|
||||
self._alias = None
|
||||
self._kT = None
|
||||
self._fissionable = False
|
||||
self._scatt_type = 'legendre'
|
||||
self._order = None
|
||||
self._tabular_legendre = None
|
||||
self._num_polar = None
|
||||
self._num_azimuthal = None
|
||||
self._total = None
|
||||
self._absorption = None
|
||||
self._scatter = None
|
||||
self._multiplicity = None
|
||||
self._fission = None
|
||||
self._nu_fission = None
|
||||
self._k_fission = None
|
||||
self._chi = None
|
||||
self._use_chi = None
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def energy_groups(self):
|
||||
return self._energy_groups
|
||||
|
||||
@property
|
||||
def representation(self):
|
||||
return self._representation
|
||||
|
||||
@property
|
||||
def alias(self):
|
||||
return self._alias
|
||||
|
||||
@property
|
||||
def kT(self):
|
||||
return self._kT
|
||||
|
||||
@property
|
||||
def scatt_type(self):
|
||||
return self._scatt_type
|
||||
|
||||
@property
|
||||
def order(self):
|
||||
return self._order
|
||||
|
||||
@property
|
||||
def tabular_legendre(self):
|
||||
return self._tabular_legendre
|
||||
|
||||
@property
|
||||
def num_polar(self):
|
||||
return self._num_polar
|
||||
|
||||
@property
|
||||
def num_azimuthal(self):
|
||||
return self._num_azimuthal
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return self._total
|
||||
|
||||
@property
|
||||
def absorption(self):
|
||||
return self._absorption
|
||||
|
||||
@property
|
||||
def scatter(self):
|
||||
return self._scatter
|
||||
|
||||
@property
|
||||
def multiplicity(self):
|
||||
return self._multiplicity
|
||||
|
||||
@property
|
||||
def fission(self):
|
||||
return self._fission
|
||||
|
||||
@property
|
||||
def nu_fission(self):
|
||||
return self._nu_fission
|
||||
|
||||
@property
|
||||
def k_fission(self):
|
||||
return self._k_fission
|
||||
|
||||
@property
|
||||
def chi(self):
|
||||
return self._chi
|
||||
|
||||
@property
|
||||
def num_orders(self):
|
||||
if (self._order is not None) and (self._scatt_type is not None):
|
||||
if self._scatt_type is 'legendre':
|
||||
return self._order + 1
|
||||
else:
|
||||
return self._order
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
check_type('name for XSdata', name, basestring)
|
||||
self._name = name
|
||||
|
||||
@energy_groups.setter
|
||||
def energy_groups(self, energy_groups):
|
||||
# Check validity of energy_groups
|
||||
check_type("energy_groups", energy_groups, EnergyGroups)
|
||||
|
||||
# Check that there is one or more groups
|
||||
if ((energy_groups.num_groups is None) or
|
||||
(energy_groups.num_groups < 1)):
|
||||
|
||||
msg = 'energy_groups object incorrectly initialized.'
|
||||
raise ValueError(msg)
|
||||
|
||||
self._energy_groups = energy_groups
|
||||
|
||||
@representation.setter
|
||||
def representation(self, representation):
|
||||
# Check it is of valid type.
|
||||
check_value('representation', representation, _REPRESENTATIONS)
|
||||
self._representation = representation
|
||||
|
||||
@alias.setter
|
||||
def alias(self, alias):
|
||||
if alias is not None:
|
||||
check_type('alias', alias, basestring)
|
||||
self._alias = alias
|
||||
else:
|
||||
self._alias = self._name
|
||||
|
||||
@kT.setter
|
||||
def kT(self, kT):
|
||||
# Check validity of type and that the kT value is >= 0
|
||||
check_type("kT", kT, Real)
|
||||
check_greater_than("kT", kT, 0.0, equality=True)
|
||||
self._kT = kT
|
||||
|
||||
@scatt_type.setter
|
||||
def scatt_type(self, scatt_type):
|
||||
# check to see it is of a valid type and value
|
||||
check_value("scatt_type", scatt_type, ['legendre', 'histogram',
|
||||
'tabular'])
|
||||
self._scatt_type = scatt_type
|
||||
|
||||
@order.setter
|
||||
def order(self, order):
|
||||
# Check type and value
|
||||
check_type("order", order, Integral)
|
||||
check_greater_than("order", order, 0, equality=True)
|
||||
self._order = order
|
||||
|
||||
@tabular_legendre.setter
|
||||
def tabular_legendre(self, tabular_legendre):
|
||||
# Check to make sure this is a dict and it has our keys with the
|
||||
# right values.
|
||||
check_type("tabular_legendre", tabular_legendre, dict)
|
||||
if 'enable' in tabular_legendre:
|
||||
enable = tabular_legendre['enable']
|
||||
check_type('enable', enable, bool)
|
||||
else:
|
||||
msg = "enable must be provided in tabular_legendre"
|
||||
raise ValueError(msg)
|
||||
if 'num_points' in tabular_legendre:
|
||||
num_points = tabular_legendre['num_points']
|
||||
check_value('num_points', num_points, Integral)
|
||||
check_greater_than('num_points', num_points, 0)
|
||||
else:
|
||||
if not enable:
|
||||
num_points = 1
|
||||
else:
|
||||
num_points = 33
|
||||
self._tabular_legendre = {'enable': enable, 'num_points': num_points}
|
||||
|
||||
@num_polar.setter
|
||||
def num_polar(self, num_polar):
|
||||
# Make sure we have positive ints
|
||||
check_value("num_polar", num_polar, Integral)
|
||||
check_greater_than("num_polar", num_polar, 0)
|
||||
self._num_polar = num_polar
|
||||
|
||||
@num_azimuthal.setter
|
||||
def num_azimuthal(self, num_azimuthal):
|
||||
check_value("num_azimuthal", num_azimuthal, Integral)
|
||||
check_greater_than("num_azimuthal", num_azimuthal, 0)
|
||||
self._num_azimuthal = num_azimuthal
|
||||
|
||||
@total.setter
|
||||
def total(self, total):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,)
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
# check we have a numpy list
|
||||
check_type("total", total, np.ndarray, expected_iter_type=Real)
|
||||
if total.shape == shape:
|
||||
self._total = np.copy(total)
|
||||
else:
|
||||
msg = 'Shape of provided total "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(total.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@absorption.setter
|
||||
def absorption(self, absorption):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,)
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
# check we have a numpy list
|
||||
check_type("absorption", absorption, np.ndarray, expected_iter_type=Real)
|
||||
if absorption.shape == shape:
|
||||
self._absorption = np.copy(absorption)
|
||||
else:
|
||||
msg = 'Shape of provided absorption "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(absorption.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@fission.setter
|
||||
def fission(self, fission):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,)
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
# check we have a numpy list
|
||||
check_type("fission", fission, np.ndarray, expected_iter_type=Real)
|
||||
if fission.shape == shape:
|
||||
self._fission = np.copy(fission)
|
||||
if np.sum(self._fission) > 0.0:
|
||||
self._fissionable = True
|
||||
else:
|
||||
msg = 'Shape of provided fission "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(fission.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@k_fission.setter
|
||||
def k_fission(self, k_fission):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,)
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
# check we have a numpy list
|
||||
check_type("k_fission", k_fission, np.ndarray, expected_iter_type=Real)
|
||||
if k_fission.shape == shape:
|
||||
self._k_fission = np.copy(k_fission)
|
||||
if np.sum(self._k_fission) > 0.0:
|
||||
self._fissionable = True
|
||||
else:
|
||||
msg = 'Shape of provided k_fission "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(k_fission.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@chi.setter
|
||||
def chi(self, chi):
|
||||
if not self._use_chi:
|
||||
msg = 'Providing chi when nu_fission already provided as matrix!'
|
||||
raise ValueError(msg)
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,)
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
# check we have a numpy list
|
||||
check_type("chi", chi, np.ndarray, expected_iter_type=Real)
|
||||
if chi.shape == shape:
|
||||
self._chi = np.copy(chi)
|
||||
else:
|
||||
msg = 'Shape of provided chi "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(chi.shape, shape)
|
||||
raise ValueError(msg)
|
||||
if self._use_chi is not None:
|
||||
self._use_chi = True
|
||||
|
||||
@scatter.setter
|
||||
def scatter(self, scatter):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self.num_orders, self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
max_depth = 3
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal, self.num_orders,
|
||||
self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
max_depth = 5
|
||||
# check we have a numpy list
|
||||
check_iterable_type("scatter", scatter, expected_type=Real,
|
||||
max_depth=max_depth)
|
||||
if scatter.shape == shape:
|
||||
self._scatter = np.copy(scatter)
|
||||
else:
|
||||
msg = 'Shape of provided scatter "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(scatter.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@multiplicity.setter
|
||||
def multiplicity(self, multiplicity):
|
||||
if self._representation is 'isotropic':
|
||||
shape = (self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
max_depth = 2
|
||||
elif self._representation is 'angle':
|
||||
shape = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
max_depth = 4
|
||||
# check we have a numpy list
|
||||
check_iterable_type("multiplicity", multiplicity, expected_type=Real,
|
||||
max_depth=max_depth)
|
||||
if multiplicity.shape == shape:
|
||||
self._multiplicity = np.copy(multiplicity)
|
||||
else:
|
||||
msg = 'Shape of provided multiplicity "{0}" does not match shape ' \
|
||||
'required, "{1}"'.format(multiplicity.shape, shape)
|
||||
raise ValueError(msg)
|
||||
|
||||
@nu_fission.setter
|
||||
def nu_fission(self, nu_fission):
|
||||
# nu_fission can be given as a vector or a matrix
|
||||
# Vector is used when chi also exists.
|
||||
# Matrix is used when chi does not exist.
|
||||
# We have to check that the correct form is given, but only if
|
||||
# chi already has been set. If not, we just check that this is OK
|
||||
# and set the use_chi flag.
|
||||
|
||||
# First lets set our dimensions here since they get used repeatedly
|
||||
# throughout this code.
|
||||
if self._representation is 'isotropic':
|
||||
shape_vec = (self._energy_groups.num_groups,)
|
||||
shape_mat = (self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
elif self._representation is 'angle':
|
||||
shape_vec = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups)
|
||||
shape_mat = (self._num_polar, self._num_azimuthal,
|
||||
self._energy_groups.num_groups,
|
||||
self._energy_groups.num_groups)
|
||||
|
||||
# Begin by checking the case when chi has already been given and thus
|
||||
# the rules for filling in nu_fission are set.
|
||||
if self._use_chi is not None:
|
||||
if self._use_chi:
|
||||
shape = shape_vec
|
||||
else:
|
||||
shape = shape_mat
|
||||
if nu_fission.shape != shape:
|
||||
msg = "Invalid Shape of Nu_fission!"
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
# Get shape of nu_fission so we can figure if we need chi or not
|
||||
if nu_fission.shape == shape_vec:
|
||||
self._use_chi = True
|
||||
shape = shape_vec
|
||||
elif nu_fission.shape == shape_mat:
|
||||
self._use_chi = False
|
||||
shape = shape_mat
|
||||
else:
|
||||
msg = "Invalid Shape of Nu_fission!"
|
||||
raise ValueError(msg)
|
||||
|
||||
# check we have a numpy list
|
||||
check_type("nu_fission", nu_fission, np.ndarray, expected_iter_type=Real)
|
||||
self._nu_fission = np.copy(nu_fission)
|
||||
if np.sum(self._nu_fission) > 0.0:
|
||||
self._fissionable = True
|
||||
|
||||
def _get_xsdata_xml(self):
|
||||
element = ET.Element("xsdata")
|
||||
element.set("name", self._name)
|
||||
|
||||
if self._alias is not None:
|
||||
subelement = ET.SubElement(element, 'alias')
|
||||
subelement.text = self.alias
|
||||
|
||||
if self._kT is not None:
|
||||
subelement = ET.SubElement(element, 'kT')
|
||||
subelement.text = str(self._kT)
|
||||
|
||||
if self._fissionable is not None:
|
||||
subelement = ET.SubElement(element, 'fissionable')
|
||||
subelement.text = str(self._fissionable)
|
||||
|
||||
if self._representation is not None:
|
||||
subelement = ET.SubElement(element, 'representation')
|
||||
subelement.text = self._representation
|
||||
|
||||
if self._representation == 'angle':
|
||||
if self._num_azimuthal is not None:
|
||||
subelement = ET.SubElement(element, 'num_azimuthal')
|
||||
subelement.text = str(self._num_azimuthal)
|
||||
if self._num_polar is not None:
|
||||
subelement = ET.SubElement(element, 'num_polar')
|
||||
subelement.text = str(self._num_polar)
|
||||
|
||||
if self._scatt_type is not None:
|
||||
subelement = ET.SubElement(element, 'scatt_type')
|
||||
subelement.text = self._scatt_type
|
||||
|
||||
if self._order is not None:
|
||||
subelement = ET.SubElement(element, 'order')
|
||||
subelement.text = str(self._order)
|
||||
|
||||
if self._tabular_legendre is not None:
|
||||
subelement = ET.SubElement(element, 'tabular_legendre')
|
||||
subelement.set('enable', str(self._tabular_legendre['enable']))
|
||||
subelement.set('num_points', str(self._tabular_legendre['num_points']))
|
||||
|
||||
if self._total is not None:
|
||||
subelement = ET.SubElement(element, 'total')
|
||||
subelement.text = ndarray_to_string(self._total)
|
||||
|
||||
if self._absorption is not None:
|
||||
subelement = ET.SubElement(element, 'absorption')
|
||||
subelement.text = ndarray_to_string(self._absorption)
|
||||
|
||||
if self._scatter is not None:
|
||||
subelement = ET.SubElement(element, 'scatter')
|
||||
subelement.text = ndarray_to_string(self._scatter)
|
||||
|
||||
if self._multiplicity is not None:
|
||||
subelement = ET.SubElement(element, 'multiplicity')
|
||||
subelement.text = ndarray_to_string(self._multiplicity)
|
||||
|
||||
if self._fissionable:
|
||||
if self._fission is not None:
|
||||
subelement = ET.SubElement(element, 'fission')
|
||||
subelement.text = ndarray_to_string(self._fission)
|
||||
|
||||
if self._k_fission is not None:
|
||||
subelement = ET.SubElement(element, 'k_fission')
|
||||
subelement.text = ndarray_to_string(self._k_fission)
|
||||
|
||||
if self._nu_fission is not None:
|
||||
subelement = ET.SubElement(element, 'nu_fission')
|
||||
subelement.text = ndarray_to_string(self._nu_fission)
|
||||
|
||||
if self._chi is not None:
|
||||
subelement = ET.SubElement(element, 'chi')
|
||||
subelement.text = ndarray_to_string(self._chi)
|
||||
|
||||
return element
|
||||
|
||||
|
||||
class MGXSLibraryFile(object):
|
||||
"""Multi-Group Cross Sections file used for an OpenMC simulation.
|
||||
Corresponds directly to the MG version of the cross_sections.xml input file.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
energy_groups : openmc.mgxs.EnergyGroups
|
||||
Energy group structure.
|
||||
inverse_velocities : Iterable of Real
|
||||
Inverse of velocities, units of sec/cm
|
||||
xsdatas : Iterable of openmc.XSdata
|
||||
Iterable of multi-Group cross section data objects
|
||||
"""
|
||||
|
||||
def __init__(self, energy_groups):
|
||||
# Initialize MGXSLibraryFile class attributes
|
||||
self._xsdatas = []
|
||||
self._energy_groups = energy_groups
|
||||
self._inverse_velocities = None
|
||||
self._cross_sections_file = ET.Element("cross_sections")
|
||||
|
||||
@property
|
||||
def inverse_velocities(self):
|
||||
return self._inverse_velocities
|
||||
|
||||
@property
|
||||
def energy_groups(self):
|
||||
return self._energy_groups
|
||||
|
||||
@inverse_velocities.setter
|
||||
def inverse_velocities(self, inverse_velocities):
|
||||
cv.check_type('inverse_velocities', inverse_velocities, Iterable, Real)
|
||||
cv.check_greater_than('number of inverse_velocities',
|
||||
len(inverse_velocities), 0.0)
|
||||
self._inverse_velocities = np.array(inverse_velocities)
|
||||
|
||||
@energy_groups.setter
|
||||
def energy_groups(self, energy_groups):
|
||||
check_type("energy groups", energy_groups, EnergyGroups)
|
||||
self._energy_groups = energy_groups
|
||||
|
||||
def add_xsdata(self, xsdata):
|
||||
"""Add an XSdata entry to the file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xsdata : openmc.XSdata
|
||||
MGXS information to add
|
||||
|
||||
"""
|
||||
|
||||
# Check the type
|
||||
if not isinstance(xsdata, XSdata):
|
||||
msg = 'Unable to add a non-XSdata "{0}" to the ' \
|
||||
'MGXSLibraryFile'.format(xsdata)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Make sure energy groups match.
|
||||
if xsdata.energy_groups != self._energy_groups:
|
||||
msg = 'Energy groups of XSdata do not match that of MGXSLibraryFile!'
|
||||
raise ValueError(msg)
|
||||
|
||||
self._xsdatas.append(xsdata)
|
||||
|
||||
def add_xsdatas(self, xsdatas):
|
||||
"""Add multiple xsdatas to the file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xsdatas : tuple or list of openmc.XSdata
|
||||
XSdatas to add
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(xsdatas, Iterable):
|
||||
msg = 'Unable to create OpenMC xsdatas.xml file from "{0}" which ' \
|
||||
'is not iterable'.format(xsdatas)
|
||||
raise ValueError(msg)
|
||||
|
||||
for xsdata in xsdatas:
|
||||
self.add_xsdata(xsdata)
|
||||
|
||||
def remove_xsdata(self, xsdata):
|
||||
"""Remove a xsdata from the file
|
||||
|
||||
Parameters
|
||||
----------
|
||||
xsdata : openmc.XSdata
|
||||
XSdata to remove
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(xsdata, XSdata):
|
||||
msg = 'Unable to remove a non-XSdata "{0}" from the ' \
|
||||
'XSdatasFile'.format(xsdata)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._xsdatas.remove(xsdata)
|
||||
|
||||
def _create_groups_subelement(self):
|
||||
if self._energy_groups is not None:
|
||||
element = ET.SubElement(self._cross_sections_file, "groups")
|
||||
element.text = str(self._energy_groups.num_groups)
|
||||
|
||||
def _create_group_structure_subelement(self):
|
||||
if self._energy_groups is not None:
|
||||
element = ET.SubElement(self._cross_sections_file,
|
||||
"group_structure")
|
||||
element.text = ' '.join(map(str, self._energy_groups.group_edges))
|
||||
|
||||
def _create_inverse_velocities_subelement(self):
|
||||
if self._inverse_velocities is not None:
|
||||
element = ET.SubElement(self._cross_sections_file,
|
||||
"inverse_velocities")
|
||||
element.text = ' '.join(map(str, self._inverse_velocities))
|
||||
|
||||
def _create_xsdata_subelements(self):
|
||||
for xsdata in self._xsdatas:
|
||||
xml_element = xsdata._get_xsdata_xml()
|
||||
self._cross_sections_file.append(xml_element)
|
||||
|
||||
def export_to_xml(self, filename='mg_cross_sections.xml'):
|
||||
"""Create an mg_cross_sections.xml file that can be used for a
|
||||
simulation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str, optional
|
||||
filename of file, default is mg_cross_sections.xml
|
||||
|
||||
"""
|
||||
|
||||
# Reset xml element tree
|
||||
self._cross_sections_file.clear()
|
||||
|
||||
self._create_groups_subelement()
|
||||
self._create_group_structure_subelement()
|
||||
self._create_inverse_velocities_subelement()
|
||||
self._create_xsdata_subelements()
|
||||
|
||||
# Clean the indentation in the file to be user-readable
|
||||
sort_xml_elements(self._cross_sections_file)
|
||||
clean_xml_indentation(self._cross_sections_file)
|
||||
|
||||
# Write the XML Tree to the xsdatas.xml file
|
||||
tree = ET.ElementTree(self._cross_sections_file)
|
||||
tree.write(filename, xml_declaration=True,
|
||||
encoding='utf-8', method="xml")
|
||||
|
|
@ -60,6 +60,12 @@ class Nuclide(object):
|
|||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
def __gt__(self, other):
|
||||
return repr(self) > repr(other)
|
||||
|
||||
def __lt__(self, other):
|
||||
return not self > other
|
||||
|
||||
def __hash__(self):
|
||||
return hash(repr(self))
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ except ImportError:
|
|||
import openmc
|
||||
from openmc.region import Intersection
|
||||
from openmc.surface import Halfspace
|
||||
import openmc.checkvalue as cv
|
||||
|
||||
|
||||
# A dictionary of all OpenMC Materials created
|
||||
|
|
@ -79,10 +80,7 @@ def get_opencg_material(openmc_material):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_material, openmc.Material):
|
||||
msg = 'Unable to create an OpenCG Material from "{0}" ' \
|
||||
'which is not an OpenMC Material'.format(openmc_material)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_material', openmc_material, openmc.Material)
|
||||
|
||||
global OPENCG_MATERIALS
|
||||
material_id = openmc_material.id
|
||||
|
|
@ -119,10 +117,7 @@ def get_openmc_material(opencg_material):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_material, opencg.Material):
|
||||
msg = 'Unable to create an OpenMC Material from "{0}" ' \
|
||||
'which is not an OpenCG Material'.format(opencg_material)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_material', opencg_material, opencg.Material)
|
||||
|
||||
global OPENMC_MATERIALS
|
||||
material_id = opencg_material.id
|
||||
|
|
@ -165,10 +160,7 @@ def is_opencg_surface_compatible(opencg_surface):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_surface, opencg.Surface):
|
||||
msg = 'Unable to check if OpenCG Surface is compatible' \
|
||||
'since "{0}" is not a Surface'.format(opencg_surface)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
|
||||
|
||||
if opencg_surface.type in ['x-squareprism',
|
||||
'y-squareprism', 'z-squareprism']:
|
||||
|
|
@ -192,10 +184,7 @@ def get_opencg_surface(openmc_surface):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_surface, openmc.Surface):
|
||||
msg = 'Unable to create an OpenCG Surface from "{0}" ' \
|
||||
'which is not an OpenMC Surface'.format(openmc_surface)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_surface', openmc_surface, openmc.Surface)
|
||||
|
||||
global OPENCG_SURFACES
|
||||
surface_id = openmc_surface.id
|
||||
|
|
@ -278,10 +267,7 @@ def get_openmc_surface(opencg_surface):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_surface, opencg.Surface):
|
||||
msg = 'Unable to create an OpenMC Surface from "{0}" which ' \
|
||||
'is not an OpenCG Surface'.format(opencg_surface)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
|
||||
|
||||
global openmc_surface
|
||||
surface_id = opencg_surface.id
|
||||
|
|
@ -369,10 +355,7 @@ def get_compatible_opencg_surfaces(opencg_surface):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_surface, opencg.Surface):
|
||||
msg = 'Unable to create an OpenMC Surface from "{0}" which ' \
|
||||
'is not an OpenCG Surface'.format(opencg_surface)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
|
||||
|
||||
global OPENMC_SURFACES
|
||||
surface_id = opencg_surface.id
|
||||
|
|
@ -451,10 +434,7 @@ def get_opencg_cell(openmc_cell):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_cell, openmc.Cell):
|
||||
msg = 'Unable to create an OpenCG Cell from "{0}" which ' \
|
||||
'is not an OpenMC Cell'.format(openmc_cell)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_cell', openmc_cell, openmc.Cell)
|
||||
|
||||
global OPENCG_CELLS
|
||||
cell_id = openmc_cell.id
|
||||
|
|
@ -469,9 +449,9 @@ def get_opencg_cell(openmc_cell):
|
|||
|
||||
fill = openmc_cell.fill
|
||||
|
||||
if (openmc_cell.fill_type == 'material'):
|
||||
if openmc_cell.fill_type == 'material':
|
||||
opencg_cell.fill = get_opencg_material(fill)
|
||||
elif (openmc_cell.fill_type == 'universe'):
|
||||
elif openmc_cell.fill_type == 'universe':
|
||||
opencg_cell.fill = get_opencg_universe(fill)
|
||||
else:
|
||||
opencg_cell.fill = get_opencg_lattice(fill)
|
||||
|
|
@ -533,20 +513,10 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
|
|||
OpenMC
|
||||
|
||||
"""
|
||||
if not isinstance(opencg_cell, opencg.Cell):
|
||||
msg = 'Unable to create compatible OpenMC Cell from "{0}" which ' \
|
||||
'is not an OpenCG Cell'.format(opencg_cell)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif not isinstance(opencg_surface, opencg.Surface):
|
||||
msg = 'Unable to create compatible OpenMC Cell since "{0}" is ' \
|
||||
'not an OpenCG Surface'.format(opencg_surface)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif halfspace not in [-1, +1]:
|
||||
msg = 'Unable to create compatible Cell since "{0}"' \
|
||||
'is not a +/-1 halfspace'.format(halfspace)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_cell', opencg_cell, opencg.Cell)
|
||||
cv.check_type('opencg_surface', opencg_surface, opencg.Surface)
|
||||
cv.check_value('halfspace', halfspace, (-1, +1))
|
||||
|
||||
# Initialize an empty list for the new compatible cells
|
||||
compatible_cells = []
|
||||
|
|
@ -575,7 +545,7 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
|
|||
num_clones = 8
|
||||
|
||||
for clone_id in range(num_clones):
|
||||
# Create a cloned OpenCG Cell with Surfaces compatible with OpenMC
|
||||
# Create cloned OpenCG Cell with Surfaces compatible with OpenMC
|
||||
clone = opencg_cell.clone()
|
||||
compatible_cells.append(clone)
|
||||
|
||||
|
|
@ -641,10 +611,7 @@ def make_opencg_cells_compatible(opencg_universe):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_universe, opencg.Universe):
|
||||
msg = 'Unable to make compatible OpenCG Cells for "{0}" which ' \
|
||||
'is not an OpenCG Universe'.format(opencg_universe)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_universe', opencg_universe, opencg.Universe)
|
||||
|
||||
# Check all OpenCG Cells in this Universe for compatibility with OpenMC
|
||||
opencg_cells = opencg_universe.cells
|
||||
|
|
@ -700,10 +667,7 @@ def get_openmc_cell(opencg_cell):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_cell, opencg.Cell):
|
||||
msg = 'Unable to create an OpenMC Cell from "{0}" which ' \
|
||||
'is not an OpenCG Cell'.format(opencg_cell)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_cell', opencg_cell, opencg.Cell)
|
||||
|
||||
global OPENMC_CELLS
|
||||
cell_id = opencg_cell.id
|
||||
|
|
@ -718,9 +682,9 @@ def get_openmc_cell(opencg_cell):
|
|||
|
||||
fill = opencg_cell.fill
|
||||
|
||||
if (opencg_cell.type == 'universe'):
|
||||
if opencg_cell.type == 'universe':
|
||||
openmc_cell.fill = get_openmc_universe(fill)
|
||||
elif (opencg_cell.type == 'lattice'):
|
||||
elif opencg_cell.type == 'lattice':
|
||||
openmc_cell.fill = get_openmc_lattice(fill)
|
||||
else:
|
||||
openmc_cell.fill = get_openmc_material(fill)
|
||||
|
|
@ -764,10 +728,7 @@ def get_opencg_universe(openmc_universe):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_universe, openmc.Universe):
|
||||
msg = 'Unable to create an OpenCG Universe from "{0}" which ' \
|
||||
'is not an OpenMC Universe'.format(openmc_universe)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_universe', openmc_universe, openmc.Universe)
|
||||
|
||||
global OPENCG_UNIVERSES
|
||||
universe_id = openmc_universe.id
|
||||
|
|
@ -811,10 +772,7 @@ def get_openmc_universe(opencg_universe):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_universe, opencg.Universe):
|
||||
msg = 'Unable to create an OpenMC Universe from "{0}" which ' \
|
||||
'is not an OpenCG Universe'.format(opencg_universe)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_universe', opencg_universe, opencg.Universe)
|
||||
|
||||
global OPENMC_UNIVERSES
|
||||
universe_id = opencg_universe.id
|
||||
|
|
@ -861,10 +819,7 @@ def get_opencg_lattice(openmc_lattice):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_lattice, openmc.Lattice):
|
||||
msg = 'Unable to create an OpenCG Lattice from "{0}" which ' \
|
||||
'is not an OpenMC Lattice'.format(openmc_lattice)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_lattice', openmc_lattice, openmc.Lattice)
|
||||
|
||||
global OPENCG_LATTICES
|
||||
lattice_id = openmc_lattice.id
|
||||
|
|
@ -958,10 +913,7 @@ def get_openmc_lattice(opencg_lattice):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_lattice, opencg.Lattice):
|
||||
msg = 'Unable to create an OpenMC Lattice from "{0}" which ' \
|
||||
'is not an OpenCG Lattice'.format(opencg_lattice)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_lattice', opencg_lattice, opencg.Lattice)
|
||||
|
||||
global OPENMC_LATTICES
|
||||
lattice_id = opencg_lattice.id
|
||||
|
|
@ -1032,10 +984,7 @@ def get_opencg_geometry(openmc_geometry):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(openmc_geometry, openmc.Geometry):
|
||||
msg = 'Unable to get OpenCG geometry from "{0}" which is ' \
|
||||
'not an OpenMC Geometry object'.format(openmc_geometry)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('openmc_geometry', openmc_geometry, openmc.Geometry)
|
||||
|
||||
# Clear dictionaries and auto-generated IDs
|
||||
OPENMC_SURFACES.clear()
|
||||
|
|
@ -1053,6 +1002,7 @@ def get_opencg_geometry(openmc_geometry):
|
|||
opencg_geometry = opencg.Geometry()
|
||||
opencg_geometry.root_universe = opencg_root_universe
|
||||
opencg_geometry.initialize_cell_offsets()
|
||||
opencg_geometry.assign_auto_ids()
|
||||
|
||||
return opencg_geometry
|
||||
|
||||
|
|
@ -1072,10 +1022,7 @@ def get_openmc_geometry(opencg_geometry):
|
|||
|
||||
"""
|
||||
|
||||
if not isinstance(opencg_geometry, opencg.Geometry):
|
||||
msg = 'Unable to get OpenMC geometry from "{0}" which is ' \
|
||||
'not an OpenCG Geometry object'.format(opencg_geometry)
|
||||
raise ValueError(msg)
|
||||
cv.check_type('opencg_geometry', opencg_geometry, opencg.Geometry)
|
||||
|
||||
# Deep copy the goemetry since it may be modified to make all Surfaces
|
||||
# compatible with OpenMC's specifications
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ class Plot(object):
|
|||
The random number seed used to generate the color scheme
|
||||
|
||||
"""
|
||||
|
||||
|
||||
cv.check_type('geometry', geometry, openmc.Geometry)
|
||||
cv.check_type('seed', seed, Integral)
|
||||
cv.check_greater_than('seed', seed, 1, equality=True)
|
||||
|
|
@ -417,7 +417,7 @@ class PlotsFile(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
plot : Plot
|
||||
plot : openmc.Plot
|
||||
Plot to add
|
||||
|
||||
"""
|
||||
|
|
@ -433,7 +433,7 @@ class PlotsFile(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
plot : Plot
|
||||
plot : openmc.Plot
|
||||
Plot to remove
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ from openmc.checkvalue import check_type
|
|||
class Region(object):
|
||||
"""Region of space that can be assigned to a cell.
|
||||
|
||||
Region is an abstract base class that is inherited by Halfspace,
|
||||
Intersection, Union, and Complement. Each of those respective classes are
|
||||
typically not instantiated directly but rather are created through operators
|
||||
of the Surface and Region classes.
|
||||
Region is an abstract base class that is inherited by
|
||||
:class:`openmc.Halfspace`, :class:`openmc.Intersection`,
|
||||
:class:`openmc.Union`, and :class:`openmc.Complement`. Each of those
|
||||
respective classes are typically not instantiated directly but rather are
|
||||
created through operators of the Surface and Region classes.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -201,11 +202,11 @@ class Intersection(Region):
|
|||
"""Intersection of two or more regions.
|
||||
|
||||
Instances of Intersection are generally created via the __and__ operator
|
||||
applied to two instances of Region. This is illustrated in the following
|
||||
example:
|
||||
applied to two instances of :class:`openmc.Region`. This is illustrated in
|
||||
the following example:
|
||||
|
||||
>>> equator = openmc.surface.ZPlane(z0=0.0)
|
||||
>>> earth = openmc.surface.Sphere(R=637.1e6)
|
||||
>>> equator = openmc.ZPlane(z0=0.0)
|
||||
>>> earth = openmc.Sphere(R=637.1e6)
|
||||
>>> northern_hemisphere = -earth & +equator
|
||||
>>> southern_hemisphere = -earth & -equator
|
||||
>>> type(northern_hemisphere)
|
||||
|
|
@ -213,12 +214,12 @@ class Intersection(Region):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
*nodes
|
||||
\*nodes
|
||||
Regions to take the intersection of
|
||||
|
||||
Attributes
|
||||
----------
|
||||
nodes : tuple of Region
|
||||
nodes : tuple of openmc.Region
|
||||
Regions to take the intersection of
|
||||
bounding_box : tuple of numpy.array
|
||||
Lower-left and upper-right coordinates of an axis-aligned bounding box
|
||||
|
|
@ -255,21 +256,22 @@ class Union(Region):
|
|||
"""Union of two or more regions.
|
||||
|
||||
Instances of Union are generally created via the __or__ operator applied to
|
||||
two instances of Region. This is illustrated in the following example:
|
||||
two instances of :class:`openmc.Region`. This is illustrated in the
|
||||
following example:
|
||||
|
||||
>>> s1 = openmc.surface.ZPlane(z0=0.0)
|
||||
>>> s2 = openmc.surface.Sphere(R=637.1e6)
|
||||
>>> s1 = openmc.ZPlane(z0=0.0)
|
||||
>>> s2 = openmc.Sphere(R=637.1e6)
|
||||
>>> type(-s2 | +s1)
|
||||
<class 'openmc.region.Union'>
|
||||
|
||||
Parameters
|
||||
----------
|
||||
*nodes
|
||||
\*nodes
|
||||
Regions to take the union of
|
||||
|
||||
Attributes
|
||||
----------
|
||||
nodes : tuple of Region
|
||||
nodes : tuple of openmc.Region
|
||||
Regions to take the union of
|
||||
bounding_box : tuple of numpy.array
|
||||
Lower-left and upper-right coordinates of an axis-aligned bounding box
|
||||
|
|
@ -305,13 +307,13 @@ class Union(Region):
|
|||
class Complement(Region):
|
||||
"""Complement of a region.
|
||||
|
||||
The Complement of an existing Region can be created by using the __invert__
|
||||
operator as the following example demonstrates:
|
||||
The Complement of an existing :class:`openmc.Region` can be created by using
|
||||
the __invert__ operator as the following example demonstrates:
|
||||
|
||||
>>> xl = openmc.surface.XPlane(x0=-10.0)
|
||||
>>> xr = openmc.surface.XPlane(x0=10.0)
|
||||
>>> yl = openmc.surface.YPlane(y0=-10.0)
|
||||
>>> yr = openmc.surface.YPlane(y0=10.0)
|
||||
>>> xl = openmc.XPlane(x0=-10.0)
|
||||
>>> xr = openmc.XPlane(x0=10.0)
|
||||
>>> yl = openmc.YPlane(y0=-10.0)
|
||||
>>> yr = openmc.YPlane(y0=10.0)
|
||||
>>> inside_box = +xl & -xr & +yl & -yl
|
||||
>>> outside_box = ~inside_box
|
||||
>>> type(outside_box)
|
||||
|
|
@ -319,12 +321,12 @@ class Complement(Region):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
node : Region
|
||||
node : openmc.Region
|
||||
Region to take the complement of
|
||||
|
||||
Attributes
|
||||
----------
|
||||
node : Region
|
||||
node : openmc.Region
|
||||
Regions to take the complement of
|
||||
bounding_box : tuple of numpy.array
|
||||
Lower-left and upper-right coordinates of an axis-aligned bounding box
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import numpy as np
|
|||
from openmc.clean_xml import *
|
||||
from openmc.checkvalue import (check_type, check_length, check_value,
|
||||
check_greater_than, check_less_than)
|
||||
from openmc import Nuclide
|
||||
from openmc.source import Source
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
|
|
@ -37,7 +38,7 @@ class SettingsFile(object):
|
|||
type are 'variance', 'std_dev', and 'rel_err'. The threshold value
|
||||
should be a float indicating the variance, standard deviation, or
|
||||
relative error used.
|
||||
source : Iterable of openmc.source.Source
|
||||
source : Iterable of openmc.Source
|
||||
Distribution of source sites in space, angle, and energy
|
||||
output : dict
|
||||
Dictionary indicating what files to output. Valid keys are 'summary',
|
||||
|
|
@ -70,15 +71,20 @@ class SettingsFile(object):
|
|||
cross_sections : str
|
||||
Indicates the path to an XML cross section listing file (usually named
|
||||
cross_sections.xml). If it is not set, the :envvar:`CROSS_SECTIONS`
|
||||
environment variable will be used to find the path to the XML cross
|
||||
section listing.
|
||||
environment variable will be used for continuous-energy calculations
|
||||
and :envvar:`MG_CROSS_SECTIONS` will be used for multi-group
|
||||
calculations to find the path to the XML cross section file.
|
||||
multipole_library : str
|
||||
Indicates the path to a directory containing a windowed multipole
|
||||
cross section library. If it is not set, the :envvar:`MULTIPOLE_LIBRARY'
|
||||
environment variable will be used. A multipole library is optional.
|
||||
energy_grid : str
|
||||
Set the method used to search energy grids. Acceptable values are
|
||||
'nuclide', 'logarithm', and 'material-union'.
|
||||
cross section library. If it is not set, the
|
||||
:envvar:`OPENMC_MULTIPOLE_LIBRARY' environment variable will be used. A
|
||||
multipole library is optional.
|
||||
energy_grid : {'nuclide', 'logarithm', 'material-union'}
|
||||
Set the method used to search energy grids.
|
||||
energy_mode : {'continuous-energy', 'multi-group'}
|
||||
Set whether the calculation should be continuous-energy or multi-group.
|
||||
max_order : int
|
||||
Maximum scattering order to apply globally when in multi-group mode.
|
||||
ptables : bool
|
||||
Determine whether probability tables are used.
|
||||
run_cmfd : bool
|
||||
|
|
@ -128,6 +134,8 @@ class SettingsFile(object):
|
|||
use_windowed_multipole : bool
|
||||
Whether or not windowed multipole can be used to evaluate resolved
|
||||
resonance cross sections.
|
||||
resonance_scattering : ResonanceScattering or iterable of ResonanceScattering
|
||||
The elastic scattering model to use for resonant isotopes
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -141,6 +149,10 @@ class SettingsFile(object):
|
|||
self._particles = None
|
||||
self._keff_trigger = None
|
||||
|
||||
# Energy mode subelement
|
||||
self._energy_mode = None
|
||||
self._max_order = None
|
||||
|
||||
# Source subelement
|
||||
self._source = None
|
||||
|
||||
|
|
@ -206,6 +218,8 @@ class SettingsFile(object):
|
|||
self._source_element = None
|
||||
self._multipole_active = None
|
||||
|
||||
self._resonance_scattering = None
|
||||
|
||||
@property
|
||||
def run_mode(self):
|
||||
return self._run_mode
|
||||
|
|
@ -230,6 +244,14 @@ class SettingsFile(object):
|
|||
def keff_trigger(self):
|
||||
return self._keff_trigger
|
||||
|
||||
@property
|
||||
def energy_mode(self):
|
||||
return self._energy_mode
|
||||
|
||||
@property
|
||||
def max_order(self):
|
||||
return self._max_order
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
return self._source
|
||||
|
|
@ -394,9 +416,13 @@ class SettingsFile(object):
|
|||
def use_windowed_multipole(self):
|
||||
return self._multipole_active
|
||||
|
||||
@property
|
||||
def resonance_scattering(self):
|
||||
return self._resonance_scattering
|
||||
|
||||
@run_mode.setter
|
||||
def run_mode(self, run_mode):
|
||||
if 'run_mode' not in ['eigenvalue', 'fixed source']:
|
||||
if run_mode not in ['eigenvalue', 'fixed source']:
|
||||
msg = 'Unable to set run mode to "{0}". Only "eigenvalue" ' \
|
||||
'and "fixed source" are supported."'.format(run_mode)
|
||||
raise ValueError(msg)
|
||||
|
|
@ -455,6 +481,18 @@ class SettingsFile(object):
|
|||
|
||||
self._keff_trigger = keff_trigger
|
||||
|
||||
@energy_mode.setter
|
||||
def energy_mode(self, energy_mode):
|
||||
check_value('energy mode', energy_mode,
|
||||
['continuous-energy', 'multi-group'])
|
||||
self._energy_mode = energy_mode
|
||||
|
||||
@max_order.setter
|
||||
def max_order(self, max_order):
|
||||
check_type('maximum scattering order', max_order, Integral)
|
||||
check_greater_than('maximum scattering order', max_order, 0, True)
|
||||
self._max_order = max_order
|
||||
|
||||
@source.setter
|
||||
def source(self, source):
|
||||
if isinstance(source, Source):
|
||||
|
|
@ -763,6 +801,16 @@ class SettingsFile(object):
|
|||
check_type('use_windowed_multipole', active, bool)
|
||||
self._multipole_active = active
|
||||
|
||||
@resonance_scattering.setter
|
||||
def resonance_scattering(self, res):
|
||||
if isinstance(res, Iterable):
|
||||
check_type('resonance_scattering', res, Iterable,
|
||||
ResonanceScattering)
|
||||
self._resonance_scattering = res
|
||||
else:
|
||||
check_type('resonance_scattering', res, ResonanceScattering)
|
||||
self._resonance_scattering = [res]
|
||||
|
||||
def _create_run_mode_subelement(self):
|
||||
|
||||
if self.run_mode == 'eigenvalue':
|
||||
|
|
@ -809,6 +857,16 @@ class SettingsFile(object):
|
|||
subelement = ET.SubElement(element, key)
|
||||
subelement.text = str(self._keff_trigger[key]).lower()
|
||||
|
||||
def _create_energy_mode_subelement(self):
|
||||
if self._energy_mode is not None:
|
||||
element = ET.SubElement(self._settings_file, "energy_mode")
|
||||
element.text = str(self._energy_mode)
|
||||
|
||||
def _create_max_order_subelement(self):
|
||||
if self._max_order is not None:
|
||||
element = ET.SubElement(self._settings_file, "max_order")
|
||||
element.text = str(self._max_order)
|
||||
|
||||
def _create_source_subelement(self):
|
||||
if self.source is not None:
|
||||
for source in self.source:
|
||||
|
|
@ -1002,7 +1060,7 @@ class SettingsFile(object):
|
|||
element = ET.SubElement(self._settings_file, "uniform_fs")
|
||||
|
||||
subelement = ET.SubElement(element, "dimension")
|
||||
subelement.text = str(self._ufs_dimension)
|
||||
subelement.text = ' '.join(map(str, self._ufs_dimension))
|
||||
|
||||
subelement = ET.SubElement(element, "lower_left")
|
||||
subelement.text = ' '.join(map(str, self._ufs_lower_left))
|
||||
|
|
@ -1043,6 +1101,17 @@ class SettingsFile(object):
|
|||
"use_windowed_multipole")
|
||||
element.text = str(self._multipole_active)
|
||||
|
||||
def _create_resonance_scattering_element(self):
|
||||
if self.resonance_scattering is None: return
|
||||
|
||||
element = ET.SubElement(self._settings_file, "resonance_scattering")
|
||||
|
||||
for r in self.resonance_scattering:
|
||||
if r.nuclide.name != r.nuclide_0K.name:
|
||||
raise ValueError("The nuclide and nuclide_0K attributes of "
|
||||
"a ResonantScattering object must have identical names.")
|
||||
r.create_xml_subelement(element)
|
||||
|
||||
def export_to_xml(self):
|
||||
"""Create a settings.xml file that can be used for a simulation.
|
||||
|
||||
|
|
@ -1064,6 +1133,8 @@ class SettingsFile(object):
|
|||
self._create_cross_sections_subelement()
|
||||
self._create_multipole_library_subelement()
|
||||
self._create_energy_grid_subelement()
|
||||
self._create_energy_mode_subelement()
|
||||
self._create_max_order_subelement()
|
||||
self._create_ptables_subelement()
|
||||
self._create_run_cmfd_subelement()
|
||||
self._create_seed_subelement()
|
||||
|
|
@ -1079,6 +1150,7 @@ class SettingsFile(object):
|
|||
self._create_ufs_subelement()
|
||||
self._create_dd_subelement()
|
||||
self._create_use_multipole_subelement()
|
||||
self._create_resonance_scattering_element()
|
||||
|
||||
# Clean the indentation in the file to be user-readable
|
||||
clean_xml_indentation(self._settings_file)
|
||||
|
|
@ -1087,3 +1159,104 @@ class SettingsFile(object):
|
|||
tree = ET.ElementTree(self._settings_file)
|
||||
tree.write("settings.xml", xml_declaration=True,
|
||||
encoding='utf-8', method="xml")
|
||||
|
||||
|
||||
class ResonanceScattering(object):
|
||||
"""Specification of the elastic scattering model for resonant isotopes
|
||||
|
||||
Attributes
|
||||
----------
|
||||
nuclide : openmc.Nuclide
|
||||
The nuclide affected by this resonance scattering treatment.
|
||||
nuclide_0K : openmc.Nuclide
|
||||
This should be the same isotope as the nuclide attribute above, but it
|
||||
should have an xs attribute that identifies 0 Kelvin data.
|
||||
method : str
|
||||
The method used to sample outgoing scattering energies. Valid options
|
||||
are 'ARES', 'CXS' (constant cross section), 'DBRC' (Doppler broadening
|
||||
rejection correction), and 'WCM' (weight correction method).
|
||||
E_min : float
|
||||
The minimum energy above which the specified method is applied. By
|
||||
default, CXS will be used below E_min.
|
||||
E_max : float
|
||||
The maximum energy below which the specified method is applied. By
|
||||
default, the asymptotic target-at-rest model is applied above E_max.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._nuclide = None
|
||||
self._nuclide_0K = None
|
||||
self._method = None
|
||||
self._E_min = None
|
||||
self._E_max = None
|
||||
|
||||
@property
|
||||
def nuclide(self):
|
||||
return self._nuclide
|
||||
|
||||
@property
|
||||
def nuclide_0K(self):
|
||||
return self._nuclide_0K
|
||||
|
||||
@property
|
||||
def method(self):
|
||||
return self._method
|
||||
|
||||
@property
|
||||
def E_min(self):
|
||||
return self._E_min
|
||||
|
||||
@property
|
||||
def E_max(self):
|
||||
return self._E_max
|
||||
|
||||
@nuclide.setter
|
||||
def nuclide(self, nuc):
|
||||
check_type('nuclide', nuc, Nuclide)
|
||||
if nuc.zaid == None: raise ValueError("The nuclide must have an "
|
||||
"explicitly defined zaid attribute.")
|
||||
self._nuclide = nuc
|
||||
|
||||
@nuclide_0K.setter
|
||||
def nuclide_0K(self, nuc):
|
||||
check_type('nuclide_0K', nuc, Nuclide)
|
||||
if nuc.zaid == None: raise ValueError("The nuclide_0K must have an "
|
||||
"explicitly defined zaid attribute.")
|
||||
self._nuclide_0K = nuc
|
||||
|
||||
@method.setter
|
||||
def method(self, m):
|
||||
check_value('method', m, ('ARES', 'CXS', 'DBRC', 'WCM'))
|
||||
self._method = m
|
||||
|
||||
@E_min.setter
|
||||
def E_min(self, E):
|
||||
check_type('E_min', E, Real)
|
||||
check_greater_than('E_min', E, 0, True)
|
||||
self._E_min = E
|
||||
|
||||
@E_max.setter
|
||||
def E_max(self, E):
|
||||
check_type('E_max', E, Real)
|
||||
check_greater_than('E_max', E, 0, True)
|
||||
self._E_max = E
|
||||
|
||||
def create_xml_subelement(self, xml_element):
|
||||
scatterer = ET.SubElement(xml_element, "scatterer")
|
||||
subelement = ET.SubElement(scatterer, 'nuclide')
|
||||
subelement.text = self.nuclide.name
|
||||
if self.method is not None:
|
||||
subelement = ET.SubElement(scatterer, 'method')
|
||||
subelement.text = self.method
|
||||
subelement = ET.SubElement(scatterer, 'xs_label')
|
||||
subelement.text = str(self.nuclide.zaid) + '.' + str(self.nuclide.xs)
|
||||
subelement = ET.SubElement(scatterer, 'xs_label_0K')
|
||||
subelement.text = str(self.nuclide_0K.zaid) + '.' \
|
||||
+ str(self.nuclide_0K.xs)
|
||||
if self.E_min is not None:
|
||||
subelement = ET.SubElement(scatterer, 'E_min')
|
||||
subelement.text = str(self.E_min)
|
||||
if self.E_max is not None:
|
||||
subelement = ET.SubElement(scatterer, 'E_max')
|
||||
subelement.text = str(self.E_max)
|
||||
|
|
|
|||
|
|
@ -18,59 +18,62 @@ class StatePoint(object):
|
|||
----------
|
||||
cmfd_on : bool
|
||||
Indicate whether CMFD is active
|
||||
cmfd_balance : ndarray
|
||||
cmfd_balance : numpy.ndarray
|
||||
Residual neutron balance for each batch
|
||||
cmfd_dominance
|
||||
Dominance ratio for each batch
|
||||
cmfd_entropy : ndarray
|
||||
cmfd_entropy : numpy.ndarray
|
||||
Shannon entropy of CMFD fission source for each batch
|
||||
cmfd_indices : ndarray
|
||||
cmfd_indices : numpy.ndarray
|
||||
Number of CMFD mesh cells and energy groups. The first three indices
|
||||
correspond to the x-, y-, and z- spatial directions and the fourth index
|
||||
is the number of energy groups.
|
||||
cmfd_srccmp : ndarray
|
||||
cmfd_srccmp : numpy.ndarray
|
||||
Root-mean-square difference between OpenMC and CMFD fission source for
|
||||
each batch
|
||||
cmfd_src : ndarray
|
||||
cmfd_src : numpy.ndarray
|
||||
CMFD fission source distribution over all mesh cells and energy groups.
|
||||
current_batch : Integral
|
||||
current_batch : int
|
||||
Number of batches simulated
|
||||
date_and_time : str
|
||||
Date and time when simulation began
|
||||
entropy : ndarray
|
||||
entropy : numpy.ndarray
|
||||
Shannon entropy of fission source at each batch
|
||||
gen_per_batch : Integral
|
||||
Number of fission generations per batch
|
||||
global_tallies : ndarray of compound datatype
|
||||
global_tallies : numpy.ndarray of compound datatype
|
||||
Global tallies for k-effective estimates and leakage. The compound
|
||||
datatype has fields 'name', 'sum', 'sum_sq', 'mean', and 'std_dev'.
|
||||
k_combined : list
|
||||
Combined estimator for k-effective and its uncertainty
|
||||
k_col_abs : Real
|
||||
k_col_abs : float
|
||||
Cross-product of collision and absorption estimates of k-effective
|
||||
k_col_tra : Real
|
||||
k_col_tra : float
|
||||
Cross-product of collision and tracklength estimates of k-effective
|
||||
k_abs_tra : Real
|
||||
k_abs_tra : float
|
||||
Cross-product of absorption and tracklength estimates of k-effective
|
||||
k_generation : ndarray
|
||||
k_generation : numpy.ndarray
|
||||
Estimate of k-effective for each batch/generation
|
||||
meshes : dict
|
||||
Dictionary whose keys are mesh IDs and whose values are Mesh objects
|
||||
n_batches : Integral
|
||||
n_batches : int
|
||||
Number of batches
|
||||
n_inactive : Integral
|
||||
n_inactive : int
|
||||
Number of inactive batches
|
||||
n_particles : Integral
|
||||
n_particles : int
|
||||
Number of particles per generation
|
||||
n_realizations : Integral
|
||||
n_realizations : int
|
||||
Number of tally realizations
|
||||
path : str
|
||||
Working directory for simulation
|
||||
run_mode : str
|
||||
Simulation run mode, e.g. 'k-eigenvalue'
|
||||
seed : Integral
|
||||
runtime : dict
|
||||
Dictionary whose keys are strings describing various runtime metrics
|
||||
and whose values are time values in seconds.
|
||||
seed : int
|
||||
Pseudorandom number generator seed
|
||||
source : ndarray of compound datatype
|
||||
source : numpy.ndarray of compound datatype
|
||||
Array of source sites. The compound datatype has fields 'wgt', 'xyz',
|
||||
'uvw', and 'E' corresponding to the weight, position, direction, and
|
||||
energy of the source site.
|
||||
|
|
@ -88,7 +91,7 @@ class StatePoint(object):
|
|||
TallyDerivative objects
|
||||
version: tuple of Integral
|
||||
Version of OpenMC
|
||||
summary : None or openmc.summary.Summary
|
||||
summary : None or openmc.Summary
|
||||
A summary object if the statepoint has been linked with a summary file
|
||||
|
||||
"""
|
||||
|
|
@ -104,13 +107,14 @@ class StatePoint(object):
|
|||
raise IOError('{} is not a statepoint file.'.format(filename))
|
||||
except AttributeError:
|
||||
raise IOError('Could not read statepoint file. This most likely '
|
||||
'means the statepoint file was produced by a different '
|
||||
'version of OpenMC than the one you are using.')
|
||||
if self._f['revision'].value != 14:
|
||||
'means the statepoint file was produced by a '
|
||||
'different version of OpenMC than the one you are '
|
||||
'using.')
|
||||
if self._f['revision'].value != 15:
|
||||
raise IOError('Statepoint file has a file revision of {} '
|
||||
'which is not consistent with the revision this '
|
||||
'version of OpenMC expects ({}).'.format(
|
||||
self._f['revision'].value, 14))
|
||||
self._f['revision'].value, 15))
|
||||
|
||||
# Set flags for what data has been read
|
||||
self._meshes_read = False
|
||||
|
|
@ -315,6 +319,11 @@ class StatePoint(object):
|
|||
def run_mode(self):
|
||||
return self._f['run_mode'].value.decode()
|
||||
|
||||
@property
|
||||
def runtime(self):
|
||||
return {name: dataset.value
|
||||
for name, dataset in self._f['runtime'].items()}
|
||||
|
||||
@property
|
||||
def seed(self):
|
||||
return self._f['seed'].value
|
||||
|
|
@ -399,7 +408,7 @@ class StatePoint(object):
|
|||
new_filter.mesh = self.meshes[key]
|
||||
|
||||
# Add Filter to the Tally
|
||||
tally.add_filter(new_filter)
|
||||
tally.filters.append(new_filter)
|
||||
|
||||
# Read Nuclide bins
|
||||
nuclide_names = \
|
||||
|
|
@ -408,7 +417,7 @@ class StatePoint(object):
|
|||
# Add all Nuclides to the Tally
|
||||
for name in nuclide_names:
|
||||
nuclide = openmc.Nuclide(name.decode().strip())
|
||||
tally.add_nuclide(nuclide)
|
||||
tally.nuclides.append(nuclide)
|
||||
|
||||
scores = self._f['{0}{1}/score_bins'.format(
|
||||
base, tally_key)].value
|
||||
|
|
@ -435,7 +444,7 @@ class StatePoint(object):
|
|||
pattern = r'-n$|-pn$|-yn$'
|
||||
score = re.sub(pattern, '-' + moments[j].decode(), score)
|
||||
|
||||
tally.add_score(score)
|
||||
tally.scores.append(score)
|
||||
|
||||
# Add Tally to the global dictionary of all Tallies
|
||||
tally.sparse = self.sparse
|
||||
|
|
@ -542,7 +551,7 @@ class StatePoint(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
tally : Tally
|
||||
tally : openmc.Tally
|
||||
A tally matching the specified criteria
|
||||
|
||||
Raises
|
||||
|
|
@ -639,7 +648,7 @@ class StatePoint(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
summary : Summary
|
||||
summary : openmc.Summary
|
||||
A Summary object.
|
||||
|
||||
Raises
|
||||
|
|
@ -656,11 +665,13 @@ class StatePoint(object):
|
|||
raise ValueError(msg)
|
||||
|
||||
for tally_id, tally in self.tallies.items():
|
||||
# Get the Tally name from the summary file
|
||||
tally.name = summary.tallies[tally_id].name
|
||||
summary_tally = summary.tallies[tally_id]
|
||||
tally.name = summary_tally.name
|
||||
tally.with_summary = True
|
||||
|
||||
for tally_filter in tally.filters:
|
||||
summary_filter = summary_tally.find_filter(tally_filter.type)
|
||||
|
||||
if tally_filter.type == 'surface':
|
||||
surface_ids = []
|
||||
for bin in tally_filter.bins:
|
||||
|
|
@ -673,6 +684,10 @@ class StatePoint(object):
|
|||
distribcell_ids.append(summary.cells[bin].id)
|
||||
tally_filter.bins = distribcell_ids
|
||||
|
||||
if tally_filter.type == 'distribcell':
|
||||
tally_filter.distribcell_paths = \
|
||||
summary_filter.distribcell_paths
|
||||
|
||||
if tally_filter.type == 'universe':
|
||||
universe_ids = []
|
||||
for bin in tally_filter.bins:
|
||||
|
|
|
|||
|
|
@ -22,12 +22,12 @@ class UnitSphere(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
reference_uvw : Iterable of Real
|
||||
reference_uvw : Iterable of float
|
||||
Direction from which polar angle is measured
|
||||
|
||||
Attributes
|
||||
----------
|
||||
reference_uvw : Iterable of Real
|
||||
reference_uvw : Iterable of float
|
||||
Direction from which polar angle is measured
|
||||
|
||||
"""
|
||||
|
|
@ -62,19 +62,19 @@ class PolarAzimuthal(UnitSphere):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
mu : Univariate
|
||||
mu : openmc.stats.Univariate
|
||||
Distribution of the cosine of the polar angle
|
||||
phi : Univariate
|
||||
phi : openmc.stats.Univariate
|
||||
Distribution of the azimuthal angle in radians
|
||||
reference_uvw : Iterable of Real
|
||||
reference_uvw : Iterable of float
|
||||
Direction from which polar angle is measured. Defaults to the positive
|
||||
z-direction.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
mu : Univariate
|
||||
mu : openmc.stats.Univariate
|
||||
Distribution of the cosine of the polar angle
|
||||
phi : Univariate
|
||||
phi : openmc.stats.Univariate
|
||||
Distribution of the azimuthal angle in radians
|
||||
|
||||
"""
|
||||
|
|
@ -142,7 +142,7 @@ class Monodirectional(UnitSphere):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
reference_uvw : Iterable of Real
|
||||
reference_uvw : Iterable of float
|
||||
Direction from which polar angle is measured. Defaults to the positive
|
||||
x-direction.
|
||||
|
||||
|
|
@ -186,20 +186,20 @@ class CartesianIndependent(Spatial):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
x : Univariate
|
||||
x : openmc.stats.Univariate
|
||||
Distribution of x-coordinates
|
||||
y : Univariate
|
||||
y : openmc.stats.Univariate
|
||||
Distribution of y-coordinates
|
||||
z : Univariate
|
||||
z : openmc.stats.Univariate
|
||||
Distribution of z-coordinates
|
||||
|
||||
Attributes
|
||||
----------
|
||||
x : Univariate
|
||||
x : openmc.stats.Univariate
|
||||
Distribution of x-coordinates
|
||||
y : Univariate
|
||||
y : openmc.stats.Univariate
|
||||
Distribution of y-coordinates
|
||||
z : Univariate
|
||||
z : openmc.stats.Univariate
|
||||
Distribution of z-coordinates
|
||||
|
||||
"""
|
||||
|
|
@ -252,9 +252,9 @@ class Box(Spatial):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
lower_left : Iterable of Real
|
||||
lower_left : Iterable of float
|
||||
Lower-left coordinates of cuboid
|
||||
upper_right : Iterable of Real
|
||||
upper_right : Iterable of float
|
||||
Upper-right coordinates of cuboid
|
||||
only_fissionable : bool, optional
|
||||
Whether spatial sites should only be accepted if they occur in
|
||||
|
|
@ -262,9 +262,9 @@ class Box(Spatial):
|
|||
|
||||
Attributes
|
||||
----------
|
||||
lower_left : Iterable of Real
|
||||
lower_left : Iterable of float
|
||||
Lower-left coordinates of cuboid
|
||||
upper_right : Iterable of Real
|
||||
upper_right : Iterable of float
|
||||
Upper-right coordinates of cuboid
|
||||
only_fissionable : bool, optional
|
||||
Whether spatial sites should only be accepted if they occur in
|
||||
|
|
@ -328,12 +328,12 @@ class Point(Spatial):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
xyz : Iterable of Real
|
||||
xyz : Iterable of float
|
||||
Cartesian coordinates of location
|
||||
|
||||
Attributes
|
||||
----------
|
||||
xyz : Iterable of Real
|
||||
xyz : Iterable of float
|
||||
Cartesian coordinates of location
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -37,16 +37,16 @@ class Discrete(Univariate):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
x : Iterable of Real
|
||||
x : Iterable of float
|
||||
Values of the random variable
|
||||
p : Iterable of Real
|
||||
p : Iterable of float
|
||||
Discrete probability for each value
|
||||
|
||||
Attributes
|
||||
----------
|
||||
x : Iterable of Real
|
||||
x : Iterable of float
|
||||
Values of the random variable
|
||||
p : Iterable of Real
|
||||
p : Iterable of float
|
||||
Discrete probability for each value
|
||||
|
||||
"""
|
||||
|
|
@ -243,9 +243,9 @@ class Tabular(Univariate):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
x : Iterable of Real
|
||||
x : Iterable of float
|
||||
Tabulated values of the random variable
|
||||
p : Iterable of Real
|
||||
p : Iterable of float
|
||||
Tabulated probabilities
|
||||
interpolation : {'histogram', 'linear-linear'}, optional
|
||||
Indicate whether the density function is constant between tabulated
|
||||
|
|
@ -253,9 +253,9 @@ class Tabular(Univariate):
|
|||
|
||||
Attributes
|
||||
----------
|
||||
x : Iterable of Real
|
||||
x : Iterable of float
|
||||
Tabulated values of the random variable
|
||||
p : Iterable of Real
|
||||
p : Iterable of float
|
||||
Tabulated probabilities
|
||||
interpolation : {'histogram', 'linear-linear'}, optional
|
||||
Indicate whether the density function is constant between tabulated
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ class Summary(object):
|
|||
# Read date and time
|
||||
self.date_and_time = self._f['date_and_time'][...]
|
||||
|
||||
# Read if continuous-energy or multi-group
|
||||
self.run_CE = (self._f['run_CE'].value == 1)
|
||||
|
||||
self.n_batches = self._f['n_batches'].value
|
||||
self.n_particles = self._f['n_particles'].value
|
||||
self.n_active = self._f['n_active'].value
|
||||
|
|
@ -279,7 +282,7 @@ class Summary(object):
|
|||
# Get the distribcell index
|
||||
ind = self._f['geometry/cells'][key]['distribcell_index'].value
|
||||
if ind != 0:
|
||||
cell.distribcell_index = ind
|
||||
cell.distribcell_index = ind
|
||||
|
||||
# Add the Cell to the global dictionary of all Cells
|
||||
self.cells[index] = cell
|
||||
|
|
@ -542,7 +545,7 @@ class Summary(object):
|
|||
# If this is a moment, use generic moment order
|
||||
pattern = r'-n$|-pn$|-yn$'
|
||||
score = re.sub(pattern, '-' + moments[j].decode(), score)
|
||||
tally.add_score(score)
|
||||
tally.scores.append(score)
|
||||
|
||||
# Read filter metadata
|
||||
num_filters = self._f['{0}/n_filters'.format(subbase)].value
|
||||
|
|
@ -562,8 +565,14 @@ class Summary(object):
|
|||
new_filter = openmc.Filter(filter_type, bins)
|
||||
new_filter.num_bins = num_bins
|
||||
|
||||
# Read in distribcell paths
|
||||
if filter_type == 'distribcell':
|
||||
paths = self._f['{0}/paths'.format(subsubbase)][...]
|
||||
paths = [str(path.decode()) for path in paths]
|
||||
new_filter.distribcell_paths = paths
|
||||
|
||||
# Add Filter to the Tally
|
||||
tally.add_filter(new_filter)
|
||||
tally.filters.append(new_filter)
|
||||
|
||||
# Add Tally to the global dictionary of all Tallies
|
||||
self.tallies[tally_id] = tally
|
||||
|
|
@ -578,7 +587,7 @@ class Summary(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
material : openmc.material.Material
|
||||
material : openmc.Material
|
||||
Material with given id
|
||||
|
||||
"""
|
||||
|
|
@ -599,7 +608,7 @@ class Summary(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
surface : openmc.surface.Surface
|
||||
surface : openmc.Surface
|
||||
Surface with given id
|
||||
|
||||
"""
|
||||
|
|
@ -620,7 +629,7 @@ class Summary(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
cell : openmc.universe.Cell
|
||||
cell : openmc.Cell
|
||||
Cell with given id
|
||||
|
||||
"""
|
||||
|
|
@ -641,7 +650,7 @@ class Summary(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
universe : openmc.universe.Universe
|
||||
universe : openmc.Universe
|
||||
Universe with given id
|
||||
|
||||
"""
|
||||
|
|
@ -662,7 +671,7 @@ class Summary(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
lattice : openmc.universe.Lattice
|
||||
lattice : openmc.Lattice
|
||||
Lattice with given id
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -153,10 +153,10 @@ class Surface(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Lower-left coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Upper-right coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
|
||||
|
|
@ -278,8 +278,7 @@ class Plane(Surface):
|
|||
|
||||
|
||||
class XPlane(Plane):
|
||||
"""A plane perpendicular to the x axis, i.e. a surface of the form :math:`x -
|
||||
x_0 = 0`
|
||||
"""A plane perpendicular to the x axis of the form :math:`x - x_0 = 0`
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -338,10 +337,10 @@ class XPlane(Plane):
|
|||
|
||||
Returns
|
||||
-------
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Lower-left coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Upper-right coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
|
||||
|
|
@ -356,8 +355,7 @@ class XPlane(Plane):
|
|||
|
||||
|
||||
class YPlane(Plane):
|
||||
"""A plane perpendicular to the y axis, i.e. a surface of the form :math:`y -
|
||||
y_0 = 0`
|
||||
"""A plane perpendicular to the y axis of the form :math:`y - y_0 = 0`
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -416,10 +414,10 @@ class YPlane(Plane):
|
|||
|
||||
Returns
|
||||
-------
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Lower-left coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Upper-right coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
|
||||
|
|
@ -434,8 +432,7 @@ class YPlane(Plane):
|
|||
|
||||
|
||||
class ZPlane(Plane):
|
||||
"""A plane perpendicular to the z axis, i.e. a surface of the form :math:`z -
|
||||
z_0 = 0`
|
||||
"""A plane perpendicular to the z axis of the form :math:`z - z_0 = 0`
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -494,10 +491,10 @@ class ZPlane(Plane):
|
|||
|
||||
Returns
|
||||
-------
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Lower-left coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Upper-right coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
|
||||
|
|
@ -641,10 +638,10 @@ class XCylinder(Cylinder):
|
|||
|
||||
Returns
|
||||
-------
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Lower-left coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Upper-right coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
|
||||
|
|
@ -740,10 +737,10 @@ class YCylinder(Cylinder):
|
|||
|
||||
Returns
|
||||
-------
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Lower-left coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Upper-right coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
|
||||
|
|
@ -839,10 +836,10 @@ class ZCylinder(Cylinder):
|
|||
|
||||
Returns
|
||||
-------
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Lower-left coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Upper-right coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
|
||||
|
|
@ -967,10 +964,10 @@ class Sphere(Surface):
|
|||
|
||||
Returns
|
||||
-------
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Lower-left coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
numpy.array
|
||||
numpy.ndarray
|
||||
Upper-right coordinates of the axis-aligned bounding box for the
|
||||
desired half-space
|
||||
|
||||
|
|
@ -1383,7 +1380,7 @@ class Halfspace(Region):
|
|||
can be created from an existing Surface through the __neg__ and __pos__
|
||||
operators, as the following example demonstrates:
|
||||
|
||||
>>> sphere = openmc.surface.Sphere(surface_id=1, R=10.0)
|
||||
>>> sphere = openmc.Sphere(surface_id=1, R=10.0)
|
||||
>>> inside_sphere = -sphere
|
||||
>>> outside_sphere = +sphere
|
||||
>>> type(inside_sphere)
|
||||
|
|
@ -1391,18 +1388,18 @@ class Halfspace(Region):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
surface : Surface
|
||||
surface : openmc.Surface
|
||||
Surface which divides Euclidean space.
|
||||
side : {'+', '-'}
|
||||
Indicates whether the positive or negative half-space is used.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
surface : Surface
|
||||
surface : openmc.Surface
|
||||
Surface which divides Euclidean space.
|
||||
side : {'+', '-'}
|
||||
Indicates whether the positive or negative half-space is used.
|
||||
bounding_box : tuple of numpy.array
|
||||
bounding_box : tuple of numpy.ndarray
|
||||
Lower-left and upper-right coordinates of an axis-aligned bounding box
|
||||
|
||||
"""
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
|
|||
1367
openmc/universe.py
1367
openmc/universe.py
File diff suppressed because it is too large
Load diff
2
setup.py
2
setup.py
|
|
@ -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']
|
||||
|
|
|
|||
815
src/ace.F90
815
src/ace.F90
File diff suppressed because it is too large
Load diff
|
|
@ -1,298 +0,0 @@
|
|||
module ace_header
|
||||
|
||||
use constants, only: MAX_FILE_LEN, ZERO
|
||||
use dict_header, only: DictIntInt
|
||||
use endf_header, only: Tab1
|
||||
use multipole_header, only: MultipoleArray
|
||||
use secondary_header, only: SecondaryDistribution, AngleEnergyContainer
|
||||
use stl_vector, only: VectorInt
|
||||
|
||||
implicit none
|
||||
|
||||
!===============================================================================
|
||||
! REACTION contains the cross-section and secondary energy and angle
|
||||
! distributions for a single reaction in a continuous-energy ACE-format table
|
||||
!===============================================================================
|
||||
|
||||
type Reaction
|
||||
integer :: MT ! ENDF MT value
|
||||
real(8) :: Q_value ! Reaction Q value
|
||||
integer :: multiplicity ! Number of secondary particles released
|
||||
type(Tab1), pointer :: multiplicity_E => null() ! Energy-dependent neutron yield
|
||||
integer :: threshold ! Energy grid index of threshold
|
||||
logical :: scatter_in_cm ! scattering system in center-of-mass?
|
||||
logical :: multiplicity_with_E = .false. ! Flag to indicate E-dependent multiplicity
|
||||
real(8), allocatable :: sigma(:) ! Cross section values
|
||||
type(SecondaryDistribution) :: secondary
|
||||
|
||||
! Type-Bound procedures
|
||||
contains
|
||||
procedure :: clear => reaction_clear ! Deallocates Reaction
|
||||
end type Reaction
|
||||
|
||||
!===============================================================================
|
||||
! URRDATA contains probability tables for the unresolved resonance range.
|
||||
!===============================================================================
|
||||
|
||||
type UrrData
|
||||
integer :: n_energy ! # of incident neutron energies
|
||||
integer :: n_prob ! # of probabilities
|
||||
integer :: interp ! inteprolation (2=lin-lin, 5=log-log)
|
||||
integer :: inelastic_flag ! inelastic competition flag
|
||||
integer :: absorption_flag ! other absorption flag
|
||||
logical :: multiply_smooth ! multiply by smooth cross section?
|
||||
real(8), allocatable :: energy(:) ! incident energies
|
||||
real(8), allocatable :: prob(:,:,:) ! actual probabibility tables
|
||||
end type UrrData
|
||||
|
||||
!===============================================================================
|
||||
! NUCLIDE contains all the data for an ACE-format continuous-energy cross
|
||||
! section. The ACE format (A Compact ENDF format) is used in MCNP and several
|
||||
! other Monte Carlo codes.
|
||||
!===============================================================================
|
||||
|
||||
type Nuclide
|
||||
character(10) :: name ! name of nuclide, e.g. 92235.03c
|
||||
integer :: zaid ! Z and A identifier, e.g. 92235
|
||||
integer :: listing ! index in xs_listings
|
||||
real(8) :: awr ! weight of nucleus in neutron masses
|
||||
real(8) :: kT ! temperature in MeV (k*T)
|
||||
|
||||
! Linked list of indices in nuclides array of instances of this same nuclide
|
||||
type(VectorInt) :: nuc_list
|
||||
|
||||
! Energy grid information
|
||||
integer :: n_grid ! # of nuclide grid points
|
||||
integer, allocatable :: grid_index(:) ! log grid mapping indices
|
||||
real(8), allocatable :: energy(:) ! energy values corresponding to xs
|
||||
|
||||
! Microscopic cross sections
|
||||
real(8), allocatable :: total(:) ! total cross section
|
||||
real(8), allocatable :: elastic(:) ! elastic scattering
|
||||
real(8), allocatable :: fission(:) ! fission
|
||||
real(8), allocatable :: nu_fission(:) ! neutron production
|
||||
real(8), allocatable :: absorption(:) ! absorption (MT > 100)
|
||||
real(8), allocatable :: heating(:) ! heating
|
||||
|
||||
! Resonance scattering info
|
||||
logical :: resonant = .false. ! resonant scatterer?
|
||||
character(10) :: name_0K = '' ! name of 0K nuclide, e.g. 92235.00c
|
||||
character(16) :: scheme ! target velocity sampling scheme
|
||||
integer :: n_grid_0K ! number of 0K energy grid points
|
||||
real(8), allocatable :: energy_0K(:) ! energy grid for 0K xs
|
||||
real(8), allocatable :: elastic_0K(:) ! Microscopic elastic cross section
|
||||
real(8), allocatable :: xs_cdf(:) ! CDF of v_rel times cross section
|
||||
real(8) :: E_min ! lower cutoff energy for res scattering
|
||||
real(8) :: E_max ! upper cutoff energy for res scattering
|
||||
|
||||
! Fission information
|
||||
logical :: fissionable ! nuclide is fissionable?
|
||||
logical :: has_partial_fission ! nuclide has partial fission reactions?
|
||||
integer :: n_fission ! # of fission reactions
|
||||
integer, allocatable :: index_fission(:) ! indices in reactions
|
||||
|
||||
! Total fission neutron emission
|
||||
integer :: nu_t_type
|
||||
real(8), allocatable :: nu_t_data(:)
|
||||
|
||||
! Prompt fission neutron emission
|
||||
integer :: nu_p_type
|
||||
real(8), allocatable :: nu_p_data(:)
|
||||
|
||||
! Delayed fission neutron emission
|
||||
integer :: nu_d_type
|
||||
integer :: n_precursor ! # of delayed neutron precursors
|
||||
real(8), allocatable :: nu_d_data(:)
|
||||
real(8), allocatable :: nu_d_precursor_data(:)
|
||||
type(AngleEnergyContainer), allocatable :: nu_d_edist(:)
|
||||
|
||||
! Unresolved resonance data
|
||||
logical :: urr_present
|
||||
integer :: urr_inelastic
|
||||
type(UrrData), pointer :: urr_data => null()
|
||||
|
||||
! Multipole data
|
||||
logical :: mp_present = .false.
|
||||
type(MultipoleArray), pointer :: multipole => null()
|
||||
|
||||
! Reactions
|
||||
integer :: n_reaction ! # of reactions
|
||||
type(Reaction), allocatable :: reactions(:)
|
||||
type(DictIntInt) :: reaction_index ! map MT values to index in reactions
|
||||
! array; used at tally-time
|
||||
|
||||
! Type-Bound procedures
|
||||
contains
|
||||
procedure :: clear => nuclide_clear ! Deallocates Nuclide
|
||||
end type Nuclide
|
||||
|
||||
!===============================================================================
|
||||
! NUCLIDE0K temporarily contains all 0K cross section data and other parameters
|
||||
! needed to treat resonance scattering before transferring them to NUCLIDE
|
||||
!===============================================================================
|
||||
|
||||
type Nuclide0K
|
||||
character(10) :: nuclide ! name of nuclide, e.g. U-238
|
||||
character(16) :: scheme = 'ares' ! target velocity sampling scheme
|
||||
character(10) :: name ! name of nuclide, e.g. 92235.03c
|
||||
character(10) :: name_0K ! name of 0K nuclide, e.g. 92235.00c
|
||||
real(8) :: E_min = 0.01e-6_8 ! lower cutoff energy for res scattering
|
||||
real(8) :: E_max = 1000.0e-6_8 ! upper cutoff energy for res scattering
|
||||
end type Nuclide0K
|
||||
|
||||
!===============================================================================
|
||||
! DISTENERGYSAB contains the secondary energy/angle distributions for inelastic
|
||||
! thermal scattering collisions which utilize a continuous secondary energy
|
||||
! representation.
|
||||
!===============================================================================
|
||||
|
||||
type DistEnergySab
|
||||
integer :: n_e_out
|
||||
real(8), allocatable :: e_out(:)
|
||||
real(8), allocatable :: e_out_pdf(:)
|
||||
real(8), allocatable :: e_out_cdf(:)
|
||||
real(8), allocatable :: mu(:,:)
|
||||
end type DistEnergySab
|
||||
|
||||
!===============================================================================
|
||||
! SALPHABETA contains S(a,b) data for thermal neutron scattering, typically off
|
||||
! of light isotopes such as water, graphite, Be, etc
|
||||
!===============================================================================
|
||||
|
||||
type SAlphaBeta
|
||||
character(10) :: name ! name of table, e.g. lwtr.10t
|
||||
real(8) :: awr ! weight of nucleus in neutron masses
|
||||
real(8) :: kT ! temperature in MeV (k*T)
|
||||
integer :: n_zaid ! Number of valid zaids
|
||||
integer, allocatable :: zaid(:) ! List of valid Z and A identifiers, e.g. 6012
|
||||
|
||||
! threshold for S(a,b) treatment (usually ~4 eV)
|
||||
real(8) :: threshold_inelastic
|
||||
real(8) :: threshold_elastic = ZERO
|
||||
|
||||
! Inelastic scattering data
|
||||
integer :: n_inelastic_e_in ! # of incoming E for inelastic
|
||||
integer :: n_inelastic_e_out ! # of outgoing E for inelastic
|
||||
integer :: n_inelastic_mu ! # of outgoing angles for inelastic
|
||||
integer :: secondary_mode ! secondary mode (equal/skewed/continuous)
|
||||
real(8), allocatable :: inelastic_e_in(:)
|
||||
real(8), allocatable :: inelastic_sigma(:)
|
||||
! The following are used only if secondary_mode is 0 or 1
|
||||
real(8), allocatable :: inelastic_e_out(:,:)
|
||||
real(8), allocatable :: inelastic_mu(:,:,:)
|
||||
! The following is used only if secondary_mode is 3
|
||||
! The different implementation is necessary because the continuous
|
||||
! representation has a variable number of outgoing energy points for each
|
||||
! incoming energy
|
||||
type(DistEnergySab), allocatable :: inelastic_data(:) ! One for each Ein
|
||||
|
||||
! Elastic scattering data
|
||||
integer :: elastic_mode ! elastic mode (discrete/exact)
|
||||
integer :: n_elastic_e_in ! # of incoming E for elastic
|
||||
integer :: n_elastic_mu ! # of outgoing angles for elastic
|
||||
real(8), allocatable :: elastic_e_in(:)
|
||||
real(8), allocatable :: elastic_P(:)
|
||||
real(8), allocatable :: elastic_mu(:,:)
|
||||
end type SAlphaBeta
|
||||
|
||||
!===============================================================================
|
||||
! XSLISTING contains data read from a cross_sections.xml file
|
||||
!===============================================================================
|
||||
|
||||
type XsListing
|
||||
character(12) :: name ! table name, e.g. 92235.70c
|
||||
character(12) :: alias ! table alias, e.g. U-235.70c
|
||||
integer :: type ! type of table (cont-E neutron, S(A,b), etc)
|
||||
integer :: zaid ! ZAID identifier = 1000*Z + A
|
||||
integer :: filetype ! ASCII or BINARY
|
||||
integer :: location ! location of table within library
|
||||
integer :: recl ! record length for library
|
||||
integer :: entries ! number of entries per record
|
||||
real(8) :: awr ! atomic weight ratio (# of neutron masses)
|
||||
real(8) :: kT ! Boltzmann constant * temperature (MeV)
|
||||
logical :: metastable ! is this nuclide metastable?
|
||||
character(MAX_FILE_LEN) :: path ! path to library containing table
|
||||
end type XsListing
|
||||
|
||||
!===============================================================================
|
||||
! NUCLIDEMICROXS contains cached microscopic cross sections for a
|
||||
! particular nuclide at the current energy
|
||||
!===============================================================================
|
||||
|
||||
type NuclideMicroXS
|
||||
integer :: index_grid ! index on nuclide energy grid
|
||||
integer :: index_temp ! temperature index for nuclide
|
||||
real(8) :: last_E = ZERO ! last evaluated energy
|
||||
real(8) :: interp_factor ! interpolation factor on nuc. energy grid
|
||||
real(8) :: total ! microscropic total xs
|
||||
real(8) :: elastic ! microscopic elastic scattering xs
|
||||
real(8) :: absorption ! microscopic absorption xs
|
||||
real(8) :: fission ! microscopic fission xs
|
||||
real(8) :: nu_fission ! microscopic production xs
|
||||
|
||||
! Information for S(a,b) use
|
||||
integer :: index_sab ! index in sab_tables (zero means no table)
|
||||
integer :: last_index_sab = 0 ! index in sab_tables last used by this nuclide
|
||||
real(8) :: elastic_sab ! microscopic elastic scattering on S(a,b) table
|
||||
|
||||
! Information for URR probability table use
|
||||
logical :: use_ptable ! in URR range with probability tables?
|
||||
real(8) :: last_prn
|
||||
|
||||
! Information for Doppler broadening
|
||||
real(8) :: last_sqrtkT = ZERO ! last temperature in sqrt(Boltzmann constant * temperature (MeV))
|
||||
end type NuclideMicroXS
|
||||
|
||||
!===============================================================================
|
||||
! MATERIALMACROXS contains cached macroscopic cross sections for the material a
|
||||
! particle is traveling through
|
||||
!===============================================================================
|
||||
|
||||
type MaterialMacroXS
|
||||
real(8) :: total ! macroscopic total xs
|
||||
real(8) :: elastic ! macroscopic elastic scattering xs
|
||||
real(8) :: absorption ! macroscopic absorption xs
|
||||
real(8) :: fission ! macroscopic fission xs
|
||||
real(8) :: nu_fission ! macroscopic production xs
|
||||
end type MaterialMacroXS
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! REACTION_CLEAR resets and deallocates data in Reaction.
|
||||
!===============================================================================
|
||||
|
||||
subroutine reaction_clear(this)
|
||||
class(Reaction), intent(inout) :: this ! The Reaction object to clear
|
||||
|
||||
if (associated(this % multiplicity_E)) deallocate(this % multiplicity_E)
|
||||
end subroutine reaction_clear
|
||||
|
||||
!===============================================================================
|
||||
! NUCLIDE_CLEAR resets and deallocates data in Nuclide.
|
||||
!===============================================================================
|
||||
|
||||
subroutine nuclide_clear(this)
|
||||
class(Nuclide), intent(inout) :: this
|
||||
|
||||
integer :: i ! Loop counter
|
||||
|
||||
if (associated(this % urr_data)) deallocate(this % urr_data)
|
||||
|
||||
if (this % mp_present) then
|
||||
deallocate(this % multipole)
|
||||
end if
|
||||
|
||||
if (allocated(this % reactions)) then
|
||||
do i = 1, size(this % reactions)
|
||||
call this % reactions(i) % clear()
|
||||
end do
|
||||
end if
|
||||
|
||||
call this % reaction_index % clear()
|
||||
|
||||
if (associated(this % multipole)) deallocate(this % multipole)
|
||||
|
||||
end subroutine nuclide_clear
|
||||
|
||||
end module ace_header
|
||||
29
src/angleenergy_header.F90
Normal file
29
src/angleenergy_header.F90
Normal 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
|
||||
|
|
@ -14,7 +14,7 @@ module bank_header
|
|||
real(C_DOUBLE) :: wgt ! weight of bank site
|
||||
real(C_DOUBLE) :: xyz(3) ! location of bank particle
|
||||
real(C_DOUBLE) :: uvw(3) ! diretional cosines
|
||||
real(C_DOUBLE) :: E ! energy
|
||||
real(C_DOUBLE) :: E ! energy / energy group if in MG mode.
|
||||
integer(C_INT) :: delayed_group ! delayed group
|
||||
end type Bank
|
||||
|
||||
|
|
|
|||
|
|
@ -49,13 +49,13 @@ contains
|
|||
|
||||
subroutine compute_xs()
|
||||
|
||||
use constants, only: FILTER_MESH, FILTER_ENERGYIN, FILTER_ENERGYOUT, &
|
||||
FILTER_SURFACE, IN_RIGHT, OUT_RIGHT, IN_FRONT, &
|
||||
OUT_FRONT, IN_TOP, OUT_TOP, CMFD_NOACCEL, ZERO, &
|
||||
ONE, TINY_BIT
|
||||
use constants, only: FILTER_MESH, FILTER_ENERGYIN, FILTER_ENERGYOUT, &
|
||||
FILTER_SURFACE, IN_RIGHT, OUT_RIGHT, IN_FRONT, &
|
||||
OUT_FRONT, IN_TOP, OUT_TOP, CMFD_NOACCEL, ZERO, &
|
||||
ONE, TINY_BIT
|
||||
use error, only: fatal_error
|
||||
use global, only: cmfd, n_cmfd_tallies, cmfd_tallies, meshes,&
|
||||
matching_bins
|
||||
matching_bins
|
||||
use mesh, only: mesh_indices_to_bin
|
||||
use mesh_header, only: RegularMesh
|
||||
use string, only: to_str
|
||||
|
|
@ -625,10 +625,10 @@ contains
|
|||
|
||||
subroutine compute_dhat()
|
||||
|
||||
use constants, only: CMFD_NOACCEL, ZERO
|
||||
use global, only: cmfd, cmfd_coremap, dhat_reset
|
||||
use output, only: write_message
|
||||
use string, only: to_str
|
||||
use constants, only: CMFD_NOACCEL, ZERO
|
||||
use global, only: cmfd, cmfd_coremap, dhat_reset
|
||||
use output, only: write_message
|
||||
use string, only: to_str
|
||||
|
||||
integer :: nx ! maximum number of cells in x direction
|
||||
integer :: ny ! maximum number of cells in y direction
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ contains
|
|||
|
||||
subroutine calc_fission_source()
|
||||
|
||||
use constants, only: CMFD_NOACCEL, ZERO, TWO
|
||||
use global, only: cmfd, cmfd_coremap, master, entropy_on, current_batch
|
||||
use string, only: to_str
|
||||
use constants, only: CMFD_NOACCEL, ZERO, TWO
|
||||
use global, only: cmfd, cmfd_coremap, master, entropy_on, current_batch
|
||||
use string, only: to_str
|
||||
|
||||
#ifdef MPI
|
||||
use global, only: mpi_err
|
||||
|
|
|
|||
|
|
@ -45,14 +45,14 @@ contains
|
|||
subroutine read_cmfd_xml()
|
||||
|
||||
use constants, only: ZERO, ONE
|
||||
use error, only: fatal_error, warning
|
||||
use error, only: fatal_error, warning
|
||||
use global
|
||||
use output, only: write_message
|
||||
use string, only: to_lower
|
||||
use output, only: write_message
|
||||
use string, only: to_lower
|
||||
use xml_interface
|
||||
use, intrinsic :: ISO_FORTRAN_ENV
|
||||
|
||||
integer :: i
|
||||
integer :: i, g
|
||||
integer :: ng
|
||||
integer :: n_params
|
||||
integer, allocatable :: iarray(:)
|
||||
|
|
@ -70,7 +70,7 @@ contains
|
|||
inquire(FILE=filename, EXIST=file_exists)
|
||||
if (.not. file_exists) then
|
||||
! CMFD is optional unless it is in on from settings
|
||||
if (cmfd_on) then
|
||||
if (cmfd_run) then
|
||||
call fatal_error("No CMFD XML file, '" // trim(filename) // "' does not&
|
||||
& exist!")
|
||||
end if
|
||||
|
|
@ -102,6 +102,23 @@ contains
|
|||
if(.not.allocated(cmfd%egrid)) allocate(cmfd%egrid(ng))
|
||||
call get_node_array(node_mesh, "energy", cmfd%egrid)
|
||||
cmfd % indices(4) = ng - 1 ! sets energy group dimension
|
||||
! If using MG mode, check to see if these egrid points at least match
|
||||
! the MG Data breakpoints
|
||||
if (.not. run_CE) then
|
||||
do i = 1, ng
|
||||
found = .false.
|
||||
do g = 1, energy_groups + 1
|
||||
if (cmfd % egrid(i) == energy_bins(g)) then
|
||||
found = .true.
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
if (.not. found) then
|
||||
call fatal_error("CMFD energy mesh boundaries must align with&
|
||||
& boundaries of multi-group data!")
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
else
|
||||
if(.not.allocated(cmfd % egrid)) allocate(cmfd % egrid(2))
|
||||
cmfd % egrid = [ ZERO, 20.0_8 ]
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ module constants
|
|||
integer, parameter :: VERSION_RELEASE = 1
|
||||
|
||||
! Revision numbers for binary files
|
||||
integer, parameter :: REVISION_STATEPOINT = 14
|
||||
integer, parameter :: REVISION_STATEPOINT = 15
|
||||
integer, parameter :: REVISION_PARTICLE_RESTART = 1
|
||||
integer, parameter :: REVISION_TRACK = 1
|
||||
integer, parameter :: REVISION_SUMMARY = 2
|
||||
integer, parameter :: REVISION_SUMMARY = 3
|
||||
|
||||
! ============================================================================
|
||||
! ADJUSTABLE PARAMETERS
|
||||
|
|
@ -163,9 +163,14 @@ module constants
|
|||
|
||||
! Angular distribution type
|
||||
integer, parameter :: &
|
||||
ANGLE_ISOTROPIC = 1, & ! Isotropic angular distribution
|
||||
ANGLE_32_EQUI = 2, & ! 32 equiprobable bins
|
||||
ANGLE_TABULAR = 3 ! Tabular angular distribution
|
||||
ANGLE_ISOTROPIC = 1, & ! Isotropic angular distribution (CE)
|
||||
ANGLE_32_EQUI = 2, & ! 32 equiprobable bins (CE)
|
||||
ANGLE_TABULAR = 3, & ! Tabular angular distribution (CE or MG)
|
||||
ANGLE_LEGENDRE = 4, & ! Legendre angular distribution (MG)
|
||||
ANGLE_HISTOGRAM = 5 ! Histogram angular distribution (MG)
|
||||
|
||||
! Number of mu bins to use when converting Legendres to tabular type
|
||||
integer, parameter :: DEFAULT_NMU = 33
|
||||
|
||||
! Secondary energy mode for S(a,b) inelastic scattering
|
||||
integer, parameter :: &
|
||||
|
|
@ -209,12 +214,23 @@ module constants
|
|||
ACE_THERMAL = 2, & ! thermal S(a,b) scattering data
|
||||
ACE_DOSIMETRY = 3 ! dosimetry cross sections
|
||||
|
||||
! MGXS Table Types
|
||||
integer, parameter :: &
|
||||
MGXS_ISOTROPIC = 1, & ! Isotropically Weighted Data
|
||||
MGXS_ANGLE = 2 ! Data by Angular Bins
|
||||
|
||||
! Fission neutron emission (nu) type
|
||||
integer, parameter :: &
|
||||
NU_NONE = 0, & ! No nu values (non-fissionable)
|
||||
NU_POLYNOMIAL = 1, & ! Nu values given by polynomial
|
||||
NU_TABULAR = 2 ! Nu values given by tabular distribution
|
||||
|
||||
! Secondary particle emission type
|
||||
integer, parameter :: &
|
||||
EMISSION_PROMPT = 1, & ! Prompt emission of secondary particle
|
||||
EMISSION_DELAYED = 2, & ! Delayed emission of secondary particle
|
||||
EMISSION_TOTAL = 3 ! Yield represents total emission (prompt + delayed)
|
||||
|
||||
! Cross section filetypes
|
||||
integer, parameter :: &
|
||||
ASCII = 1, & ! ASCII cross section file
|
||||
|
|
@ -363,10 +379,11 @@ module constants
|
|||
! ============================================================================
|
||||
! RANDOM NUMBER STREAM CONSTANTS
|
||||
|
||||
integer, parameter :: N_STREAMS = 3
|
||||
integer, parameter :: STREAM_TRACKING = 1
|
||||
integer, parameter :: STREAM_TALLIES = 2
|
||||
integer, parameter :: STREAM_SOURCE = 3
|
||||
integer, parameter :: N_STREAMS = 4
|
||||
integer, parameter :: STREAM_TRACKING = 1
|
||||
integer, parameter :: STREAM_TALLIES = 2
|
||||
integer, parameter :: STREAM_SOURCE = 3
|
||||
integer, parameter :: STREAM_URR_PTABLE = 4
|
||||
|
||||
! ============================================================================
|
||||
! MISCELLANEOUS CONSTANTS
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue