mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 14:15:42 -04:00
Merge remote-tracking branch 'upstream/develop' into pyapi_filters
This commit is contained in:
commit
57442f8ca6
268 changed files with 28528 additions and 18830 deletions
|
|
@ -42,7 +42,7 @@ install: true
|
|||
|
||||
before_script:
|
||||
- if [[ ! -e $HOME/nndc_hdf5/cross_sections.xml ]]; then
|
||||
wget https://anl.box.com/shared/static/6pwyfjnufam0sb96kqwwrve6vdn8m7u4.xz -O - | tar -C $HOME -xvJ;
|
||||
wget https://anl.box.com/shared/static/68b2yhu8e6mx1f6hnbzz9mxsgg42d9ls.xz -O - | tar -C $HOME -xvJ;
|
||||
fi
|
||||
- export OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml
|
||||
|
||||
|
|
|
|||
BIN
data/fission_Q_data_endfb71.h5
Normal file
BIN
data/fission_Q_data_endfb71.h5
Normal file
Binary file not shown.
|
|
@ -151,5 +151,6 @@ if not response or response.lower().startswith('y'):
|
|||
env = os.environ.copy()
|
||||
env['PYTHONPATH'] = os.path.join(cwd, '..')
|
||||
|
||||
subprocess.call(['../scripts/openmc-ace-to-hdf5', '-d', 'nndc_hdf5']
|
||||
subprocess.call(['../scripts/openmc-ace-to-hdf5', '-d', 'nndc_hdf5',
|
||||
'--fission_energy_release', 'fission_Q_data_endfb71.h5']
|
||||
+ ace_files, env=env)
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.8 KiB |
BIN
docs/source/_images/openmc_logo.png
Normal file
BIN
docs/source/_images/openmc_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
60
docs/source/_images/openmc_logo.svg
Normal file
60
docs/source/_images/openmc_logo.svg
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="257.157px" height="60px" viewBox="0 0 257.157 60" enable-background="new 0 0 257.157 60" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path fill="#656565" d="M81.676,48.809c-2.67,0-5.128-0.469-7.367-1.409c-2.243-0.937-4.179-2.206-5.81-3.805
|
||||
c-1.632-1.601-2.901-3.477-3.807-5.638c-0.908-2.158-1.36-4.475-1.36-6.947v-0.099c0-2.472,0.46-4.788,1.385-6.945
|
||||
c0.922-2.159,2.199-4.057,3.832-5.688c1.63-1.631,3.576-2.916,5.834-3.856s4.724-1.409,7.392-1.409
|
||||
c2.67,0,5.125,0.469,7.367,1.409s4.179,2.21,5.812,3.808c1.63,1.6,2.899,3.479,3.805,5.637c0.907,2.161,1.361,4.476,1.361,6.947
|
||||
v0.099c0,2.472-0.463,4.79-1.385,6.945c-0.922,2.161-2.2,4.059-3.832,5.688c-1.63,1.629-3.576,2.918-5.834,3.854
|
||||
C86.81,48.34,84.344,48.809,81.676,48.809z M81.773,41.788c1.517,0,2.918-0.279,4.203-0.839c1.286-0.561,2.382-1.336,3.288-2.325
|
||||
c0.907-0.986,1.614-2.131,2.126-3.437c0.512-1.302,0.768-2.694,0.768-4.178v-0.099c0-1.481-0.256-2.883-0.768-4.202
|
||||
c-0.512-1.318-1.235-2.475-2.175-3.461c-0.94-0.991-2.053-1.771-3.338-2.35c-1.285-0.576-2.687-0.864-4.201-0.864
|
||||
c-1.552,0-2.958,0.279-4.228,0.838c-1.27,0.562-2.358,1.336-3.264,2.327c-0.907,0.987-1.616,2.132-2.125,3.436
|
||||
c-0.511,1.303-0.768,2.693-0.768,4.178v0.099c0,1.483,0.257,2.884,0.768,4.205c0.51,1.318,1.234,2.47,2.175,3.46
|
||||
c0.939,0.988,2.042,1.772,3.313,2.347C78.815,41.499,80.224,41.788,81.773,41.788z"/>
|
||||
<path fill="#656565" d="M102.145,21.715h7.516v3.808c0.924-1.253,2.035-2.283,3.338-3.09c1.301-0.809,2.942-1.211,4.92-1.211
|
||||
c1.549,0,3.048,0.297,4.498,0.889c1.453,0.594,2.737,1.475,3.859,2.645c1.119,1.17,2.019,2.604,2.694,4.301
|
||||
c0.675,1.7,1.013,3.652,1.013,5.859v0.1c0,2.209-0.338,4.162-1.013,5.858c-0.675,1.697-1.565,3.133-2.67,4.301
|
||||
c-1.105,1.173-2.381,2.054-3.832,2.648c-1.452,0.594-2.966,0.889-4.548,0.889c-2.012,0-3.667-0.397-4.971-1.187
|
||||
c-1.302-0.79-2.396-1.712-3.287-2.768v11.371h-7.516V21.715z M115.989,42.332c0.889,0,1.722-0.172,2.498-0.518
|
||||
c0.773-0.348,1.458-0.843,2.051-1.484c0.592-0.641,1.064-1.408,1.409-2.298c0.347-0.89,0.52-1.896,0.52-3.018v-0.1
|
||||
c0-1.087-0.173-2.082-0.52-2.99c-0.345-0.907-0.816-1.682-1.409-2.325c-0.593-0.642-1.278-1.137-2.051-1.482
|
||||
c-0.776-0.345-1.609-0.52-2.498-0.52s-1.722,0.175-2.496,0.52c-0.775,0.346-1.451,0.841-2.028,1.482
|
||||
c-0.577,0.644-1.037,1.418-1.385,2.325c-0.345,0.908-0.518,1.903-0.518,2.99v0.1c0,1.087,0.173,2.086,0.518,2.991
|
||||
c0.348,0.906,0.808,1.684,1.385,2.324c0.577,0.642,1.253,1.137,2.028,1.484C114.267,42.16,115.101,42.332,115.989,42.332z"/>
|
||||
<path fill="#656565" d="M145.62,48.809c-1.979,0-3.814-0.329-5.514-0.986c-1.696-0.661-3.162-1.6-4.399-2.816
|
||||
c-1.237-1.223-2.199-2.666-2.891-4.331c-0.693-1.661-1.041-3.517-1.041-5.559v-0.102c0-1.877,0.321-3.658,0.964-5.34
|
||||
c0.645-1.682,1.543-3.147,2.695-4.4c1.154-1.251,2.53-2.241,4.129-2.967c1.6-0.725,3.37-1.086,5.316-1.086
|
||||
c2.206,0,4.118,0.394,5.733,1.187c1.617,0.791,2.958,1.854,4.03,3.188c1.072,1.335,1.864,2.867,2.374,4.598
|
||||
c0.511,1.731,0.766,3.537,0.766,5.417c0,0.295-0.008,0.61-0.024,0.938c-0.016,0.331-0.04,0.676-0.074,1.038h-18.442
|
||||
c0.364,1.716,1.112,3.009,2.25,3.881c1.138,0.873,2.547,1.313,4.228,1.313c1.251,0,2.373-0.216,3.363-0.646
|
||||
c0.988-0.427,2.01-1.118,3.064-2.076l4.304,3.81c-1.254,1.547-2.771,2.76-4.552,3.633C150.12,48.372,148.026,48.809,145.62,48.809
|
||||
z M150.464,32.89c-0.227-1.681-0.821-3.042-1.777-4.08c-0.956-1.037-2.226-1.557-3.807-1.557c-1.582,0-2.861,0.512-3.832,1.53
|
||||
c-0.973,1.023-1.607,2.393-1.904,4.106H150.464z"/>
|
||||
<path fill="#656565" d="M159.415,21.715h7.518v3.787c0.428-0.562,0.896-1.103,1.408-1.616c0.512-0.517,1.08-0.971,1.706-1.371
|
||||
c0.625-0.397,1.318-0.712,2.078-0.946c0.757-0.233,1.613-0.347,2.569-0.347c2.867,0,5.084,0.873,6.65,2.619
|
||||
c1.565,1.746,2.35,4.152,2.35,7.219v17.156h-7.518V33.471c0-1.776-0.395-3.118-1.186-4.021c-0.792-0.903-1.913-1.355-3.361-1.355
|
||||
c-1.451,0-2.598,0.452-3.437,1.355c-0.841,0.903-1.261,2.245-1.261,4.021v14.745h-7.518V21.715z"/>
|
||||
<path fill="#A31F34" d="M187.056,13.607h8.209l9.095,14.634l9.099-14.634h8.207v34.608h-7.513V25.619l-9.742,14.784h-0.196
|
||||
l-9.645-14.634v22.446h-7.514V13.607z"/>
|
||||
<path fill="#A31F34" d="M242.235,48.809c-2.537,0-4.893-0.459-7.071-1.385c-2.175-0.922-4.052-2.18-5.636-3.78
|
||||
c-1.583-1.601-2.819-3.485-3.709-5.66c-0.888-2.179-1.333-4.501-1.333-6.974v-0.099c0-2.472,0.445-4.788,1.333-6.945
|
||||
c0.89-2.159,2.126-4.057,3.709-5.688c1.584-1.631,3.477-2.916,5.687-3.856c2.207-0.94,4.647-1.409,7.317-1.409
|
||||
c1.613,0,3.091,0.132,4.425,0.396c1.336,0.265,2.547,0.625,3.635,1.089c1.088,0.461,2.093,1.021,3.016,1.682
|
||||
c0.923,0.657,1.781,1.385,2.57,2.175l-4.846,5.586c-1.349-1.218-2.728-2.175-4.128-2.866c-1.401-0.692-2.974-1.038-4.721-1.038
|
||||
c-1.453,0-2.793,0.279-4.03,0.838c-1.235,0.562-2.299,1.336-3.188,2.327c-0.89,0.987-1.582,2.132-2.077,3.436
|
||||
c-0.494,1.303-0.742,2.693-0.742,4.178v0.099c0,1.483,0.248,2.884,0.742,4.205c0.495,1.318,1.178,2.47,2.053,3.46
|
||||
c0.872,0.988,1.929,1.772,3.164,2.347c1.235,0.576,2.594,0.865,4.079,0.865c1.979,0,3.65-0.362,5.02-1.086
|
||||
c1.366-0.726,2.726-1.714,4.079-2.968l4.844,4.895c-0.89,0.958-1.814,1.814-2.768,2.571c-0.958,0.758-2.003,1.411-3.142,1.953
|
||||
c-1.136,0.544-2.379,0.959-3.733,1.236C245.433,48.669,243.915,48.809,242.235,48.809z"/>
|
||||
</g>
|
||||
<path fill="#A31F34" d="M30.614,1.024c-10.866,0-20.325,5.991-25.284,14.845h20.495l13.167,22.804l-11.609,20.11
|
||||
c1.062,0.119,2.139,0.192,3.231,0.192c16.003,0,28.975-12.972,28.975-28.976C59.589,13.997,46.617,1.024,30.614,1.024z"/>
|
||||
<path fill="#656565" d="M16.416,55.243h5.807l9.567-16.57l-9.567-16.566H3.093l-0.607,1.052C1.954,25.355,1.639,27.638,1.639,30
|
||||
C1.639,40.84,7.603,50.274,16.416,55.243z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
|
|
@ -16,3 +16,7 @@
|
|||
.wy-table, .rst-content table.docutils, .rst-content table.field-list {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.wy-side-nav-search {
|
||||
background-color: #343131;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ MOCK_MODULES = ['numpy', 'numpy.polynomial', 'numpy.polynomial.polynomial',
|
|||
'h5py', 'pandas', 'opencg']
|
||||
sys.modules.update((mod_name, MagicMock()) for mod_name in MOCK_MODULES)
|
||||
|
||||
import numpy as np
|
||||
np.polynomial.Polynomial = MagicMock
|
||||
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
|
|
@ -126,7 +129,7 @@ if not on_rtd:
|
|||
html_theme = 'sphinx_rtd_theme'
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
||||
|
||||
html_logo = '_images/openmc200px.png'
|
||||
html_logo = '_images/openmc_logo.png'
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
|
|
|
|||
53
docs/source/io_formats/fission_energy.rst
Normal file
53
docs/source/io_formats/fission_energy.rst
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
.. _usersguide_fission_energy:
|
||||
|
||||
==================================
|
||||
Fission Energy Release File Format
|
||||
==================================
|
||||
|
||||
This file is a compact HDF5 representation of the ENDF MT=1, MF=458 data (see
|
||||
ENDF-102_ for details). It gives the information needed to compute the energy
|
||||
carried away from fission reactions by each reaction product (e.g. fragment
|
||||
nuclei, neutrons) which depends on the incident neutron energy. OpenMC is
|
||||
distributed with one of these files under
|
||||
data/fission_Q_data_endfb71.h5. More files of this format can be created from
|
||||
ENDF files with the
|
||||
``openmc.data.write_compact_458_library`` function. They can be read with the
|
||||
``openmc.data.FissionEnergyRelease.from_compact_hdf5`` class method.
|
||||
|
||||
:Attributes: - **comment** (*char[]*) -- An optional text comment
|
||||
- **component order** (*char[][]*) -- An array of strings
|
||||
specifying the order each reaction product occurs in the data
|
||||
arrays. The components use the 2-3 letter abbreviations
|
||||
specified in ENDF-102 e.g. EFR for fission fragments and ENP for
|
||||
prompt neutrons.
|
||||
|
||||
**/<nuclide name>/**
|
||||
Nuclides are named by concatenating their atomic symbol and mass number. For
|
||||
example, 'U235' or 'Pu239'. Metastable nuclides are appended with an
|
||||
'_m' and their metastable number. For example, 'Am242_m1'
|
||||
|
||||
:Datasets:
|
||||
- **data** (*double[][][]*) -- The energy release coefficients. The
|
||||
first axis indexes the component type. The second axis specifies
|
||||
values or uncertainties. The third axis indexes the polynomial
|
||||
order. If the data uses the Sher-Beck format, then the last axis
|
||||
will have a length of one and ENDF-102 should be consulted for
|
||||
energy dependence. Otherwise, the data uses the Madland format
|
||||
which is a polynomial of incident energy.
|
||||
|
||||
For example, if 'EFR' is given first in the **component order**
|
||||
attribute and the data uses the Madland format, then the energy
|
||||
released in the form of fission fragments at an incident energy
|
||||
:math:`E` is given by
|
||||
|
||||
.. math::
|
||||
\text{data}[0, 0, 0] + \text{data}[0, 0, 1] \cdot E
|
||||
+ \text{data}[0, 0, 2] \cdot E^2 + \ldots
|
||||
|
||||
And its uncertainty is
|
||||
|
||||
.. math::
|
||||
\text{data}[0, 1, 0] + \text{data}[0, 1, 1] \cdot E
|
||||
+ \text{data}[0, 1, 2] \cdot E^2 + \ldots
|
||||
|
||||
.. _ENDF-102: http://www.nndc.bnl.gov/endfdocs/ENDF-102-2012.pdf
|
||||
|
|
@ -15,6 +15,7 @@ Data Files
|
|||
nuclear_data
|
||||
mgxs_library
|
||||
data_wmp
|
||||
fission_energy
|
||||
|
||||
------------
|
||||
Output Files
|
||||
|
|
@ -30,3 +31,4 @@ Output Files
|
|||
particle_restart
|
||||
track
|
||||
voxel
|
||||
volume
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
.. _usersguide_nuclear_data:
|
||||
.. _io_nuclear_data:
|
||||
|
||||
========================
|
||||
Nuclear Data File Format
|
||||
|
|
@ -16,29 +16,53 @@ Incident Neutron Data
|
|||
- **metastable** (*int*) -- Metastable state (0=ground, 1=first
|
||||
excited, etc.)
|
||||
- **atomic_weight_ratio** (*double*) -- Mass in units of neutron masses
|
||||
- **temperature** (*double*) -- Temperature in MeV
|
||||
- **n_reaction** (*int*) -- Number of reactions
|
||||
|
||||
:Datasets: - **energy** (*double[]*) -- Energy points at which cross sections are tabulated
|
||||
|
||||
**/<nuclide name>/kTs/**
|
||||
|
||||
<TTT>K is the temperature in Kelvin, rounded to the nearest integer, of the
|
||||
temperature-dependent data set. For example, the data set corresponding to
|
||||
300 Kelvin would be located at `300K`.
|
||||
|
||||
:Datasets:
|
||||
- **<TTT>K** (*double*) -- kT values (in MeV) for each Temperature
|
||||
TTT (in Kelvin)
|
||||
|
||||
**/<nuclide name>/reactions/reaction_<mt>/**
|
||||
|
||||
:Attributes: - **mt** (*int*) -- ENDF MT reaction number
|
||||
- **label** (*char[]*) -- Name of the reaction
|
||||
- **Q_value** (*double*) -- Q value in MeV
|
||||
- **threshold_idx** (*int*) -- Index on the energy grid that the
|
||||
reaction threshold corresponds to
|
||||
- **center_of_mass** (*int*) -- Whether the reference frame for
|
||||
scattering is center-of-mass (1) or laboratory (0)
|
||||
- **n_product** (*int*) -- Number of reaction products
|
||||
|
||||
:Datasets: - **xs** (*double[]*) -- Cross section values tabulated against the nuclide energy grid
|
||||
**/<nuclide name>/reactions/reaction_<mt>/<TTT>K/**
|
||||
|
||||
<TTT>K is the temperature in Kelvin, rounded to the nearest integer, of the
|
||||
temperature-dependent data set. For example, the data set corresponding to
|
||||
300 Kelvin would be located at `300K`.
|
||||
|
||||
:Datasets:
|
||||
- **xs** (*double[]*) -- Cross section values tabulated against the
|
||||
nuclide energy grid for temperature TTT (in Kelvin)
|
||||
|
||||
:Attributes:
|
||||
- **threshold_idx** (*int*) -- Index on the energy
|
||||
grid that the reaction threshold corresponds to for
|
||||
temperature TTT (in Kelvin)
|
||||
|
||||
**/<nuclide name>/reactions/reaction_<mt>/product_<j>/**
|
||||
|
||||
Reaction product data is described in :ref:`product`.
|
||||
|
||||
**/<nuclide name>/urr**
|
||||
**/<nuclide name>/urr/<TTT>K/**
|
||||
|
||||
<TTT>K is the temperature in Kelvin, rounded to the nearest integer, of the
|
||||
temperature-dependent data set. For example, the data set corresponding to
|
||||
300 Kelvin would be located at `300K`.
|
||||
|
||||
:Attributes: - **interpolation** (*int*) -- interpolation scheme
|
||||
- **inelastic** (*int*) -- flag indicating inelastic scattering
|
||||
|
|
@ -55,6 +79,36 @@ Incident Neutron Data
|
|||
from fission. It is formatted as a reaction product, described in
|
||||
:ref:`product`.
|
||||
|
||||
**/<nuclide name>/fission_energy_release/**
|
||||
|
||||
:Datasets: - **fragments** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
released in the form of fragments as a function of incident
|
||||
neutron energy.
|
||||
- **prompt_neutrons** (:ref:`polynomial <1d_polynomial>` or
|
||||
:ref:`tabulated <1d_tabulated>`) -- Energy released in the form of
|
||||
prompt neutrons as a function of incident neutron energy.
|
||||
- **delayed_neutrons** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
released in the form of delayed neutrons as a function of incident
|
||||
neutron energy.
|
||||
- **prompt_photons** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
released in the form of prompt photons as a function of incident
|
||||
neutron energy.
|
||||
- **delayed_photons** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
released in the form of delayed photons as a function of incident
|
||||
neutron energy.
|
||||
- **betas** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
released in the form of betas as a function of incident
|
||||
neutron energy.
|
||||
- **neutrinos** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
released in the form of neutrinos as a function of incident
|
||||
neutron energy.
|
||||
- **q_prompt** (:ref:`polynomial <1d_polynomial>` or
|
||||
:ref:`tabulated <1d_tabulated>`) -- The prompt fission Q-value
|
||||
(fragments + prompt neutrons + prompt photons - incident energy)
|
||||
- **q_recoverable** (:ref:`polynomial <1d_polynomial>` or
|
||||
:ref:`tabulated <1d_tabulated>`) -- The recoverable fission Q-value
|
||||
(Q_prompt + delayed neutrons + delayed photons + betas)
|
||||
|
||||
-------------------------------
|
||||
Thermal Neutron Scattering Data
|
||||
-------------------------------
|
||||
|
|
@ -62,32 +116,48 @@ Thermal Neutron Scattering Data
|
|||
**/<thermal name>/**
|
||||
|
||||
:Attributes: - **atomic_weight_ratio** (*double*) -- Mass in units of neutron masses
|
||||
- **temperature** (*double*) -- Temperature in MeV
|
||||
- **zaids** (*int[]*) -- ZAID identifiers for which the thermal
|
||||
- **nuclides** (*char[][]*) -- Names of nuclides for which the thermal
|
||||
scattering data applies to
|
||||
|
||||
**/<thermal name>/elastic/**
|
||||
|
||||
:Datasets: - **xs** (:ref:`tabulated <1d_tabulated>`) -- Thermal inelastic
|
||||
scattering cross section
|
||||
- **mu_out** (*double[][]*) -- Distribution of outgoing energies
|
||||
and angles for coherent elastic scattering
|
||||
|
||||
**/<thermal name>/inelastic/**
|
||||
|
||||
:Attributes:
|
||||
- **secondary_mode** (*char[]*) -- Indicates how the inelastic
|
||||
outgoing angle-energy distributions are represented ('equal',
|
||||
'skewed', or 'continuous').
|
||||
|
||||
**/<thermal name>/kTs/**
|
||||
|
||||
<TTT>K is the temperature in Kelvin, rounded to the nearest integer, of the
|
||||
temperature-dependent data set. For example, the data set corresponding to
|
||||
300 Kelvin would be located at `300K`.
|
||||
|
||||
:Datasets:
|
||||
- **<TTT>K** (*double*) -- kT values (in MeV) for each Temperature
|
||||
TTT (in Kelvin)
|
||||
|
||||
**/<thermal name>/elastic/<TTT>K/**
|
||||
|
||||
<TTT>K is the temperature in Kelvin, rounded to the nearest integer, of the
|
||||
temperature-dependent data set. For example, the data set corresponding to
|
||||
300 Kelvin would be located at `300K`.
|
||||
|
||||
:Datasets: - **xs** (:ref:`tabulated <1d_tabulated>`) -- Thermal inelastic
|
||||
scattering cross section
|
||||
scattering cross section for temperature TTT (in Kelvin)
|
||||
- **mu_out** (*double[][]*) -- Distribution of outgoing energies
|
||||
and angles for coherent elastic scattering for temperature TTT
|
||||
(in Kelvin)
|
||||
|
||||
**/<thermal name>/inelastic/<TTT>K/**
|
||||
|
||||
<TTT>K is the temperature in Kelvin, rounded to the nearest integer, of the
|
||||
temperature-dependent data set. For example, the data set corresponding to
|
||||
300 Kelvin would be located at `300K`.
|
||||
|
||||
:Datasets: - **xs** (:ref:`tabulated <1d_tabulated>`) -- Thermal inelastic
|
||||
scattering cross section for temperature TTT (in Kelvin)
|
||||
- **energy_out** (*double[][]*) -- Distribution of outgoing
|
||||
energies for each incoming energy. Only present if secondary mode
|
||||
is not continuous.
|
||||
energies for each incoming energy for temperature TTT (in Kelvin).
|
||||
Only present if secondary mode is not continuous.
|
||||
- **mu_out** (*double[][][]*) -- Distribution of scattering cosines
|
||||
for each pair of incoming and outgoing energies. Only present if
|
||||
secondary mode is not continuous.
|
||||
for each pair of incoming and outgoing energies. for temperature
|
||||
TTT (in Kelvin). Only present if secondary mode is not continuous.
|
||||
|
||||
If the secondary mode is continuous, the outgoing energy-angle distribution is
|
||||
given as a :ref:`correlated angle-energy distribution
|
||||
|
|
@ -142,17 +212,19 @@ Tabulated
|
|||
:Object type: Dataset
|
||||
:Datatype: *double[2][]*
|
||||
:Description: x-values are listed first followed by corresponding y-values
|
||||
:Attributes: - **type** (*char[]*) -- 'tabulated'
|
||||
:Attributes: - **type** (*char[]*) -- 'Tabulated1D'
|
||||
- **breakpoints** (*int[]*) -- Region breakpoints
|
||||
- **interpolation** (*int[]*) -- Region interpolation codes
|
||||
|
||||
.. _1d_polynomial:
|
||||
|
||||
Polynomial
|
||||
----------
|
||||
|
||||
:Object type: Dataset
|
||||
:Datatype: *double[]*
|
||||
:Description: Polynomial coefficients listed in order of increasing power
|
||||
:Attributes: - **type** (*char[]*) -- 'polynomial'
|
||||
:Attributes: - **type** (*char[]*) -- 'Polynomial'
|
||||
|
||||
Coherent elastic scattering
|
||||
---------------------------
|
||||
|
|
|
|||
22
docs/source/io_formats/volume.rst
Normal file
22
docs/source/io_formats/volume.rst
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
.. _io_volume:
|
||||
|
||||
==================
|
||||
Volume File Format
|
||||
==================
|
||||
|
||||
**/**
|
||||
|
||||
:Attributes: - **samples** (*int*) -- Number of samples
|
||||
- **lower_left** (*double[3]*) -- Lower-left coordinates of
|
||||
bounding box
|
||||
- **upper_right** (*double[3]*) -- Upper-right coordinates of
|
||||
bounding box
|
||||
|
||||
**/cell_<id>/**
|
||||
|
||||
:Datasets: - **volume** (*double[2]*) -- Calculated volume and its uncertainty
|
||||
in cubic centimeters
|
||||
- **nuclides** (*char[][]*) -- Names of nuclides identified in the
|
||||
cell
|
||||
- **atoms** (*double[][2]*) -- Total number of atoms of each nuclide
|
||||
and its uncertainty
|
||||
|
|
@ -53,12 +53,12 @@ speed up the calculation.
|
|||
Logarithmic Mapping
|
||||
+++++++++++++++++++
|
||||
|
||||
To speed up energy grid searches, OpenMC uses logarithmic mapping technique
|
||||
[Brown]_ to limit the range of energies that must be searched for each
|
||||
nuclide. The entire energy range is divided up into equal-lethargy segments, and
|
||||
the bounding energies of each segment are mapped to bounding indices on each of
|
||||
the nuclide energy grids. By default, OpenMC uses 8000 equal-lethargy segments
|
||||
as recommended by Brown.
|
||||
To speed up energy grid searches, OpenMC uses a `logarithmic mapping technique`_
|
||||
to limit the range of energies that must be searched for each nuclide. The
|
||||
entire energy range is divided up into equal-lethargy segments, and the bounding
|
||||
energies of each segment are mapped to bounding indices on each of the nuclide
|
||||
energy grids. By default, OpenMC uses 8000 equal-lethargy segments as
|
||||
recommended by Brown.
|
||||
|
||||
Other Methods
|
||||
+++++++++++++
|
||||
|
|
@ -74,9 +74,9 @@ 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
|
||||
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:
|
||||
|
||||
|
|
@ -232,21 +232,10 @@ sections. This allows flexibility for the model to use highly anisotropic
|
|||
scattering information in the water while the fuel can be simulated with linear
|
||||
or even isotropic scattering.
|
||||
|
||||
.. only:: html
|
||||
|
||||
.. rubric:: References
|
||||
|
||||
.. [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
|
||||
|
||||
.. _logarithmic mapping technique:
|
||||
https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf
|
||||
.. _Hwang: http://www.ans.org/pubs/journals/nse/a_16381
|
||||
.. _Josey: 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
|
||||
|
|
|
|||
BIN
docs/source/pythonapi/examples/images/mdgxs.png
Normal file
BIN
docs/source/pythonapi/examples/images/mdgxs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
1379
docs/source/pythonapi/examples/mdgxs-part-i.ipynb
Normal file
1379
docs/source/pythonapi/examples/mdgxs-part-i.ipynb
Normal file
File diff suppressed because one or more lines are too long
13
docs/source/pythonapi/examples/mdgxs-part-i.rst
Normal file
13
docs/source/pythonapi/examples/mdgxs-part-i.rst
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
.. _notebook_mdgxs_part_i:
|
||||
|
||||
==========================
|
||||
MDGXS Part I: Introduction
|
||||
==========================
|
||||
|
||||
.. only:: html
|
||||
|
||||
.. notebook:: mdgxs-part-i.ipynb
|
||||
|
||||
.. only:: latex
|
||||
|
||||
IPython notebooks must be viewed in the online HTML documentation.
|
||||
1328
docs/source/pythonapi/examples/mdgxs-part-ii.ipynb
Normal file
1328
docs/source/pythonapi/examples/mdgxs-part-ii.ipynb
Normal file
File diff suppressed because one or more lines are too long
13
docs/source/pythonapi/examples/mdgxs-part-ii.rst
Normal file
13
docs/source/pythonapi/examples/mdgxs-part-ii.rst
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
.. _notebook_mdgxs_part_ii:
|
||||
|
||||
================================
|
||||
MDGXS Part II: Advanced Features
|
||||
================================
|
||||
|
||||
.. only:: html
|
||||
|
||||
.. notebook:: mdgxs-part-ii.ipynb
|
||||
|
||||
.. only:: latex
|
||||
|
||||
IPython notebooks must be viewed in the online HTML documentation.
|
||||
|
|
@ -214,7 +214,6 @@
|
|||
"source": [
|
||||
"# Instantiate a Materials collection and export to XML\n",
|
||||
"materials_file = openmc.Materials([inf_medium])\n",
|
||||
"materials_file.default_xs = '71c'\n",
|
||||
"materials_file.export_to_xml()"
|
||||
]
|
||||
},
|
||||
|
|
@ -499,23 +498,37 @@
|
|||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
" .d88888b. 888b d888 .d8888b.\n",
|
||||
" d88P\" \"Y88b 8888b d8888 d88P Y88b\n",
|
||||
" 888 888 88888b.d88888 888 888\n",
|
||||
" 888 888 88888b. .d88b. 88888b. 888Y88888P888 888 \n",
|
||||
" 888 888 888 \"88b d8P Y8b 888 \"88b 888 Y888P 888 888 \n",
|
||||
" 888 888 888 888 88888888 888 888 888 Y8P 888 888 888\n",
|
||||
" Y88b. .d88P 888 d88P Y8b. 888 888 888 \" 888 Y88b d88P\n",
|
||||
" \"Y88888P\" 88888P\" \"Y8888 888 888 888 888 \"Y8888P\"\n",
|
||||
"__________________888______________________________________________________\n",
|
||||
" 888\n",
|
||||
" 888\n",
|
||||
" %%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ############### %%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ################## %%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ################### %%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" #################### %%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ##################### %%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ###################### %%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ####################### %%%%%%%%%%%%%%%%%%\n",
|
||||
" ####################### %%%%%%%%%%%%%%%%%\n",
|
||||
" ###################### %%%%%%%%%%%%%%%%%\n",
|
||||
" #################### %%%%%%%%%%%%%%%%%\n",
|
||||
" ################# %%%%%%%%%%%%%%%%%\n",
|
||||
" ############### %%%%%%%%%%%%%%%%\n",
|
||||
" ############ %%%%%%%%%%%%%%%\n",
|
||||
" ######## %%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%\n",
|
||||
"\n",
|
||||
" Copyright: 2011-2016 Massachusetts Institute of Technology\n",
|
||||
" License: http://openmc.readthedocs.io/en/latest/license.html\n",
|
||||
" Version: 0.7.1\n",
|
||||
" Git SHA1: 3d68c07625e33cd64188df03ee03e9c31b3d4b74\n",
|
||||
" Date/Time: 2016-07-22 21:03:18\n",
|
||||
" | The OpenMC Monte Carlo Code\n",
|
||||
" Copyright | 2011-2016 Massachusetts Institute of Technology\n",
|
||||
" License | http://openmc.readthedocs.io/en/latest/license.html\n",
|
||||
" Version | 0.8.0\n",
|
||||
" Git SHA1 | fbebf7bf709fe2fe1813af95bff9b29c0d59312c\n",
|
||||
" Date/Time | 2016-08-31 10:40:13\n",
|
||||
" OpenMP Threads | 4\n",
|
||||
"\n",
|
||||
" ===========================================================================\n",
|
||||
" ========================> INITIALIZATION <=========================\n",
|
||||
|
|
@ -525,12 +538,12 @@
|
|||
" Reading geometry XML file...\n",
|
||||
" Reading cross sections XML file...\n",
|
||||
" Reading materials XML file...\n",
|
||||
" Reading H1.71c from /home/romano/openmc/data/nndc_hdf5/H1_71c.h5\n",
|
||||
" Reading O16.71c from /home/romano/openmc/data/nndc_hdf5/O16_71c.h5\n",
|
||||
" Reading U235.71c from /home/romano/openmc/data/nndc_hdf5/U235_71c.h5\n",
|
||||
" Reading U238.71c from /home/romano/openmc/data/nndc_hdf5/U238_71c.h5\n",
|
||||
" Reading Zr90.71c from /home/romano/openmc/data/nndc_hdf5/Zr90_71c.h5\n",
|
||||
" Maximum neutron transport energy: 20.0000 MeV for H1.71c\n",
|
||||
" Reading H1 from /home/romano/openmc/data/nndc_hdf5/H1.h5\n",
|
||||
" Reading O16 from /home/romano/openmc/data/nndc_hdf5/O16.h5\n",
|
||||
" Reading U235 from /home/romano/openmc/data/nndc_hdf5/U235.h5\n",
|
||||
" Reading U238 from /home/romano/openmc/data/nndc_hdf5/U238.h5\n",
|
||||
" Reading Zr90 from /home/romano/openmc/data/nndc_hdf5/Zr90.h5\n",
|
||||
" Maximum neutron transport energy: 20.0000 MeV for H1\n",
|
||||
" Reading tallies XML file...\n",
|
||||
" Building neighboring cells lists for each surface...\n",
|
||||
" Initializing source particles...\n",
|
||||
|
|
@ -600,20 +613,20 @@
|
|||
"\n",
|
||||
" =======================> TIMING STATISTICS <=======================\n",
|
||||
"\n",
|
||||
" Total time for initialization = 3.2300E-01 seconds\n",
|
||||
" Reading cross sections = 1.6900E-01 seconds\n",
|
||||
" Total time in simulation = 1.9882E+01 seconds\n",
|
||||
" Time in transport only = 1.9869E+01 seconds\n",
|
||||
" Time in inactive batches = 2.6590E+00 seconds\n",
|
||||
" Time in active batches = 1.7223E+01 seconds\n",
|
||||
" Total time for initialization = 3.9900E-01 seconds\n",
|
||||
" Reading cross sections = 2.6500E-01 seconds\n",
|
||||
" Total time in simulation = 1.1488E+01 seconds\n",
|
||||
" Time in transport only = 1.1152E+01 seconds\n",
|
||||
" Time in inactive batches = 1.2180E+00 seconds\n",
|
||||
" Time in active batches = 1.0270E+01 seconds\n",
|
||||
" Time synchronizing fission bank = 4.0000E-03 seconds\n",
|
||||
" Sampling source sites = 4.0000E-03 seconds\n",
|
||||
" SEND/RECV source sites = 0.0000E+00 seconds\n",
|
||||
" Sampling source sites = 3.0000E-03 seconds\n",
|
||||
" SEND/RECV source sites = 1.0000E-03 seconds\n",
|
||||
" Time accumulating tallies = 0.0000E+00 seconds\n",
|
||||
" Total time for finalization = 0.0000E+00 seconds\n",
|
||||
" Total time elapsed = 2.0217E+01 seconds\n",
|
||||
" Calculation Rate (inactive) = 9402.03 neutrons/second\n",
|
||||
" Calculation Rate (active) = 5806.19 neutrons/second\n",
|
||||
" Total time for finalization = 1.0000E-03 seconds\n",
|
||||
" Total time elapsed = 1.1901E+01 seconds\n",
|
||||
" Calculation Rate (inactive) = 20525.5 neutrons/second\n",
|
||||
" Calculation Rate (active) = 9737.10 neutrons/second\n",
|
||||
"\n",
|
||||
" ============================> RESULTS <============================\n",
|
||||
"\n",
|
||||
|
|
@ -894,7 +907,7 @@
|
|||
" <td>6.250000e-07</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((total / flux) - (absorption / flux)) - (sca...</td>\n",
|
||||
" <td>-3.774758e-15</td>\n",
|
||||
" <td>-2.886580e-15</td>\n",
|
||||
" <td>0.011292</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
|
|
@ -904,7 +917,7 @@
|
|||
" <td>2.000000e+01</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((total / flux) - (absorption / flux)) - (sca...</td>\n",
|
||||
" <td>1.443290e-15</td>\n",
|
||||
" <td>-5.551115e-16</td>\n",
|
||||
" <td>0.002570</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
|
|
@ -917,8 +930,8 @@
|
|||
"1 1 6.25e-07 2.00e+01 total \n",
|
||||
"\n",
|
||||
" 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 "
|
||||
"0 (((total / flux) - (absorption / flux)) - (sca... -2.89e-15 1.13e-02 \n",
|
||||
"1 (((total / flux) - (absorption / flux)) - (sca... -5.55e-16 2.57e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 22,
|
||||
|
|
@ -1167,21 +1180,21 @@
|
|||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 2",
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python2"
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 2
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython2",
|
||||
"version": "2.7.11"
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.5.2"
|
||||
}
|
||||
},
|
||||
"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 one or more lines are too long
|
|
@ -105,7 +105,6 @@
|
|||
"source": [
|
||||
"# Instantiate a Materials collection\n",
|
||||
"materials_file = openmc.Materials((fuel, water, zircaloy))\n",
|
||||
"materials_file.default_xs = '71c'\n",
|
||||
"\n",
|
||||
"# Export to \"materials.xml\"\n",
|
||||
"materials_file.export_to_xml()"
|
||||
|
|
@ -136,8 +135,8 @@
|
|||
"max_x = openmc.XPlane(x0=+0.63, boundary_type='reflective')\n",
|
||||
"min_y = openmc.YPlane(y0=-0.63, boundary_type='reflective')\n",
|
||||
"max_y = openmc.YPlane(y0=+0.63, boundary_type='reflective')\n",
|
||||
"min_z = openmc.ZPlane(z0=-0.63, boundary_type='reflective')\n",
|
||||
"max_z = openmc.ZPlane(z0=+0.63, boundary_type='reflective')"
|
||||
"min_z = openmc.ZPlane(z0=-100., boundary_type='vacuum')\n",
|
||||
"max_z = openmc.ZPlane(z0=+100., boundary_type='vacuum')"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -264,7 +263,7 @@
|
|||
"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",
|
||||
"bounds = [-0.63, -0.63, -100., 0.63, 0.63, 100.]\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",
|
||||
|
|
@ -339,7 +338,7 @@
|
|||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\nAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX///9yEhLpgJFNv8Tq\nQYT7AAAAAWJLR0QAiAUdSAAAAAd0SU1FB+AHFwInLqDpadAAAALKSURBVGje7dpLcqQwDAbgHHE2\nYeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmNP+HDhw8fPnz48Kf6VH9G\n+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4zPji99z0/AJ4n1lfvJ6f\nnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6pA0wfln+ho/fwgYYn19C\n/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tNDbSGz7T0SBEWw4vLXzbQ\n6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X58wZaxWd1+fMGiuFvir8b\nvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV873hB8UnM3xzANtf8nb4\ndwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7T/ppARBvp48UwJnelT5S\nACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4//Jve+fhsH6Ctv7n8PTzj\nvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V32/o9+fl389Xnx+g5x/o\n+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6/4Le/6D3T/D9V67Y/ZsV\nQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/gPs/0P4TtP8F7r9J3AIO\n9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTuf4X7b+H+X7T/+BPuf3aM\n8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTYtMDctMjJUMjE6Mzk6\nNDYtMDU6MDBOOEOsAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE2LTA3LTIyVDIxOjM5OjQ2LTA1OjAw\nP2X7EAAAAABJRU5ErkJggg==\n",
|
||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\nAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX///9yEhLpgJFNv8Tq\nQYT7AAAAAWJLR0QAiAUdSAAAAAd0SU1FB+AJAwQmKDRX/78AAALKSURBVGje7dpLcqQwDAbgHHE2\nYeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmNP+HDhw8fPnz48Kf6VH9G\n+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4zPji99z0/AJ4n1lfvJ6f\nnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6pA0wfln+ho/fwgYYn19C\n/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tNDbSGz7T0SBEWw4vLXzbQ\n6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X58wZaxWd1+fMGiuFvir8b\nvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV873hB8UnM3xzANtf8nb4\ndwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7T/ppARBvp48UwJnelT5S\nACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4//Jve+fhsH6Ctv7n8PTzj\nvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V32/o9+fl389Xnx+g5x/o\n+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6/4Le/6D3T/D9V67Y/ZsV\nQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/gPs/0P4TtP8F7r9J3AIO\n9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTuf4X7b+H+X7T/+BPuf3aM\n8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTYtMDktMDNUMDQ6Mzg6\nNDAtMDQ6MDBo/hqzAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE2LTA5LTAzVDA0OjM4OjQwLTA0OjAw\nGaOiDwAAAABJRU5ErkJggg==\n",
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Image object>"
|
||||
]
|
||||
|
|
@ -410,7 +409,32 @@
|
|||
"tally.filters.append(energy_filter)\n",
|
||||
"tally.scores = ['absorption', 'total']\n",
|
||||
"tally.nuclides = [o16, h1]\n",
|
||||
"tallies_file.append(tally)"
|
||||
"tallies_file.append(tally)\n",
|
||||
"\n",
|
||||
"# Instantiate a tally mesh \n",
|
||||
"mesh = openmc.Mesh(mesh_id=1)\n",
|
||||
"mesh.type = 'regular'\n",
|
||||
"mesh.dimension = [1, 1, 1]\n",
|
||||
"mesh.lower_left = [-0.63, -0.63, -100.]\n",
|
||||
"mesh.width = [1.26, 1.26, 200.]\n",
|
||||
"mesh_filter = openmc.Filter(type='mesh', bins=[mesh.id])\n",
|
||||
"mesh_filter.mesh = mesh\n",
|
||||
"\n",
|
||||
"# Instantiate thermal, fast, and total leakage tallies\n",
|
||||
"leak = openmc.Tally(name='leakage')\n",
|
||||
"leak.filters = [mesh_filter]\n",
|
||||
"leak.scores = ['current']\n",
|
||||
"tallies_file.append(leak)\n",
|
||||
"\n",
|
||||
"thermal_leak = openmc.Tally(name='thermal leakage')\n",
|
||||
"thermal_leak.filters = [mesh_filter, openmc.Filter(type='energy', bins=[0., 0.625e-6])]\n",
|
||||
"thermal_leak.scores = ['current']\n",
|
||||
"tallies_file.append(thermal_leak)\n",
|
||||
"\n",
|
||||
"fast_leak = openmc.Tally(name='fast leakage')\n",
|
||||
"fast_leak.filters = [mesh_filter, openmc.Filter(type='energy', bins=[0.625e-6, 20.])]\n",
|
||||
"fast_leak.scores = ['current']\n",
|
||||
"tallies_file.append(fast_leak)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -484,12 +508,12 @@
|
|||
"outputs": [],
|
||||
"source": [
|
||||
"# Instantiate energy filter to illustrate Tally slicing\n",
|
||||
"energy_filter = openmc.Filter(type='energy', bins=np.logspace(np.log10(1e-8), np.log10(20), 10))\n",
|
||||
"fine_energy_filter = openmc.Filter(type='energy', bins=np.logspace(np.log10(1e-8), np.log10(20), 10))\n",
|
||||
"\n",
|
||||
"# Instantiate flux Tally in moderator and fuel\n",
|
||||
"tally = openmc.Tally(name='need-to-slice')\n",
|
||||
"tally.filters = [openmc.Filter(type='cell', bins=[fuel_cell.id, moderator_cell.id])]\n",
|
||||
"tally.filters.append(energy_filter)\n",
|
||||
"tally.filters.append(fine_energy_filter)\n",
|
||||
"tally.scores = ['nu-fission', 'scatter']\n",
|
||||
"tally.nuclides = [h1, u238]\n",
|
||||
"tallies_file.append(tally)"
|
||||
|
|
@ -526,24 +550,39 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"rm: cannot remove 'statepoint.*': No such file or directory\n",
|
||||
"\n",
|
||||
" .d88888b. 888b d888 .d8888b.\n",
|
||||
" d88P\" \"Y88b 8888b d8888 d88P Y88b\n",
|
||||
" 888 888 88888b.d88888 888 888\n",
|
||||
" 888 888 88888b. .d88b. 88888b. 888Y88888P888 888 \n",
|
||||
" 888 888 888 \"88b d8P Y8b 888 \"88b 888 Y888P 888 888 \n",
|
||||
" 888 888 888 888 88888888 888 888 888 Y8P 888 888 888\n",
|
||||
" Y88b. .d88P 888 d88P Y8b. 888 888 888 \" 888 Y88b d88P\n",
|
||||
" \"Y88888P\" 88888P\" \"Y8888 888 888 888 888 \"Y8888P\"\n",
|
||||
"__________________888______________________________________________________\n",
|
||||
" 888\n",
|
||||
" 888\n",
|
||||
" %%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ############### %%%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ################## %%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ################### %%%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" #################### %%%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ##################### %%%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ###################### %%%%%%%%%%%%%%%%%%%%\n",
|
||||
" ####################### %%%%%%%%%%%%%%%%%%\n",
|
||||
" ####################### %%%%%%%%%%%%%%%%%\n",
|
||||
" ###################### %%%%%%%%%%%%%%%%%\n",
|
||||
" #################### %%%%%%%%%%%%%%%%%\n",
|
||||
" ################# %%%%%%%%%%%%%%%%%\n",
|
||||
" ############### %%%%%%%%%%%%%%%%\n",
|
||||
" ############ %%%%%%%%%%%%%%%\n",
|
||||
" ######## %%%%%%%%%%%%%%\n",
|
||||
" %%%%%%%%%%%\n",
|
||||
"\n",
|
||||
" Copyright: 2011-2016 Massachusetts Institute of Technology\n",
|
||||
" License: http://openmc.readthedocs.io/en/latest/license.html\n",
|
||||
" Version: 0.7.1\n",
|
||||
" Git SHA1: 3d68c07625e33cd64188df03ee03e9c31b3d4b74\n",
|
||||
" Date/Time: 2016-07-22 21:39:46\n",
|
||||
" | The OpenMC Monte Carlo Code\n",
|
||||
" Copyright | 2011-2016 Massachusetts Institute of Technology\n",
|
||||
" License | http://openmc.readthedocs.io/en/latest/license.html\n",
|
||||
" Version | 0.8.0\n",
|
||||
" Git SHA1 | 623b705a399f16c8e5063732bc6e6a357611542d\n",
|
||||
" Date/Time | 2016-09-03 04:38:41\n",
|
||||
" OpenMP Threads | 4\n",
|
||||
"\n",
|
||||
" ===========================================================================\n",
|
||||
" ========================> INITIALIZATION <=========================\n",
|
||||
|
|
@ -553,13 +592,13 @@
|
|||
" Reading geometry XML file...\n",
|
||||
" Reading cross sections XML file...\n",
|
||||
" Reading materials XML file...\n",
|
||||
" Reading U235.71c from /home/romano/openmc/data/nndc_hdf5/U235_71c.h5\n",
|
||||
" Reading U238.71c from /home/romano/openmc/data/nndc_hdf5/U238_71c.h5\n",
|
||||
" Reading O16.71c from /home/romano/openmc/data/nndc_hdf5/O16_71c.h5\n",
|
||||
" Reading H1.71c from /home/romano/openmc/data/nndc_hdf5/H1_71c.h5\n",
|
||||
" Reading B10.71c from /home/romano/openmc/data/nndc_hdf5/B10_71c.h5\n",
|
||||
" Reading Zr90.71c from /home/romano/openmc/data/nndc_hdf5/Zr90_71c.h5\n",
|
||||
" Maximum neutron transport energy: 20.0000 MeV for U235.71c\n",
|
||||
" Reading U235 from /opt/xsdata/nndc_new/U235.h5\n",
|
||||
" Reading U238 from /opt/xsdata/nndc_new/U238.h5\n",
|
||||
" Reading O16 from /opt/xsdata/nndc_new/O16.h5\n",
|
||||
" Reading H1 from /opt/xsdata/nndc_new/H1.h5\n",
|
||||
" Reading B10 from /opt/xsdata/nndc_new/B10.h5\n",
|
||||
" Reading Zr90 from /opt/xsdata/nndc_new/Zr90.h5\n",
|
||||
" Maximum neutron transport energy: 20.0000 MeV for U235\n",
|
||||
" Reading tallies XML file...\n",
|
||||
" Building neighboring cells lists for each surface...\n",
|
||||
" Initializing source particles...\n",
|
||||
|
|
@ -570,26 +609,26 @@
|
|||
"\n",
|
||||
" Bat./Gen. k Average k \n",
|
||||
" ========= ======== ==================== \n",
|
||||
" 1/1 1.03471 \n",
|
||||
" 2/1 1.03257 \n",
|
||||
" 3/1 1.00600 \n",
|
||||
" 4/1 1.04547 \n",
|
||||
" 5/1 1.02287 \n",
|
||||
" 6/1 1.05752 \n",
|
||||
" 7/1 1.04283 1.05017 +/- 0.00734\n",
|
||||
" 8/1 1.05189 1.05074 +/- 0.00428\n",
|
||||
" 9/1 1.01645 1.04217 +/- 0.00909\n",
|
||||
" 10/1 1.04978 1.04369 +/- 0.00721\n",
|
||||
" 11/1 1.03459 1.04218 +/- 0.00608\n",
|
||||
" 12/1 1.04019 1.04189 +/- 0.00514\n",
|
||||
" 13/1 1.05985 1.04414 +/- 0.00499\n",
|
||||
" 14/1 1.02111 1.04158 +/- 0.00509\n",
|
||||
" 15/1 1.04774 1.04219 +/- 0.00459\n",
|
||||
" 16/1 1.00733 1.03902 +/- 0.00523\n",
|
||||
" 17/1 1.02224 1.03763 +/- 0.00497\n",
|
||||
" 18/1 1.03263 1.03724 +/- 0.00459\n",
|
||||
" 19/1 1.01611 1.03573 +/- 0.00451\n",
|
||||
" 20/1 1.04692 1.03648 +/- 0.00426\n",
|
||||
" 1/1 0.96168 \n",
|
||||
" 2/1 0.96651 \n",
|
||||
" 3/1 1.00678 \n",
|
||||
" 4/1 0.98773 \n",
|
||||
" 5/1 1.01883 \n",
|
||||
" 6/1 1.02959 \n",
|
||||
" 7/1 0.99859 1.01409 +/- 0.01550\n",
|
||||
" 8/1 1.03441 1.02086 +/- 0.01123\n",
|
||||
" 9/1 1.06097 1.03089 +/- 0.01279\n",
|
||||
" 10/1 1.06094 1.03690 +/- 0.01159\n",
|
||||
" 11/1 1.04687 1.03856 +/- 0.00961\n",
|
||||
" 12/1 1.02982 1.03731 +/- 0.00821\n",
|
||||
" 13/1 1.03520 1.03705 +/- 0.00712\n",
|
||||
" 14/1 0.99508 1.03239 +/- 0.00782\n",
|
||||
" 15/1 1.03973 1.03312 +/- 0.00703\n",
|
||||
" 16/1 1.03807 1.03357 +/- 0.00638\n",
|
||||
" 17/1 1.03091 1.03335 +/- 0.00583\n",
|
||||
" 18/1 1.01421 1.03188 +/- 0.00556\n",
|
||||
" 19/1 0.99339 1.02913 +/- 0.00583\n",
|
||||
" 20/1 1.04827 1.03040 +/- 0.00558\n",
|
||||
" Creating state point statepoint.20.h5...\n",
|
||||
"\n",
|
||||
" ===========================================================================\n",
|
||||
|
|
@ -599,28 +638,28 @@
|
|||
"\n",
|
||||
" =======================> TIMING STATISTICS <=======================\n",
|
||||
"\n",
|
||||
" Total time for initialization = 3.5600E-01 seconds\n",
|
||||
" Reading cross sections = 2.3400E-01 seconds\n",
|
||||
" Total time in simulation = 1.8333E+01 seconds\n",
|
||||
" Time in transport only = 1.8325E+01 seconds\n",
|
||||
" Time in inactive batches = 2.6950E+00 seconds\n",
|
||||
" Time in active batches = 1.5638E+01 seconds\n",
|
||||
" Total time for initialization = 3.8900E-01 seconds\n",
|
||||
" Reading cross sections = 2.7000E-01 seconds\n",
|
||||
" Total time in simulation = 4.6960E+00 seconds\n",
|
||||
" Time in transport only = 4.6760E+00 seconds\n",
|
||||
" Time in inactive batches = 6.6400E-01 seconds\n",
|
||||
" Time in active batches = 4.0320E+00 seconds\n",
|
||||
" Time synchronizing fission bank = 1.0000E-03 seconds\n",
|
||||
" Sampling source sites = 0.0000E+00 seconds\n",
|
||||
" SEND/RECV source sites = 1.0000E-03 seconds\n",
|
||||
" Sampling source sites = 1.0000E-03 seconds\n",
|
||||
" SEND/RECV source sites = 0.0000E+00 seconds\n",
|
||||
" Time accumulating tallies = 0.0000E+00 seconds\n",
|
||||
" Total time for finalization = 1.0000E-03 seconds\n",
|
||||
" Total time elapsed = 1.8711E+01 seconds\n",
|
||||
" Calculation Rate (inactive) = 4638.22 neutrons/second\n",
|
||||
" Calculation Rate (active) = 2398.00 neutrons/second\n",
|
||||
" Total time elapsed = 5.0960E+00 seconds\n",
|
||||
" Calculation Rate (inactive) = 18825.3 neutrons/second\n",
|
||||
" Calculation Rate (active) = 9300.60 neutrons/second\n",
|
||||
"\n",
|
||||
" ============================> RESULTS <============================\n",
|
||||
"\n",
|
||||
" k-effective (Collision) = 1.03296 +/- 0.00669\n",
|
||||
" k-effective (Track-length) = 1.03648 +/- 0.00426\n",
|
||||
" k-effective (Absorption) = 1.03431 +/- 0.00702\n",
|
||||
" Combined k-effective = 1.03621 +/- 0.00456\n",
|
||||
" Leakage Fraction = 0.00000 +/- 0.00000\n",
|
||||
" k-effective (Collision) = 1.02791 +/- 0.00553\n",
|
||||
" k-effective (Track-length) = 1.03040 +/- 0.00558\n",
|
||||
" k-effective (Absorption) = 1.02011 +/- 0.00491\n",
|
||||
" Combined k-effective = 1.02461 +/- 0.00398\n",
|
||||
" Leakage Fraction = 0.01677 +/- 0.00109\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
|
|
@ -674,8 +713,8 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We have a tally of the total fission rate and the total absorption rate, so we can calculate k-infinity as:\n",
|
||||
"$$k_\\infty = \\frac{\\langle \\nu \\Sigma_f \\phi \\rangle}{\\langle \\Sigma_a \\phi \\rangle}$$\n",
|
||||
"We have a tally of the total fission rate and the total absorption rate, so we can calculate k-eff as:\n",
|
||||
"$$k_{eff} = \\frac{\\langle \\nu \\Sigma_f \\phi \\rangle}{\\langle \\Sigma_a \\phi \\rangle + \\langle L \\rangle}$$\n",
|
||||
"In this notation, $\\langle \\cdot \\rangle^a_b$ represents an OpenMC that is integrated over region $a$ and energy range $b$. If $a$ or $b$ is not reported, it means the value represents an integral over all space or all energy, respectively."
|
||||
]
|
||||
},
|
||||
|
|
@ -704,17 +743,17 @@
|
|||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(nu-fission / absorption)</td>\n",
|
||||
" <td>1.038387</td>\n",
|
||||
" <td>0.006141</td>\n",
|
||||
" <td>(nu-fission / (absorption + current))</td>\n",
|
||||
" <td>1.02431</td>\n",
|
||||
" <td>0.00704</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" nuclide score mean std. dev.\n",
|
||||
"0 total (nu-fission / absorption) 1.04e+00 6.14e-03"
|
||||
" nuclide score mean std. dev.\n",
|
||||
"0 total (nu-fission / (absorption + current)) 1.02e+00 7.04e-03"
|
||||
]
|
||||
},
|
||||
"execution_count": 24,
|
||||
|
|
@ -723,10 +762,17 @@
|
|||
}
|
||||
],
|
||||
"source": [
|
||||
"# Compute k-infinity using tally arithmetic\n",
|
||||
"# Get the fission and absorption rate tallies\n",
|
||||
"fiss_rate = sp.get_tally(name='fiss. rate')\n",
|
||||
"abs_rate = sp.get_tally(name='abs. rate')\n",
|
||||
"keff = fiss_rate / abs_rate\n",
|
||||
"\n",
|
||||
"# Get the leakage tally\n",
|
||||
"leak = sp.get_tally(name='leakage')\n",
|
||||
"leak = leak.summation(filter_type='surface', remove_filter=True)\n",
|
||||
"leak = leak.summation(filter_type='mesh', remove_filter=True)\n",
|
||||
"\n",
|
||||
"# Compute k-infinity using tally arithmetic\n",
|
||||
"keff = fiss_rate / (abs_rate + leak)\n",
|
||||
"keff.get_pandas_dataframe()"
|
||||
]
|
||||
},
|
||||
|
|
@ -734,9 +780,9 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Notice that even though the neutron production rate and absorption rate are separate tallies, we still get a first-order estimate of the uncertainty on the quotient of them automatically!\n",
|
||||
"Notice that even though the neutron production rate, absorption rate, and current are separate tallies, we still get a first-order estimate of the uncertainty on the quotient of them automatically!\n",
|
||||
"\n",
|
||||
"Often in textbooks you'll see k-infinity represented using the four-factor formula $$k_\\infty = p \\epsilon f \\eta.$$ Let's analyze each of these factors, starting with the resonance escape probability which is defined as $$p=\\frac{\\langle\\Sigma_a\\phi\\rangle_T}{\\langle\\Sigma_a\\phi\\rangle}$$ where the subscript $T$ means thermal energies."
|
||||
"Often in textbooks you'll see k-eff represented using the six-factor formula $$k_{eff} = p \\epsilon f \\eta P_{FNL} P_{TNL}.$$ Let's analyze each of these factors, starting with the resonance escape probability which is defined as $$p=\\frac{\\langle\\Sigma_a\\phi\\rangle_T + \\langle L \\rangle_T}{\\langle\\Sigma_a\\phi\\rangle + \\langle L \\rangle_T}$$ where the subscript $T$ means thermal energies."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -768,17 +814,20 @@
|
|||
" <td>0.0</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>absorption</td>\n",
|
||||
" <td>0.693337</td>\n",
|
||||
" <td>0.004109</td>\n",
|
||||
" <td>(absorption + current)</td>\n",
|
||||
" <td>0.695303</td>\n",
|
||||
" <td>0.005091</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" energy low [MeV] energy high [MeV] nuclide score mean std. dev.\n",
|
||||
"0 0.00e+00 6.25e-07 total absorption 6.93e-01 4.11e-03"
|
||||
" energy low [MeV] energy high [MeV] nuclide score \\\n",
|
||||
"0 0.00e+00 6.25e-07 total (absorption + current) \n",
|
||||
"\n",
|
||||
" mean std. dev. \n",
|
||||
"0 6.95e-01 5.09e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 25,
|
||||
|
|
@ -789,7 +838,10 @@
|
|||
"source": [
|
||||
"# Compute resonance escape probability using tally arithmetic\n",
|
||||
"therm_abs_rate = sp.get_tally(name='therm. abs. rate')\n",
|
||||
"res_esc = therm_abs_rate / abs_rate\n",
|
||||
"thermal_leak = sp.get_tally(name='thermal leakage')\n",
|
||||
"thermal_leak = thermal_leak.summation(filter_type='surface', remove_filter=True)\n",
|
||||
"thermal_leak = thermal_leak.summation(filter_type='mesh', remove_filter=True)\n",
|
||||
"res_esc = (therm_abs_rate + thermal_leak) / (abs_rate + thermal_leak)\n",
|
||||
"res_esc.get_pandas_dataframe()"
|
||||
]
|
||||
},
|
||||
|
|
@ -831,8 +883,8 @@
|
|||
" <td>6.250000e-07</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>nu-fission</td>\n",
|
||||
" <td>1.203042</td>\n",
|
||||
" <td>0.0076</td>\n",
|
||||
" <td>1.202639</td>\n",
|
||||
" <td>0.010348</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
|
|
@ -840,7 +892,7 @@
|
|||
],
|
||||
"text/plain": [
|
||||
" energy low [MeV] energy high [MeV] nuclide score mean std. dev.\n",
|
||||
"0 0.00e+00 6.25e-07 total nu-fission 1.20e+00 7.60e-03"
|
||||
"0 0.00e+00 6.25e-07 total nu-fission 1.20e+00 1.03e-02"
|
||||
]
|
||||
},
|
||||
"execution_count": 26,
|
||||
|
|
@ -896,8 +948,8 @@
|
|||
" <td>10000</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>absorption</td>\n",
|
||||
" <td>0.748413</td>\n",
|
||||
" <td>0.004723</td>\n",
|
||||
" <td>0.749349</td>\n",
|
||||
" <td>0.006731</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
|
|
@ -905,10 +957,10 @@
|
|||
],
|
||||
"text/plain": [
|
||||
" energy low [MeV] energy high [MeV] cell nuclide score mean \\\n",
|
||||
"0 0.00e+00 6.25e-07 10000 total absorption 7.48e-01 \n",
|
||||
"0 0.00e+00 6.25e-07 10000 total absorption 7.49e-01 \n",
|
||||
"\n",
|
||||
" std. dev. \n",
|
||||
"0 4.72e-03 "
|
||||
"0 6.73e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 27,
|
||||
|
|
@ -927,7 +979,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The final factor is the number of fission neutrons produced per absorption in fuel, calculated as $$\\eta = \\frac{\\langle \\nu\\Sigma_f\\phi \\rangle_T}{\\langle \\Sigma_a \\phi \\rangle^F_T}$$"
|
||||
"The next factor is the number of fission neutrons produced per absorption in fuel, calculated as $$\\eta = \\frac{\\langle \\nu\\Sigma_f\\phi \\rangle_T}{\\langle \\Sigma_a \\phi \\rangle^F_T}$$"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -962,8 +1014,8 @@
|
|||
" <td>10000</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(nu-fission / absorption)</td>\n",
|
||||
" <td>1.663385</td>\n",
|
||||
" <td>0.011253</td>\n",
|
||||
" <td>1.663736</td>\n",
|
||||
" <td>0.015707</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
|
|
@ -974,7 +1026,7 @@
|
|||
"0 0.00e+00 6.25e-07 10000 total \n",
|
||||
"\n",
|
||||
" score mean std. dev. \n",
|
||||
"0 (nu-fission / absorption) 1.66e+00 1.13e-02 "
|
||||
"0 (nu-fission / absorption) 1.66e+00 1.57e-02 "
|
||||
]
|
||||
},
|
||||
"execution_count": 28,
|
||||
|
|
@ -992,7 +1044,7 @@
|
|||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now we can calculate $k_\\infty$ using the product of the factors form the four-factor formula."
|
||||
"There are two leakage factors to account for fast and thermal leakage. The fast non-leakage probability is computed as $$P_{FNL} = \\frac{\\langle \\Sigma_a\\phi \\rangle + \\langle L \\rangle_T}{\\langle \\Sigma_a \\phi \\rangle + \\langle L \\rangle}$$"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -1001,6 +1053,130 @@
|
|||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></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",
|
||||
" <th>std. dev.</th>\n",
|
||||
" </tr>\n",
|
||||
" </thead>\n",
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>0.0</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(absorption + current)</td>\n",
|
||||
" <td>0.985102</td>\n",
|
||||
" <td>0.005855</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" energy low [MeV] energy high [MeV] nuclide score \\\n",
|
||||
"0 0.00e+00 6.25e-07 total (absorption + current) \n",
|
||||
"\n",
|
||||
" mean std. dev. \n",
|
||||
"0 9.85e-01 5.86e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 29,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"p_fnl = (abs_rate + thermal_leak) / (abs_rate + leak)\n",
|
||||
"p_fnl.get_pandas_dataframe()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The final factor is the thermal non-leakage probability and is computed as $$P_{TNL} = \\frac{\\langle \\Sigma_a\\phi \\rangle_T}{\\langle \\Sigma_a \\phi \\rangle_T + \\langle L \\rangle_T}$$"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></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",
|
||||
" <th>std. dev.</th>\n",
|
||||
" </tr>\n",
|
||||
" </thead>\n",
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>0.0</td>\n",
|
||||
" <td>6.250000e-07</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(absorption / (absorption + current))</td>\n",
|
||||
" <td>0.997407</td>\n",
|
||||
" <td>0.008492</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" energy low [MeV] energy high [MeV] nuclide \\\n",
|
||||
"0 0.00e+00 6.25e-07 total \n",
|
||||
"\n",
|
||||
" score mean std. dev. \n",
|
||||
"0 (absorption / (absorption + current)) 9.97e-01 8.49e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 30,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"p_tnl = therm_abs_rate / (therm_abs_rate + thermal_leak)\n",
|
||||
"p_tnl.get_pandas_dataframe()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Now we can calculate $k_{eff}$ using the product of the factors form the four-factor formula."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
|
|
@ -1026,9 +1202,9 @@
|
|||
" <td>6.250000e-07</td>\n",
|
||||
" <td>10000</td>\n",
|
||||
" <td>total</td>\n",
|
||||
" <td>(((absorption * nu-fission) * absorption) * (n...</td>\n",
|
||||
" <td>1.038387</td>\n",
|
||||
" <td>0.01316</td>\n",
|
||||
" <td>((((((absorption + current) * nu-fission) * ab...</td>\n",
|
||||
" <td>1.02431</td>\n",
|
||||
" <td>0.02062</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
|
|
@ -1039,16 +1215,16 @@
|
|||
"0 0.00e+00 6.25e-07 10000 total \n",
|
||||
"\n",
|
||||
" score mean std. dev. \n",
|
||||
"0 (((absorption * nu-fission) * absorption) * (n... 1.04e+00 1.32e-02 "
|
||||
"0 ((((((absorption + current) * nu-fission) * ab... 1.02e+00 2.06e-02 "
|
||||
]
|
||||
},
|
||||
"execution_count": 29,
|
||||
"execution_count": 31,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"keff = res_esc * fast_fiss * therm_util * eta\n",
|
||||
"keff = res_esc * fast_fiss * therm_util * eta * p_fnl * p_tnl\n",
|
||||
"keff.get_pandas_dataframe()"
|
||||
]
|
||||
},
|
||||
|
|
@ -1063,7 +1239,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"execution_count": 32,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"scrolled": true
|
||||
|
|
@ -1079,7 +1255,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"execution_count": 33,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
|
|
@ -1109,8 +1285,8 @@
|
|||
" <td>6.250000e-07</td>\n",
|
||||
" <td>(U238 / total)</td>\n",
|
||||
" <td>(nu-fission / flux)</td>\n",
|
||||
" <td>6.636968e-07</td>\n",
|
||||
" <td>4.132875e-09</td>\n",
|
||||
" <td>6.662479e-07</td>\n",
|
||||
" <td>6.039323e-09</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
|
|
@ -1119,8 +1295,8 @@
|
|||
" <td>6.250000e-07</td>\n",
|
||||
" <td>(U238 / total)</td>\n",
|
||||
" <td>(scatter / flux)</td>\n",
|
||||
" <td>2.099856e-01</td>\n",
|
||||
" <td>1.232455e-03</td>\n",
|
||||
" <td>2.099897e-01</td>\n",
|
||||
" <td>1.843251e-03</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2</th>\n",
|
||||
|
|
@ -1129,8 +1305,8 @@
|
|||
" <td>6.250000e-07</td>\n",
|
||||
" <td>(U235 / total)</td>\n",
|
||||
" <td>(nu-fission / flux)</td>\n",
|
||||
" <td>3.552458e-01</td>\n",
|
||||
" <td>2.252681e-03</td>\n",
|
||||
" <td>3.568130e-01</td>\n",
|
||||
" <td>3.255144e-03</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>3</th>\n",
|
||||
|
|
@ -1139,8 +1315,8 @@
|
|||
" <td>6.250000e-07</td>\n",
|
||||
" <td>(U235 / total)</td>\n",
|
||||
" <td>(scatter / flux)</td>\n",
|
||||
" <td>5.554345e-03</td>\n",
|
||||
" <td>3.265385e-05</td>\n",
|
||||
" <td>5.555326e-03</td>\n",
|
||||
" <td>4.893022e-05</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>4</th>\n",
|
||||
|
|
@ -1149,8 +1325,8 @@
|
|||
" <td>2.000000e+01</td>\n",
|
||||
" <td>(U238 / total)</td>\n",
|
||||
" <td>(nu-fission / flux)</td>\n",
|
||||
" <td>7.126668e-03</td>\n",
|
||||
" <td>5.296883e-05</td>\n",
|
||||
" <td>7.215044e-03</td>\n",
|
||||
" <td>4.968448e-05</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>5</th>\n",
|
||||
|
|
@ -1159,8 +1335,8 @@
|
|||
" <td>2.000000e+01</td>\n",
|
||||
" <td>(U238 / total)</td>\n",
|
||||
" <td>(scatter / flux)</td>\n",
|
||||
" <td>2.277460e-01</td>\n",
|
||||
" <td>1.003558e-03</td>\n",
|
||||
" <td>2.273966e-01</td>\n",
|
||||
" <td>8.969811e-04</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>6</th>\n",
|
||||
|
|
@ -1169,8 +1345,8 @@
|
|||
" <td>2.000000e+01</td>\n",
|
||||
" <td>(U235 / total)</td>\n",
|
||||
" <td>(nu-fission / flux)</td>\n",
|
||||
" <td>8.010911e-03</td>\n",
|
||||
" <td>6.802256e-05</td>\n",
|
||||
" <td>7.969615e-03</td>\n",
|
||||
" <td>5.374119e-05</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>7</th>\n",
|
||||
|
|
@ -1179,8 +1355,8 @@
|
|||
" <td>2.000000e+01</td>\n",
|
||||
" <td>(U235 / total)</td>\n",
|
||||
" <td>(scatter / flux)</td>\n",
|
||||
" <td>3.367794e-03</td>\n",
|
||||
" <td>1.443644e-05</td>\n",
|
||||
" <td>3.362798e-03</td>\n",
|
||||
" <td>1.286767e-05</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
|
|
@ -1198,17 +1374,17 @@
|
|||
"7 10000 6.25e-07 2.00e+01 (U235 / total) \n",
|
||||
"\n",
|
||||
" score mean std. dev. \n",
|
||||
"0 (nu-fission / flux) 6.64e-07 4.13e-09 \n",
|
||||
"1 (scatter / flux) 2.10e-01 1.23e-03 \n",
|
||||
"2 (nu-fission / flux) 3.55e-01 2.25e-03 \n",
|
||||
"3 (scatter / flux) 5.55e-03 3.27e-05 \n",
|
||||
"4 (nu-fission / flux) 7.13e-03 5.30e-05 \n",
|
||||
"5 (scatter / flux) 2.28e-01 1.00e-03 \n",
|
||||
"6 (nu-fission / flux) 8.01e-03 6.80e-05 \n",
|
||||
"7 (scatter / flux) 3.37e-03 1.44e-05 "
|
||||
"0 (nu-fission / flux) 6.66e-07 6.04e-09 \n",
|
||||
"1 (scatter / flux) 2.10e-01 1.84e-03 \n",
|
||||
"2 (nu-fission / flux) 3.57e-01 3.26e-03 \n",
|
||||
"3 (scatter / flux) 5.56e-03 4.89e-05 \n",
|
||||
"4 (nu-fission / flux) 7.22e-03 4.97e-05 \n",
|
||||
"5 (scatter / flux) 2.27e-01 8.97e-04 \n",
|
||||
"6 (nu-fission / flux) 7.97e-03 5.37e-05 \n",
|
||||
"7 (scatter / flux) 3.36e-03 1.29e-05 "
|
||||
]
|
||||
},
|
||||
"execution_count": 31,
|
||||
"execution_count": 33,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
|
|
@ -1227,7 +1403,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 32,
|
||||
"execution_count": 34,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
|
|
@ -1236,11 +1412,11 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[[[ 6.63696783e-07]\n",
|
||||
" [ 3.55245846e-01]]\n",
|
||||
"[[[ 6.66247898e-07]\n",
|
||||
" [ 3.56812954e-01]]\n",
|
||||
"\n",
|
||||
" [[ 7.12666800e-03]\n",
|
||||
" [ 8.01091088e-03]]]\n"
|
||||
" [[ 7.21504433e-03]\n",
|
||||
" [ 7.96961502e-03]]]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
@ -1259,7 +1435,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 33,
|
||||
"execution_count": 35,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
|
|
@ -1268,9 +1444,9 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[[[ 0.00555435]]\n",
|
||||
"[[[ 0.00555533]]\n",
|
||||
"\n",
|
||||
" [[ 0.00336779]]]\n"
|
||||
" [[ 0.0033628 ]]]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
@ -1283,7 +1459,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 34,
|
||||
"execution_count": 36,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
|
|
@ -1292,8 +1468,8 @@
|
|||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[[[ 0.22774598]\n",
|
||||
" [ 0.00336779]]]\n"
|
||||
"[[[ 0.22739657]\n",
|
||||
" [ 0.0033628 ]]]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
@ -1314,7 +1490,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 35,
|
||||
"execution_count": 37,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
|
|
@ -1345,7 +1521,7 @@
|
|||
" <td>U238</td>\n",
|
||||
" <td>nu-fission</td>\n",
|
||||
" <td>0.000002</td>\n",
|
||||
" <td>7.473789e-09</td>\n",
|
||||
" <td>1.057199e-08</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
|
|
@ -1354,8 +1530,8 @@
|
|||
" <td>6.250000e-07</td>\n",
|
||||
" <td>U235</td>\n",
|
||||
" <td>nu-fission</td>\n",
|
||||
" <td>0.861547</td>\n",
|
||||
" <td>4.131310e-03</td>\n",
|
||||
" <td>0.856784</td>\n",
|
||||
" <td>5.730044e-03</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2</th>\n",
|
||||
|
|
@ -1364,8 +1540,8 @@
|
|||
" <td>2.000000e+01</td>\n",
|
||||
" <td>U238</td>\n",
|
||||
" <td>nu-fission</td>\n",
|
||||
" <td>0.082356</td>\n",
|
||||
" <td>5.560461e-04</td>\n",
|
||||
" <td>0.082495</td>\n",
|
||||
" <td>5.176027e-04</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>3</th>\n",
|
||||
|
|
@ -1374,8 +1550,8 @@
|
|||
" <td>2.000000e+01</td>\n",
|
||||
" <td>U235</td>\n",
|
||||
" <td>nu-fission</td>\n",
|
||||
" <td>0.092574</td>\n",
|
||||
" <td>7.315442e-04</td>\n",
|
||||
" <td>0.091123</td>\n",
|
||||
" <td>5.574052e-04</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
|
|
@ -1383,19 +1559,19 @@
|
|||
],
|
||||
"text/plain": [
|
||||
" cell energy low [MeV] energy high [MeV] nuclide score mean \\\n",
|
||||
"0 10000 0.00e+00 6.25e-07 U238 nu-fission 1.61e-06 \n",
|
||||
"1 10000 0.00e+00 6.25e-07 U235 nu-fission 8.62e-01 \n",
|
||||
"2 10000 6.25e-07 2.00e+01 U238 nu-fission 8.24e-02 \n",
|
||||
"3 10000 6.25e-07 2.00e+01 U235 nu-fission 9.26e-02 \n",
|
||||
"0 10000 0.00e+00 6.25e-07 U238 nu-fission 1.60e-06 \n",
|
||||
"1 10000 0.00e+00 6.25e-07 U235 nu-fission 8.57e-01 \n",
|
||||
"2 10000 6.25e-07 2.00e+01 U238 nu-fission 8.25e-02 \n",
|
||||
"3 10000 6.25e-07 2.00e+01 U235 nu-fission 9.11e-02 \n",
|
||||
"\n",
|
||||
" std. dev. \n",
|
||||
"0 7.47e-09 \n",
|
||||
"1 4.13e-03 \n",
|
||||
"2 5.56e-04 \n",
|
||||
"3 7.32e-04 "
|
||||
"0 1.06e-08 \n",
|
||||
"1 5.73e-03 \n",
|
||||
"2 5.18e-04 \n",
|
||||
"3 5.57e-04 "
|
||||
]
|
||||
},
|
||||
"execution_count": 35,
|
||||
"execution_count": 37,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
|
|
@ -1408,7 +1584,7 @@
|
|||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 36,
|
||||
"execution_count": 38,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
|
|
@ -1438,8 +1614,8 @@
|
|||
" <td>1.080060e-07</td>\n",
|
||||
" <td>H1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>4.599225</td>\n",
|
||||
" <td>0.015973</td>\n",
|
||||
" <td>4.547947</td>\n",
|
||||
" <td>0.028000</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
|
|
@ -1448,8 +1624,8 @@
|
|||
" <td>1.166529e-06</td>\n",
|
||||
" <td>H1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>2.037260</td>\n",
|
||||
" <td>0.011236</td>\n",
|
||||
" <td>2.003068</td>\n",
|
||||
" <td>0.008587</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2</th>\n",
|
||||
|
|
@ -1458,8 +1634,8 @@
|
|||
" <td>1.259921e-05</td>\n",
|
||||
" <td>H1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>1.662552</td>\n",
|
||||
" <td>0.010280</td>\n",
|
||||
" <td>1.647225</td>\n",
|
||||
" <td>0.011136</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>3</th>\n",
|
||||
|
|
@ -1468,8 +1644,8 @@
|
|||
" <td>1.360790e-04</td>\n",
|
||||
" <td>H1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>1.872201</td>\n",
|
||||
" <td>0.012136</td>\n",
|
||||
" <td>1.831367</td>\n",
|
||||
" <td>0.010196</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>4</th>\n",
|
||||
|
|
@ -1478,8 +1654,8 @@
|
|||
" <td>1.469734e-03</td>\n",
|
||||
" <td>H1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>2.080459</td>\n",
|
||||
" <td>0.013155</td>\n",
|
||||
" <td>2.039613</td>\n",
|
||||
" <td>0.008059</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>5</th>\n",
|
||||
|
|
@ -1488,8 +1664,8 @@
|
|||
" <td>1.587401e-02</td>\n",
|
||||
" <td>H1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>2.154996</td>\n",
|
||||
" <td>0.011975</td>\n",
|
||||
" <td>2.137523</td>\n",
|
||||
" <td>0.012885</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>6</th>\n",
|
||||
|
|
@ -1498,8 +1674,8 @@
|
|||
" <td>1.714488e-01</td>\n",
|
||||
" <td>H1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>2.218740</td>\n",
|
||||
" <td>0.008528</td>\n",
|
||||
" <td>2.170725</td>\n",
|
||||
" <td>0.012669</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>7</th>\n",
|
||||
|
|
@ -1508,8 +1684,8 @@
|
|||
" <td>1.851749e+00</td>\n",
|
||||
" <td>H1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>2.010517</td>\n",
|
||||
" <td>0.009187</td>\n",
|
||||
" <td>2.002724</td>\n",
|
||||
" <td>0.010768</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>8</th>\n",
|
||||
|
|
@ -1518,8 +1694,8 @@
|
|||
" <td>2.000000e+01</td>\n",
|
||||
" <td>H1</td>\n",
|
||||
" <td>scatter</td>\n",
|
||||
" <td>0.372022</td>\n",
|
||||
" <td>0.003196</td>\n",
|
||||
" <td>0.371624</td>\n",
|
||||
" <td>0.002959</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
|
|
@ -1527,29 +1703,29 @@
|
|||
],
|
||||
"text/plain": [
|
||||
" cell energy low [MeV] energy high [MeV] nuclide score mean \\\n",
|
||||
"0 10002 1.00e-08 1.08e-07 H1 scatter 4.60e+00 \n",
|
||||
"1 10002 1.08e-07 1.17e-06 H1 scatter 2.04e+00 \n",
|
||||
"2 10002 1.17e-06 1.26e-05 H1 scatter 1.66e+00 \n",
|
||||
"3 10002 1.26e-05 1.36e-04 H1 scatter 1.87e+00 \n",
|
||||
"4 10002 1.36e-04 1.47e-03 H1 scatter 2.08e+00 \n",
|
||||
"5 10002 1.47e-03 1.59e-02 H1 scatter 2.15e+00 \n",
|
||||
"6 10002 1.59e-02 1.71e-01 H1 scatter 2.22e+00 \n",
|
||||
"7 10002 1.71e-01 1.85e+00 H1 scatter 2.01e+00 \n",
|
||||
"0 10002 1.00e-08 1.08e-07 H1 scatter 4.55e+00 \n",
|
||||
"1 10002 1.08e-07 1.17e-06 H1 scatter 2.00e+00 \n",
|
||||
"2 10002 1.17e-06 1.26e-05 H1 scatter 1.65e+00 \n",
|
||||
"3 10002 1.26e-05 1.36e-04 H1 scatter 1.83e+00 \n",
|
||||
"4 10002 1.36e-04 1.47e-03 H1 scatter 2.04e+00 \n",
|
||||
"5 10002 1.47e-03 1.59e-02 H1 scatter 2.14e+00 \n",
|
||||
"6 10002 1.59e-02 1.71e-01 H1 scatter 2.17e+00 \n",
|
||||
"7 10002 1.71e-01 1.85e+00 H1 scatter 2.00e+00 \n",
|
||||
"8 10002 1.85e+00 2.00e+01 H1 scatter 3.72e-01 \n",
|
||||
"\n",
|
||||
" std. dev. \n",
|
||||
"0 1.60e-02 \n",
|
||||
"1 1.12e-02 \n",
|
||||
"2 1.03e-02 \n",
|
||||
"3 1.21e-02 \n",
|
||||
"4 1.32e-02 \n",
|
||||
"5 1.20e-02 \n",
|
||||
"6 8.53e-03 \n",
|
||||
"7 9.19e-03 \n",
|
||||
"8 3.20e-03 "
|
||||
"0 2.80e-02 \n",
|
||||
"1 8.59e-03 \n",
|
||||
"2 1.11e-02 \n",
|
||||
"3 1.02e-02 \n",
|
||||
"4 8.06e-03 \n",
|
||||
"5 1.29e-02 \n",
|
||||
"6 1.27e-02 \n",
|
||||
"7 1.08e-02 \n",
|
||||
"8 2.96e-03 "
|
||||
]
|
||||
},
|
||||
"execution_count": 36,
|
||||
"execution_count": 38,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ Example Jupyter Notebooks
|
|||
examples/mgxs-part-ii
|
||||
examples/mgxs-part-iii
|
||||
examples/mgxs-part-iv
|
||||
examples/mdgxs-part-i
|
||||
examples/mdgxs-part-ii
|
||||
examples/nuclear-data
|
||||
|
||||
------------------------------------
|
||||
|
|
@ -284,6 +286,19 @@ Multi-group Cross Sections
|
|||
openmc.mgxs.TotalXS
|
||||
openmc.mgxs.TransportXS
|
||||
|
||||
Multi-delayed-group Cross Sections
|
||||
----------------------------------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclassinherit.rst
|
||||
|
||||
openmc.mgxs.MDGXS
|
||||
openmc.mgxs.ChiDelayed
|
||||
openmc.mgxs.DelayedNuFissionXS
|
||||
openmc.mgxs.Beta
|
||||
|
||||
Multi-group Cross Section Libraries
|
||||
-----------------------------------
|
||||
|
||||
|
|
@ -319,6 +334,7 @@ Functions
|
|||
:nosignatures:
|
||||
|
||||
openmc.model.create_triso_lattice
|
||||
openmc.model.pack_trisos
|
||||
|
||||
--------------------------------------------
|
||||
:mod:`openmc.data` -- Nuclear Data Interface
|
||||
|
|
@ -348,6 +364,7 @@ Core Classes
|
|||
openmc.data.Tabulated1D
|
||||
openmc.data.ThermalScattering
|
||||
openmc.data.CoherentElastic
|
||||
openmc.data.FissionEnergyRelease
|
||||
|
||||
Angle-Energy Distributions
|
||||
--------------------------
|
||||
|
|
@ -381,21 +398,22 @@ Classes
|
|||
+++++++
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.data.ace.Library
|
||||
openmc.data.ace.Table
|
||||
openmc.data.ace.Library
|
||||
openmc.data.ace.Table
|
||||
|
||||
Functions
|
||||
+++++++++
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
|
||||
openmc.data.ace.ascii_to_binary
|
||||
openmc.data.ace.ascii_to_binary
|
||||
openmc.data.write_compact_458_library
|
||||
|
||||
.. _Jupyter: https://jupyter.org/
|
||||
.. _NumPy: http://www.numpy.org/
|
||||
|
|
|
|||
|
|
@ -281,6 +281,8 @@ based on the recommended value in LA-UR-14-24530_.
|
|||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
.. _multipole_library:
|
||||
|
||||
``<multipole_library>`` Element
|
||||
-------------------------------
|
||||
|
||||
|
|
@ -290,8 +292,8 @@ 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:`OPENMC_MULTIPOLE_LIBRARY` environment variable will be used.
|
||||
|
||||
.. note:: The <use_windowed_multipole> element must also be set to "true"
|
||||
for windowed multipole functionality.
|
||||
.. note:: The :ref:`temperature_method` must also be set to "multipole" for
|
||||
windowed multipole functionality.
|
||||
|
||||
``<max_order>`` Element
|
||||
---------------------------
|
||||
|
|
@ -395,19 +397,16 @@ attributes or sub-elements:
|
|||
|
||||
:scatterer:
|
||||
An element with attributes/sub-elements called ``nuclide``, ``method``,
|
||||
``xs_label``, ``xs_label_0K``, ``E_min``, and ``E_max``. The ``nuclide``
|
||||
attribute is the name, as given by the ``name`` attribute within the
|
||||
``nuclide`` sub-element of the ``material`` element in ``materials.xml``,
|
||||
of the nuclide to which a resonance scattering treatment is to be applied.
|
||||
``E_min``, and ``E_max``. The ``nuclide`` attribute is the name, as given
|
||||
by the ``name`` attribute within the ``nuclide`` sub-element of the
|
||||
``material`` element in ``materials.xml``, of the nuclide to which a
|
||||
resonance scattering treatment is to be applied.
|
||||
The ``method`` attribute gives the type of resonance scattering treatment
|
||||
that is to be applied to the ``nuclide``. Acceptable inputs - none of
|
||||
which are case-sensitive - for the ``method`` attribute are ``ARES``,
|
||||
``CXS``, ``WCM``, and ``DBRC``. Descriptions of each of these methods
|
||||
are documented here_. The ``xs_label`` attribute gives the label for the
|
||||
cross section data of the ``nuclide`` at a given temperature. The
|
||||
``xs_label_0K`` gives the label for the 0 K cross section data for the
|
||||
``nuclide``. The ``E_min`` attribute gives the minimum energy above
|
||||
which the ``method`` is applied. The ``E_max`` attribute gives the
|
||||
are documented here_. The ``E_min`` attribute gives the minimum energy
|
||||
above which the ``method`` is applied. The ``E_max`` attribute gives the
|
||||
maximum energy below which the ``method`` is applied. One example would
|
||||
be as follows:
|
||||
|
||||
|
|
@ -419,16 +418,12 @@ attributes or sub-elements:
|
|||
<scatterer>
|
||||
<nuclide>U-238</nuclide>
|
||||
<method>ARES</method>
|
||||
<xs_label>92238.72c</xs_label>
|
||||
<xs_label_0K>92238.00c</xs_label_0K>
|
||||
<E_min>5.0e-6</E_min>
|
||||
<E_max>40.0e-6</E_max>
|
||||
</scatterer>
|
||||
<scatterer>
|
||||
<nuclide>Pu-239</nuclide>
|
||||
<method>dbrc</method>
|
||||
<xs_label>94239.72c</xs_label>
|
||||
<xs_label_0K>94239.00c</xs_label_0K>
|
||||
<E_min>0.01e-6</E_min>
|
||||
<E_max>210.0e-6</E_max>
|
||||
</scatterer>
|
||||
|
|
@ -714,6 +709,45 @@ survival biasing, otherwise known as implicit capture or absorption.
|
|||
|
||||
*Default*: false
|
||||
|
||||
.. _temperature_default:
|
||||
|
||||
``<temperature_default>`` Element
|
||||
---------------------------------
|
||||
|
||||
The ``<temperature_default>`` element specifies a default temperature in Kelvin
|
||||
that is to be applied to cells in the absence of an explicit cell temperature or
|
||||
a material default temperature.
|
||||
|
||||
*Default*: 293.6 K
|
||||
|
||||
.. _temperature_method:
|
||||
|
||||
``<temperature_method>`` Element
|
||||
--------------------------------
|
||||
|
||||
The ``<temperature_method>`` element has an accepted value of "nearest" or
|
||||
"interpolation". A value of "nearest" indicates that for each cell, the nearest
|
||||
temperature at which cross sections are given is to be applied, within a given
|
||||
tolerance (see :ref:`temperature_tolerance`). A value of "multipole" indicates
|
||||
that the windowed multipole method should be used to evaluate
|
||||
temperature-dependent cross sections in the resolved resonance range (a
|
||||
:ref:`windowed multipole library <multipole_library>` must also be available).
|
||||
|
||||
*Default*: "nearest"
|
||||
|
||||
.. _temperature_tolerance:
|
||||
|
||||
``<temperature_tolerance>`` Element
|
||||
-----------------------------------
|
||||
|
||||
The ``<temperature_tolerance>`` element specifies a tolerance in Kelvin that is
|
||||
to be applied when the "nearest" temperature method is used. For example, if a
|
||||
cell temperature is 340 K and the tolerance is 15 K, then the closest
|
||||
temperature in the range of 325 K to 355 K will be used to evaluate cross
|
||||
sections.
|
||||
|
||||
*Default*: 10 K
|
||||
|
||||
``<threads>`` Element
|
||||
---------------------
|
||||
|
||||
|
|
@ -836,6 +870,35 @@ displayed. This element takes the following attributes:
|
|||
|
||||
*Default*: 5
|
||||
|
||||
``<volume_calc>`` Element
|
||||
-------------------------
|
||||
|
||||
The ``<volume_calc>`` element indicates that a stochastic volume calculation
|
||||
should be run at the beginning of the simulation. This element has the following
|
||||
sub-elements/attributes:
|
||||
|
||||
:cells:
|
||||
The unique IDs of cells for which the volume should be estimated.
|
||||
|
||||
*Default*: None
|
||||
|
||||
:samples:
|
||||
The number of samples used to estimate volumes.
|
||||
|
||||
*Default*: None
|
||||
|
||||
:lower_left:
|
||||
The lower-left Cartesian coordinates of a bounding box that is used to
|
||||
sample points within.
|
||||
|
||||
*Default*: None
|
||||
|
||||
:upper_right:
|
||||
The upper-right Cartesian coordinates of a bounding box that is used to
|
||||
sample points within.
|
||||
|
||||
*Default*: None
|
||||
|
||||
--------------------------------------
|
||||
Geometry Specification -- geometry.xml
|
||||
--------------------------------------
|
||||
|
|
@ -1061,7 +1124,9 @@ Each ``<cell>`` element can have the following attributes or sub-elements:
|
|||
specified for the "distributed temperature" feature. This will give each
|
||||
unique instance of the cell its own temperature.
|
||||
|
||||
*Default*: The temperature of the coldest nuclide in the cell's material(s)
|
||||
*Default*: If a material default temperature is supplied, it is used. In the
|
||||
absence of a material default temperature, the :ref:`global default
|
||||
temperature <temperature_default>` is used.
|
||||
|
||||
:rotation:
|
||||
If the cell is filled with a universe, this element specifies the angles in
|
||||
|
|
@ -1266,6 +1331,14 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
|
||||
*Default*: ""
|
||||
|
||||
:temperature:
|
||||
An element with no attributes which is used to set the default temperature
|
||||
of the material in Kelvin.
|
||||
|
||||
*Default*: If a material default temperature is not given and a cell
|
||||
temperature is not specified, the :ref:`global default temperature
|
||||
<temperature_default>` is used.
|
||||
|
||||
:density:
|
||||
An element with attributes/sub-elements called ``value`` and ``units``. The
|
||||
``value`` attribute is the numeric value of the density while the ``units``
|
||||
|
|
@ -1286,17 +1359,16 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
``nuclide``, ``element``, or ``sab`` quantity.
|
||||
|
||||
:nuclide:
|
||||
An element with attributes/sub-elements called ``name``, ``xs``, and ``ao``
|
||||
An element with attributes/sub-elements called ``name``, and ``ao``
|
||||
or ``wo``. The ``name`` attribute is the name of the cross-section for a
|
||||
desired nuclide while the ``xs`` attribute is the cross-section
|
||||
identifier. Finally, the ``ao`` and ``wo`` attributes specify the atom or
|
||||
desired nuclide. Finally, the ``ao`` and ``wo`` attributes specify the atom or
|
||||
weight percent of that nuclide within the material, respectively. One
|
||||
example would be as follows:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<nuclide name="H-1" xs="70c" ao="2.0" />
|
||||
<nuclide name="O-16" xs="70c" ao="1.0" />
|
||||
<nuclide name="H1" ao="2.0" />
|
||||
<nuclide name="O16" ao="1.0" />
|
||||
|
||||
.. note:: If one nuclide is specified in atom percent, all others must also
|
||||
be given in atom percent. The same applies for weight percentages.
|
||||
|
|
@ -1320,11 +1392,10 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
Specifies that a natural element is present in the material. The natural
|
||||
element is split up into individual isotopes based on `IUPAC Isotopic
|
||||
Compositions of the Elements 2009`_. This element has
|
||||
attributes/sub-elements called ``name``, ``xs``, and ``ao``. The ``name``
|
||||
attribute is the atomic symbol of the element while the ``xs`` attribute is
|
||||
the cross-section identifier. Finally, the ``ao`` attribute specifies the
|
||||
atom percent of the element within the material, respectively. One example
|
||||
would be as follows:
|
||||
attributes/sub-elements called ``name``, and ``ao``. The ``name``
|
||||
attribute is the atomic symbol of the element. Finally, the ``ao``
|
||||
attribute specifies the atom percent of the element within the material,
|
||||
respectively. One example would be as follows:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
|
|
@ -1354,10 +1425,9 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
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
|
||||
is the name of the S(a,b) table that should be associated with the material,
|
||||
and ``xs`` is the cross-section identifier for the table.
|
||||
Associates an S(a,b) table with the material. This element has one
|
||||
attribute/sub-element called ``name``. The ``name`` attribute
|
||||
is the name of the S(a,b) table that should be associated with the material.
|
||||
|
||||
*Default*: None
|
||||
|
||||
|
|
@ -1368,14 +1438,13 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
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``.
|
||||
macroscopic element has one attribute/sub-element called ``name``.
|
||||
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:
|
||||
desired nuclide. One example would be as follows:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<macroscopic name="UO2" xs="71c" />
|
||||
<macroscopic name="UO2" />
|
||||
|
||||
.. note:: This element is only used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
|
|
@ -1384,18 +1453,6 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
.. _IUPAC Isotopic Compositions of the Elements 2009:
|
||||
http://pac.iupac.org/publications/pac/pdf/2011/pdf/8302x0397.pdf
|
||||
|
||||
``<default_xs>`` Element
|
||||
------------------------
|
||||
|
||||
In some circumstances, the cross-section identifier may be the same for many or
|
||||
all nuclides in a given problem. In this case, rather than specifying the
|
||||
``xs=...`` attribute on every nuclide, a ``<default_xs>`` element can be used to
|
||||
set the default cross-section identifier for any nuclide without an identifier
|
||||
explicitly listed. This element has no attributes and accepts a 3-letter string
|
||||
that indicates the default cross-section identifier, e.g. "70c".
|
||||
|
||||
*Default*: None
|
||||
|
||||
------------------------------------
|
||||
Tallies Specification -- tallies.xml
|
||||
------------------------------------
|
||||
|
|
@ -1809,6 +1866,27 @@ The ``<tally>`` element accepts the following sub-elements:
|
|||
| |:math:`\gamma`-rays are assumed to deposit their |
|
||||
| |energy locally. Units are MeV per source particle. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|fission-q-prompt |The prompt fission energy production rate. This |
|
||||
| |energy comes in the form of fission fragment |
|
||||
| |nuclei, prompt neutrons, and prompt |
|
||||
| |:math:`\gamma`-rays. This value depends on the |
|
||||
| |incident energy and it requires that the nuclear |
|
||||
| |data library contains the optional fission energy |
|
||||
| |release data. Energy is assumed to be deposited |
|
||||
| |locally. Units are MeV per source particle. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|fission-q-recoverable |The recoverable fission energy production rate. |
|
||||
| |This energy comes in the form of fission fragment |
|
||||
| |nuclei, prompt and delayed neutrons, prompt and |
|
||||
| |delayed :math:`\gamma`-rays, and delayed |
|
||||
| |:math:`\beta`-rays. This tally differs from the |
|
||||
| |kappa-fission tally in that it is dependent on |
|
||||
| |incident neutron energy and it requires that the |
|
||||
| |nuclear data library contains the optional fission |
|
||||
| |energy release data. Energy is assumed to be |
|
||||
| |deposited locally. Units are MeV per source |
|
||||
| |paticle. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|
||||
.. note::
|
||||
The ``analog`` estimator is actually identical to the ``collision``
|
||||
|
|
@ -2055,8 +2133,8 @@ attributes or sub-elements. These are not used in "voxel" plots:
|
|||
*Default*: None
|
||||
|
||||
:meshlines:
|
||||
The ``meshlines`` sub-element allows for plotting the boundaries of
|
||||
a tally mesh on top of a plot. Only one ``meshlines`` element is allowed per
|
||||
The ``meshlines`` sub-element allows for plotting the boundaries of a
|
||||
regular mesh on top of a plot. Only one ``meshlines`` element is allowed per
|
||||
``plot`` element, and it must contain as attributes or sub-elements a mesh
|
||||
type and a linewidth. Optionally, a color may be specified for the overlay:
|
||||
|
||||
|
|
|
|||
|
|
@ -204,20 +204,22 @@ should be used:
|
|||
Compiling with MPI
|
||||
++++++++++++++++++
|
||||
|
||||
To compile with MPI, set the :envvar:`FC` environment variable to the path to
|
||||
the MPI Fortran wrapper. For example, in a bash shell:
|
||||
To compile with MPI, set the :envvar:`FC` and :envvar:`CC` environment variables
|
||||
to the path to the MPI Fortran and C wrappers, respectively. For example, in a
|
||||
bash shell:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
export FC=mpif90
|
||||
export CC=mpicc
|
||||
cmake /path/to/openmc
|
||||
|
||||
Note that in many shells, an environment variable can be set for a single
|
||||
command, i.e.
|
||||
Note that in many shells, environment variables can be set for a single command,
|
||||
i.e.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
FC=mpif90 cmake /path/to/openmc
|
||||
FC=mpif90 CC=mpicc cmake /path/to/openmc
|
||||
|
||||
Selecting HDF5 Installation
|
||||
+++++++++++++++++++++++++++
|
||||
|
|
@ -343,7 +345,7 @@ compiler, it is necessary to specify that all objects be compiled with the
|
|||
.. code-block:: sh
|
||||
|
||||
mkdir build && cd build
|
||||
FC=ifort FFLAGS=-mmic cmake -Dopenmp=on ..
|
||||
FC=ifort CC=icc FFLAGS=-mmic cmake -Dopenmp=on ..
|
||||
make
|
||||
|
||||
Note that unless an HDF5 build for the Intel Xeon Phi is already on your target
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ moderator = openmc.Material(material_id=41, name='moderator')
|
|||
moderator.set_density('g/cc', 1.0)
|
||||
moderator.add_nuclide(h1, 2.)
|
||||
moderator.add_nuclide(o16, 1.)
|
||||
moderator.add_s_alpha_beta('c_H_in_H2O', '71t')
|
||||
moderator.add_s_alpha_beta('c_H_in_H2O')
|
||||
|
||||
fuel = openmc.Material(material_id=40, name='fuel')
|
||||
fuel.set_density('g/cc', 4.5)
|
||||
|
|
@ -33,7 +33,6 @@ fuel.add_nuclide(u235, 1.)
|
|||
|
||||
# Instantiate a Materials collection and export to XML
|
||||
materials_file = openmc.Materials([moderator, fuel])
|
||||
materials_file.default_xs = '71c'
|
||||
materials_file.export_to_xml()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -34,11 +34,10 @@ moderator = openmc.Material(material_id=3, name='moderator')
|
|||
moderator.set_density('g/cc', 1.0)
|
||||
moderator.add_nuclide(h1, 2.)
|
||||
moderator.add_nuclide(o16, 1.)
|
||||
moderator.add_s_alpha_beta('c_H_in_H2O', '71t')
|
||||
moderator.add_s_alpha_beta('c_H_in_H2O')
|
||||
|
||||
# Instantiate a Materials collection and export to XML
|
||||
materials_file = openmc.Materials([fuel1, fuel2, moderator])
|
||||
materials_file.default_xs = '71c'
|
||||
materials_file.export_to_xml()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ moderator = openmc.Material(material_id=2, name='moderator')
|
|||
moderator.set_density('g/cc', 1.0)
|
||||
moderator.add_nuclide(h1, 2.)
|
||||
moderator.add_nuclide(o16, 1.)
|
||||
moderator.add_s_alpha_beta('c_H_in_H2O', '71t')
|
||||
moderator.add_s_alpha_beta('c_H_in_H2O')
|
||||
|
||||
iron = openmc.Material(material_id=3, name='iron')
|
||||
iron.set_density('g/cc', 7.9)
|
||||
|
|
@ -37,7 +37,6 @@ iron.add_nuclide(fe56, 1.)
|
|||
|
||||
# Instantiate a Materials collection and export to XML
|
||||
materials_file = openmc.Materials([moderator, fuel, iron])
|
||||
materials_file.default_xs = '71c'
|
||||
materials_file.export_to_xml()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,11 +28,10 @@ moderator = openmc.Material(material_id=2, name='moderator')
|
|||
moderator.set_density('g/cc', 1.0)
|
||||
moderator.add_nuclide(h1, 2.)
|
||||
moderator.add_nuclide(o16, 1.)
|
||||
moderator.add_s_alpha_beta('c_H_in_H2O', '71t')
|
||||
moderator.add_s_alpha_beta('c_H_in_H2O')
|
||||
|
||||
# Instantiate a Materials collection and export to XML
|
||||
materials_file = openmc.Materials((moderator, fuel))
|
||||
materials_file.default_xs = '71c'
|
||||
materials_file.export_to_xml()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,11 +28,10 @@ moderator = openmc.Material(material_id=2, name='moderator')
|
|||
moderator.set_density('g/cc', 1.0)
|
||||
moderator.add_nuclide(h1, 2.)
|
||||
moderator.add_nuclide(o16, 1.)
|
||||
moderator.add_s_alpha_beta('c_H_in_H2O', '71t')
|
||||
moderator.add_s_alpha_beta('c_H_in_H2O')
|
||||
|
||||
# Instantiate a Materials collection and export to XML
|
||||
materials_file = openmc.Materials([moderator, fuel])
|
||||
materials_file.default_xs = '71c'
|
||||
materials_file.export_to_xml()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -98,11 +98,10 @@ borated_water.add_nuclide(h1, 4.9457e-2)
|
|||
borated_water.add_nuclide(h2, 7.4196e-6)
|
||||
borated_water.add_nuclide(o16, 2.4672e-2)
|
||||
borated_water.add_nuclide(o17, 6.0099e-5)
|
||||
borated_water.add_s_alpha_beta('c_H_in_H2O', '71t')
|
||||
borated_water.add_s_alpha_beta('c_H_in_H2O')
|
||||
|
||||
# Instantiate a Materials collection and export to XML
|
||||
materials_file = openmc.Materials([uo2, helium, zircaloy, borated_water])
|
||||
materials_file.default_xs = '71c'
|
||||
materials_file.export_to_xml()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ 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 = openmc.XSdata('UO2', groups)
|
||||
uo2_xsdata.order = 0
|
||||
uo2_xsdata.total = [0.1779492, 0.3298048, 0.4803882, 0.5543674,
|
||||
0.3118013, 0.3951678, 0.5644058]
|
||||
|
|
@ -41,7 +41,7 @@ uo2_xsdata.nu_fission = [2.005998E-02, 2.027303E-03, 1.570599E-02,
|
|||
uo2_xsdata.chi = [5.8791E-01, 4.1176E-01, 3.3906E-04, 1.1761E-07,
|
||||
0.0000E+00, 0.0000E+00, 0.0000E+00]
|
||||
|
||||
h2o_xsdata = openmc.XSdata('LWTR.300K', groups)
|
||||
h2o_xsdata = openmc.XSdata('LWTR', groups)
|
||||
h2o_xsdata.order = 0
|
||||
h2o_xsdata.total = [0.15920605, 0.412969593, 0.59030986, 0.58435,
|
||||
0.718, 1.2544497, 2.650379]
|
||||
|
|
@ -66,8 +66,8 @@ mg_cross_sections_file.export_to_xml()
|
|||
###############################################################################
|
||||
|
||||
# Instantiate some Macroscopic Data
|
||||
uo2_data = openmc.Macroscopic('UO2', '300K')
|
||||
h2o_data = openmc.Macroscopic('LWTR', '300K')
|
||||
uo2_data = openmc.Macroscopic('UO2')
|
||||
h2o_data = openmc.Macroscopic('LWTR')
|
||||
|
||||
# Instantiate some Materials and register the appropriate Macroscopic objects
|
||||
uo2 = openmc.Material(material_id=1, name='UO2 fuel')
|
||||
|
|
@ -80,7 +80,6 @@ water.add_macroscopic(h2o_data)
|
|||
|
||||
# Instantiate a Materials collection and export to XML
|
||||
materials_file = openmc.Materials([uo2, water])
|
||||
materials_file.default_xs = '300K'
|
||||
materials_file.export_to_xml()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ fuel.add_nuclide(u235, 1.)
|
|||
|
||||
# Instantiate a Materials collection and export to XML
|
||||
materials_file = openmc.Materials([fuel])
|
||||
materials_file.default_xs = '71c'
|
||||
materials_file.export_to_xml()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<materials>
|
||||
|
||||
<default_xs>71c</default_xs>
|
||||
|
||||
<material id="40">
|
||||
<density value="4.5" units="g/cc" />
|
||||
<nuclide name="U235" ao="1.0" />
|
||||
|
|
@ -12,7 +10,7 @@
|
|||
<density value="1.0" units="g/cc" />
|
||||
<nuclide name="H1" ao="2.0" />
|
||||
<nuclide name="O16" ao="1.0" />
|
||||
<sab name="c_H_in_H2O" xs="71t" />
|
||||
<sab name="c_H_in_H2O"/>
|
||||
</material>
|
||||
|
||||
</materials>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<materials>
|
||||
|
||||
<default_xs>71c</default_xs>
|
||||
|
||||
<material id="1">
|
||||
<density value="4.5" units="g/cc" />
|
||||
<nuclide name="U235" ao="1.0" />
|
||||
|
|
@ -17,7 +15,7 @@
|
|||
<density value="1.0" units="g/cc" />
|
||||
<nuclide name="O16" ao="1.0" />
|
||||
<nuclide name="H1" ao="2.0" />
|
||||
<sab name="c_H_in_H2O" xs="71t" />
|
||||
<sab name="c_H_in_H2O" />
|
||||
</material>
|
||||
|
||||
</materials>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<materials>
|
||||
|
||||
<default_xs>71c</default_xs>
|
||||
|
||||
<!-- Definition of materials -->
|
||||
<material id="1">
|
||||
<density value="4.5" units="g/cc" />
|
||||
|
|
@ -13,7 +11,7 @@
|
|||
<density value="1.0" units="g/cc" />
|
||||
<nuclide name="H1" ao="2.0" />
|
||||
<nuclide name="O16" ao="1.0" />
|
||||
<sab name="c_H_in_H2O" xs="71t" />
|
||||
<sab name="c_H_in_H2O" />
|
||||
</material>
|
||||
|
||||
</materials>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<materials>
|
||||
|
||||
<default_xs>71c</default_xs>
|
||||
|
||||
<!-- Definition of materials -->
|
||||
<material id="1">
|
||||
<density value="4.5" units="g/cc" />
|
||||
|
|
@ -13,7 +11,7 @@
|
|||
<density value="1.0" units="g/cc" />
|
||||
<nuclide name="H1" ao="2.0" />
|
||||
<nuclide name="O16" ao="1.0" />
|
||||
<sab name="c_H_in_H2O" xs="71t" />
|
||||
<sab name="c_H_in_H2O" />
|
||||
</material>
|
||||
|
||||
</materials>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<materials>
|
||||
|
||||
<!-- By default, use 300K cross sections -->
|
||||
<default_xs>71c</default_xs>
|
||||
|
||||
<!--
|
||||
Since O-18 is not present in ENDF/B-VII, it was necessary to combine the
|
||||
atom densities for O-17 and O-18 in any materials containing Oxygen.
|
||||
|
|
@ -64,7 +61,7 @@
|
|||
<nuclide name="H2" ao="7.4196e-06" />
|
||||
<nuclide name="O16" ao="2.4672e-02" />
|
||||
<nuclide name="O17" ao="6.0099e-05" />
|
||||
<sab name="c_H_in_H2O" xs="71t" />
|
||||
<sab name="c_H_in_H2O" />
|
||||
</material>
|
||||
|
||||
</materials>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
<?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" />
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
-->
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>UO2.300K</name>
|
||||
<alias>UO2.300K</alias>
|
||||
<name>UO2</name>
|
||||
<alias>UO2</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
|
@ -67,8 +67,8 @@
|
|||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>MOX1.300K</name>
|
||||
<alias>MOX1.300K</alias>
|
||||
<name>MOX1</name>
|
||||
<alias>MOX1</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
|
@ -124,8 +124,8 @@
|
|||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>MOX2.300K</name>
|
||||
<alias>MOX2.300K</alias>
|
||||
<name>MOX2</name>
|
||||
<alias>MOX2</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
|
@ -180,8 +180,8 @@
|
|||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>MOX3.300K</name>
|
||||
<alias>MOX3.300K</alias>
|
||||
<name>MOX3</name>
|
||||
<alias>MOX3</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
|
@ -236,8 +236,8 @@
|
|||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>FC.300K</name>
|
||||
<alias>FC.300K</alias>
|
||||
<name>FC</name>
|
||||
<alias>FC</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>true</fissionable>
|
||||
|
|
@ -286,8 +286,8 @@
|
|||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>GT.300K</name>
|
||||
<alias>GT.300K</alias>
|
||||
<name>GT</name>
|
||||
<alias>GT</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>false</fissionable>
|
||||
|
|
@ -318,8 +318,8 @@
|
|||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>LWTR.300K</name>
|
||||
<alias>LWTR.300K</alias>
|
||||
<name>LWTR</name>
|
||||
<alias>LWTR</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>false</fissionable>
|
||||
|
|
@ -351,8 +351,8 @@
|
|||
|
||||
<xsdata>
|
||||
<!-- Meta data for this data -->
|
||||
<name>CR.300K</name>
|
||||
<alias>CR.300K</alias>
|
||||
<name>CR</name>
|
||||
<alias>CR</alias>
|
||||
<kT> 2.53E-8 </kT> <!-- in MeV -->
|
||||
<order>0</order>
|
||||
<fissionable>false</fissionable>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<materials>
|
||||
|
||||
<default_xs>71c</default_xs>
|
||||
|
||||
<material id="1">
|
||||
<density value="4.5" units="g/cc" />
|
||||
<nuclide name="U235" ao="1.0" />
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
openmc \- Executes the OpenMC Monte Carlo code
|
||||
.SH DESCRIPTION
|
||||
This command is used to execute the OpenMC Monte Carlo code. It is assumed that
|
||||
a set of XML input files has already been created and that ACE format cross
|
||||
a set of XML input files has already been created and that HDF5 format cross
|
||||
sections are available.
|
||||
.SH SYNOPSIS
|
||||
\fBopenmc\fR [\fIoptions\fR] [\fIpath\fR]
|
||||
|
|
@ -40,11 +40,21 @@ The behavior of
|
|||
.B openmc
|
||||
is affected by the following environment variables.
|
||||
.TP
|
||||
.B CROSS_SECTIONS
|
||||
.B OPENMC_CROSS_SECTIONS
|
||||
Indicates the default path to the cross_sections.xml summary file that is used
|
||||
to locate ACE format cross section libraries if the user has not specified the
|
||||
to locate HDF5 format cross section libraries if the user has not specified the
|
||||
<cross_sections> tag in
|
||||
.I settings.xml\fP.
|
||||
.TP
|
||||
.B OPENMC_MG_CROSS_SECTIONS
|
||||
Indicates the default path to the mgxs.xml file that contains multi-group cross
|
||||
section libraries if the user has not specified the <cross_sections> tag in
|
||||
.I settings.xml\fP.
|
||||
.TP
|
||||
.B OPENMC_MULTIPOLE_LIBRARY
|
||||
Indicates the default path to a directory containing windowed multipole data if
|
||||
the user has not specified the <multipole_library> tag in
|
||||
.I settings.xml\fP.
|
||||
.SH LICENSE
|
||||
Copyright \(co 2011-2016 Massachusetts Institute of Technology.
|
||||
.PP
|
||||
|
|
|
|||
|
|
@ -6,21 +6,23 @@ from openmc.nuclide import *
|
|||
from openmc.macroscopic import *
|
||||
from openmc.material import *
|
||||
from openmc.plots import *
|
||||
from openmc.region import *
|
||||
from openmc.volume import *
|
||||
from openmc.source import *
|
||||
from openmc.settings import *
|
||||
from openmc.surface import *
|
||||
from openmc.universe import *
|
||||
from openmc.mesh import *
|
||||
from openmc.mgxs_library import *
|
||||
from openmc.filter import *
|
||||
from openmc.trigger import *
|
||||
from openmc.tallies import *
|
||||
from openmc.mgxs_library import *
|
||||
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 *
|
||||
from openmc.mixin import *
|
||||
|
||||
try:
|
||||
from openmc.opencg_compatible import *
|
||||
|
|
|
|||
|
|
@ -85,6 +85,10 @@ class Cell(object):
|
|||
Array of offsets used for distributed cell searches
|
||||
distribcell_index : int
|
||||
Index of this cell in distribcell arrays
|
||||
volume_information : dict
|
||||
Estimate of the volume and total number of atoms of each nuclide from a
|
||||
stochastic volume calculation. This information is set with the
|
||||
:meth:`Cell.add_volume_information` method.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -100,6 +104,7 @@ class Cell(object):
|
|||
self._translation = None
|
||||
self._offsets = None
|
||||
self._distribcell_index = None
|
||||
self._volume_information = None
|
||||
|
||||
def __contains__(self, point):
|
||||
if self.region is None:
|
||||
|
|
@ -212,6 +217,10 @@ class Cell(object):
|
|||
def distribcell_index(self):
|
||||
return self._distribcell_index
|
||||
|
||||
@property
|
||||
def volume_information(self):
|
||||
return self._volume_information
|
||||
|
||||
@id.setter
|
||||
def id(self, cell_id):
|
||||
if cell_id is None:
|
||||
|
|
@ -351,6 +360,25 @@ class Cell(object):
|
|||
else:
|
||||
self.region = Intersection(self.region, region)
|
||||
|
||||
def add_volume_information(self, volume_calc):
|
||||
"""Add volume information to a cell.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
volume_calc : openmc.VolumeCalculation
|
||||
Results from a stochastic volume calculation
|
||||
|
||||
"""
|
||||
if volume_calc.domain_type == 'cell':
|
||||
for cell_id in volume_calc.results:
|
||||
if cell_id == self.id:
|
||||
self._volume_information = volume_calc.results[cell_id]
|
||||
break
|
||||
else:
|
||||
raise ValueError('No volume information found for this cell.')
|
||||
else:
|
||||
raise ValueError('No volume information found for this cell.')
|
||||
|
||||
def get_cell_instance(self, path, distribcell_index):
|
||||
|
||||
# If the Cell is filled by a Material
|
||||
|
|
@ -368,8 +396,19 @@ class Cell(object):
|
|||
|
||||
return offset
|
||||
|
||||
def get_all_nuclides(self):
|
||||
"""Return all nuclides contained in the cell
|
||||
def get_nuclides(self):
|
||||
"""Returns all nuclides in the cell
|
||||
|
||||
Returns
|
||||
-------
|
||||
nuclides : list of str
|
||||
List of nuclide names
|
||||
|
||||
"""
|
||||
return self.fill.get_nuclides() if self.fill_type != 'void' else []
|
||||
|
||||
def get_nuclide_densities(self):
|
||||
"""Return all nuclides contained in the cell and their densities
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -381,8 +420,24 @@ class Cell(object):
|
|||
|
||||
nuclides = OrderedDict()
|
||||
|
||||
if self.fill_type != 'void':
|
||||
nuclides.update(self.fill.get_all_nuclides())
|
||||
if self.fill_type == 'material':
|
||||
nuclides.update(self.fill.get_nuclide_densities())
|
||||
elif self.fill_type == 'void':
|
||||
pass
|
||||
else:
|
||||
if self.volume_information is not None:
|
||||
volume = self.volume_information['volume'][0]
|
||||
for full_name, atoms in self.volume_information['atoms']:
|
||||
name, xs = full_name.split('.')
|
||||
nuclide = openmc.Nuclide(name, xs)
|
||||
density = 1.0e-24 * atoms[0]/volume # density in atoms/b-cm
|
||||
nuclides[name] = (nuclide, density)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
'Volume information is needed to calculate microscopic cross '
|
||||
'sections for cell {}. This can be done by running a '
|
||||
'stochastic volume calculation via the '
|
||||
'openmc.VolumeCalculation object'.format(self.id))
|
||||
|
||||
return nuclides
|
||||
|
||||
|
|
|
|||
|
|
@ -14,3 +14,4 @@ from .nbody import *
|
|||
from .thermal import *
|
||||
from .urr import *
|
||||
from .library import *
|
||||
from .fission_energy import *
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import sys
|
|||
|
||||
import numpy as np
|
||||
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
|
@ -131,7 +133,7 @@ def get_table(filename, name=None):
|
|||
.format(name))
|
||||
|
||||
|
||||
class Library(object):
|
||||
class Library(EqualityMixin):
|
||||
"""A Library objects represents an ACE-formatted file which may contain
|
||||
multiple tables with data.
|
||||
|
||||
|
|
@ -353,7 +355,7 @@ class Library(object):
|
|||
lines = [ace_file.readline() for i in range(13)]
|
||||
|
||||
|
||||
class Table(object):
|
||||
class Table(EqualityMixin):
|
||||
"""ACE cross section table
|
||||
|
||||
Parameters
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@ from numbers import Real
|
|||
import numpy as np
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
from openmc.stats import Univariate, Tabular, Uniform
|
||||
from .function import INTERPOLATION_SCHEME
|
||||
|
||||
|
||||
class AngleDistribution(object):
|
||||
class AngleDistribution(EqualityMixin):
|
||||
"""Angle distribution as a function of incoming energy
|
||||
|
||||
Parameters
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import openmc.data
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
||||
|
||||
class AngleEnergy(object):
|
||||
class AngleEnergy(EqualityMixin):
|
||||
"""Distribution in angle and energy of a secondary particle."""
|
||||
|
||||
__metaclass = ABCMeta
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ class CorrelatedAngleEnergy(AngleEnergy):
|
|||
@property
|
||||
def interpolation(self):
|
||||
return self._interpolation
|
||||
|
||||
@property
|
||||
def energy(self):
|
||||
return self._energy
|
||||
|
|
|
|||
|
|
@ -218,3 +218,8 @@ def atomic_mass(isotope):
|
|||
isotope = isotope[:isotope.find('_')]
|
||||
|
||||
return _ATOMIC_MASS.get(isotope.lower())
|
||||
|
||||
# The value of the Boltzman constant in units of MeV / K
|
||||
# Values here are from the Committee on Data for Science and Technology
|
||||
# (CODATA) 2010 recommendation (doi:10.1103/RevModPhys.84.1527).
|
||||
K_BOLTZMANN = 8.6173324E-11
|
||||
|
|
|
|||
44
openmc/data/endf_utils.py
Normal file
44
openmc/data/endf_utils.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
"""This module contains a few utility functions for reading ENDF_ data. It is by
|
||||
no means enough to read an entire ENDF file. For a more complete ENDF reader,
|
||||
see Pyne_.
|
||||
|
||||
.. _ENDF: http://www.nndc.bnl.gov/endf
|
||||
.. _Pyne: http://www.pyne.io
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
def read_float(float_string):
|
||||
"""Parse ENDF 6E11.0 formatted string into a float."""
|
||||
assert len(float_string) == 11
|
||||
pattern = r'([\s\-]\d+\.\d+)([\+\-]\d+)'
|
||||
return float(re.sub(pattern, r'\1e\2', float_string))
|
||||
|
||||
|
||||
def read_CONT_line(line):
|
||||
"""Parse 80-column line from ENDF CONT record into floats and ints."""
|
||||
return (read_float(line[0:11]), read_float(line[11:22]), int(line[22:33]),
|
||||
int(line[33:44]), int(line[44:55]), int(line[55:66]),
|
||||
int(line[66:70]), int(line[70:72]), int(line[72:75]),
|
||||
int(line[75:80]))
|
||||
|
||||
|
||||
def identify_nuclide(fname):
|
||||
"""Read the header of an ENDF file and extract identifying information."""
|
||||
with open(fname, 'r') as fh:
|
||||
# Skip the tape id (TPID).
|
||||
line = fh.readline()
|
||||
|
||||
# Read the first HEAD and CONT info.
|
||||
line = fh.readline()
|
||||
ZA, AW, LRP, LFI, NLIB, NMOD, MAT, MF, MT, NS = read_CONT_line(line)
|
||||
line = fh.readline()
|
||||
ELIS, STA, LIS, LISO, junk, NFOR, MAT, MF, MT, NS = read_CONT_line(line)
|
||||
|
||||
# Return dictionary of the most important identifying information.
|
||||
return {'Z': int(ZA) // 1000,
|
||||
'A': int(ZA) % 1000,
|
||||
'LFI': bool(LFI),
|
||||
'LIS': LIS,
|
||||
'LISO': LISO}
|
||||
|
|
@ -8,9 +8,10 @@ import numpy as np
|
|||
from .function import Tabulated1D, INTERPOLATION_SCHEME
|
||||
from openmc.stats.univariate import Univariate, Tabular, Discrete, Mixture
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
||||
|
||||
class EnergyDistribution(object):
|
||||
class EnergyDistribution(EqualityMixin):
|
||||
"""Abstract superclass for all energy distributions."""
|
||||
|
||||
__metaclass__ = ABCMeta
|
||||
|
|
|
|||
593
openmc/data/fission_energy.py
Normal file
593
openmc/data/fission_energy.py
Normal file
|
|
@ -0,0 +1,593 @@
|
|||
from collections import Callable
|
||||
from copy import deepcopy
|
||||
import sys
|
||||
|
||||
import h5py
|
||||
import numpy as np
|
||||
|
||||
from .data import ATOMIC_SYMBOL
|
||||
from .endf_utils import read_float, read_CONT_line, identify_nuclide
|
||||
from .function import Function1D, Tabulated1D, Polynomial, Sum
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
|
||||
def _extract_458_data(filename):
|
||||
"""Read an ENDF file and extract the MF=1, MT=458 values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
Path to and ENDF file
|
||||
|
||||
Returns
|
||||
-------
|
||||
value : dict of str to list of float
|
||||
Dictionary that gives lists of coefficients for each energy component.
|
||||
The keys are the 2-3 letter strings used in ENDF-102, e.g. 'EFR' and
|
||||
'ET'. The list will have a length of 1 for Sher-Beck data, more for
|
||||
polynomial data.
|
||||
uncertainty : dict of str to list of float
|
||||
A dictionary with the same format as above. This is probably a
|
||||
one-standard deviation value, but that is not specified explicitly in
|
||||
ENDF-102. Also, some evaluations will give zero uncertainty. Use with
|
||||
caution.
|
||||
|
||||
"""
|
||||
ident = identify_nuclide(filename)
|
||||
|
||||
if not ident['LFI']:
|
||||
# This nuclide isn't fissionable.
|
||||
return None
|
||||
|
||||
# Extract the MF=1, MT=458 section.
|
||||
lines = []
|
||||
with open(filename, 'r') as fh:
|
||||
line = fh.readline()
|
||||
while line != '':
|
||||
if line[70:75] == ' 1458':
|
||||
lines.append(line)
|
||||
line = fh.readline()
|
||||
|
||||
if len(lines) == 0:
|
||||
# No 458 data here.
|
||||
return None
|
||||
|
||||
# Read the number of coefficients in this LIST record.
|
||||
NPL = read_CONT_line(lines[1])[4]
|
||||
|
||||
# Parse the ENDF LIST into an array.
|
||||
data = []
|
||||
for i in range(NPL):
|
||||
row, column = divmod(i, 6)
|
||||
data.append(read_float(lines[2 + row][11*column:11*(column+1)]))
|
||||
|
||||
# Declare the coefficient names and the order they are given in. The LIST
|
||||
# contains a value followed immediately by an uncertainty for each of these
|
||||
# components, times the polynomial order + 1.
|
||||
labels = ('EFR', 'ENP', 'END', 'EGP', 'EGD', 'EB', 'ENU', 'ER', 'ET')
|
||||
|
||||
# Associate each set of values and uncertainties with its label.
|
||||
value = {}
|
||||
uncertainty = {}
|
||||
for i, label in enumerate(labels):
|
||||
value[label] = data[2*i::18]
|
||||
uncertainty[label] = data[2*i + 1::18]
|
||||
|
||||
# In ENDF/B-7.1, data for 2nd-order coefficients were mistakenly not
|
||||
# converted from MeV to eV. Check for this error and fix it if present.
|
||||
n_coeffs = len(value['EFR'])
|
||||
if n_coeffs == 3: # Only check 2nd-order data.
|
||||
# Check each energy component for the error. If a 1 MeV neutron
|
||||
# causes a change of more than 100 MeV, we know something is wrong.
|
||||
error_present = False
|
||||
for coeffs in value.values():
|
||||
second_order = coeffs[2]
|
||||
if abs(second_order) * 1e12 > 1e8:
|
||||
error_present = True
|
||||
break
|
||||
|
||||
# If we found the error, reduce all 2nd-order coeffs by 10**6.
|
||||
if error_present:
|
||||
for coeffs in value.values(): coeffs[2] *= 1e-6
|
||||
for coeffs in uncertainty.values(): coeffs[2] *= 1e-6
|
||||
|
||||
# Convert eV to MeV.
|
||||
for coeffs in value.values():
|
||||
for i in range(len(coeffs)):
|
||||
coeffs[i] *= 10**(-6 + 6*i)
|
||||
for coeffs in uncertainty.values():
|
||||
for i in range(len(coeffs)):
|
||||
coeffs[i] *= 10**(-6 + 6*i)
|
||||
|
||||
return value, uncertainty
|
||||
|
||||
|
||||
def write_compact_458_library(endf_files, output_name='fission_Q_data.h5',
|
||||
comment=None, verbose=False):
|
||||
"""Read ENDF files, strip the MF=1 MT=458 data and write to small HDF5.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
endf_files : Collection of str
|
||||
Strings giving the paths to the ENDF files that will be parsed for data.
|
||||
output_name : str
|
||||
Name of the output HDF5 file. Default is 'fission_Q_data.h5'.
|
||||
comment : str
|
||||
Comment to write in the output HDF5 file. Defaults to no comment.
|
||||
verbose : bool
|
||||
If True, print the name of each isomer as it is read. Defaults to
|
||||
False.
|
||||
|
||||
"""
|
||||
# Open the output file.
|
||||
out = h5py.File(output_name, 'w', libver='latest')
|
||||
|
||||
# Write comments, if given. This commented out comment is the one used for
|
||||
# the library distributed with OpenMC.
|
||||
#comment = ('This data is extracted from ENDF/B-VII.1 library. Thanks '
|
||||
# 'evaluators, for all your hard work :) Citation: '
|
||||
# 'M. B. Chadwick, M. Herman, P. Oblozinsky, '
|
||||
# 'M. E. Dunn, Y. Danon, A. C. Kahler, D. L. Smith, '
|
||||
# 'B. Pritychenko, G. Arbanas, R. Arcilla, R. Brewer, '
|
||||
# 'D. A. Brown, R. Capote, A. D. Carlson, Y. S. Cho, H. Derrien, '
|
||||
# 'K. Guber, G. M. Hale, S. Hoblit, S. Holloway, T. D. Johnson, '
|
||||
# 'T. Kawano, B. C. Kiedrowski, H. Kim, S. Kunieda, '
|
||||
# 'N. M. Larson, L. Leal, J. P. Lestone, R. C. Little, '
|
||||
# 'E. A. McCutchan, R. E. MacFarlane, M. MacInnes, '
|
||||
# 'C. M. Mattoon, R. D. McKnight, S. F. Mughabghab, '
|
||||
# 'G. P. A. Nobre, G. Palmiotti, A. Palumbo, M. T. Pigni, '
|
||||
# 'V. G. Pronyaev, R. O. Sayer, A. A. Sonzogni, N. C. Summers, '
|
||||
# 'P. Talou, I. J. Thompson, A. Trkov, R. L. Vogt, '
|
||||
# 'S. C. van der Marck, A. Wallner, M. C. White, D. Wiarda, '
|
||||
# 'and P. G. Young. ENDF/B-VII.1 nuclear data for science and '
|
||||
# 'technology: Cross sections, covariances, fission product '
|
||||
# 'yields and decay data", Nuclear Data Sheets, '
|
||||
# '112(12):2887-2996 (2011).')
|
||||
if comment is not None:
|
||||
out.attrs['comment'] = np.string_(comment)
|
||||
|
||||
# Declare the order of the components. Use fixed-length numpy strings
|
||||
# because they work well with h5py.
|
||||
labels = np.array(('EFR', 'ENP', 'END', 'EGP', 'EGD', 'EB', 'ENU', 'ER',
|
||||
'ET'), dtype='S3')
|
||||
out.attrs['component order'] = labels
|
||||
|
||||
# Iterate over the given files.
|
||||
if verbose: print('Reading ENDF files:')
|
||||
for fname in endf_files:
|
||||
if verbose: print(fname)
|
||||
|
||||
ident = identify_nuclide(fname)
|
||||
|
||||
# Skip non-fissionable nuclides.
|
||||
if not ident['LFI']: continue
|
||||
|
||||
# Get the important bits.
|
||||
data = _extract_458_data(fname)
|
||||
if data is None: continue
|
||||
value, uncertainty = data
|
||||
|
||||
# Make a group for this isomer.
|
||||
name = ATOMIC_SYMBOL[ident['Z']] + str(ident['A'])
|
||||
if ident['LISO'] != 0:
|
||||
name += '_m' + str(ident['LISO'])
|
||||
nuclide_group = out.create_group(name)
|
||||
|
||||
# Write all the coefficients into one array. The first dimension gives
|
||||
# the component (e.g. fragments or prompt neutrons); the second switches
|
||||
# between value and uncertainty; the third gives the polynomial order.
|
||||
n_coeffs = len(value['EFR'])
|
||||
data_out = np.zeros((len(labels), 2, n_coeffs))
|
||||
for i, label in enumerate(labels):
|
||||
data_out[i, 0, :] = value[label.decode()]
|
||||
data_out[i, 1, :] = uncertainty[label.decode()]
|
||||
nuclide_group.create_dataset('data', data=data_out)
|
||||
|
||||
out.close()
|
||||
|
||||
|
||||
class FissionEnergyRelease(EqualityMixin):
|
||||
"""Energy relased by fission reactions.
|
||||
|
||||
Energy is carried away from fission reactions by many different particles.
|
||||
The attributes of this class specify how much energy is released in the form
|
||||
of fission fragments, neutrons, photons, etc. Each component is also (in
|
||||
general) a function of the incident neutron energy.
|
||||
|
||||
Following a fission reaction, most of the energy release is carried by the
|
||||
daughter nuclei fragments. These fragments accelerate apart from the
|
||||
Coulomb force on the time scale of ~10^-20 s [1]. Those fragments emit
|
||||
prompt neutrons between ~10^-18 and ~10^-13 s after scission (although some
|
||||
prompt neutrons may come directly from the scission point) [1]. Prompt
|
||||
photons follow with a time scale of ~10^-14 to ~10^-7 s [1]. The fission
|
||||
products then emit delayed neutrons with half lives between 0.1 and 100 s.
|
||||
The remaining fission energy comes from beta decays of the fission products
|
||||
which release beta particles, photons, and neutrinos (that escape the
|
||||
reactor and do not produce usable heat).
|
||||
|
||||
Use the class methods to instantiate this class from an HDF5 or ENDF
|
||||
dataset. The :meth:`FissionEnergyRelease.from_hdf5` method builds this
|
||||
class from the usual OpenMC HDF5 data files.
|
||||
:meth:`FissionEnergyRelease.from_endf` uses ENDF-formatted data.
|
||||
:meth:`FissionEnergyRelease.from_compact_hdf5` uses a different HDF5 format
|
||||
that is meant to be compact and store the exact same data as the ENDF
|
||||
format. Files with this format can be generated with the
|
||||
:func:`openmc.data.write_compact_458_library` function.
|
||||
|
||||
References
|
||||
----------
|
||||
[1] D. G. Madland, "Total prompt energy release in the neutron-induced
|
||||
fission of ^235U, ^238U, and ^239Pu", Nuclear Physics A 772:113--137 (2006).
|
||||
<http://dx.doi.org/10.1016/j.nuclphysa.2006.03.013>
|
||||
|
||||
Attributes
|
||||
----------
|
||||
fragments : Callable
|
||||
Function that accepts incident neutron energy value(s) and returns the
|
||||
kinetic energy of the fission daughter nuclides (after prompt neutron
|
||||
emission).
|
||||
prompt_neutrons : Callable
|
||||
Function of energy that returns the kinetic energy of prompt fission
|
||||
neutrons.
|
||||
delayed_neutrons : Callable
|
||||
Function of energy that returns the kinetic energy of delayed neutrons
|
||||
emitted from fission products.
|
||||
prompt_photons : Callable
|
||||
Function of energy that returns the kinetic energy of prompt fission
|
||||
photons.
|
||||
delayed_photons : Callable
|
||||
Function of energy that returns the kinetic energy of delayed photons.
|
||||
betas : Callable
|
||||
Function of energy that returns the kinetic energy of delayed beta
|
||||
particles.
|
||||
neutrinos : Callable
|
||||
Function of energy that returns the kinetic energy of neutrinos.
|
||||
recoverable : Callable
|
||||
Function of energy that returns the kinetic energy of all products that
|
||||
can be absorbed in the reactor (all of the energy except for the
|
||||
neutrinos).
|
||||
total : Callable
|
||||
Function of energy that returns the kinetic energy of all products.
|
||||
q_prompt : Callable
|
||||
Function of energy that returns the prompt fission Q-value (fragments +
|
||||
prompt neutrons + prompt photons - incident neutron energy).
|
||||
q_recoverable : Callable
|
||||
Function of energy that returns the recoverable fission Q-value
|
||||
(total release - neutrinos - incident neutron energy). This value is
|
||||
sometimes referred to as the pseudo-Q-value.
|
||||
q_total : Callable
|
||||
Function of energy that returns the total fission Q-value (total release
|
||||
- incident neutron energy).
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
self._fragments = None
|
||||
self._prompt_neutrons = None
|
||||
self._delayed_neutrons = None
|
||||
self._prompt_photons = None
|
||||
self._delayed_photons = None
|
||||
self._betas = None
|
||||
self._neutrinos = None
|
||||
|
||||
@property
|
||||
def fragments(self):
|
||||
return self._fragments
|
||||
|
||||
@property
|
||||
def prompt_neutrons(self):
|
||||
return self._prompt_neutrons
|
||||
|
||||
@property
|
||||
def delayed_neutrons(self):
|
||||
return self._delayed_neutrons
|
||||
|
||||
@property
|
||||
def prompt_photons(self):
|
||||
return self._prompt_photons
|
||||
|
||||
@property
|
||||
def delayed_photons(self):
|
||||
return self._delayed_photons
|
||||
|
||||
@property
|
||||
def betas(self):
|
||||
return self._betas
|
||||
|
||||
@property
|
||||
def neutrinos(self):
|
||||
return self._neutrinos
|
||||
|
||||
@property
|
||||
def recoverable(self):
|
||||
return Sum([self.fragments, self.prompt_neutrons, self.delayed_neutrons,
|
||||
self.prompt_photons, self.delayed_photons, self.betas])
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return Sum([self.fragments, self.prompt_neutrons, self.delayed_neutrons,
|
||||
self.prompt_photons, self.delayed_photons, self.betas,
|
||||
self.neutrinos])
|
||||
|
||||
@property
|
||||
def q_prompt(self):
|
||||
return Sum([self.fragments, self.prompt_neutrons, self.prompt_photons,
|
||||
lambda E: -E])
|
||||
|
||||
@property
|
||||
def q_recoverable(self):
|
||||
return Sum([self.recoverable, lambda E: -E])
|
||||
|
||||
@property
|
||||
def q_total(self):
|
||||
return Sum([self.total, lambda E: -E])
|
||||
|
||||
@fragments.setter
|
||||
def fragments(self, energy_release):
|
||||
cv.check_type('fragments', energy_release, Callable)
|
||||
self._fragments = energy_release
|
||||
|
||||
@prompt_neutrons.setter
|
||||
def prompt_neutrons(self, energy_release):
|
||||
cv.check_type('prompt_neutrons', energy_release, Callable)
|
||||
self._prompt_neutrons = energy_release
|
||||
|
||||
@delayed_neutrons.setter
|
||||
def delayed_neutrons(self, energy_release):
|
||||
cv.check_type('delayed_neutrons', energy_release, Callable)
|
||||
self._delayed_neutrons = energy_release
|
||||
|
||||
@prompt_photons.setter
|
||||
def prompt_photons(self, energy_release):
|
||||
cv.check_type('prompt_photons', energy_release, Callable)
|
||||
self._prompt_photons = energy_release
|
||||
|
||||
@delayed_photons.setter
|
||||
def delayed_photons(self, energy_release):
|
||||
cv.check_type('delayed_photons', energy_release, Callable)
|
||||
self._delayed_photons = energy_release
|
||||
|
||||
@betas.setter
|
||||
def betas(self, energy_release):
|
||||
cv.check_type('betas', energy_release, Callable)
|
||||
self._betas = energy_release
|
||||
|
||||
@neutrinos.setter
|
||||
def neutrinos(self, energy_release):
|
||||
cv.check_type('neutrinos', energy_release, Callable)
|
||||
self._neutrinos = energy_release
|
||||
|
||||
@classmethod
|
||||
def _from_dictionary(cls, energy_release, incident_neutron):
|
||||
"""Generate fission energy release data from a dictionary.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
energy_release : dict of str to list of float
|
||||
Dictionary that gives lists of coefficients for each energy
|
||||
component. The keys are the 2-3 letter strings used in ENDF-102,
|
||||
e.g. 'EFR' and 'ET'. The list will have a length of 1 for Sher-Beck
|
||||
data, more for polynomial data.
|
||||
|
||||
incident_neutron : openmc.data.IncidentNeutron
|
||||
Corresponding incident neutron dataset
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.FissionEnergyRelease
|
||||
Fission energy release data
|
||||
|
||||
"""
|
||||
out = cls()
|
||||
|
||||
# How many coefficients are given for each component? If we only find
|
||||
# one value for each, then we need to use the Sher-Beck formula for
|
||||
# energy dependence. Otherwise, it is a polynomial.
|
||||
n_coeffs = len(energy_release['EFR'])
|
||||
if n_coeffs > 1:
|
||||
out.fragments = Polynomial(energy_release['EFR'])
|
||||
out.prompt_neutrons = Polynomial(energy_release['ENP'])
|
||||
out.delayed_neutrons = Polynomial(energy_release['END'])
|
||||
out.prompt_photons = Polynomial(energy_release['EGP'])
|
||||
out.delayed_photons = Polynomial(energy_release['EGD'])
|
||||
out.betas = Polynomial(energy_release['EB'])
|
||||
out.neutrinos = Polynomial(energy_release['ENU'])
|
||||
else:
|
||||
# EFR and ENP are energy independent. Use 0-order polynomials to
|
||||
# make a constant function. The energy-dependence of END is
|
||||
# unspecified in ENDF-102 so assume it is independent.
|
||||
out.fragments = Polynomial((energy_release['EFR'][0]))
|
||||
out.prompt_photons = Polynomial((energy_release['EGP'][0]))
|
||||
out.delayed_neutrons = Polynomial((energy_release['END'][0]))
|
||||
|
||||
# EDP, EB, and ENU are linear.
|
||||
out.delayed_photons = Polynomial((energy_release['EGD'][0], -0.075))
|
||||
out.betas = Polynomial((energy_release['EB'][0], -0.075))
|
||||
out.neutrinos = Polynomial((energy_release['ENU'][0], -0.105))
|
||||
|
||||
# Prompt neutrons require nu-data. It is not clear from ENDF-102
|
||||
# whether prompt or total nu value should be used, but the delayed
|
||||
# neutron fraction is so small that the difference is negligible.
|
||||
# MT=18 (n, fission) might not be available so try MT=19 (n, f) as
|
||||
# well.
|
||||
if 18 in incident_neutron.reactions:
|
||||
nu_prompt = [p for p in incident_neutron[18].products
|
||||
if p.particle == 'neutron'
|
||||
and p.emission_mode == 'prompt']
|
||||
elif 19 in incident_neutron.reactions:
|
||||
nu_prompt = [p for p in incident_neutron[19].products
|
||||
if p.particle == 'neutron'
|
||||
and p.emission_mode == 'prompt']
|
||||
else:
|
||||
raise ValueError('IncidentNeutron data has no fission '
|
||||
'reaction.')
|
||||
if len(nu_prompt) == 0:
|
||||
raise ValueError('Nu data is needed to compute fission energy '
|
||||
'release with the Sher-Beck format.')
|
||||
if len(nu_prompt) > 1:
|
||||
raise ValueError('Ambiguous prompt value.')
|
||||
if not isinstance(nu_prompt[0].yield_, Tabulated1D):
|
||||
raise TypeError('Sher-Beck fission energy release currently '
|
||||
'only supports Tabulated1D nu data.')
|
||||
ENP = deepcopy(nu_prompt[0].yield_)
|
||||
ENP.y = (energy_release['ENP'] + 1.307 * ENP.x
|
||||
- 8.07 * (ENP.y - ENP.y[0]))
|
||||
out.prompt_neutrons = ENP
|
||||
|
||||
return out
|
||||
|
||||
@classmethod
|
||||
def from_endf(cls, filename, incident_neutron):
|
||||
"""Generate fission energy release data from an ENDF file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
Name of the ENDF file containing fission energy release data
|
||||
|
||||
incident_neutron : openmc.data.IncidentNeutron
|
||||
Corresponding incident neutron dataset
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.FissionEnergyRelease
|
||||
Fission energy release data
|
||||
|
||||
"""
|
||||
|
||||
# Check to make sure this ENDF file matches the expected isomer.
|
||||
ident = identify_nuclide(filename)
|
||||
if ident['Z'] != incident_neutron.atomic_number:
|
||||
raise ValueError('The atomic number of the ENDF evaluation does '
|
||||
'not match the given IncidentNeutron.')
|
||||
if ident['A'] != incident_neutron.mass_number:
|
||||
raise ValueError('The atomic mass of the ENDF evaluation does '
|
||||
'not match the given IncidentNeutron.')
|
||||
if ident['LISO'] != incident_neutron.metastable:
|
||||
raise ValueError('The metastable state of the ENDF evaluation does '
|
||||
'not match the given IncidentNeutron.')
|
||||
if not ident['LFI']:
|
||||
raise ValueError('The ENDF evaluation is not fissionable.')
|
||||
|
||||
# Read the 458 data from the ENDF file.
|
||||
value, uncertainty = _extract_458_data(filename)
|
||||
|
||||
# Build the object.
|
||||
return cls._from_dictionary(value, incident_neutron)
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group):
|
||||
"""Generate fission energy release data from an HDF5 group.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
group : h5py.Group
|
||||
HDF5 group to read from
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.FissionEnergyRelease
|
||||
Fission energy release data
|
||||
|
||||
"""
|
||||
|
||||
obj = cls()
|
||||
|
||||
obj.fragments = Function1D.from_hdf5(group['fragments'])
|
||||
obj.prompt_neutrons = Function1D.from_hdf5(group['prompt_neutrons'])
|
||||
obj.delayed_neutrons = Function1D.from_hdf5(group['delayed_neutrons'])
|
||||
obj.prompt_photons = Function1D.from_hdf5(group['prompt_photons'])
|
||||
obj.delayed_photons = Function1D.from_hdf5(group['delayed_photons'])
|
||||
obj.betas = Function1D.from_hdf5(group['betas'])
|
||||
obj.neutrinos = Function1D.from_hdf5(group['neutrinos'])
|
||||
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def from_compact_hdf5(cls, fname, incident_neutron):
|
||||
"""Generate fission energy release data from a small HDF5 library.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
Path to an HDF5 file containing fission energy release data. This
|
||||
file should have been generated form the
|
||||
:func:`openmc.data.write_compact_458_library` function.
|
||||
|
||||
incident_neutron : openmc.data.IncidentNeutron
|
||||
Corresponding incident neutron dataset
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.FissionEnergyRelease or None
|
||||
Fission energy release data for the given nuclide if it is present
|
||||
in the data file
|
||||
|
||||
"""
|
||||
|
||||
fin = h5py.File(fname, 'r')
|
||||
|
||||
components = [s.decode() for s in fin.attrs['component order']]
|
||||
|
||||
nuclide_name = ATOMIC_SYMBOL[incident_neutron.atomic_number]
|
||||
nuclide_name += str(incident_neutron.mass_number)
|
||||
if incident_neutron.metastable != 0:
|
||||
nuclide_name += '_m' + str(incident_neutron.metastable)
|
||||
|
||||
if nuclide_name not in fin: return None
|
||||
|
||||
data = {c: fin[nuclide_name + '/data'][i, 0, :]
|
||||
for i, c in enumerate(components)}
|
||||
|
||||
return cls._from_dictionary(data, incident_neutron)
|
||||
|
||||
def to_hdf5(self, group):
|
||||
"""Write energy release data to an HDF5 group
|
||||
|
||||
Parameters
|
||||
----------
|
||||
group : h5py.Group
|
||||
HDF5 group to write to
|
||||
|
||||
"""
|
||||
|
||||
self.fragments.to_hdf5(group, 'fragments')
|
||||
self.prompt_neutrons.to_hdf5(group, 'prompt_neutrons')
|
||||
self.delayed_neutrons.to_hdf5(group, 'delayed_neutrons')
|
||||
self.prompt_photons.to_hdf5(group, 'prompt_photons')
|
||||
self.delayed_photons.to_hdf5(group, 'delayed_photons')
|
||||
self.betas.to_hdf5(group, 'betas')
|
||||
self.neutrinos.to_hdf5(group, 'neutrinos')
|
||||
|
||||
if isinstance(self.prompt_neutrons, Polynomial):
|
||||
# Add the polynomials for the relevant components together. Use a
|
||||
# Polynomial((0.0, -1.0)) to subtract incident energy.
|
||||
q_prompt = (self.fragments + self.prompt_neutrons +
|
||||
self.prompt_photons + Polynomial((0.0, -1.0)))
|
||||
q_prompt.to_hdf5(group, 'q_prompt')
|
||||
q_recoverable = (self.fragments + self.prompt_neutrons +
|
||||
self.delayed_neutrons + self.prompt_photons +
|
||||
self.delayed_photons + self.betas +
|
||||
Polynomial((0.0, -1.0)))
|
||||
q_recoverable.to_hdf5(group, 'q_recoverable')
|
||||
|
||||
elif isinstance(self.prompt_neutrons, Tabulated1D):
|
||||
# Make a Tabulated1D and evaluate the polynomial components at the
|
||||
# table x points to get new y points. Subtract x from y to remove
|
||||
# incident energy.
|
||||
q_prompt = deepcopy(self.prompt_neutrons)
|
||||
q_prompt.y += self.fragments(q_prompt.x)
|
||||
q_prompt.y += self.prompt_photons(q_prompt.x)
|
||||
q_prompt.y -= q_prompt.x
|
||||
q_prompt.to_hdf5(group, 'q_prompt')
|
||||
q_recoverable = q_prompt
|
||||
q_recoverable.y += self.delayed_neutrons(q_recoverable.x)
|
||||
q_recoverable.y += self.delayed_photons(q_recoverable.x)
|
||||
q_recoverable.y += self.betas(q_recoverable.x)
|
||||
q_recoverable.to_hdf5(group, 'q_recoverable')
|
||||
|
||||
else:
|
||||
raise ValueError('Unrecognized energy release format')
|
||||
|
|
@ -1,15 +1,61 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
from collections import Iterable, Callable
|
||||
from numbers import Real, Integral
|
||||
|
||||
import numpy as np
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
||||
INTERPOLATION_SCHEME = {1: 'histogram', 2: 'linear-linear', 3: 'linear-log',
|
||||
4: 'log-linear', 5: 'log-log'}
|
||||
|
||||
|
||||
class Tabulated1D(object):
|
||||
class Function1D(EqualityMixin):
|
||||
"""A function of one independent variable with HDF5 support."""
|
||||
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@abstractmethod
|
||||
def __call__(self): pass
|
||||
|
||||
@abstractmethod
|
||||
def to_hdf5(self, group, name='xy'):
|
||||
"""Write function to an HDF5 group
|
||||
|
||||
Parameters
|
||||
----------
|
||||
group : h5py.Group
|
||||
HDF5 group to write to
|
||||
name : str
|
||||
Name of the dataset to create
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, dataset):
|
||||
"""Generate function from an HDF5 dataset
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dataset : h5py.Dataset
|
||||
Dataset to read from
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.Function1D
|
||||
Function read from dataset
|
||||
|
||||
"""
|
||||
for subclass in cls.__subclasses__():
|
||||
if dataset.attrs['type'].decode() == subclass.__name__:
|
||||
return subclass.from_hdf5(dataset)
|
||||
raise ValueError("Unrecognized Function1D class: '"
|
||||
+ dataset.attrs['type'].decode() + "'")
|
||||
|
||||
|
||||
class Tabulated1D(Function1D):
|
||||
"""A one-dimensional tabulated function.
|
||||
|
||||
This class mirrors the TAB1 type from the ENDF-6 format. A tabulated
|
||||
|
|
@ -239,7 +285,7 @@ class Tabulated1D(object):
|
|||
"""
|
||||
dataset = group.create_dataset(name, data=np.vstack(
|
||||
[self.x, self.y]))
|
||||
dataset.attrs['type'] = np.string_('tab1')
|
||||
dataset.attrs['type'] = np.string_(type(self).__name__)
|
||||
dataset.attrs['breakpoints'] = self.breakpoints
|
||||
dataset.attrs['interpolation'] = self.interpolation
|
||||
|
||||
|
|
@ -258,6 +304,10 @@ class Tabulated1D(object):
|
|||
Function read from dataset
|
||||
|
||||
"""
|
||||
if dataset.attrs['type'].decode() != cls.__name__:
|
||||
raise ValueError("Expected an HDF5 attribute 'type' equal to '"
|
||||
+ cls.__name__ + "'")
|
||||
|
||||
x = dataset.value[0, :]
|
||||
y = dataset.value[1, :]
|
||||
breakpoints = dataset.attrs['breakpoints']
|
||||
|
|
@ -304,7 +354,43 @@ class Tabulated1D(object):
|
|||
return Tabulated1D(x, y, breakpoints, interpolation)
|
||||
|
||||
|
||||
class Sum(object):
|
||||
class Polynomial(np.polynomial.Polynomial, Function1D):
|
||||
def to_hdf5(self, group, name='xy'):
|
||||
"""Write polynomial function to an HDF5 group
|
||||
|
||||
Parameters
|
||||
----------
|
||||
group : h5py.Group
|
||||
HDF5 group to write to
|
||||
name : str
|
||||
Name of the dataset to create
|
||||
|
||||
"""
|
||||
dataset = group.create_dataset(name, data=self.coef)
|
||||
dataset.attrs['type'] = np.string_(type(self).__name__)
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, dataset):
|
||||
"""Generate function from an HDF5 dataset
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dataset : h5py.Dataset
|
||||
Dataset to read from
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.Function1D
|
||||
Function read from dataset
|
||||
|
||||
"""
|
||||
if dataset.attrs['type'].decode() != cls.__name__:
|
||||
raise ValueError("Expected an HDF5 attribute 'type' equal to '"
|
||||
+ cls.__name__ + "'")
|
||||
return cls(dataset.value)
|
||||
|
||||
|
||||
class Sum(EqualityMixin):
|
||||
"""Sum of multiple functions.
|
||||
|
||||
This class allows you to create a callable object which represents the sum
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ import xml.etree.ElementTree as ET
|
|||
|
||||
import h5py
|
||||
|
||||
from openmc.mixin import EqualityMixin
|
||||
from openmc.clean_xml import clean_xml_indentation
|
||||
|
||||
class DataLibrary(object):
|
||||
|
||||
class DataLibrary(EqualityMixin):
|
||||
def __init__(self):
|
||||
self.libraries = []
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,95 @@
|
|||
from __future__ import division, unicode_literals
|
||||
import sys
|
||||
from collections import OrderedDict, Iterable, Mapping
|
||||
from collections import OrderedDict, Iterable, Mapping, MutableMapping
|
||||
from itertools import chain
|
||||
from numbers import Integral, Real
|
||||
from warnings import warn
|
||||
|
||||
import numpy as np
|
||||
import h5py
|
||||
|
||||
from .data import ATOMIC_SYMBOL, SUM_RULES
|
||||
from .data import ATOMIC_SYMBOL, SUM_RULES, K_BOLTZMANN
|
||||
from .ace import Table, get_table
|
||||
from .fission_energy import FissionEnergyRelease
|
||||
from .function import Tabulated1D, Sum
|
||||
from .product import Product
|
||||
from .reaction import Reaction, _get_photon_products
|
||||
from .urr import ProbabilityTables
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
|
||||
class IncidentNeutron(object):
|
||||
def _get_metadata(zaid, metastable_scheme='nndc'):
|
||||
"""Return basic identifying data for a nuclide with a given ZAID.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
zaid : int
|
||||
ZAID (1000*Z + A) obtained from a library
|
||||
metastable_scheme : {'nndc', 'mcnp'}
|
||||
Determine how ZAID identifiers are to be interpreted in the case of
|
||||
a metastable nuclide. Because the normal ZAID (=1000*Z + A) does not
|
||||
encode metastable information, different conventions are used among
|
||||
different libraries. In MCNP libraries, the convention is to add 400
|
||||
for a metastable nuclide except for Am242m, for which 95242 is
|
||||
metastable and 95642 (or 1095242 in newer libraries) is the ground
|
||||
state. For NNDC libraries, ZAID is given as 1000*Z + A + 100*m.
|
||||
|
||||
Returns
|
||||
-------
|
||||
name : str
|
||||
Name of the table
|
||||
element : str
|
||||
The atomic symbol of the isotope in the table; e.g., Zr.
|
||||
Z : int
|
||||
Number of protons in the nucleus
|
||||
mass_number : int
|
||||
Number of nucleons in the nucleus
|
||||
metastable : int
|
||||
Metastable state of the nucleus. A value of zero indicates ground state.
|
||||
|
||||
"""
|
||||
|
||||
cv.check_type('zaid', zaid, int)
|
||||
cv.check_value('metastable_scheme', metastable_scheme, ['nndc', 'mcnp'])
|
||||
|
||||
Z = zaid // 1000
|
||||
mass_number = zaid % 1000
|
||||
|
||||
if metastable_scheme == 'mcnp':
|
||||
if zaid > 1000000:
|
||||
# New SZA format
|
||||
Z = Z % 1000
|
||||
if zaid == 1095242:
|
||||
metastable = 0
|
||||
else:
|
||||
metastable = zaid // 1000000
|
||||
else:
|
||||
if zaid == 95242:
|
||||
metastable = 1
|
||||
elif zaid == 95642:
|
||||
metastable = 0
|
||||
else:
|
||||
metastable = 1 if mass_number > 300 else 0
|
||||
elif metastable_scheme == 'nndc':
|
||||
metastable = 1 if mass_number > 300 else 0
|
||||
|
||||
while mass_number > 3 * Z:
|
||||
mass_number -= 100
|
||||
|
||||
# Determine name
|
||||
element = ATOMIC_SYMBOL[Z]
|
||||
name = '{}{}'.format(element, mass_number)
|
||||
if metastable > 0:
|
||||
name += '_m{}'.format(metastable)
|
||||
|
||||
return (name, element, Z, mass_number, metastable)
|
||||
|
||||
|
||||
class IncidentNeutron(EqualityMixin):
|
||||
"""Continuous-energy neutron interaction data.
|
||||
|
||||
Instances of this class are not normally instantiated by the user but rather
|
||||
|
|
@ -29,7 +99,7 @@ class IncidentNeutron(object):
|
|||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of the table
|
||||
Name of the nuclide using the GND naming convention
|
||||
atomic_number : int
|
||||
Number of protons in the nucleus
|
||||
mass_number : int
|
||||
|
|
@ -38,8 +108,9 @@ class IncidentNeutron(object):
|
|||
Metastable state of the nucleus. A value of zero indicates ground state.
|
||||
atomic_weight_ratio : float
|
||||
Atomic mass ratio of the target nuclide.
|
||||
temperature : float
|
||||
Temperature of the target nuclide in MeV.
|
||||
kTs : Iterable of float
|
||||
List of temperatures of the target nuclide in the data set.
|
||||
The temperatures have units of MeV.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
@ -49,14 +120,19 @@ class IncidentNeutron(object):
|
|||
Atomic symbol of the nuclide, e.g., 'Zr'
|
||||
atomic_weight_ratio : float
|
||||
Atomic weight ratio of the target nuclide.
|
||||
energy : numpy.ndarray
|
||||
energy : dict of numpy.ndarray
|
||||
The energy values (MeV) at which reaction cross-sections are tabulated.
|
||||
They keys of the dict are the temperature string ('294K') for each
|
||||
set of energies
|
||||
fission_energy : None or openmc.data.FissionEnergyRelease
|
||||
The energy released by fission, tabulated by component (e.g. prompt
|
||||
neutrons or beta particles) and dependent on incident neutron energy
|
||||
mass_number : int
|
||||
Number of nucleons in the nucleus
|
||||
metastable : int
|
||||
Metastable state of the nucleus. A value of zero indicates ground state.
|
||||
name : str
|
||||
ZAID identifier of the table, e.g. 92235.70c.
|
||||
Name of the nuclide using the GND naming convention
|
||||
reactions : collections.OrderedDict
|
||||
Contains the cross sections, secondary angle and energy distributions,
|
||||
and other associated data for each reaction. The keys are the MT values
|
||||
|
|
@ -64,26 +140,32 @@ class IncidentNeutron(object):
|
|||
summed_reactions : collections.OrderedDict
|
||||
Contains summed cross sections, e.g., the total cross section. The keys
|
||||
are the MT values and the values are Reaction objects.
|
||||
temperature : float
|
||||
Temperature of the target nuclide in MeV.
|
||||
urr : None or openmc.data.ProbabilityTables
|
||||
Unresolved resonance region probability tables
|
||||
temperatures : list of str
|
||||
List of string representations the temperatures of the target nuclide
|
||||
in the data set. The temperatures are strings of the temperature,
|
||||
rounded to the nearest integer; e.g., '294K'
|
||||
kTs : Iterable of float
|
||||
List of temperatures of the target nuclide in the data set.
|
||||
The temperatures have units of MeV.
|
||||
urr : dict
|
||||
Dictionary whose keys are temperatures (e.g., '294K') and values are
|
||||
unresolved resonance region probability tables.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, atomic_number, mass_number, metastable,
|
||||
atomic_weight_ratio, temperature):
|
||||
atomic_weight_ratio, kTs):
|
||||
self.name = name
|
||||
self.atomic_number = atomic_number
|
||||
self.mass_number = mass_number
|
||||
self.metastable = metastable
|
||||
self.atomic_weight_ratio = atomic_weight_ratio
|
||||
self.temperature = temperature
|
||||
|
||||
self._energy = None
|
||||
self.kTs = kTs
|
||||
self.energy = {}
|
||||
self._fission_energy = None
|
||||
self.reactions = OrderedDict()
|
||||
self.summed_reactions = OrderedDict()
|
||||
self.urr = None
|
||||
self._urr = {}
|
||||
|
||||
def __contains__(self, mt):
|
||||
return mt in self.reactions or mt in self.summed_reactions
|
||||
|
|
@ -123,12 +205,8 @@ class IncidentNeutron(object):
|
|||
return self._atomic_weight_ratio
|
||||
|
||||
@property
|
||||
def energy(self):
|
||||
return self._energy
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
return self._temperature
|
||||
def fission_energy(self):
|
||||
return self._fission_energy
|
||||
|
||||
@property
|
||||
def reactions(self):
|
||||
|
|
@ -142,6 +220,10 @@ class IncidentNeutron(object):
|
|||
def urr(self):
|
||||
return self._urr
|
||||
|
||||
@property
|
||||
def temperatures(self):
|
||||
return ["{}K".format(int(round(kT / K_BOLTZMANN))) for kT in self.kTs]
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
cv.check_type('name', name, basestring)
|
||||
|
|
@ -149,7 +231,7 @@ class IncidentNeutron(object):
|
|||
|
||||
@property
|
||||
def atomic_symbol(self):
|
||||
return atomic_symbol[self.atomic_number]
|
||||
return ATOMIC_SYMBOL[self.atomic_number]
|
||||
|
||||
@atomic_number.setter
|
||||
def atomic_number(self, atomic_number):
|
||||
|
|
@ -175,16 +257,11 @@ class IncidentNeutron(object):
|
|||
cv.check_greater_than('atomic weight ratio', atomic_weight_ratio, 0.0)
|
||||
self._atomic_weight_ratio = atomic_weight_ratio
|
||||
|
||||
@temperature.setter
|
||||
def temperature(self, temperature):
|
||||
cv.check_type('temperature', temperature, Real)
|
||||
cv.check_greater_than('temperature', temperature, 0.0, True)
|
||||
self._temperature = temperature
|
||||
|
||||
@energy.setter
|
||||
def energy(self, energy):
|
||||
cv.check_type('energy grid', energy, Iterable, Real)
|
||||
self._energy = energy
|
||||
@fission_energy.setter
|
||||
def fission_energy(self, fission_energy):
|
||||
cv.check_type('fission energy release', fission_energy,
|
||||
FissionEnergyRelease)
|
||||
self._fission_energy = fission_energy
|
||||
|
||||
@reactions.setter
|
||||
def reactions(self, reactions):
|
||||
|
|
@ -198,10 +275,61 @@ class IncidentNeutron(object):
|
|||
|
||||
@urr.setter
|
||||
def urr(self, urr):
|
||||
cv.check_type('probability tables', urr,
|
||||
(ProbabilityTables, type(None)))
|
||||
cv.check_type('probability table dictionary', urr, MutableMapping)
|
||||
for key, value in urr:
|
||||
cv.check_type('probability table temperature', key, basestring)
|
||||
cv.check_type('probability tables', value, ProbabilityTables)
|
||||
self._urr = urr
|
||||
|
||||
def add_temperature_from_ace(self, ace_or_filename, metastable_scheme='nndc'):
|
||||
"""Append data from an ACE file at a different temperature.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ace_or_filename : openmc.data.ace.Table or str
|
||||
ACE table to read from. If given as a string, it is assumed to be
|
||||
the filename for the ACE file.
|
||||
metastable_scheme : {'nndc', 'mcnp'}
|
||||
Determine how ZAID identifiers are to be interpreted in the case of
|
||||
a metastable nuclide. Because the normal ZAID (=1000*Z + A) does not
|
||||
encode metastable information, different conventions are used among
|
||||
different libraries. In MCNP libraries, the convention is to add 400
|
||||
for a metastable nuclide except for Am242m, for which 95242 is
|
||||
metastable and 95642 (or 1095242 in newer libraries) is the ground
|
||||
state. For NNDC libraries, ZAID is given as 1000*Z + A + 100*m.
|
||||
|
||||
"""
|
||||
|
||||
data = IncidentNeutron.from_ace(ace_or_filename, metastable_scheme)
|
||||
|
||||
# Check if temprature already exists
|
||||
strT = data.temperatures[0]
|
||||
if strT in self.temperatures:
|
||||
warn('Cross sections at T={} already exist.'.format(strT))
|
||||
return
|
||||
|
||||
# Check that name matches
|
||||
if data.name != self.name:
|
||||
raise ValueError('Data provided for an incorrect nuclide.')
|
||||
|
||||
# Add temperature
|
||||
self.kTs += data.kTs
|
||||
|
||||
# Add energy grid
|
||||
self.energy[strT] = data.energy[strT]
|
||||
|
||||
# Add normal and summed reactions
|
||||
for mt in chain(data.reactions, data.summed_reactions):
|
||||
if mt not in self:
|
||||
raise ValueError("Tried to add cross sections for MT={} at T={}"
|
||||
" but this reaction doesn't exist.".format(
|
||||
mt, strT))
|
||||
self[mt].xs[strT] = data[mt].xs[strT]
|
||||
|
||||
# Add probability tables
|
||||
if strT in data.urr:
|
||||
self.urr[strT] = data.urr[strT]
|
||||
|
||||
def get_reaction_components(self, mt):
|
||||
"""Determine what reactions make up summed reaction.
|
||||
|
||||
|
|
@ -255,10 +383,14 @@ class IncidentNeutron(object):
|
|||
g.attrs['A'] = self.mass_number
|
||||
g.attrs['metastable'] = self.metastable
|
||||
g.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio
|
||||
g.attrs['temperature'] = self.temperature
|
||||
ktg = g.create_group('kTs')
|
||||
for i, temperature in enumerate(self.temperatures):
|
||||
ktg.create_dataset(temperature, data=self.kTs[i])
|
||||
|
||||
# Write energy grid
|
||||
g.create_dataset('energy', data=self.energy)
|
||||
eg = g.create_group('energy')
|
||||
for temperature in self.temperatures:
|
||||
eg.create_dataset(temperature, data=self.energy[temperature])
|
||||
|
||||
# Write reaction data
|
||||
rxs_group = g.create_group('reactions')
|
||||
|
|
@ -272,9 +404,16 @@ class IncidentNeutron(object):
|
|||
rx.derived_products[0].to_hdf5(tgroup)
|
||||
|
||||
# Write unresolved resonance probability tables
|
||||
if self.urr is not None:
|
||||
if self.urr:
|
||||
urr_group = g.create_group('urr')
|
||||
self.urr.to_hdf5(urr_group)
|
||||
for temperature, urr in self.urr.items():
|
||||
tgroup = urr_group.create_group(temperature)
|
||||
urr.to_hdf5(tgroup)
|
||||
|
||||
# Write fission energy release data
|
||||
if self.fission_energy is not None:
|
||||
fer_group = g.create_group('fission_energy_release')
|
||||
self.fission_energy.to_hdf5(fer_group)
|
||||
|
||||
f.close()
|
||||
|
||||
|
|
@ -306,13 +445,18 @@ class IncidentNeutron(object):
|
|||
mass_number = group.attrs['A']
|
||||
metastable = group.attrs['metastable']
|
||||
atomic_weight_ratio = group.attrs['atomic_weight_ratio']
|
||||
temperature = group.attrs['temperature']
|
||||
kTg = group['kTs']
|
||||
kTs = []
|
||||
for temp in kTg:
|
||||
kTs.append(kTg[temp].value)
|
||||
|
||||
data = cls(name, atomic_number, mass_number, metastable,
|
||||
atomic_weight_ratio, temperature)
|
||||
atomic_weight_ratio, kTs)
|
||||
|
||||
# Read energy grid
|
||||
data.energy = group['energy'].value
|
||||
e_group = group['energy']
|
||||
for temperature, dset in e_group.items():
|
||||
data.energy[temperature] = dset.value
|
||||
|
||||
# Read reaction data
|
||||
rxs_group = group['reactions']
|
||||
|
|
@ -326,21 +470,26 @@ class IncidentNeutron(object):
|
|||
tgroup = group['total_nu']
|
||||
rx.derived_products.append(Product.from_hdf5(tgroup))
|
||||
|
||||
# Build summed reactions. Start from the highest MT number because high
|
||||
# MTs never depend on lower MTs.
|
||||
# Build summed reactions. Start from the highest MT number because
|
||||
# high MTs never depend on lower MTs.
|
||||
for mt_sum in sorted(SUM_RULES, reverse=True):
|
||||
if mt_sum not in data:
|
||||
xs_components = [data[mt].xs for mt in SUM_RULES[mt_sum]
|
||||
if mt in data]
|
||||
if len(xs_components) > 0:
|
||||
rxn = Reaction(mt_sum)
|
||||
rxn.xs = Sum(xs_components)
|
||||
data.summed_reactions[mt_sum] = rxn
|
||||
rxs = [data[mt] for mt in SUM_RULES[mt_sum] if mt in data]
|
||||
if len(rxs) > 0:
|
||||
data.summed_reactions[mt_sum] = rx = Reaction(mt_sum)
|
||||
for T in data.temperatures:
|
||||
rx.xs[T] = Sum([rx.xs[T] for rx in rxs])
|
||||
|
||||
# Read unresolved resonance probability tables
|
||||
if 'urr' in group:
|
||||
urr_group = group['urr']
|
||||
data.urr = ProbabilityTables.from_hdf5(urr_group)
|
||||
for temperature, tgroup in urr_group.items():
|
||||
data.urr[temperature] = ProbabilityTables.from_hdf5(tgroup)
|
||||
|
||||
# Read fission energy release data
|
||||
if 'fission_energy_release' in group:
|
||||
fer_group = group['fission_energy_release']
|
||||
data.fission_energy = FissionEnergyRelease.from_hdf5(fer_group)
|
||||
|
||||
return data
|
||||
|
||||
|
|
@ -350,9 +499,9 @@ class IncidentNeutron(object):
|
|||
|
||||
Parameters
|
||||
----------
|
||||
ace : openmc.data.ace.Table or str
|
||||
ACE table to read from. If given as a string, it is assumed to be
|
||||
the filename for the ACE file.
|
||||
ace_or_filename : openmc.data.ace.Table or str
|
||||
ACE table to read from. If the value is a string, it is assumed to
|
||||
be the filename for the ACE file.
|
||||
metastable_scheme : {'nndc', 'mcnp'}
|
||||
Determine how ZAID identifiers are to be interpreted in the case of
|
||||
a metastable nuclide. Because the normal ZAID (=1000*Z + A) does not
|
||||
|
|
@ -368,6 +517,8 @@ class IncidentNeutron(object):
|
|||
Incident neutron continuous-energy data
|
||||
|
||||
"""
|
||||
|
||||
# First obtain the data for the first provided ACE table/file
|
||||
if isinstance(ace_or_filename, Table):
|
||||
ace = ace_or_filename
|
||||
else:
|
||||
|
|
@ -375,55 +526,35 @@ class IncidentNeutron(object):
|
|||
|
||||
# If mass number hasn't been specified, make an educated guess
|
||||
zaid, xs = ace.name.split('.')
|
||||
zaid = int(zaid)
|
||||
Z = zaid // 1000
|
||||
mass_number = zaid % 1000
|
||||
name, element, Z, mass_number, metastable = \
|
||||
_get_metadata(int(zaid), metastable_scheme)
|
||||
|
||||
if metastable_scheme == 'mcnp':
|
||||
if zaid > 1000000:
|
||||
# New SZA format
|
||||
Z = Z % 1000
|
||||
if zaid == 1095242:
|
||||
metastable = 0
|
||||
else:
|
||||
metastable = zaid // 1000000
|
||||
else:
|
||||
if zaid == 95242:
|
||||
metastable = 1
|
||||
elif zaid == 95642:
|
||||
metastable = 0
|
||||
else:
|
||||
metastable = 1 if mass_number > 300 else 0
|
||||
elif metastable_scheme == 'nndc':
|
||||
metastable = 1 if mass_number > 300 else 0
|
||||
|
||||
while mass_number > 3*Z:
|
||||
mass_number -= 100
|
||||
|
||||
# Determine name for group
|
||||
element = ATOMIC_SYMBOL[Z]
|
||||
if metastable > 0:
|
||||
name = '{}{}_m{}.{}'.format(element, mass_number, metastable, xs)
|
||||
else:
|
||||
name = '{}{}.{}'.format(element, mass_number, xs)
|
||||
# Assign temperature to the running list
|
||||
kTs = [ace.temperature]
|
||||
|
||||
data = cls(name, Z, mass_number, metastable,
|
||||
ace.atomic_weight_ratio, ace.temperature)
|
||||
ace.atomic_weight_ratio, kTs)
|
||||
|
||||
# Get string of temperature to use as a dictionary key
|
||||
strT = data.temperatures[0]
|
||||
|
||||
# Read energy grid
|
||||
n_energy = ace.nxs[3]
|
||||
energy = ace.xss[ace.jxs[1]:ace.jxs[1] + n_energy]
|
||||
data.energy = energy
|
||||
total_xs = ace.xss[ace.jxs[1] + n_energy:ace.jxs[1] + 2*n_energy]
|
||||
absorption_xs = ace.xss[ace.jxs[1] + 2*n_energy:ace.jxs[1] + 3*n_energy]
|
||||
data.energy[strT] = energy
|
||||
total_xs = ace.xss[ace.jxs[1] + n_energy:ace.jxs[1] + 2 * n_energy]
|
||||
absorption_xs = ace.xss[ace.jxs[1] + 2 * n_energy:ace.jxs[1] +
|
||||
3 * n_energy]
|
||||
|
||||
# Create summed reactions (total and absorption)
|
||||
total = Reaction(1)
|
||||
total.xs = Tabulated1D(energy, total_xs)
|
||||
total.xs[strT] = Tabulated1D(energy, total_xs)
|
||||
data.summed_reactions[1] = total
|
||||
absorption = Reaction(27)
|
||||
absorption.xs = Tabulated1D(energy, absorption_xs)
|
||||
data.summed_reactions[27] = absorption
|
||||
|
||||
if np.count_nonzero(absorption_xs) > 0:
|
||||
absorption = Reaction(27)
|
||||
absorption.xs[strT] = Tabulated1D(energy, absorption_xs)
|
||||
data.summed_reactions[27] = absorption
|
||||
|
||||
# Read each reaction
|
||||
n_reaction = ace.nxs[4] + 1
|
||||
|
|
@ -452,13 +583,16 @@ class IncidentNeutron(object):
|
|||
warn('Photon production is present for MT={} but no '
|
||||
'reaction components exist.'.format(mt))
|
||||
continue
|
||||
rx.xs = Sum([data.reactions[mt_i].xs for mt_i in mts])
|
||||
rx.xs[strT] = Sum([data.reactions[mt_i].xs[strT]
|
||||
for mt_i in mts])
|
||||
|
||||
# Determine summed cross section
|
||||
rx.products += _get_photon_products(ace, rx)
|
||||
data.summed_reactions[mt] = rx
|
||||
|
||||
# Read unresolved resonance probability tables
|
||||
data.urr = ProbabilityTables.from_ace(ace)
|
||||
urr = ProbabilityTables.from_ace(ace)
|
||||
if urr is not None:
|
||||
data.urr[strT] = urr
|
||||
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -3,17 +3,17 @@ from numbers import Real
|
|||
import sys
|
||||
|
||||
import numpy as np
|
||||
from numpy.polynomial.polynomial import Polynomial
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
from .function import Tabulated1D
|
||||
from openmc.mixin import EqualityMixin
|
||||
from .function import Tabulated1D, Polynomial, Function1D
|
||||
from .angle_energy import AngleEnergy
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
|
||||
class Product(object):
|
||||
class Product(EqualityMixin):
|
||||
"""Secondary particle emitted in a nuclear reaction
|
||||
|
||||
Parameters
|
||||
|
|
@ -36,7 +36,7 @@ class Product(object):
|
|||
yield represents particles from prompt and delayed sources.
|
||||
particle : str
|
||||
What particle the reaction product is.
|
||||
yield_ : float or openmc.data.Tabulated1D or numpy.polynomial.Polynomial
|
||||
yield_ : openmc.data.Function1D
|
||||
Yield of secondary particle in the reaction.
|
||||
|
||||
"""
|
||||
|
|
@ -47,7 +47,7 @@ class Product(object):
|
|||
self.emission_mode = 'prompt'
|
||||
self.distribution = []
|
||||
self.applicability = []
|
||||
self.yield_ = 1
|
||||
self.yield_ = Polynomial((1,)) # 0-order polynomial i.e. a constant
|
||||
|
||||
def __repr__(self):
|
||||
if isinstance(self.yield_, Real):
|
||||
|
|
@ -119,8 +119,7 @@ class Product(object):
|
|||
|
||||
@yield_.setter
|
||||
def yield_(self, yield_):
|
||||
cv.check_type('product yield', yield_,
|
||||
(Real, Tabulated1D, Polynomial))
|
||||
cv.check_type('product yield', yield_, Function1D)
|
||||
self._yield = yield_
|
||||
|
||||
def to_hdf5(self, group):
|
||||
|
|
@ -138,16 +137,7 @@ class Product(object):
|
|||
group.attrs['decay_rate'] = self.decay_rate
|
||||
|
||||
# Write yield
|
||||
if isinstance(self.yield_, Tabulated1D):
|
||||
self.yield_.to_hdf5(group, 'yield')
|
||||
dset = group['yield']
|
||||
dset.attrs['type'] = np.string_('tabulated')
|
||||
elif isinstance(self.yield_, Polynomial):
|
||||
dset = group.create_dataset('yield', data=self.yield_.coef)
|
||||
dset.attrs['type'] = np.string_('polynomial')
|
||||
else:
|
||||
dset = group.create_dataset('yield', data=float(self.yield_))
|
||||
dset.attrs['type'] = np.string_('constant')
|
||||
self.yield_.to_hdf5(group, 'yield')
|
||||
|
||||
# Write applicability/distribution
|
||||
group.attrs['n_distribution'] = len(self.distribution)
|
||||
|
|
@ -180,13 +170,7 @@ class Product(object):
|
|||
p.decay_rate = group.attrs['decay_rate']
|
||||
|
||||
# Read yield
|
||||
yield_type = group['yield'].attrs['type'].decode()
|
||||
if yield_type == 'constant':
|
||||
p.yield_ = group['yield'].value
|
||||
elif yield_type == 'polynomial':
|
||||
p.yield_ = Polynomial(group['yield'].value)
|
||||
elif yield_type == 'tabulated':
|
||||
p.yield_ = Tabulated1D.from_hdf5(group['yield'])
|
||||
p.yield_ = Function1D.from_hdf5(group['yield'])
|
||||
|
||||
# Read applicability/distribution
|
||||
n_distribution = group.attrs['n_distribution']
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
from __future__ import division, unicode_literals
|
||||
from collections import Iterable, Callable
|
||||
from collections import Iterable, Callable, MutableMapping
|
||||
from copy import deepcopy
|
||||
from numbers import Real
|
||||
from numbers import Real, Integral
|
||||
from warnings import warn
|
||||
|
||||
import numpy as np
|
||||
from numpy.polynomial import Polynomial
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
from openmc.stats import Uniform
|
||||
from .angle_distribution import AngleDistribution
|
||||
from .angle_energy import AngleEnergy
|
||||
from .function import Tabulated1D
|
||||
from .data import REACTION_NAME
|
||||
from .function import Tabulated1D, Polynomial, Function1D
|
||||
from .data import REACTION_NAME, K_BOLTZMANN
|
||||
from .product import Product
|
||||
from .uncorrelated import UncorrelatedAngleEnergy
|
||||
|
||||
|
|
@ -211,7 +211,7 @@ def _get_photon_products(ace, rx):
|
|||
|
||||
# Get photon production cross section
|
||||
photon_prod_xs = ace.xss[idx + 2:idx + 2 + n_energy]
|
||||
neutron_xs = rx.xs(energy)
|
||||
neutron_xs = list(rx.xs.values())[0](energy)
|
||||
idx = np.where(neutron_xs > 0.)
|
||||
|
||||
# Calculate photon yield
|
||||
|
|
@ -251,7 +251,7 @@ def _get_photon_products(ace, rx):
|
|||
return photons
|
||||
|
||||
|
||||
class Reaction(object):
|
||||
class Reaction(EqualityMixin):
|
||||
"""A nuclear reaction
|
||||
|
||||
A Reaction object represents a single reaction channel for a nuclide with
|
||||
|
|
@ -261,8 +261,7 @@ class Reaction(object):
|
|||
Parameters
|
||||
----------
|
||||
mt : int
|
||||
The ENDF MT number for this reaction. On occasion, MCNP uses MT numbers
|
||||
that don't correspond exactly to the ENDF specification.
|
||||
The ENDF MT number for this reaction.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
@ -274,16 +273,12 @@ class Reaction(object):
|
|||
The ENDF MT number for this reaction.
|
||||
q_value : float
|
||||
The Q-value of this reaction in MeV.
|
||||
table : openmc.data.ace.Table
|
||||
The ACE table which contains this reaction.
|
||||
threshold : float
|
||||
Threshold of the reaction in MeV
|
||||
threshold_idx : int
|
||||
The index on the energy grid corresponding to the threshold of this
|
||||
reaction.
|
||||
xs : callable
|
||||
xs : dict of str to openmc.data.Function1D
|
||||
Microscopic cross section for this reaction as a function of incident
|
||||
energy
|
||||
energy; these cross sections are provided in a dictionary where the key
|
||||
is the temperature of the cross section set.
|
||||
products : Iterable of openmc.data.Product
|
||||
Reaction products
|
||||
derived_products : Iterable of openmc.data.Product
|
||||
|
|
@ -293,13 +288,13 @@ class Reaction(object):
|
|||
"""
|
||||
|
||||
def __init__(self, mt):
|
||||
self.center_of_mass = True
|
||||
self._center_of_mass = True
|
||||
self._q_value = 0.
|
||||
self._xs = {}
|
||||
self._products = []
|
||||
self._derived_products = []
|
||||
|
||||
self.mt = mt
|
||||
self.q_value = 0.
|
||||
self.threshold_idx = 0
|
||||
self._xs = None
|
||||
self.products = []
|
||||
self.derived_products = []
|
||||
|
||||
def __repr__(self):
|
||||
if self.mt in REACTION_NAME:
|
||||
|
|
@ -320,8 +315,8 @@ class Reaction(object):
|
|||
return self._products
|
||||
|
||||
@property
|
||||
def threshold(self):
|
||||
return self.xs.x[0]
|
||||
def derived_products(self):
|
||||
return self._derived_products
|
||||
|
||||
@property
|
||||
def xs(self):
|
||||
|
|
@ -342,12 +337,18 @@ class Reaction(object):
|
|||
cv.check_type('reaction products', products, Iterable, Product)
|
||||
self._products = products
|
||||
|
||||
@derived_products.setter
|
||||
def derived_products(self, derived_products):
|
||||
cv.check_type('reaction derived products', derived_products,
|
||||
Iterable, Product)
|
||||
self._derived_products = derived_products
|
||||
|
||||
@xs.setter
|
||||
def xs(self, xs):
|
||||
cv.check_type('reaction cross section', xs, Callable)
|
||||
if isinstance(xs, Tabulated1D):
|
||||
for y in xs.y:
|
||||
cv.check_greater_than('reaction cross section', y, 0.0, True)
|
||||
cv.check_type('reaction cross section dictionary', xs, MutableMapping)
|
||||
for key, value in xs.items():
|
||||
cv.check_type('reaction cross section temperature', key, basestring)
|
||||
cv.check_type('reaction cross section', value, Function1D)
|
||||
self._xs = xs
|
||||
|
||||
def to_hdf5(self, group):
|
||||
|
|
@ -366,10 +367,16 @@ class Reaction(object):
|
|||
else:
|
||||
group.attrs['label'] = np.string_(self.mt)
|
||||
group.attrs['Q_value'] = self.q_value
|
||||
group.attrs['threshold_idx'] = self.threshold_idx + 1
|
||||
group.attrs['center_of_mass'] = 1 if self.center_of_mass else 0
|
||||
if self.xs is not None:
|
||||
group.create_dataset('xs', data=self.xs.y)
|
||||
for T in self.xs:
|
||||
Tgroup = group.create_group(T)
|
||||
if self.xs[T] is not None:
|
||||
dset = Tgroup.create_dataset('xs', data=self.xs[T].y)
|
||||
if hasattr(self.xs[T], '_threshold_idx'):
|
||||
threshold_idx = self.xs[T]._threshold_idx + 1
|
||||
else:
|
||||
threshold_idx = 1
|
||||
dset.attrs['threshold_idx'] = threshold_idx
|
||||
for i, p in enumerate(self.products):
|
||||
pgroup = group.create_group('product_{}'.format(i))
|
||||
p.to_hdf5(pgroup)
|
||||
|
|
@ -382,8 +389,9 @@ class Reaction(object):
|
|||
----------
|
||||
group : h5py.Group
|
||||
HDF5 group to write to
|
||||
energy : Iterable of float
|
||||
Array of energies at which cross sections are tabulated at
|
||||
energy : dict
|
||||
Dictionary whose keys are temperatures (e.g., '300K') and values are
|
||||
arrays of energies at which cross sections are tabulated at.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -391,16 +399,27 @@ class Reaction(object):
|
|||
Reaction data
|
||||
|
||||
"""
|
||||
|
||||
mt = group.attrs['mt']
|
||||
rx = cls(mt)
|
||||
rx.q_value = group.attrs['Q_value']
|
||||
rx.threshold_idx = group.attrs['threshold_idx'] - 1
|
||||
rx.center_of_mass = bool(group.attrs['center_of_mass'])
|
||||
|
||||
# Read cross section
|
||||
if 'xs' in group:
|
||||
xs = group['xs'].value
|
||||
rx.xs = Tabulated1D(energy[rx.threshold_idx:], xs)
|
||||
# Read cross section at each temperature
|
||||
for T, Tgroup in group.items():
|
||||
if T.endswith('K'):
|
||||
if 'xs' in Tgroup:
|
||||
# Make sure temperature has associated energy grid
|
||||
if T not in energy:
|
||||
raise ValueError(
|
||||
'Could not create reaction cross section for MT={} '
|
||||
'at T={} because no corresponding energy grid '
|
||||
'exists.'.format(mt, T))
|
||||
xs = Tgroup['xs'].value
|
||||
threshold_idx = Tgroup['xs'].attrs['threshold_idx'] - 1
|
||||
tabulated_xs = Tabulated1D(energy[T][threshold_idx:], xs)
|
||||
tabulated_xs._threshold_idx = threshold_idx
|
||||
rx.xs[T] = tabulated_xs
|
||||
|
||||
# Determine number of products
|
||||
n_product = 0
|
||||
|
|
@ -421,6 +440,10 @@ class Reaction(object):
|
|||
n_grid = ace.nxs[3]
|
||||
grid = ace.xss[ace.jxs[1]:ace.jxs[1] + n_grid]
|
||||
|
||||
# Convert data temperature to a "300.0K" number for indexing
|
||||
# temperature data
|
||||
strT = str(int(round(ace.temperature / K_BOLTZMANN))) + "K"
|
||||
|
||||
if i_reaction > 0:
|
||||
mt = int(ace.xss[ace.jxs[3] + i_reaction - 1])
|
||||
rx = cls(mt)
|
||||
|
|
@ -435,11 +458,11 @@ class Reaction(object):
|
|||
loc = int(ace.xss[ace.jxs[6] + i_reaction - 1])
|
||||
|
||||
# Determine starting index on energy grid
|
||||
rx.threshold_idx = int(ace.xss[ace.jxs[7] + loc - 1]) - 1
|
||||
threshold_idx = int(ace.xss[ace.jxs[7] + loc - 1]) - 1
|
||||
|
||||
# Determine number of energies in reaction
|
||||
n_energy = int(ace.xss[ace.jxs[7] + loc])
|
||||
energy = grid[rx.threshold_idx:rx.threshold_idx + n_energy]
|
||||
energy = grid[threshold_idx:threshold_idx + n_energy]
|
||||
|
||||
# Read reaction cross section
|
||||
xs = ace.xss[ace.jxs[7] + loc + 1:ace.jxs[7] + loc + 1 + n_energy]
|
||||
|
|
@ -450,7 +473,9 @@ class Reaction(object):
|
|||
"to zero.".format(rx.mt, ace.name))
|
||||
xs[xs < 0.0] = 0.0
|
||||
|
||||
rx.xs = Tabulated1D(energy, xs)
|
||||
tabulated_xs = Tabulated1D(energy, xs)
|
||||
tabulated_xs._threshold_idx = threshold_idx
|
||||
rx.xs[strT] = tabulated_xs
|
||||
|
||||
# ==================================================================
|
||||
# YIELD AND ANGLE-ENERGY DISTRIBUTION
|
||||
|
|
@ -465,7 +490,8 @@ class Reaction(object):
|
|||
idx = ace.jxs[11] + abs(ty) - 101
|
||||
yield_ = Tabulated1D.from_ace(ace, idx)
|
||||
else:
|
||||
yield_ = abs(ty)
|
||||
# 0-order polynomial i.e. a constant
|
||||
yield_ = Polynomial((abs(ty),))
|
||||
|
||||
neutron = Product('neutron')
|
||||
neutron.yield_ = yield_
|
||||
|
|
@ -508,7 +534,9 @@ class Reaction(object):
|
|||
"Setting to zero.".format(ace.name))
|
||||
elastic_xs[elastic_xs < 0.0] = 0.0
|
||||
|
||||
rx.xs = Tabulated1D(grid, elastic_xs)
|
||||
tabulated_xs = Tabulated1D(grid, elastic_xs)
|
||||
tabulated_xs._threshold_idx = 0
|
||||
rx.xs[strT] = tabulated_xs
|
||||
|
||||
# No energy distribution for elastic scattering
|
||||
neutron = Product('neutron')
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import numpy as np
|
|||
import h5py
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
from .data import K_BOLTZMANN, ATOMIC_SYMBOL
|
||||
from .ace import Table, get_table
|
||||
from .angle_energy import AngleEnergy
|
||||
from .function import Tabulated1D
|
||||
|
|
@ -33,6 +35,7 @@ _THERMAL_NAMES = {'al': 'c_Al27', 'al27': 'c_Al27',
|
|||
'orthod': 'c_ortho_D', 'dortho': 'c_ortho_D',
|
||||
'orthoh': 'c_ortho_H', 'hortho': 'c_ortho_H',
|
||||
'ouo2': 'c_O_in_UO2', 'o2-u': 'c_O_in_UO2', 'o2/u': 'c_O_in_UO2',
|
||||
'sio2': 'c_SiO2',
|
||||
'parad': 'c_para_D', 'dpara': 'c_para_D',
|
||||
'parah': 'c_para_H', 'hpara': 'c_para_H',
|
||||
'sch4': 'c_solid_CH4', 'smeth': 'c_solid_CH4',
|
||||
|
|
@ -40,7 +43,27 @@ _THERMAL_NAMES = {'al': 'c_Al27', 'al27': 'c_Al27',
|
|||
'zrzrh': 'c_Zr_in_ZrH', 'zr-h': 'c_Zr_in_ZrH', 'zr/h': 'c_Zr_in_ZrH'}
|
||||
|
||||
|
||||
class CoherentElastic(object):
|
||||
def get_thermal_name(name):
|
||||
"""Get proper S(a,b) table name, e.g. 'HH2O' -> 'c_H_in_H2O'"""
|
||||
|
||||
if name in _THERMAL_NAMES.values():
|
||||
return name
|
||||
elif name.lower() in _THERMAL_NAMES:
|
||||
return _THERMAL_NAMES[name.lower()]
|
||||
else:
|
||||
# Make an educated guess?? This actually works well for
|
||||
# JEFF-3.2 which stupidly uses names like lw00.32t,
|
||||
# lw01.32t, etc. for different temperatures
|
||||
matches = get_close_matches(
|
||||
name.lower(), _THERMAL_NAMES.keys(), cutoff=0.5)
|
||||
if len(matches) > 0:
|
||||
return _THERMAL_NAMES[matches[0]]
|
||||
else:
|
||||
# OK, we give up. Just use the ACE name.
|
||||
return 'c_' + name
|
||||
|
||||
|
||||
class CoherentElastic(EqualityMixin):
|
||||
r"""Coherent elastic scattering data from a crystalline material
|
||||
|
||||
Parameters
|
||||
|
|
@ -67,7 +90,7 @@ class CoherentElastic(object):
|
|||
if isinstance(E, Iterable):
|
||||
E = np.asarray(E)
|
||||
idx = np.searchsorted(self.bragg_edges, E)
|
||||
return self.factors[idx]/E
|
||||
return self.factors[idx] / E
|
||||
|
||||
def __len__(self):
|
||||
return len(self.bragg_edges)
|
||||
|
|
@ -126,18 +149,19 @@ class CoherentElastic(object):
|
|||
return cls(bragg_edges, factors)
|
||||
|
||||
|
||||
class ThermalScattering(object):
|
||||
class ThermalScattering(EqualityMixin):
|
||||
"""A ThermalScattering object contains thermal scattering data as represented by
|
||||
an S(alpha, beta) table.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
ZAID identifier of the table, e.g. lwtr.10t.
|
||||
Name of the material using GND convention, e.g. c_H_in_H2O
|
||||
atomic_weight_ratio : float
|
||||
Atomic mass ratio of the target nuclide.
|
||||
temperature : float
|
||||
Temperature of the target nuclide in eV.
|
||||
kTs : Iterable of float
|
||||
List of temperatures of the target nuclide in the data set.
|
||||
The temperatures have units of MeV.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
@ -150,25 +174,33 @@ class ThermalScattering(object):
|
|||
Inelastic scattering cross section derived in the incoherent
|
||||
approximation
|
||||
name : str
|
||||
Name of the table, e.g. lwtr.20t.
|
||||
temperature : float
|
||||
Temperature of the target nuclide in eV.
|
||||
zaids : Iterable of int
|
||||
ZAID identifiers that the thermal scattering data applies to
|
||||
Name of the material using GND convention, e.g. c_H_in_H2O
|
||||
temperatures : Iterable of str
|
||||
List of string representations the temperatures of the target nuclide
|
||||
in the data set. The temperatures are strings of the temperature,
|
||||
rounded to the nearest integer; e.g., '294K'
|
||||
kTs : Iterable of float
|
||||
List of temperatures of the target nuclide in the data set.
|
||||
The temperatures have units of MeV.
|
||||
nuclides : Iterable of str
|
||||
Nuclide names that the thermal scattering data applies to
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, atomic_weight_ratio, temperature):
|
||||
def __init__(self, name, atomic_weight_ratio, kTs):
|
||||
self.name = name
|
||||
self.atomic_weight_ratio = atomic_weight_ratio
|
||||
self.temperature = temperature
|
||||
self.elastic_xs = None
|
||||
self.elastic_mu_out = None
|
||||
self.inelastic_xs = None
|
||||
self.inelastic_e_out = None
|
||||
self.inelastic_mu_out = None
|
||||
self.kTs = kTs
|
||||
self.temperatures = [str(int(round(kT / K_BOLTZMANN))) + "K"
|
||||
for kT in kTs]
|
||||
self.elastic_xs = {}
|
||||
self.elastic_mu_out = {}
|
||||
self.inelastic_xs = {}
|
||||
self.inelastic_e_out = {}
|
||||
self.inelastic_mu_out = {}
|
||||
self.inelastic_dist = {}
|
||||
self.secondary_mode = None
|
||||
self.zaids = []
|
||||
self.nuclides = []
|
||||
|
||||
def __repr__(self):
|
||||
if hasattr(self, 'name'):
|
||||
|
|
@ -194,83 +226,44 @@ class ThermalScattering(object):
|
|||
# Write basic data
|
||||
g = f.create_group(self.name)
|
||||
g.attrs['atomic_weight_ratio'] = self.atomic_weight_ratio
|
||||
g.attrs['temperature'] = self.temperature
|
||||
g.attrs['zaids'] = self.zaids
|
||||
g.attrs['nuclides'] = np.array(self.nuclides, dtype='S')
|
||||
g.attrs['secondary_mode'] = np.string_(self.secondary_mode)
|
||||
ktg = g.create_group('kTs')
|
||||
for i, temperature in enumerate(self.temperatures):
|
||||
ktg.create_dataset(temperature, data=self.kTs[i])
|
||||
|
||||
# Write thermal elastic scattering
|
||||
if self.elastic_xs is not None:
|
||||
elastic_group = g.create_group('elastic')
|
||||
self.elastic_xs.to_hdf5(elastic_group, 'xs')
|
||||
if self.elastic_mu_out is not None:
|
||||
elastic_group.create_dataset('mu_out', data=self.elastic_mu_out)
|
||||
for T in self.temperatures:
|
||||
Tg = g.create_group(T)
|
||||
# Write thermal elastic scattering
|
||||
if self.elastic_xs:
|
||||
elastic_group = Tg.create_group('elastic')
|
||||
|
||||
# Write thermal inelastic scattering
|
||||
if self.inelastic_xs is not None:
|
||||
inelastic_group = g.create_group('inelastic')
|
||||
self.inelastic_xs.to_hdf5(inelastic_group, 'xs')
|
||||
inelastic_group.attrs['secondary_mode'] = np.string_(self.secondary_mode)
|
||||
if self.secondary_mode in ('equal', 'skewed'):
|
||||
inelastic_group.create_dataset('energy_out', data=self.inelastic_e_out)
|
||||
inelastic_group.create_dataset('mu_out', data=self.inelastic_mu_out)
|
||||
elif self.secondary_mode == 'continuous':
|
||||
self.inelastic_dist.to_hdf5(inelastic_group)
|
||||
self.elastic_xs[T].to_hdf5(elastic_group, 'xs')
|
||||
if self.elastic_mu_out:
|
||||
elastic_group.create_dataset('mu_out',
|
||||
data=self.elastic_mu_out[T])
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group):
|
||||
"""Generate thermal scattering data from HDF5 group
|
||||
# Write thermal inelastic scattering
|
||||
if self.inelastic_xs:
|
||||
inelastic_group = Tg.create_group('inelastic')
|
||||
self.inelastic_xs[T].to_hdf5(inelastic_group, 'xs')
|
||||
if self.secondary_mode in ('equal', 'skewed'):
|
||||
inelastic_group.create_dataset('energy_out',
|
||||
data=self.inelastic_e_out[T])
|
||||
inelastic_group.create_dataset('mu_out',
|
||||
data=self.inelastic_mu_out[T])
|
||||
elif self.secondary_mode == 'continuous':
|
||||
self.inelastic_dist[T].to_hdf5(inelastic_group)
|
||||
|
||||
f.close()
|
||||
|
||||
def add_temperature_from_ace(self, ace_or_filename, name=None):
|
||||
"""Add data to the ThermalScattering object from an ACE file at a
|
||||
different temperature.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
group : h5py.Group
|
||||
HDF5 group to read from
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.ThermalScattering
|
||||
Neutron thermal scattering data
|
||||
|
||||
"""
|
||||
name = group.name[1:]
|
||||
atomic_weight_ratio = group.attrs['atomic_weight_ratio']
|
||||
temperature = group.attrs['temperature']
|
||||
table = cls(name, atomic_weight_ratio, temperature)
|
||||
table.zaids = group.attrs['zaids']
|
||||
|
||||
# Read thermal elastic scattering
|
||||
if 'elastic' in group:
|
||||
elastic_group = group['elastic']
|
||||
|
||||
# Cross section
|
||||
elastic_xs_type = elastic_group['xs'].attrs['type'].decode()
|
||||
if elastic_xs_type == 'tab1':
|
||||
table.elastic_xs = Tabulated1D.from_hdf5(elastic_group['xs'])
|
||||
elif elastic_xs_type == 'bragg':
|
||||
table.elastic_xs = CoherentElastic.from_hdf5(elastic_group['xs'])
|
||||
|
||||
# Angular distribution
|
||||
if 'mu_out' in elastic_group:
|
||||
table.elastic_mu_out = elastic_group['mu_out'].value
|
||||
|
||||
# Read thermal inelastic scattering
|
||||
if 'inelastic' in group:
|
||||
inelastic_group = group['inelastic']
|
||||
table.secondary_mode = inelastic_group.attrs['secondary_mode'].decode()
|
||||
table.inelastic_xs = Tabulated1D.from_hdf5(inelastic_group['xs'])
|
||||
if table.secondary_mode in ('equal', 'skewed'):
|
||||
table.inelastic_e_out = inelastic_group['energy_out']
|
||||
table.inelastic_mu_out = inelastic_group['mu_out']
|
||||
elif table.secondary_mode == 'continuous':
|
||||
table.inelastic_dist = AngleEnergy.from_hdf5(inelastic_group)
|
||||
|
||||
return table
|
||||
|
||||
@classmethod
|
||||
def from_ace(cls, ace_or_filename, name=None):
|
||||
"""Generate thermal scattering data from an ACE table
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ace : openmc.data.ace.Table or str
|
||||
ace_or_filename : openmc.data.ace.Table or str
|
||||
ACE table to read from. If given as a string, it is assumed to be
|
||||
the filename for the ACE file.
|
||||
name : str
|
||||
|
|
@ -293,7 +286,235 @@ class ThermalScattering(object):
|
|||
ace_name, xs = ace.name.split('.')
|
||||
if name is None:
|
||||
if ace_name.lower() in _THERMAL_NAMES:
|
||||
name = _THERMAL_NAMES[ace_name.lower()] + '.' + xs
|
||||
name = _THERMAL_NAMES[ace_name.lower()]
|
||||
else:
|
||||
# Make an educated guess? This actually works well for JEFF-3.2
|
||||
# which stupidly uses names like lw00.32t, lw01.32t, etc. for
|
||||
# different temperatures
|
||||
matches = get_close_matches(
|
||||
ace_name.lower(), _THERMAL_NAMES.keys(), cutoff=0.5)
|
||||
if len(matches) > 0:
|
||||
name = _THERMAL_NAMES[matches[0]]
|
||||
else:
|
||||
# OK, we give up. Just use the ACE name.
|
||||
name = 'c_' + ace.name
|
||||
warn('Thermal scattering material "{}" is not recognized. '
|
||||
'Assigning a name of {}.'.format(ace.name, name))
|
||||
|
||||
# If this ACE data matches the data within self then get the data
|
||||
if ace.temperature not in self.kTs:
|
||||
if name == self.name:
|
||||
# Add temperature and kTs
|
||||
strT = str(int(round(ace.temperature / K_BOLTZMANN))) + "K"
|
||||
self.temperatures.append(strT)
|
||||
self.kTs.append(ace.temperature)
|
||||
|
||||
# Incoherent inelastic scattering cross section
|
||||
idx = ace.jxs[1]
|
||||
n_energy = int(ace.xss[idx])
|
||||
energy = ace.xss[idx + 1: idx + 1 + n_energy]
|
||||
xs = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy]
|
||||
self.inelastic_xs[strT] = Tabulated1D(energy, xs)
|
||||
|
||||
# Make sure secondary_mode is always equal. This should always
|
||||
# be the case, but to reduce future debugging should something
|
||||
# change, this will alert the developers to the issue.
|
||||
if ace.nxs[7] == 0:
|
||||
secondary_mode = 'equal'
|
||||
elif ace.nxs[7] == 1:
|
||||
secondary_mode = 'skewed'
|
||||
elif ace.nxs[7] == 2:
|
||||
secondary_mode = 'continuous'
|
||||
|
||||
if secondary_mode != self.secondary_mode:
|
||||
raise ValueError('Secondary Modes are inconsistent.')
|
||||
|
||||
n_energy_out = ace.nxs[4]
|
||||
if self.secondary_mode in ('equal', 'skewed'):
|
||||
n_mu = ace.nxs[3]
|
||||
idx = ace.jxs[3]
|
||||
self.inelastic_e_out[strT] = \
|
||||
ace.xss[idx:idx + n_energy * n_energy_out * (n_mu + 2):
|
||||
n_mu + 2]
|
||||
self.inelastic_e_out[strT].shape = \
|
||||
(n_energy, n_energy_out)
|
||||
|
||||
self.inelastic_mu_out[strT] = \
|
||||
ace.xss[idx:idx + n_energy * n_energy_out * (n_mu + 2)]
|
||||
self.inelastic_mu_out[strT].shape = \
|
||||
(n_energy, n_energy_out, n_mu + 2)
|
||||
self.inelastic_mu_out[strT] = \
|
||||
self.inelastic_mu_out[strT][:, :, 1:]
|
||||
else:
|
||||
n_mu = ace.nxs[3] - 1
|
||||
idx = ace.jxs[3]
|
||||
locc = ace.xss[idx:idx + n_energy].astype(int)
|
||||
n_energy_out = \
|
||||
ace.xss[idx + n_energy:idx + 2 * n_energy].astype(int)
|
||||
energy_out = []
|
||||
mu_out = []
|
||||
for i in range(n_energy):
|
||||
idx = locc[i]
|
||||
|
||||
# Outgoing energy distribution for incoming energy i
|
||||
e = ace.xss[idx + 1:idx + 1 + n_energy_out[i]*(n_mu + 3):
|
||||
n_mu + 3]
|
||||
p = ace.xss[idx + 2:idx + 2 + n_energy_out[i]*(n_mu + 3):
|
||||
n_mu + 3]
|
||||
c = ace.xss[idx + 3:idx + 3 + n_energy_out[i]*(n_mu + 3):
|
||||
n_mu + 3]
|
||||
eout_i = Tabular(e, p, 'linear-linear', ignore_negative=True)
|
||||
eout_i.c = c
|
||||
|
||||
# Outgoing angle distribution for each
|
||||
# (incoming, outgoing) energy pair
|
||||
mu_i = []
|
||||
for j in range(n_energy_out[i]):
|
||||
mu = ace.xss[idx + 4:idx + 4 + n_mu]
|
||||
p_mu = 1. / n_mu * np.ones(n_mu)
|
||||
mu_ij = Discrete(mu, p_mu)
|
||||
mu_ij.c = np.cumsum(p_mu)
|
||||
mu_i.append(mu_ij)
|
||||
idx += 3 + n_mu
|
||||
|
||||
energy_out.append(eout_i)
|
||||
mu_out.append(mu_i)
|
||||
|
||||
# Create correlated angle-energy distribution
|
||||
breakpoints = [n_energy]
|
||||
interpolation = [2]
|
||||
energy = self.inelastic_xs[strT].x
|
||||
self.inelastic_dist[strT] = CorrelatedAngleEnergy(
|
||||
breakpoints, interpolation, energy, energy_out, mu_out)
|
||||
|
||||
# Incoherent/coherent elastic scattering cross section
|
||||
idx = ace.jxs[4]
|
||||
if idx != 0:
|
||||
n_energy = int(ace.xss[idx])
|
||||
energy = ace.xss[idx + 1: idx + 1 + n_energy]
|
||||
P = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy]
|
||||
|
||||
if ace.nxs[5] == 4:
|
||||
self.elastic_xs[strT] = CoherentElastic(energy, P)
|
||||
else:
|
||||
self.elastic_xs[strT] = Tabulated1D(energy, P)
|
||||
|
||||
# Angular distribution
|
||||
n_mu = ace.nxs[6]
|
||||
if n_mu != -1:
|
||||
idx = ace.jxs[6]
|
||||
self.elastic_mu_out[strT] = \
|
||||
ace.xss[idx:idx + n_energy * n_mu]
|
||||
self.elastic_mu_out[strT].shape = \
|
||||
(n_energy, n_mu)
|
||||
|
||||
else:
|
||||
raise ValueError('Data provided for an incorrect library')
|
||||
else:
|
||||
raise Warning('Temperature data set already within '
|
||||
'IncidentNeutron object')
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group_or_filename):
|
||||
"""Generate thermal scattering data from HDF5 group
|
||||
|
||||
Parameters
|
||||
----------
|
||||
group_or_filename : h5py.Group or str
|
||||
HDF5 group containing interaction data. If given as a string, it is
|
||||
assumed to be the filename for the HDF5 file, and the first group
|
||||
is used to read from.
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.ThermalScattering
|
||||
Neutron thermal scattering data
|
||||
|
||||
"""
|
||||
if isinstance(group_or_filename, h5py.Group):
|
||||
group = group_or_filename
|
||||
else:
|
||||
h5file = h5py.File(group_or_filename, 'r')
|
||||
group = list(h5file.values())[0]
|
||||
|
||||
name = group.name[1:]
|
||||
atomic_weight_ratio = group.attrs['atomic_weight_ratio']
|
||||
kTg = group['kTs']
|
||||
kTs = []
|
||||
for temp in kTg:
|
||||
kTs.append(kTg[temp].value)
|
||||
temperatures = [str(int(round(kT / K_BOLTZMANN))) + "K" for kT in kTs]
|
||||
|
||||
table = cls(name, atomic_weight_ratio, kTs)
|
||||
table.nuclides = [nuc.decode() for nuc in group.attrs['nuclides']]
|
||||
table.secondary_mode = group.attrs['secondary_mode'].decode()
|
||||
|
||||
# Read thermal elastic scattering
|
||||
for T in temperatures:
|
||||
Tgroup = group[T]
|
||||
if 'elastic' in Tgroup:
|
||||
elastic_group = Tgroup['elastic']
|
||||
|
||||
# Cross section
|
||||
elastic_xs_type = elastic_group['xs'].attrs['type'].decode()
|
||||
if elastic_xs_type == 'Tabulated1D':
|
||||
table.elastic_xs[T] = \
|
||||
Tabulated1D.from_hdf5(elastic_group['xs'])
|
||||
elif elastic_xs_type == 'bragg':
|
||||
table.elastic_xs[T] = \
|
||||
CoherentElastic.from_hdf5(elastic_group['xs'])
|
||||
|
||||
# Angular distribution
|
||||
if 'mu_out' in elastic_group:
|
||||
table.elastic_mu_out[T] = \
|
||||
elastic_group['mu_out'].value
|
||||
|
||||
# Read thermal inelastic scattering
|
||||
if 'inelastic' in Tgroup:
|
||||
inelastic_group = Tgroup['inelastic']
|
||||
table.inelastic_xs[T] = \
|
||||
Tabulated1D.from_hdf5(inelastic_group['xs'])
|
||||
if table.secondary_mode in ('equal', 'skewed'):
|
||||
table.inelastic_e_out[T] = \
|
||||
inelastic_group['energy_out']
|
||||
table.inelastic_mu_out[T] = \
|
||||
inelastic_group['mu_out']
|
||||
elif table.secondary_mode == 'continuous':
|
||||
table.inelastic_dist[T] = \
|
||||
AngleEnergy.from_hdf5(inelastic_group)
|
||||
|
||||
return table
|
||||
|
||||
@classmethod
|
||||
def from_ace(cls, ace_or_filename, name=None):
|
||||
"""Generate thermal scattering data from an ACE table
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ace_or_filename : openmc.data.ace.Table or str
|
||||
ACE table to read from. If given as a string, it is assumed to be
|
||||
the filename for the ACE file.
|
||||
name : str
|
||||
GND-conforming name of the material, e.g. c_H_in_H2O. If none is
|
||||
passed, the appropriate name is guessed based on the name of the ACE
|
||||
table.
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.ThermalScattering
|
||||
Thermal scattering data
|
||||
|
||||
"""
|
||||
if isinstance(ace_or_filename, Table):
|
||||
ace = ace_or_filename
|
||||
else:
|
||||
ace = get_table(ace_or_filename)
|
||||
|
||||
# Get new name that is GND-consistent
|
||||
ace_name, xs = ace.name.split('.')
|
||||
if name is None:
|
||||
if ace_name.lower() in _THERMAL_NAMES:
|
||||
name = _THERMAL_NAMES[ace_name.lower()]
|
||||
else:
|
||||
# Make an educated guess?? This actually works well for JEFF-3.2
|
||||
# which stupidly uses names like lw00.32t, lw01.32t, etc. for
|
||||
|
|
@ -301,21 +522,25 @@ class ThermalScattering(object):
|
|||
matches = get_close_matches(
|
||||
ace_name.lower(), _THERMAL_NAMES.keys(), cutoff=0.5)
|
||||
if len(matches) > 0:
|
||||
name = _THERMAL_NAMES[matches[0]] + '.' + xs
|
||||
name = _THERMAL_NAMES[matches[0]]
|
||||
else:
|
||||
# OK, we give up. Just use the ACE name.
|
||||
name = 'c_' + ace.name
|
||||
warn('Thermal scattering material "{}" is not recognized. '
|
||||
'Assigning a name of {}.'.format(ace.name, name))
|
||||
|
||||
table = cls(name, ace.atomic_weight_ratio, ace.temperature)
|
||||
# Assign temperature to the running list
|
||||
kTs = [ace.temperature]
|
||||
temperatures = [str(int(round(ace.temperature / K_BOLTZMANN))) + "K"]
|
||||
|
||||
table = cls(name, ace.atomic_weight_ratio, kTs)
|
||||
|
||||
# Incoherent inelastic scattering cross section
|
||||
idx = ace.jxs[1]
|
||||
n_energy = int(ace.xss[idx])
|
||||
energy = ace.xss[idx+1 : idx+1+n_energy]
|
||||
xs = ace.xss[idx+1+n_energy : idx+1+2*n_energy]
|
||||
table.inelastic_xs = Tabulated1D(energy, xs)
|
||||
table.inelastic_xs[temperatures[0]] = Tabulated1D(energy, xs)
|
||||
|
||||
if ace.nxs[7] == 0:
|
||||
table.secondary_mode = 'equal'
|
||||
|
|
@ -328,34 +553,45 @@ class ThermalScattering(object):
|
|||
if table.secondary_mode in ('equal', 'skewed'):
|
||||
n_mu = ace.nxs[3]
|
||||
idx = ace.jxs[3]
|
||||
table.inelastic_e_out = ace.xss[idx:idx+n_energy*n_energy_out*(n_mu+2):n_mu+2]
|
||||
table.inelastic_e_out.shape = (n_energy, n_energy_out)
|
||||
table.inelastic_e_out[temperatures[0]] = \
|
||||
ace.xss[idx:idx + n_energy * n_energy_out * (n_mu + 2):
|
||||
n_mu + 2]
|
||||
table.inelastic_e_out[temperatures[0]].shape = \
|
||||
(n_energy, n_energy_out)
|
||||
|
||||
table.inelastic_mu_out = ace.xss[idx:idx+n_energy*n_energy_out*(n_mu+2)]
|
||||
table.inelastic_mu_out.shape = (n_energy, n_energy_out, n_mu+2)
|
||||
table.inelastic_mu_out = table.inelastic_mu_out[:, :, 1:]
|
||||
table.inelastic_mu_out[temperatures[0]] = \
|
||||
ace.xss[idx:idx + n_energy * n_energy_out * (n_mu + 2)]
|
||||
table.inelastic_mu_out[temperatures[0]].shape = \
|
||||
(n_energy, n_energy_out, n_mu+2)
|
||||
table.inelastic_mu_out[temperatures[0]] = \
|
||||
table.inelastic_mu_out[temperatures[0]][:, :, 1:]
|
||||
else:
|
||||
n_mu = ace.nxs[3] - 1
|
||||
idx = ace.jxs[3]
|
||||
locc = ace.xss[idx:idx + n_energy].astype(int)
|
||||
n_energy_out = ace.xss[idx + n_energy:idx + 2*n_energy].astype(int)
|
||||
n_energy_out = \
|
||||
ace.xss[idx + n_energy:idx + 2 * n_energy].astype(int)
|
||||
energy_out = []
|
||||
mu_out = []
|
||||
for i in range(n_energy):
|
||||
idx = locc[i]
|
||||
|
||||
# Outgoing energy distribution for incoming energy i
|
||||
e = ace.xss[idx + 1:idx + 1 + n_energy_out[i]*(n_mu + 3):n_mu + 3]
|
||||
p = ace.xss[idx + 2:idx + 2 + n_energy_out[i]*(n_mu + 3):n_mu + 3]
|
||||
c = ace.xss[idx + 3:idx + 3 + n_energy_out[i]*(n_mu + 3):n_mu + 3]
|
||||
e = ace.xss[idx + 1:idx + 1 + n_energy_out[i]*(n_mu + 3):
|
||||
n_mu + 3]
|
||||
p = ace.xss[idx + 2:idx + 2 + n_energy_out[i]*(n_mu + 3):
|
||||
n_mu + 3]
|
||||
c = ace.xss[idx + 3:idx + 3 + n_energy_out[i]*(n_mu + 3):
|
||||
n_mu + 3]
|
||||
eout_i = Tabular(e, p, 'linear-linear', ignore_negative=True)
|
||||
eout_i.c = c
|
||||
|
||||
# Outgoing angle distribution for each (incoming, outgoing) energy pair
|
||||
# Outgoing angle distribution for each
|
||||
# (incoming, outgoing) energy pair
|
||||
mu_i = []
|
||||
for j in range(n_energy_out[i]):
|
||||
mu = ace.xss[idx + 4:idx + 4 + n_mu]
|
||||
p_mu = 1./n_mu*np.ones(n_mu)
|
||||
p_mu = 1. / n_mu * np.ones(n_mu)
|
||||
mu_ij = Discrete(mu, p_mu)
|
||||
mu_ij.c = np.cumsum(p_mu)
|
||||
mu_i.append(mu_ij)
|
||||
|
|
@ -367,31 +603,35 @@ class ThermalScattering(object):
|
|||
# Create correlated angle-energy distribution
|
||||
breakpoints = [n_energy]
|
||||
interpolation = [2]
|
||||
energy = table.inelastic_xs.x
|
||||
table.inelastic_dist = CorrelatedAngleEnergy(
|
||||
energy = table.inelastic_xs[temperatures[0]].x
|
||||
table.inelastic_dist[temperatures[0]] = CorrelatedAngleEnergy(
|
||||
breakpoints, interpolation, energy, energy_out, mu_out)
|
||||
|
||||
# Incoherent/coherent elastic scattering cross section
|
||||
idx = ace.jxs[4]
|
||||
if idx != 0:
|
||||
n_energy = int(ace.xss[idx])
|
||||
energy = ace.xss[idx+1 : idx+1+n_energy]
|
||||
P = ace.xss[idx+1+n_energy : idx+1+2*n_energy]
|
||||
energy = ace.xss[idx + 1: idx + 1 + n_energy]
|
||||
P = ace.xss[idx + 1 + n_energy: idx + 1 + 2 * n_energy]
|
||||
|
||||
if ace.nxs[5] == 4:
|
||||
table.elastic_xs = CoherentElastic(energy, P)
|
||||
table.elastic_xs[temperatures[0]] = CoherentElastic(energy, P)
|
||||
else:
|
||||
table.elastic_xs = Tabulated1D(energy, P)
|
||||
table.elastic_xs[temperatures[0]] = Tabulated1D(energy, P)
|
||||
|
||||
# Angular distribution
|
||||
n_mu = ace.nxs[6]
|
||||
if n_mu != -1:
|
||||
idx = ace.jxs[6]
|
||||
table.elastic_mu_out = ace.xss[idx:idx + n_energy*n_mu]
|
||||
table.elastic_mu_out.shape = (n_energy, n_mu)
|
||||
table.elastic_mu_out[temperatures[0]] = \
|
||||
ace.xss[idx:idx + n_energy * n_mu]
|
||||
table.elastic_mu_out[temperatures[0]].shape = \
|
||||
(n_energy, n_mu)
|
||||
|
||||
# Get relevant ZAIDs
|
||||
pairs = np.fromiter(map(lambda p: p[0], ace.pairs), int)
|
||||
table.zaids = pairs[np.nonzero(pairs)]
|
||||
# Get relevant nuclides
|
||||
for zaid, awr in ace.pairs:
|
||||
if zaid > 0:
|
||||
Z, A = divmod(zaid, 1000)
|
||||
table.nuclides.append(ATOMIC_SYMBOL[Z] + str(A))
|
||||
|
||||
return table
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ from numbers import Integral, Real
|
|||
import numpy as np
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
||||
|
||||
class ProbabilityTables(object):
|
||||
class ProbabilityTables(EqualityMixin):
|
||||
r"""Unresolved resonance region probability tables.
|
||||
|
||||
Parameters
|
||||
|
|
|
|||
|
|
@ -19,38 +19,28 @@ class Element(object):
|
|||
----------
|
||||
name : str
|
||||
Chemical symbol of the element, e.g. Pu
|
||||
xs : str
|
||||
Cross section identifier, e.g. 71c
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name : str
|
||||
Chemical symbol of the element, e.g. Pu
|
||||
xs : str
|
||||
Cross section identifier, e.g. 71c
|
||||
scattering : {'data', 'iso-in-lab', None}
|
||||
The type of angular scattering distribution to use
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name='', xs=None):
|
||||
def __init__(self, name=''):
|
||||
# Initialize class attributes
|
||||
self._name = ''
|
||||
self._xs = None
|
||||
self._scattering = None
|
||||
|
||||
# Set class attributes
|
||||
self.name = name
|
||||
|
||||
if xs is not None:
|
||||
self.xs = xs
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Element):
|
||||
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:
|
||||
|
|
@ -72,17 +62,12 @@ class Element(object):
|
|||
|
||||
def __repr__(self):
|
||||
string = 'Element - {0}\n'.format(self._name)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
|
||||
if self.scattering is not None:
|
||||
string += '{0: <16}{1}{2}\n'.format('\tscattering', '=\t',
|
||||
self.scattering)
|
||||
|
||||
return string
|
||||
|
||||
@property
|
||||
def xs(self):
|
||||
return self._xs
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
|
@ -91,11 +76,6 @@ class Element(object):
|
|||
def scattering(self):
|
||||
return self._scattering
|
||||
|
||||
@xs.setter
|
||||
def xs(self, xs):
|
||||
check_type('cross section identifier', xs, basestring)
|
||||
self._xs = xs
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
check_type('element name', name, basestring)
|
||||
|
|
@ -127,6 +107,6 @@ class Element(object):
|
|||
isotopes = []
|
||||
for isotope, abundance in sorted(NATURAL_ABUNDANCE.items()):
|
||||
if re.match(r'{}\d+'.format(self.name), isotope):
|
||||
nuc = openmc.Nuclide(isotope, self.xs)
|
||||
nuc = openmc.Nuclide(isotope)
|
||||
isotopes.append((nuc, abundance))
|
||||
return isotopes
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ _FILTER_TYPES = ['universe', 'material', 'cell', 'cellborn', 'surface',
|
|||
'mesh', 'energy', 'energyout', 'mu', 'polar', 'azimuthal',
|
||||
'distribcell', 'delayedgroup']
|
||||
|
||||
_CURRENT_NAMES = {1: 'x-min out', 2: 'x-max out',
|
||||
3: 'y-min out', 4: 'y-max out',
|
||||
5: 'z-min out', 6: 'z-max out',
|
||||
7: 'x-min in', 8: 'x-max in',
|
||||
9: 'y-min in', 10: 'y-max in',
|
||||
11: 'z-min in', 12: 'z-max in'}
|
||||
class FilterMeta(ABCMeta):
|
||||
def __new__(cls, name, bases, namespace, **kwargs):
|
||||
if not name.endswith('Filter'):
|
||||
|
|
@ -260,7 +266,7 @@ class Filter(with_metaclass(FilterMeta, object)):
|
|||
cell instance ID for 'distribcell' Filters. The bin is a 2-tuple of
|
||||
floats for 'energy' and 'energyout' filters corresponding to the
|
||||
energy boundaries of the bin of interest. The bin is an (x,y,z)
|
||||
3-tuple for 'mesh' filters corresponding to the mesh cell
|
||||
3-tuple for 'mesh' filters corresponding to the mesh cell of
|
||||
interest.
|
||||
|
||||
Returns
|
||||
|
|
@ -387,7 +393,6 @@ class Filter(with_metaclass(FilterMeta, object)):
|
|||
filter_bins = np.repeat(self.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.short_name.lower() : filter_bins})])
|
||||
|
||||
|
|
@ -416,7 +421,22 @@ class UniverseFilter(IntegralFilter): pass
|
|||
class MaterialFilter(IntegralFilter): pass
|
||||
class CellFilter(IntegralFilter): pass
|
||||
class CellbornFilter(IntegralFilter): pass
|
||||
class SurfaceFilter(IntegralFilter): pass
|
||||
|
||||
|
||||
class SurfaceFilter(IntegralFilter):
|
||||
def get_pandas_dataframe(self, data_size, distribcell_paths=True):
|
||||
# Initialize Pandas DataFrame
|
||||
import pandas as pd
|
||||
df = pd.DataFrame()
|
||||
|
||||
filter_bins = np.repeat(self.bins, self.stride)
|
||||
tile_factor = data_size / len(filter_bins)
|
||||
filter_bins = np.tile(filter_bins, tile_factor)
|
||||
filter_bins = [_CURRENT_NAMES[x] for x in filter_bins]
|
||||
df = pd.concat([df, pd.DataFrame(
|
||||
{self.short_name.lower() : filter_bins})])
|
||||
|
||||
return df
|
||||
|
||||
|
||||
class MeshFilter(Filter):
|
||||
|
|
|
|||
|
|
@ -50,6 +50,20 @@ class Geometry(object):
|
|||
|
||||
self._root_universe = root_universe
|
||||
|
||||
def add_volume_information(self, volume_calc):
|
||||
"""Add volume information from a stochastic volume calculation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
volume_calc : openmc.VolumeCalculation
|
||||
Results from a stochastic volume calculation
|
||||
|
||||
"""
|
||||
if volume_calc.domain_type == 'cell':
|
||||
for cell in self.get_all_cells():
|
||||
if cell.id in volume_calc.results:
|
||||
cell.add_volume_information(volume_calc)
|
||||
|
||||
def export_to_xml(self):
|
||||
"""Create a geometry.xml file that can be used for a simulation.
|
||||
|
||||
|
|
@ -166,24 +180,6 @@ class Geometry(object):
|
|||
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
|
||||
Nuclides in the geometry
|
||||
|
||||
"""
|
||||
|
||||
nuclides = OrderedDict()
|
||||
materials = self.get_all_materials()
|
||||
|
||||
for material in materials:
|
||||
nuclides.update(material.get_all_nuclides())
|
||||
|
||||
return nuclides
|
||||
|
||||
def get_all_materials(self):
|
||||
"""Return all materials assigned to a cell
|
||||
|
||||
|
|
|
|||
|
|
@ -144,25 +144,26 @@ class Lattice(object):
|
|||
|
||||
return univs
|
||||
|
||||
def get_all_nuclides(self):
|
||||
"""Return all nuclides contained in the lattice
|
||||
def get_nuclides(self):
|
||||
"""Returns all nuclides in the lattice
|
||||
|
||||
Returns
|
||||
-------
|
||||
nuclides : collections.OrderedDict
|
||||
Dictionary whose keys are nuclide names and values are 2-tuples of
|
||||
(nuclide, density)
|
||||
nuclides : list of str
|
||||
List of nuclide names
|
||||
|
||||
"""
|
||||
|
||||
nuclides = OrderedDict()
|
||||
nuclides = []
|
||||
|
||||
# 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())
|
||||
for universe in unique_universes.values():
|
||||
for nuclide in universe.get_nuclides():
|
||||
if nuclide not in nuclides:
|
||||
nuclides.append(nuclide)
|
||||
|
||||
return nuclides
|
||||
|
||||
|
|
|
|||
|
|
@ -13,35 +13,25 @@ class Macroscopic(object):
|
|||
----------
|
||||
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):
|
||||
def __init__(self, name=''):
|
||||
# Initialize class attributes
|
||||
self._name = ''
|
||||
self._xs = None
|
||||
|
||||
# Set the Material class attributes
|
||||
# Set the Macroscopic 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:
|
||||
|
|
@ -53,27 +43,17 @@ class Macroscopic(object):
|
|||
return not self == other
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self._name, self._xs))
|
||||
return hash((self._name))
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from xml.etree import ElementTree as ET
|
|||
import sys
|
||||
|
||||
import openmc
|
||||
import openmc.data
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.clean_xml import sort_xml_elements, clean_xml_indentation
|
||||
|
||||
|
|
@ -40,11 +41,19 @@ class Material(object):
|
|||
name : str, optional
|
||||
Name of the material. If not specified, the name will be the empty
|
||||
string.
|
||||
temperature : str, optional
|
||||
The temperature identifier applied to this material. The units are
|
||||
in Kelvin and the temperature rounded to the nearest integer.
|
||||
For example, a tempreature of 293.6K would be provided as '294K'
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id : int
|
||||
Unique identifier for the material
|
||||
temperature : str
|
||||
The temperature identifier applied to this material. The units are
|
||||
in Kelvin and the temperature rounded to the nearest integer.
|
||||
For example, a tempreature of 293.6K would be provided as '294K'
|
||||
density : float
|
||||
Density of the material (units defined separately)
|
||||
density_units : str
|
||||
|
|
@ -62,10 +71,11 @@ class Material(object):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, material_id=None, name=''):
|
||||
def __init__(self, material_id=None, name='', temperature=None):
|
||||
# Initialize class attributes
|
||||
self.id = material_id
|
||||
self.name = name
|
||||
self.temperature = temperature
|
||||
self._density = None
|
||||
self._density_units = ''
|
||||
|
||||
|
|
@ -79,7 +89,7 @@ class Material(object):
|
|||
# A list of tuples (element, percent, percent type)
|
||||
self._elements = []
|
||||
|
||||
# If specified, a list of tuples of (table name, xs identifier)
|
||||
# If specified, a list of table names
|
||||
self._sab = []
|
||||
|
||||
# If true, the material will be initialized as distributed
|
||||
|
|
@ -119,6 +129,8 @@ class Material(object):
|
|||
string = 'Material\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('\Temperature', '=\t',
|
||||
self._temperature)
|
||||
|
||||
string += '{0: <16}{1}{2}'.format('\tDensity', '=\t', self._density)
|
||||
string += ' [{0}]\n'.format(self._density_units)
|
||||
|
|
@ -126,13 +138,12 @@ class Material(object):
|
|||
string += '{0: <16}\n'.format('\tS(a,b) Tables')
|
||||
|
||||
for sab in self._sab:
|
||||
string += '{0: <16}{1}[{2}{3}]\n'.format('\tS(a,b)', '=\t',
|
||||
sab[0], sab[1])
|
||||
string += '{0: <16}{1}{2}\n'.format('\tS(a,b)', '=\t', sab)
|
||||
|
||||
string += '{0: <16}\n'.format('\tNuclides')
|
||||
|
||||
for nuclide, percent, percent_type in self._nuclides:
|
||||
string += '{0: <16}'.format('\t{0.name}.{0.xs}'.format(nuclide))
|
||||
string += '{0: <16}'.format('\t{0.name}'.format(nuclide))
|
||||
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
|
||||
|
||||
if self._macroscopic is not None:
|
||||
|
|
@ -142,7 +153,7 @@ class Material(object):
|
|||
string += '{0: <16}\n'.format('\tElements')
|
||||
|
||||
for element, percent, percent_type in self._elements:
|
||||
string += '{0: <16}'.format('\t{0.name}.{0.xs}'.format(element))
|
||||
string += '{0: <16}'.format('\t{0.name}'.format(element))
|
||||
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
|
||||
|
||||
return string
|
||||
|
|
@ -155,6 +166,10 @@ class Material(object):
|
|||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
return self._temperature
|
||||
|
||||
@property
|
||||
def density(self):
|
||||
return self._density
|
||||
|
|
@ -200,6 +215,15 @@ class Material(object):
|
|||
else:
|
||||
self._name = ''
|
||||
|
||||
@temperature.setter
|
||||
def temperature(self, temperature):
|
||||
if temperature is not None:
|
||||
cv.check_type('Temperature for Material ID="{0}"'.format(self._id),
|
||||
temperature, basestring)
|
||||
self._temperature = temperature
|
||||
else:
|
||||
self._temperature = ''
|
||||
|
||||
def set_density(self, units, density=None):
|
||||
"""Set the density of the material
|
||||
|
||||
|
|
@ -457,15 +481,13 @@ class Material(object):
|
|||
if element == elm:
|
||||
self._nuclides.remove(elm)
|
||||
|
||||
def add_s_alpha_beta(self, name, xs):
|
||||
def add_s_alpha_beta(self, name):
|
||||
r"""Add an :math:`S(\alpha,\beta)` table to the material
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of the :math:`S(\alpha,\beta)` table
|
||||
xs : str
|
||||
Cross section identifier, e.g. '71t'
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -479,12 +501,14 @@ class Material(object):
|
|||
'non-string table name "{1}"'.format(self._id, name)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(xs, basestring):
|
||||
msg = 'Unable to add an S(a,b) table to Material ID="{0}" with a ' \
|
||||
'non-string cross-section identifier "{1}"'.format(self._id, xs)
|
||||
raise ValueError(msg)
|
||||
new_name = openmc.data.get_thermal_name(name)
|
||||
if new_name != name:
|
||||
msg = 'OpenMC S(a,b) tables follow the GND naming convention. ' \
|
||||
'Table "{}" is being renamed as "{}".'.format(name, new_name)
|
||||
warnings.warn(msg)
|
||||
|
||||
self._sab.append(new_name)
|
||||
|
||||
self._sab.append((name, xs))
|
||||
|
||||
def make_isotropic_in_lab(self):
|
||||
for nuclide, percent, percent_type in self._nuclides:
|
||||
|
|
@ -492,9 +516,31 @@ class Material(object):
|
|||
for element, percent, percent_type in self._elements:
|
||||
element.scattering = 'iso-in-lab'
|
||||
|
||||
def get_all_nuclides(self):
|
||||
def get_nuclides(self):
|
||||
"""Returns all nuclides in the material
|
||||
|
||||
Returns
|
||||
-------
|
||||
nuclides : list of str
|
||||
List of nuclide names
|
||||
|
||||
"""
|
||||
|
||||
nuclides = []
|
||||
|
||||
for nuclide, density, density_type in self._nuclides:
|
||||
nuclides.append(nuclide.name)
|
||||
|
||||
for element, density, density_type in self._elements:
|
||||
# Expand natural element into isotopes
|
||||
for isotope, abundance in element.expand():
|
||||
nuclides.append(isotope.name)
|
||||
|
||||
return nuclides
|
||||
|
||||
def get_nuclide_densities(self):
|
||||
"""Returns all nuclides in the material and their densities
|
||||
|
||||
Returns
|
||||
-------
|
||||
nuclides : dict
|
||||
|
|
@ -525,9 +571,6 @@ class Material(object):
|
|||
else:
|
||||
xml_element.set("wo", str(nuclide[1]))
|
||||
|
||||
if nuclide[0].xs is not None:
|
||||
xml_element.set("xs", nuclide[0].xs)
|
||||
|
||||
if not nuclide[0].scattering is None:
|
||||
xml_element.set("scattering", nuclide[0].scattering)
|
||||
|
||||
|
|
@ -537,9 +580,6 @@ class Material(object):
|
|||
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):
|
||||
|
|
@ -552,9 +592,6 @@ class Material(object):
|
|||
else:
|
||||
xml_element.set("wo", str(element[1]))
|
||||
|
||||
if element[0].xs is not None:
|
||||
xml_element.set("xs", element[0].xs)
|
||||
|
||||
if not element[0].scattering is None:
|
||||
xml_element.set("scattering", element[0].scattering)
|
||||
|
||||
|
|
@ -593,6 +630,11 @@ class Material(object):
|
|||
if len(self._name) > 0:
|
||||
element.set("name", str(self._name))
|
||||
|
||||
# Create temperature XML subelement
|
||||
if len(self.temperature) > 0:
|
||||
subelement = ET.SubElement(element, "temperature")
|
||||
subelement.text = self.temperature
|
||||
|
||||
# Create density XML subelement
|
||||
subelement = ET.SubElement(element, "density")
|
||||
if self._density_units is not 'sum':
|
||||
|
|
@ -657,8 +699,7 @@ class Material(object):
|
|||
if len(self._sab) > 0:
|
||||
for sab in self._sab:
|
||||
subelement = ET.SubElement(element, "sab")
|
||||
subelement.set("name", sab[0])
|
||||
subelement.set("xs", sab[1])
|
||||
subelement.set("name", sab)
|
||||
|
||||
return element
|
||||
|
||||
|
|
@ -683,30 +724,14 @@ class Materials(cv.CheckedList):
|
|||
materials : Iterable of openmc.Material
|
||||
Materials to add to the collection
|
||||
|
||||
Attributes
|
||||
----------
|
||||
default_xs : str
|
||||
The default cross section identifier applied to a nuclide when none is
|
||||
specified
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, materials=None):
|
||||
super(Materials, self).__init__(Material, 'materials collection')
|
||||
self._default_xs = None
|
||||
self._materials_file = ET.Element("materials")
|
||||
if materials is not None:
|
||||
self += materials
|
||||
|
||||
@property
|
||||
def default_xs(self):
|
||||
return self._default_xs
|
||||
|
||||
@default_xs.setter
|
||||
def default_xs(self, xs):
|
||||
cv.check_type('default xs', xs, basestring)
|
||||
self._default_xs = xs
|
||||
|
||||
def add_material(self, material):
|
||||
"""Append material to collection
|
||||
|
||||
|
|
@ -788,10 +813,6 @@ class Materials(cv.CheckedList):
|
|||
material.make_isotropic_in_lab()
|
||||
|
||||
def _create_material_subelements(self):
|
||||
if self._default_xs is not None:
|
||||
subelement = ET.SubElement(self._materials_file, "default_xs")
|
||||
subelement.text = self._default_xs
|
||||
|
||||
for material in self:
|
||||
xml_element = material.get_material_xml()
|
||||
self._materials_file.append(xml_element)
|
||||
|
|
|
|||
129
openmc/mesh.py
129
openmc/mesh.py
|
|
@ -6,6 +6,7 @@ import sys
|
|||
import numpy as np
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
import openmc
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
|
|
@ -181,6 +182,38 @@ class Mesh(object):
|
|||
string += '{0: <16}{1}{2}\n'.format('\tPixels', '=\t', self._width)
|
||||
return string
|
||||
|
||||
def cell_generator(self):
|
||||
"""Generator function to traverse through every [i,j,k] index
|
||||
of the mesh.
|
||||
|
||||
For example the following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
for mesh_index in mymesh.cell_generator():
|
||||
print mesh_index
|
||||
|
||||
will produce the following output for a 3-D 2x2x2 mesh in mymesh::
|
||||
|
||||
[1, 1, 1]
|
||||
[1, 1, 2]
|
||||
[1, 2, 1]
|
||||
[1, 2, 2]
|
||||
...
|
||||
|
||||
|
||||
"""
|
||||
|
||||
if len(self.dimension) == 2:
|
||||
for x in range(self.dimension[0]):
|
||||
for y in range(self.dimension[1]):
|
||||
yield [x + 1, y + 1, 1]
|
||||
else:
|
||||
for x in range(self.dimension[0]):
|
||||
for y in range(self.dimension[1]):
|
||||
for z in range(self.dimension[2]):
|
||||
yield [x + 1, y + 1, z + 1]
|
||||
|
||||
def get_mesh_xml(self):
|
||||
"""Return XML representation of the mesh
|
||||
|
||||
|
|
@ -210,3 +243,99 @@ class Mesh(object):
|
|||
subelement.text = ' '.join(map(str, self._width))
|
||||
|
||||
return element
|
||||
|
||||
def build_cells(self, bc=['reflective'] * 6):
|
||||
"""Generates a lattice of universes with the same dimensionality
|
||||
as the mesh object. The individual cells/universes produced
|
||||
will not have material definitions applied and so downstream code
|
||||
will have to apply that information.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bc : iterable of {'reflective', 'periodic', 'transmission', or 'vacuum'}
|
||||
Boundary conditions for each of the four faces of a rectangle
|
||||
(if aplying to a 2D mesh) or six faces of a parallelepiped
|
||||
(if applying to a 3D mesh) provided in the following order:
|
||||
[x min, x max, y min, y max, z min, z max]. 2-D cells do not
|
||||
contain the z min and z max entries.
|
||||
|
||||
Returns
|
||||
-------
|
||||
root_cell : openmc.Cell
|
||||
The cell containing the lattice representing the mesh geometry;
|
||||
this cell is a single parallelepiped with boundaries matching
|
||||
the outermost mesh boundary with the boundary conditions from bc
|
||||
applied.
|
||||
cells : iterable of openmc.Cell
|
||||
The list of cells within each lattice position mimicking the mesh
|
||||
geometry.
|
||||
|
||||
"""
|
||||
|
||||
twod = len(self.dimension) == 2
|
||||
cv.check_length('bc', bc, length_min=4, length_max=6)
|
||||
for entry in bc:
|
||||
cv.check_value('bc', entry, ['transmission', 'vacuum',
|
||||
'reflective', 'periodic'])
|
||||
|
||||
# Build the cell which will contain the lattice
|
||||
xplanes = [openmc.XPlane(x0=self.lower_left[0],
|
||||
boundary_type=bc[0]),
|
||||
openmc.XPlane(x0=self.upper_right[0],
|
||||
boundary_type=bc[1])]
|
||||
yplanes = [openmc.YPlane(y0=self.lower_left[1],
|
||||
boundary_type=bc[2]),
|
||||
openmc.YPlane(y0=self.upper_right[1],
|
||||
boundary_type=bc[3])]
|
||||
if twod:
|
||||
zplanes = [openmc.ZPlane(z0=np.finfo(np.float).min,
|
||||
boundary_type='reflective'),
|
||||
openmc.ZPlane(z0=np.finfo(np.float).max,
|
||||
boundary_type='reflective')]
|
||||
else:
|
||||
zplanes = [openmc.ZPlane(z0=self.lower_left[2],
|
||||
boundary_type=bc[4]),
|
||||
openmc.ZPlane(z0=self.upper_right[2],
|
||||
boundary_type=bc[5])]
|
||||
root_cell = openmc.Cell()
|
||||
root_cell.region = ((+xplanes[0] & -xplanes[1]) &
|
||||
(+yplanes[0] & -yplanes[1]) &
|
||||
(+zplanes[0] & -zplanes[1]))
|
||||
|
||||
# Build the universes which will be used for each of the [i,j,k]
|
||||
# locations within the mesh.
|
||||
# We will also have to build cells to assign to these universes
|
||||
universes = np.ndarray(self.dimension[::-1], dtype=np.object)
|
||||
cells = []
|
||||
for [i, j, k] in self.cell_generator():
|
||||
if twod:
|
||||
universes[j - 1, i - 1] = openmc.Universe()
|
||||
cells.append(openmc.Cell())
|
||||
universes[j - 1, i - 1].add_cells([cells[-1]])
|
||||
else:
|
||||
universes[k - 1, j - 1, i - 1] = openmc.Universe()
|
||||
cells.append(openmc.Cell())
|
||||
universes[k - 1, j - 1, i - 1].add_cells([cells[-1]])
|
||||
|
||||
lattice = openmc.RectLattice()
|
||||
lattice.lower_left = self.lower_left
|
||||
|
||||
if self.width is not None:
|
||||
lattice.pitch = self.width
|
||||
else:
|
||||
dx = ((self.upper_right[0] - self.lower_left[0]) /
|
||||
self.dimension[0])
|
||||
dy = ((self.upper_right[1] - self.lower_left[1]) /
|
||||
self.dimension[1])
|
||||
if twod:
|
||||
lattice.pitch = [dx, dy]
|
||||
else:
|
||||
dz = ((self.upper_right[2] - self.lower_left[2]) /
|
||||
self.dimension[2])
|
||||
lattice.pitch = [dx, dy, dz]
|
||||
lattice.universes = universes
|
||||
|
||||
# Fill Cell with the Lattice
|
||||
root_cell.fill = lattice
|
||||
|
||||
return root_cell, cells
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from openmc.mgxs.groups import EnergyGroups
|
||||
from openmc.mgxs.library import Library
|
||||
from openmc.mgxs.mgxs import *
|
||||
from openmc.mgxs.mdgxs import *
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import numpy as np
|
|||
import openmc
|
||||
import openmc.mgxs
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.tallies import ESTIMATOR_TYPES
|
||||
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
|
|
@ -18,17 +19,18 @@ if sys.version_info[0] >= 3:
|
|||
|
||||
|
||||
class Library(object):
|
||||
"""A multi-group cross section library for some energy group structure.
|
||||
"""A multi-energy-group and multi-delayed-group cross section library for
|
||||
some energy group structure.
|
||||
|
||||
This class can be used for both OpenMC input generation and tally data
|
||||
post-processing to compute spatially-homogenized and energy-integrated
|
||||
multi-group cross sections for deterministic neutronics calculations.
|
||||
|
||||
This class helps automate the generation of MGXS objects for some energy
|
||||
group structure and domain type. The Library serves as a collection for
|
||||
MGXS objects with routines to automate the initialization of tallies for
|
||||
input files, the loading of tally data from statepoint files, data storage,
|
||||
energy group condensation and more.
|
||||
This class helps automate the generation of MGXS and MDGXS objects for some
|
||||
energy group structure and domain type. The Library serves as a collection
|
||||
for MGXS and MDGXS objects with routines to automate the initialization of
|
||||
tallies for input files, the loading of tally data from statepoint files,
|
||||
data storage, energy group condensation and more.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -39,7 +41,7 @@ class Library(object):
|
|||
mgxs_types : Iterable of str
|
||||
The types of cross sections in the library (e.g., ['total', 'scatter'])
|
||||
name : str, optional
|
||||
Name of the multi-group cross section. library Used as a label to
|
||||
Name of the multi-group cross section library. Used as a label to
|
||||
identify tallies in OpenMC 'tallies.xml' file.
|
||||
|
||||
Attributes
|
||||
|
|
@ -64,6 +66,11 @@ class Library(object):
|
|||
The highest legendre moment in the scattering matrices (default is 0)
|
||||
energy_groups : openmc.mgxs.EnergyGroups
|
||||
Energy group structure for energy condensation
|
||||
delayed_groups : list of int
|
||||
Delayed groups to filter out the xs
|
||||
estimator : str or None
|
||||
The tally estimator used to compute multi-group cross sections. If None,
|
||||
the default for each MGXS type is used.
|
||||
tally_trigger : openmc.Trigger
|
||||
An (optional) tally precision trigger given to each tally used to
|
||||
compute the cross section
|
||||
|
|
@ -95,6 +102,7 @@ class Library(object):
|
|||
self._domain_type = None
|
||||
self._domains = 'all'
|
||||
self._energy_groups = None
|
||||
self._delayed_groups = None
|
||||
self._correction = 'P0'
|
||||
self._legendre_order = 0
|
||||
self._tally_trigger = None
|
||||
|
|
@ -102,6 +110,7 @@ class Library(object):
|
|||
self._sp_filename = None
|
||||
self._keff = None
|
||||
self._sparse = False
|
||||
self._estimator = None
|
||||
|
||||
self.name = name
|
||||
self.openmc_geometry = openmc_geometry
|
||||
|
|
@ -126,6 +135,7 @@ class Library(object):
|
|||
clone._correction = self.correction
|
||||
clone._legendre_order = self.legendre_order
|
||||
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
|
||||
clone._delayed_groups = copy.deepcopy(self.delayed_groups, memo)
|
||||
clone._tally_trigger = copy.deepcopy(self.tally_trigger, memo)
|
||||
clone._all_mgxs = copy.deepcopy(self.all_mgxs)
|
||||
clone._sp_filename = self._sp_filename
|
||||
|
|
@ -194,6 +204,10 @@ class Library(object):
|
|||
def energy_groups(self):
|
||||
return self._energy_groups
|
||||
|
||||
@property
|
||||
def delayed_groups(self):
|
||||
return self._delayed_groups
|
||||
|
||||
@property
|
||||
def correction(self):
|
||||
return self._correction
|
||||
|
|
@ -206,10 +220,21 @@ class Library(object):
|
|||
def tally_trigger(self):
|
||||
return self._tally_trigger
|
||||
|
||||
@property
|
||||
def estimator(self):
|
||||
return self._estimator
|
||||
|
||||
@property
|
||||
def num_groups(self):
|
||||
return self.energy_groups.num_groups
|
||||
|
||||
@property
|
||||
def num_delayed_groups(self):
|
||||
if self.delayed_groups == None:
|
||||
return 0
|
||||
else:
|
||||
return len(self.delayed_groups)
|
||||
|
||||
@property
|
||||
def all_mgxs(self):
|
||||
return self._all_mgxs
|
||||
|
|
@ -239,22 +264,33 @@ class Library(object):
|
|||
|
||||
@mgxs_types.setter
|
||||
def mgxs_types(self, mgxs_types):
|
||||
all_mgxs_types = openmc.mgxs.MGXS_TYPES + openmc.mgxs.MDGXS_TYPES
|
||||
if mgxs_types == 'all':
|
||||
self._mgxs_types = openmc.mgxs.MGXS_TYPES
|
||||
self._mgxs_types = all_mgxs_types
|
||||
else:
|
||||
cv.check_iterable_type('mgxs_types', mgxs_types, basestring)
|
||||
for mgxs_type in mgxs_types:
|
||||
cv.check_value('mgxs_type', mgxs_type, openmc.mgxs.MGXS_TYPES)
|
||||
cv.check_value('mgxs_type', mgxs_type, all_mgxs_types)
|
||||
self._mgxs_types = mgxs_types
|
||||
|
||||
@by_nuclide.setter
|
||||
def by_nuclide(self, by_nuclide):
|
||||
cv.check_type('by_nuclide', by_nuclide, bool)
|
||||
|
||||
if by_nuclide == True and self.domain_type == 'mesh':
|
||||
raise ValueError('Unable to create MGXS library by nuclide with '
|
||||
'mesh domain')
|
||||
|
||||
self._by_nuclide = by_nuclide
|
||||
|
||||
@domain_type.setter
|
||||
def domain_type(self, domain_type):
|
||||
cv.check_value('domain type', domain_type, openmc.mgxs.DOMAIN_TYPES)
|
||||
|
||||
if self.by_nuclide == True and domain_type == 'mesh':
|
||||
raise ValueError('Unable to create MGXS library by nuclide with '
|
||||
'mesh domain')
|
||||
|
||||
self._domain_type = domain_type
|
||||
|
||||
@domains.setter
|
||||
|
|
@ -298,6 +334,23 @@ class Library(object):
|
|||
cv.check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups)
|
||||
self._energy_groups = energy_groups
|
||||
|
||||
@delayed_groups.setter
|
||||
def delayed_groups(self, delayed_groups):
|
||||
|
||||
if delayed_groups != None:
|
||||
|
||||
cv.check_type('delayed groups', delayed_groups, list, int)
|
||||
cv.check_greater_than('num delayed groups', len(delayed_groups), 0)
|
||||
|
||||
# Check that the groups are within [1, MAX_DELAYED_GROUPS]
|
||||
for group in delayed_groups:
|
||||
cv.check_greater_than('delayed group', group, 0)
|
||||
cv.check_less_than('delayed group', group,
|
||||
openmc.mgxs.MAX_DELAYED_GROUPS,
|
||||
equality=True)
|
||||
|
||||
self._delayed_groups = delayed_groups
|
||||
|
||||
@correction.setter
|
||||
def correction(self, correction):
|
||||
cv.check_value('correction', correction, ('P0', None))
|
||||
|
|
@ -327,6 +380,11 @@ class Library(object):
|
|||
cv.check_type('tally trigger', tally_trigger, openmc.Trigger)
|
||||
self._tally_trigger = tally_trigger
|
||||
|
||||
@estimator.setter
|
||||
def estimator(self, estimator):
|
||||
cv.check_value('estimator', estimator, ESTIMATOR_TYPES)
|
||||
self._estimator = estimator
|
||||
|
||||
@sparse.setter
|
||||
def sparse(self, sparse):
|
||||
"""Convert tally data from NumPy arrays to SciPy list of lists (LIL)
|
||||
|
|
@ -363,14 +421,23 @@ class Library(object):
|
|||
for domain in self.domains:
|
||||
self.all_mgxs[domain.id] = OrderedDict()
|
||||
for mgxs_type in self.mgxs_types:
|
||||
mgxs = openmc.mgxs.MGXS.get_mgxs(mgxs_type, name=self.name)
|
||||
if mgxs_type in openmc.mgxs.MDGXS_TYPES:
|
||||
mgxs = openmc.mgxs.MDGXS.get_mgxs(mgxs_type, name=self.name)
|
||||
else:
|
||||
mgxs = openmc.mgxs.MGXS.get_mgxs(mgxs_type, name=self.name)
|
||||
|
||||
mgxs.domain = domain
|
||||
mgxs.domain_type = self.domain_type
|
||||
mgxs.energy_groups = self.energy_groups
|
||||
mgxs.by_nuclide = self.by_nuclide
|
||||
if self.estimator is not None:
|
||||
mgxs.estimator = self.estimator
|
||||
|
||||
if mgxs_type in openmc.mgxs.MDGXS_TYPES:
|
||||
mgxs.delayed_groups = self.delayed_groups
|
||||
|
||||
# If a tally trigger was specified, add it to the MGXS
|
||||
if self.tally_trigger:
|
||||
if self.tally_trigger is not None:
|
||||
mgxs.tally_trigger = self.tally_trigger
|
||||
|
||||
# Specify whether to use a transport ('P0') correction
|
||||
|
|
@ -460,7 +527,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', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', chi', 'chi-prompt', 'inverse-velocity', 'prompt-nu-fission'}
|
||||
mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', chi', 'chi-prompt', 'inverse-velocity', 'prompt-nu-fission', 'delayed-nu-fission', 'chi-delayed', 'beta'}
|
||||
The type of multi-group cross section object to return
|
||||
|
||||
Returns
|
||||
|
|
@ -755,15 +822,15 @@ class Library(object):
|
|||
return pickle.load(open(full_filename, 'rb'))
|
||||
|
||||
def get_xsdata(self, domain, xsdata_name, nuclide='total', xs_type='macro',
|
||||
xs_id='1m', order=None, tabular_legendre=None,
|
||||
tabular_points=33):
|
||||
order=None, tabular_legendre=None, tabular_points=33,
|
||||
subdomain=None):
|
||||
"""Generates an openmc.XSdata object describing a multi-group cross section
|
||||
data set for eventual combination in to an openmc.MGXSLibrary object
|
||||
(i.e., the library).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
domain : openmc.Material or openmc.Cell or openmc.Universe
|
||||
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
|
||||
The domain for spatial homogenization
|
||||
xsdata_name : str
|
||||
Name to apply to the "xsdata" entry produced by this method
|
||||
|
|
@ -774,8 +841,6 @@ class Library(object):
|
|||
Provide the macro or micro cross section in units of cm^-1 or
|
||||
barns. Defaults to 'macro'. If the Library object is not tallied by
|
||||
nuclide this will be set to 'macro' regardless.
|
||||
xs_ids : str
|
||||
Cross section set identifier. Defaults to '1m'.
|
||||
order : int
|
||||
Scattering order for this data entry. Default is None,
|
||||
which will set the XSdata object to use the order of the
|
||||
|
|
@ -792,6 +857,12 @@ class Library(object):
|
|||
parameter is set to `True`. In this case, this parameter sets the
|
||||
number of equally-spaced points in the domain of [-1,1] to be used
|
||||
in building the tabular distribution. Default is `33`.
|
||||
subdomain : iterable of int
|
||||
This parameter is not used unless using a mesh domain. In that
|
||||
case, the subdomain is an [i,j,k] index (1-based indexing) of the
|
||||
mesh cell of interest in the openmc.Mesh object. Note:
|
||||
this parameter currently only supports subdomains within a mesh,
|
||||
and not the subdomains of a distribcell.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -811,11 +882,10 @@ class Library(object):
|
|||
"""
|
||||
|
||||
cv.check_type('domain', domain, (openmc.Material, openmc.Cell,
|
||||
openmc.Cell))
|
||||
openmc.Universe, openmc.Mesh))
|
||||
cv.check_type('xsdata_name', xsdata_name, basestring)
|
||||
cv.check_type('nuclide', nuclide, basestring)
|
||||
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
|
||||
cv.check_type('xs_id', xs_id, basestring)
|
||||
cv.check_type('order', order, (type(None), Integral))
|
||||
if order is not None:
|
||||
cv.check_greater_than('order', order, 0, equality=True)
|
||||
|
|
@ -824,6 +894,9 @@ class Library(object):
|
|||
(type(None), bool))
|
||||
if tabular_points is not None:
|
||||
cv.check_greater_than('tabular_points', tabular_points, 1)
|
||||
if subdomain is not None:
|
||||
cv.check_iterable_type('subdomain', subdomain, Integral,
|
||||
max_depth=3)
|
||||
|
||||
# Make sure statepoint has been loaded
|
||||
if self._sp_filename is None:
|
||||
|
|
@ -839,7 +912,6 @@ class Library(object):
|
|||
name = xsdata_name
|
||||
if nuclide is not 'total':
|
||||
name += '_' + nuclide
|
||||
name += '.' + xs_id
|
||||
xsdata = openmc.XSdata(name, self.energy_groups)
|
||||
|
||||
if order is None:
|
||||
|
|
@ -859,52 +931,66 @@ class Library(object):
|
|||
xsdata.zaid = self._nuclides[nuclide][0]
|
||||
xsdata.awr = self._nuclides[nuclide][1]
|
||||
|
||||
if subdomain is None:
|
||||
subdomain = 'all'
|
||||
else:
|
||||
subdomain = [subdomain]
|
||||
|
||||
# Now get xs data itself
|
||||
if 'nu-transport' in self.mgxs_types and self.correction == 'P0':
|
||||
mymgxs = self.get_mgxs(domain, 'nu-transport')
|
||||
xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide])
|
||||
xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide],
|
||||
subdomains=subdomain)
|
||||
elif 'total' in self.mgxs_types:
|
||||
mymgxs = self.get_mgxs(domain, 'total')
|
||||
xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide])
|
||||
xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide],
|
||||
subdomain=subdomain)
|
||||
if 'absorption' in self.mgxs_types:
|
||||
mymgxs = self.get_mgxs(domain, 'absorption')
|
||||
xsdata.set_absorption_mgxs(mymgxs, xs_type=xs_type,
|
||||
nuclide=[nuclide])
|
||||
nuclide=[nuclide],
|
||||
subdomain=subdomain)
|
||||
if 'fission' in self.mgxs_types:
|
||||
mymgxs = self.get_mgxs(domain, 'fission')
|
||||
xsdata.set_fission_mgxs(mymgxs, xs_type=xs_type,
|
||||
nuclide=[nuclide])
|
||||
nuclide=[nuclide], subdomain=subdomain)
|
||||
if 'kappa-fission' in self.mgxs_types:
|
||||
mymgxs = self.get_mgxs(domain, 'kappa-fission')
|
||||
xsdata.set_kappa_fission_mgxs(mymgxs, xs_type=xs_type,
|
||||
nuclide=[nuclide])
|
||||
nuclide=[nuclide],
|
||||
subdomain=subdomain)
|
||||
# For chi and nu-fission we can either have only a nu-fission matrix
|
||||
# provided, or vectors of chi and nu-fission provided
|
||||
if 'nu-fission matrix' in self.mgxs_types:
|
||||
mymgxs = self.get_mgxs(domain, 'nu-fission matrix')
|
||||
xsdata.set_nu_fission_mgxs(mymgxs, xs_type=xs_type,
|
||||
nuclide=[nuclide])
|
||||
nuclide=[nuclide],
|
||||
subdomain=subdomain)
|
||||
else:
|
||||
if 'chi' in self.mgxs_types:
|
||||
mymgxs = self.get_mgxs(domain, 'chi')
|
||||
xsdata.set_chi_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide])
|
||||
xsdata.set_chi_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide],
|
||||
subdomain=subdomain)
|
||||
if 'nu-fission' in self.mgxs_types:
|
||||
mymgxs = self.get_mgxs(domain, 'nu-fission')
|
||||
xsdata.set_nu_fission_mgxs(mymgxs, xs_type=xs_type,
|
||||
nuclide=[nuclide])
|
||||
nuclide=[nuclide],
|
||||
subdomain=subdomain)
|
||||
# If multiplicity matrix is available, prefer that
|
||||
if 'multiplicity matrix' in self.mgxs_types:
|
||||
mymgxs = self.get_mgxs(domain, 'multiplicity matrix')
|
||||
xsdata.set_multiplicity_mgxs(mymgxs, xs_type=xs_type,
|
||||
nuclide=[nuclide])
|
||||
nuclide=[nuclide],
|
||||
subdomain=subdomain)
|
||||
using_multiplicity = True
|
||||
# multiplicity wil fall back to using scatter and nu-scatter
|
||||
# multiplicity will fall back to using scatter and nu-scatter
|
||||
elif ((('scatter matrix' in self.mgxs_types) and
|
||||
('nu-scatter matrix' in self.mgxs_types))):
|
||||
scatt_mgxs = self.get_mgxs(domain, 'scatter matrix')
|
||||
nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix')
|
||||
xsdata.set_multiplicity_mgxs(nuscatt_mgxs, scatt_mgxs,
|
||||
xs_type=xs_type, nuclide=[nuclide])
|
||||
xs_type=xs_type, nuclide=[nuclide],
|
||||
subdomain=subdomain)
|
||||
using_multiplicity = True
|
||||
else:
|
||||
using_multiplicity = False
|
||||
|
|
@ -912,12 +998,13 @@ class Library(object):
|
|||
if using_multiplicity:
|
||||
nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix')
|
||||
xsdata.set_scatter_mgxs(nuscatt_mgxs, xs_type=xs_type,
|
||||
nuclide=[nuclide])
|
||||
nuclide=[nuclide], subdomain=subdomain)
|
||||
else:
|
||||
if 'nu-scatter matrix' in self.mgxs_types:
|
||||
nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix')
|
||||
xsdata.set_scatter_mgxs(nuscatt_mgxs, xs_type=xs_type,
|
||||
nuclide=[nuclide])
|
||||
nuclide=[nuclide],
|
||||
subdomain=subdomain)
|
||||
|
||||
# Since we are not using multiplicity, then
|
||||
# scattering multiplication (nu-scatter) must be
|
||||
|
|
@ -931,8 +1018,7 @@ class Library(object):
|
|||
return xsdata
|
||||
|
||||
def create_mg_library(self, xs_type='macro', xsdata_names=None,
|
||||
xs_ids=None, tabular_legendre=None,
|
||||
tabular_points=33):
|
||||
tabular_legendre=None, tabular_points=33):
|
||||
"""Creates an openmc.MGXSLibrary object to contain the MGXS data for the
|
||||
Multi-Group mode of OpenMC.
|
||||
|
||||
|
|
@ -945,10 +1031,6 @@ class Library(object):
|
|||
xsdata_names : Iterable of str
|
||||
List of names to apply to the "xsdata" entries in the
|
||||
resultant mgxs data file. Defaults to 'set1', 'set2', ...
|
||||
xs_ids : str or Iterable of str
|
||||
Cross section set identifier (i.e., '71c') for all
|
||||
data sets (if only str) or for each individual one
|
||||
(if iterable of str). Defaults to '1m'.
|
||||
tabular_legendre : None or bool
|
||||
Flag to denote whether or not the Legendre expansion of the
|
||||
scattering angular distribution is to be converted to a tabular
|
||||
|
|
@ -988,15 +1070,6 @@ class Library(object):
|
|||
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
|
||||
if xsdata_names is not None:
|
||||
cv.check_iterable_type('xsdata_names', xsdata_names, basestring)
|
||||
if xs_ids is not None:
|
||||
if isinstance(xs_ids, basestring):
|
||||
# If we only have a string lets convert it now to a list
|
||||
# of strings.
|
||||
xs_ids = [xs_ids for i in range(len(self.domains))]
|
||||
else:
|
||||
cv.check_iterable_type('xs_ids', xs_ids, basestring)
|
||||
else:
|
||||
xs_ids = ['1m' for i in range(len(self.domains))]
|
||||
|
||||
# If gathering material-specific data, set the xs_type to macro
|
||||
if not self.by_nuclide:
|
||||
|
|
@ -1005,32 +1078,55 @@ class Library(object):
|
|||
# Initialize file
|
||||
mgxs_file = openmc.MGXSLibrary(self.energy_groups)
|
||||
|
||||
# Create the xsdata object and add it to the mgxs_file
|
||||
for i, domain in enumerate(self.domains):
|
||||
if self.by_nuclide:
|
||||
nuclides = list(domain.get_all_nuclides().keys())
|
||||
else:
|
||||
nuclides = ['total']
|
||||
for nuclide in nuclides:
|
||||
# Build & add metadata to XSdata object
|
||||
if xsdata_names is None:
|
||||
xsdata_name = 'set' + str(i + 1)
|
||||
if self.domain_type == 'mesh':
|
||||
# Create the xsdata objects and add to the mgxs_file
|
||||
i = 0
|
||||
for domain in self.domains:
|
||||
if self.by_nuclide:
|
||||
raise NotImplementedError("Mesh domains do not currently "
|
||||
"support nuclidic tallies")
|
||||
for subdomain in domain.cell_generator():
|
||||
# Build & add metadata to XSdata object
|
||||
if xsdata_names is None:
|
||||
xsdata_name = 'set' + str(i + 1)
|
||||
else:
|
||||
xsdata_name = xsdata_names[i]
|
||||
|
||||
# Create XSdata and Macroscopic for this domain
|
||||
xsdata = self.get_xsdata(domain, xsdata_name,
|
||||
tabular_legendre=tabular_legendre,
|
||||
tabular_points=tabular_points,
|
||||
subdomain=subdomain)
|
||||
mgxs_file.add_xsdata(xsdata)
|
||||
i += 1
|
||||
|
||||
else:
|
||||
# Create the xsdata object and add it to the mgxs_file
|
||||
for i, domain in enumerate(self.domains):
|
||||
if self.by_nuclide:
|
||||
nuclides = domain.get_nuclides()
|
||||
else:
|
||||
xsdata_name = xsdata_names[i]
|
||||
if nuclide is not 'total':
|
||||
xsdata_name += '_' + nuclide
|
||||
nuclides = ['total']
|
||||
for nuclide in nuclides:
|
||||
# Build & add metadata to XSdata object
|
||||
if xsdata_names is None:
|
||||
xsdata_name = 'set' + str(i + 1)
|
||||
else:
|
||||
xsdata_name = xsdata_names[i]
|
||||
if nuclide is not 'total':
|
||||
xsdata_name += '_' + nuclide
|
||||
|
||||
xsdata = self.get_xsdata(domain, xsdata_name, nuclide=nuclide,
|
||||
xs_type=xs_type, xs_id=xs_ids[i],
|
||||
tabular_legendre=tabular_legendre,
|
||||
tabular_points=tabular_points)
|
||||
xsdata = self.get_xsdata(domain, xsdata_name,
|
||||
nuclide=nuclide, xs_type=xs_type,
|
||||
tabular_legendre=tabular_legendre,
|
||||
tabular_points=tabular_points)
|
||||
|
||||
mgxs_file.add_xsdata(xsdata)
|
||||
mgxs_file.add_xsdata(xsdata)
|
||||
|
||||
return mgxs_file
|
||||
|
||||
def create_mg_mode(self, xsdata_names=None, xs_ids=None,
|
||||
tabular_legendre=None, tabular_points=33):
|
||||
def create_mg_mode(self, xsdata_names=None, tabular_legendre=None,
|
||||
tabular_points=33, bc=['reflective'] * 6):
|
||||
"""Creates an openmc.MGXSLibrary object to contain the MGXS data for the
|
||||
Multi-Group mode of OpenMC as well as the associated openmc.Materials
|
||||
and openmc.Geometry objects. The created Geometry is the same as that
|
||||
|
|
@ -1044,10 +1140,6 @@ class Library(object):
|
|||
xsdata_names : Iterable of str
|
||||
List of names to apply to the "xsdata" entries in the
|
||||
resultant mgxs data file. Defaults to 'set1', 'set2', ...
|
||||
xs_ids : str or Iterable of str
|
||||
Cross section set identifier (i.e., '71c') for all
|
||||
data sets (if only str) or for each individual one
|
||||
(if iterable of str). Defaults to '1m'.
|
||||
tabular_legendre : None or bool
|
||||
Flag to denote whether or not the Legendre expansion of the
|
||||
scattering angular distribution is to be converted to a tabular
|
||||
|
|
@ -1060,6 +1152,12 @@ class Library(object):
|
|||
parameter is set to `True`. In this case, this parameter sets the
|
||||
number of equally-spaced points in the domain of [-1,1] to be used
|
||||
in building the tabular distribution. Default is `33`.
|
||||
bc : iterable of {'reflective', 'periodic', 'transmission', or 'vacuum'}
|
||||
Boundary conditions for each of the four faces of a rectangle
|
||||
(if applying to a 2D mesh) or six faces of a parallelepiped
|
||||
(if applying to a 3D mesh) provided in the following order:
|
||||
[x min, x max, y min, y max, z min, z max]. 2-D cells do not
|
||||
contain the z min and z max entries.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -1090,62 +1188,76 @@ class Library(object):
|
|||
# multi-group cross section types
|
||||
self.check_library_for_openmc_mgxs()
|
||||
|
||||
if xsdata_names is not None:
|
||||
cv.check_iterable_type('xsdata_names', xsdata_names, basestring)
|
||||
if xs_ids is not None:
|
||||
if isinstance(xs_ids, basestring):
|
||||
# If we only have a string lets convert it now to a list
|
||||
# of strings.
|
||||
xs_ids = [xs_ids for i in range(len(self.domains))]
|
||||
else:
|
||||
cv.check_iterable_type('xs_ids', xs_ids, basestring)
|
||||
# If the domain type is a mesh, then there can only be one domain for
|
||||
# this method. This is because we can build a model automatically if
|
||||
# the user provided multiple mesh domains for library generation since
|
||||
# the multiple meshes could be overlapping or in disparate regions
|
||||
# of the continuous energy model. The next step makes sure there is
|
||||
# only one before continuing.
|
||||
if self.domain_type == 'mesh':
|
||||
cv.check_length("domains", self.domains, 1, 1)
|
||||
|
||||
# Get the MGXS File Data
|
||||
mgxs_file = self.create_mg_library('macro', xsdata_names,
|
||||
tabular_legendre, tabular_points)
|
||||
|
||||
# Now move on the creating the geometry and assigning materials
|
||||
if self.domain_type == 'mesh':
|
||||
root = openmc.Universe(name='root', universe_id=0)
|
||||
|
||||
# Add cells representative of the mesh with reflective BC
|
||||
root_cell, cells = \
|
||||
self.domains[0].build_cells(bc)
|
||||
root.add_cell(root_cell)
|
||||
geometry = openmc.Geometry()
|
||||
geometry.root_universe = root
|
||||
materials = openmc.Materials()
|
||||
|
||||
for i, subdomain in enumerate(self.domains[0].cell_generator()):
|
||||
xsdata = mgxs_file.xsdatas[i]
|
||||
|
||||
# Build the macroscopic and assign it to the cell of
|
||||
# interest
|
||||
macroscopic = openmc.Macroscopic(name=xsdata.name)
|
||||
|
||||
# Create Material and add to collection
|
||||
material = openmc.Material(name=xsdata.name)
|
||||
material.add_macroscopic(macroscopic)
|
||||
materials.append(material)
|
||||
|
||||
# Set the materials for each of the universes
|
||||
cells[i].fill = materials[i]
|
||||
|
||||
else:
|
||||
xs_ids = ['1m' for i in range(len(self.domains))]
|
||||
xs_type = 'macro'
|
||||
# Create a copy of the Geometry for these Macroscopics
|
||||
geometry = copy.deepcopy(self.openmc_geometry)
|
||||
materials = openmc.Materials()
|
||||
|
||||
# Initialize MGXS File
|
||||
mgxs_file = openmc.MGXSLibrary(self.energy_groups)
|
||||
# Get all Cells from the Geometry for differentiation
|
||||
all_cells = geometry.get_all_material_cells()
|
||||
|
||||
# Create a copy of the Geometry to differentiate for these Macroscopics
|
||||
geometry = copy.deepcopy(self.openmc_geometry)
|
||||
materials = openmc.Materials()
|
||||
# Create the xsdata object and add it to the mgxs_file
|
||||
for i, domain in enumerate(self.domains):
|
||||
xsdata = mgxs_file.xsdatas[i]
|
||||
|
||||
# Get all Cells from the Geometry for differentiation
|
||||
all_cells = geometry.get_all_material_cells()
|
||||
macroscopic = openmc.Macroscopic(name=xsdata.name)
|
||||
|
||||
# Create the xsdata object and add it to the mgxs_file
|
||||
for i, domain in enumerate(self.domains):
|
||||
# Create Material and add to collection
|
||||
material = openmc.Material(name=xsdata.name)
|
||||
material.add_macroscopic(macroscopic)
|
||||
materials.append(material)
|
||||
|
||||
# Build & add metadata to XSdata object
|
||||
if xsdata_names is None:
|
||||
xsdata_name = 'set' + str(i + 1)
|
||||
else:
|
||||
xsdata_name = xsdata_names[i]
|
||||
# Differentiate Geometry with new Material
|
||||
if self.domain_type == 'material':
|
||||
# Fill all appropriate Cells with new Material
|
||||
for cell in all_cells:
|
||||
if cell.fill.id == domain.id:
|
||||
cell.fill = material
|
||||
|
||||
# Create XSdata and Macroscopic for this domain
|
||||
xsdata = self.get_xsdata(domain, xsdata_name, nuclide='total',
|
||||
xs_type=xs_type, xs_id=xs_ids[i],
|
||||
tabular_legendre=tabular_legendre,
|
||||
tabular_points=tabular_points)
|
||||
mgxs_file.add_xsdata(xsdata)
|
||||
macroscopic = openmc.Macroscopic(name=xsdata_name, xs=xs_ids[i])
|
||||
|
||||
# Create Material and add to collection
|
||||
material = openmc.Material(name=xsdata_name + '.' + xs_ids[i])
|
||||
material.add_macroscopic(macroscopic)
|
||||
materials.append(material)
|
||||
|
||||
# Differentiate Geometry with new Material
|
||||
if self.domain_type == 'material':
|
||||
# Fill all appropriate Cells with new Material
|
||||
for cell in all_cells:
|
||||
if cell.fill.id == domain.id:
|
||||
cell.fill = material
|
||||
|
||||
elif self.domain_type == 'cell':
|
||||
for cell in all_cells:
|
||||
if cell.id == domain.id:
|
||||
cell.fill = material
|
||||
elif self.domain_type == 'cell':
|
||||
for cell in all_cells:
|
||||
if cell.id == domain.id:
|
||||
cell.fill = material
|
||||
|
||||
return mgxs_file, materials, geometry
|
||||
|
||||
|
|
|
|||
1576
openmc/mgxs/mdgxs.py
Normal file
1576
openmc/mgxs/mdgxs.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -13,6 +13,7 @@ import numpy as np
|
|||
|
||||
import openmc
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.tallies import ESTIMATOR_TYPES
|
||||
from openmc.mgxs import EnergyGroups
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
|
|
@ -39,7 +40,6 @@ MGXS_TYPES = ['total',
|
|||
'inverse-velocity',
|
||||
'prompt-nu-fission']
|
||||
|
||||
|
||||
# Supported domain types
|
||||
DOMAIN_TYPES = ['cell',
|
||||
'distribcell',
|
||||
|
|
@ -102,7 +102,7 @@ class MGXS(object):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section
|
||||
|
|
@ -144,11 +144,11 @@ class MGXS(object):
|
|||
|
||||
def __init__(self, domain=None, domain_type=None,
|
||||
energy_groups=None, by_nuclide=False, name=''):
|
||||
|
||||
self._name = ''
|
||||
self._rxn_type = None
|
||||
self._by_nuclide = None
|
||||
self._nuclides = None
|
||||
self._estimator = 'tracklength'
|
||||
self._domain = None
|
||||
self._domain_type = None
|
||||
self._energy_groups = None
|
||||
|
|
@ -160,6 +160,7 @@ class MGXS(object):
|
|||
self._loaded_sp = False
|
||||
self._derived = False
|
||||
self._hdf5_key = None
|
||||
self._valid_estimators = ESTIMATOR_TYPES
|
||||
|
||||
self.name = name
|
||||
self.by_nuclide = by_nuclide
|
||||
|
|
@ -250,7 +251,7 @@ class MGXS(object):
|
|||
|
||||
@property
|
||||
def estimator(self):
|
||||
return 'tracklength'
|
||||
return self._estimator
|
||||
|
||||
@property
|
||||
def tallies(self):
|
||||
|
|
@ -288,9 +289,7 @@ class MGXS(object):
|
|||
|
||||
# If this is a by-nuclide cross-section, add nuclides to Tally
|
||||
if self.by_nuclide and score != 'flux':
|
||||
all_nuclides = self.get_all_nuclides()
|
||||
for nuclide in all_nuclides:
|
||||
self._tallies[key].nuclides.append(nuclide)
|
||||
self._tallies[key].nuclides += self.get_nuclides()
|
||||
else:
|
||||
self._tallies[key].nuclides.append('total')
|
||||
|
||||
|
|
@ -329,16 +328,16 @@ class MGXS(object):
|
|||
@property
|
||||
def num_nuclides(self):
|
||||
if self.by_nuclide:
|
||||
return len(self.get_all_nuclides())
|
||||
return len(self.get_nuclides())
|
||||
else:
|
||||
return 1
|
||||
|
||||
@property
|
||||
def nuclides(self):
|
||||
if self.by_nuclide:
|
||||
return self.get_all_nuclides()
|
||||
return self.get_nuclides()
|
||||
else:
|
||||
return 'sum'
|
||||
return ['sum']
|
||||
|
||||
@property
|
||||
def loaded_sp(self):
|
||||
|
|
@ -370,6 +369,11 @@ class MGXS(object):
|
|||
cv.check_iterable_type('nuclides', nuclides, basestring)
|
||||
self._nuclides = nuclides
|
||||
|
||||
@estimator.setter
|
||||
def estimator(self, estimator):
|
||||
cv.check_value('estimator', estimator, self._valid_estimators)
|
||||
self._estimator = estimator
|
||||
|
||||
@domain.setter
|
||||
def domain(self, domain):
|
||||
cv.check_type('domain', domain, _DOMAINS)
|
||||
|
|
@ -503,7 +507,7 @@ class MGXS(object):
|
|||
mgxs.name = name
|
||||
return mgxs
|
||||
|
||||
def get_all_nuclides(self):
|
||||
def get_nuclides(self):
|
||||
"""Get all nuclides in the cross section's spatial domain.
|
||||
|
||||
Returns
|
||||
|
|
@ -528,8 +532,7 @@ class MGXS(object):
|
|||
|
||||
# Otherwise, return all nuclides in the spatial domain
|
||||
else:
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
return list(nuclides.keys())
|
||||
return self.domain.get_nuclides()
|
||||
|
||||
def get_nuclide_density(self, nuclide):
|
||||
"""Get the atomic number density in units of atoms/b-cm for a nuclide
|
||||
|
|
@ -545,26 +548,14 @@ class MGXS(object):
|
|||
float
|
||||
The atomic number density (atom/b-cm) for the nuclide of interest
|
||||
|
||||
Raises
|
||||
-------
|
||||
ValueError
|
||||
When the density is requested for a nuclide which is not found in
|
||||
the spatial domain.
|
||||
|
||||
"""
|
||||
|
||||
cv.check_type('nuclide', nuclide, basestring)
|
||||
|
||||
# Get list of all nuclides in the spatial domain
|
||||
nuclides = self.domain.get_all_nuclides()
|
||||
nuclides = self.domain.get_nuclide_densities()
|
||||
|
||||
if nuclide not in nuclides:
|
||||
msg = 'Unable to get density for nuclide "{0}" which is not in ' \
|
||||
'{1} "{2}"'.format(nuclide, self.domain_type, self.domain.id)
|
||||
ValueError(msg)
|
||||
|
||||
density = nuclides[nuclide][1]
|
||||
return density
|
||||
return nuclides[nuclide][1] if nuclide in nuclides else 0.0
|
||||
|
||||
def get_nuclide_densities(self, nuclides='all'):
|
||||
"""Get an array of atomic number densities in units of atom/b-cm for all
|
||||
|
|
@ -597,14 +588,14 @@ class MGXS(object):
|
|||
|
||||
# Sum the atomic number densities for all nuclides
|
||||
if nuclides == 'sum':
|
||||
nuclides = self.get_all_nuclides()
|
||||
nuclides = self.get_nuclides()
|
||||
densities = np.zeros(1, dtype=np.float)
|
||||
for nuclide in nuclides:
|
||||
densities[0] += self.get_nuclide_density(nuclide)
|
||||
|
||||
# Tabulate the atomic number densities for all nuclides
|
||||
elif nuclides == 'all':
|
||||
nuclides = self.get_all_nuclides()
|
||||
nuclides = self.get_nuclides()
|
||||
densities = np.zeros(self.num_nuclides, dtype=np.float)
|
||||
for i, nuclide in enumerate(nuclides):
|
||||
densities[i] += self.get_nuclide_density(nuclide)
|
||||
|
|
@ -635,7 +626,7 @@ class MGXS(object):
|
|||
# If computing xs for each nuclide, replace CrossNuclides with originals
|
||||
if self.by_nuclide:
|
||||
self.xs_tally._nuclides = []
|
||||
nuclides = self.get_all_nuclides()
|
||||
nuclides = self.get_nuclides()
|
||||
for nuclide in nuclides:
|
||||
self.xs_tally.nuclides.append(openmc.Nuclide(nuclide))
|
||||
|
||||
|
|
@ -725,11 +716,13 @@ class MGXS(object):
|
|||
|
||||
def get_xs(self, groups='all', subdomains='all', nuclides='all',
|
||||
xs_type='macro', order_groups='increasing',
|
||||
value='mean', **kwargs):
|
||||
value='mean', squeeze=True, **kwargs):
|
||||
r"""Returns an array of multi-group cross sections.
|
||||
|
||||
This method constructs a 2D NumPy array for the requested multi-group
|
||||
cross section data data for one or more energy groups and subdomains.
|
||||
This method constructs a 3D NumPy array for the requested
|
||||
multi-group cross section data for one or more subdomains
|
||||
(1st dimension), energy groups (2nd dimension), and nuclides
|
||||
(3rd dimension).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -751,6 +744,9 @@ class MGXS(object):
|
|||
Defaults to 'increasing'.
|
||||
value : {'mean', 'std_dev', 'rel_err'}
|
||||
A string for the type of value to return. Defaults to 'mean'.
|
||||
squeeze : bool
|
||||
A boolean representing whether to eliminate the extra dimensions
|
||||
of the multi-dimensional array to be returned. Defaults to True.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -781,7 +777,8 @@ class MGXS(object):
|
|||
|
||||
# Construct a collection of the domain filter bins
|
||||
if not isinstance(subdomains, basestring):
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3)
|
||||
cv.check_iterable_type('subdomains', subdomains, Integral,
|
||||
max_depth=3)
|
||||
for subdomain in subdomains:
|
||||
filters.append(self.domain_type)
|
||||
filter_bins.append((subdomain,))
|
||||
|
|
@ -796,7 +793,7 @@ class MGXS(object):
|
|||
# Construct a collection of the nuclides to retrieve from the xs tally
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']:
|
||||
query_nuclides = self.get_all_nuclides()
|
||||
query_nuclides = self.get_nuclides()
|
||||
else:
|
||||
query_nuclides = nuclides
|
||||
else:
|
||||
|
|
@ -820,25 +817,29 @@ class MGXS(object):
|
|||
if value == 'mean' or value == 'std_dev':
|
||||
xs /= densities[np.newaxis, :, np.newaxis]
|
||||
|
||||
# Eliminate the trivial score dimension
|
||||
xs = np.squeeze(xs, axis=len(xs.shape) - 1)
|
||||
xs = np.nan_to_num(xs)
|
||||
|
||||
if groups == 'all':
|
||||
num_groups = self.num_groups
|
||||
else:
|
||||
num_groups = len(groups)
|
||||
|
||||
# Reshape tally data array with separate axes for domain and energy
|
||||
num_subdomains = int(xs.shape[0] / num_groups)
|
||||
new_shape = (num_subdomains, num_groups) + xs.shape[1:]
|
||||
xs = np.reshape(xs, new_shape)
|
||||
|
||||
# Reverse data if user requested increasing energy groups since
|
||||
# tally data is stored in order of increasing energies
|
||||
if order_groups == 'increasing':
|
||||
if groups == 'all':
|
||||
num_groups = self.num_groups
|
||||
else:
|
||||
num_groups = len(groups)
|
||||
|
||||
# Reshape tally data array with separate axes for domain and energy
|
||||
num_subdomains = int(xs.shape[0] / num_groups)
|
||||
new_shape = (num_subdomains, num_groups) + xs.shape[1:]
|
||||
xs = np.reshape(xs, new_shape)
|
||||
|
||||
# Reverse energies to align with increasing energy groups
|
||||
xs = xs[:, ::-1, :]
|
||||
|
||||
# Eliminate trivial dimensions
|
||||
xs = np.squeeze(xs)
|
||||
xs = np.atleast_1d(xs)
|
||||
if squeeze:
|
||||
xs = np.squeeze(xs)
|
||||
xs = np.atleast_1d(xs)
|
||||
|
||||
return xs
|
||||
|
||||
def get_condensed_xs(self, coarse_groups):
|
||||
|
|
@ -1164,7 +1165,7 @@ class MGXS(object):
|
|||
# Construct a collection of the nuclides to report
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all':
|
||||
nuclides = self.get_all_nuclides()
|
||||
nuclides = self.get_nuclides()
|
||||
elif nuclides == 'sum':
|
||||
nuclides = ['sum']
|
||||
else:
|
||||
|
|
@ -1302,7 +1303,7 @@ class MGXS(object):
|
|||
# Construct a collection of the nuclides to report
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all':
|
||||
nuclides = self.get_all_nuclides()
|
||||
nuclides = self.get_nuclides()
|
||||
densities = np.zeros(len(nuclides), dtype=np.float)
|
||||
elif nuclides == 'sum':
|
||||
nuclides = ['sum']
|
||||
|
|
@ -1351,8 +1352,6 @@ class MGXS(object):
|
|||
std_dev = self.get_xs(subdomains=[subdomain], nuclides=[nuclide],
|
||||
xs_type=xs_type, value='std_dev',
|
||||
row_column=row_column)
|
||||
average = average.squeeze()
|
||||
std_dev = std_dev.squeeze()
|
||||
|
||||
# Add MGXS results data to the HDF5 group
|
||||
nuclide_group.require_dataset('average', dtype=np.float64,
|
||||
|
|
@ -1484,25 +1483,27 @@ class MGXS(object):
|
|||
if self.by_nuclide and nuclides == 'sum':
|
||||
|
||||
# Use tally summation to sum across all nuclides
|
||||
query_nuclides = self.get_all_nuclides()
|
||||
xs_tally = self.xs_tally.summation(nuclides=query_nuclides)
|
||||
query_nuclides = [nuclides]
|
||||
xs_tally = self.xs_tally.summation(nuclides=self.get_nuclides())
|
||||
df = xs_tally.get_pandas_dataframe(
|
||||
distribcell_paths=distribcell_paths)
|
||||
|
||||
# Remove nuclide column since it is homogeneous and redundant
|
||||
if self.domain_type == 'mesh':
|
||||
df.drop('nuclide', axis=1, level=0, inplace=True)
|
||||
df.drop('sum(nuclide)', axis=1, level=0, inplace=True)
|
||||
else:
|
||||
df.drop('nuclide', axis=1, inplace=True)
|
||||
df.drop('sum(nuclide)', axis=1, inplace=True)
|
||||
|
||||
# If the user requested a specific set of nuclides
|
||||
elif self.by_nuclide and nuclides != 'all':
|
||||
query_nuclides = nuclides
|
||||
xs_tally = self.xs_tally.get_slice(nuclides=nuclides)
|
||||
df = xs_tally.get_pandas_dataframe(
|
||||
distribcell_paths=distribcell_paths)
|
||||
|
||||
# If the user requested all nuclides, keep nuclide column in dataframe
|
||||
else:
|
||||
query_nuclides = self.nuclides
|
||||
df = self.xs_tally.get_pandas_dataframe(
|
||||
distribcell_paths=distribcell_paths)
|
||||
|
||||
|
|
@ -1514,19 +1515,19 @@ 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)
|
||||
all_groups = np.repeat(all_groups, len(query_nuclides))
|
||||
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, df.shape[0] / in_groups.size)
|
||||
in_groups = np.tile(all_groups, int(self.num_subdomains))
|
||||
in_groups = np.repeat(in_groups, int(df.shape[0] / in_groups.size))
|
||||
df['group in'] = in_groups
|
||||
del df['energy high [MeV]']
|
||||
|
||||
df.rename(columns={'energyout low [MeV]': 'group out'},
|
||||
inplace=True)
|
||||
out_groups = np.repeat(all_groups, self.xs_tally.num_scores)
|
||||
out_groups = np.tile(out_groups, df.shape[0] / out_groups.size)
|
||||
out_groups = np.tile(out_groups, int(df.shape[0] / out_groups.size))
|
||||
df['group out'] = out_groups
|
||||
del df['energyout high [MeV]']
|
||||
columns = ['group in', 'group out']
|
||||
|
|
@ -1534,14 +1535,14 @@ class MGXS(object):
|
|||
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)
|
||||
in_groups = np.tile(all_groups, int(df.shape[0] / all_groups.size))
|
||||
df['group out'] = in_groups
|
||||
del df['energyout high [MeV]']
|
||||
columns = ['group out']
|
||||
|
||||
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)
|
||||
in_groups = np.tile(all_groups, int(df.shape[0] / all_groups.size))
|
||||
df['group in'] = in_groups
|
||||
del df['energy high [MeV]']
|
||||
columns = ['group in']
|
||||
|
|
@ -1564,6 +1565,10 @@ class MGXS(object):
|
|||
df['mean'] /= np.tile(densities, tile_factor)
|
||||
df['std. dev.'] /= np.tile(densities, tile_factor)
|
||||
|
||||
# Replace NaNs by zeros (happens if nuclide density is zero)
|
||||
df['mean'].replace(np.nan, 0.0, inplace=True)
|
||||
df['std. dev.'].replace(np.nan, 0.0, inplace=True)
|
||||
|
||||
# Sort the dataframe by domain type id (e.g., distribcell id) and
|
||||
# energy groups such that data is from fast to thermal
|
||||
if self.domain_type == 'mesh':
|
||||
|
|
@ -1572,6 +1577,7 @@ class MGXS(object):
|
|||
(mesh_str, 'z')] + columns, inplace=True)
|
||||
else:
|
||||
df.sort_values(by=[self.domain_type] + columns, inplace=True)
|
||||
|
||||
return df
|
||||
|
||||
def get_units(self, xs_type='macro'):
|
||||
|
|
@ -1646,7 +1652,7 @@ class MatrixMGXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section
|
||||
|
|
@ -1695,18 +1701,16 @@ class MatrixMGXS(MGXS):
|
|||
|
||||
return [[energy], [energy, energyout]]
|
||||
|
||||
@property
|
||||
def estimator(self):
|
||||
return 'analog'
|
||||
|
||||
def get_xs(self, in_groups='all', out_groups='all',
|
||||
subdomains='all', nuclides='all',
|
||||
xs_type='macro', order_groups='increasing',
|
||||
row_column='inout', value='mean', **kwargs):
|
||||
row_column='inout', value='mean', squeeze=True, **kwargs):
|
||||
"""Returns an array of multi-group cross sections.
|
||||
|
||||
This method constructs a 2D NumPy array for the requested multi-group
|
||||
matrix data for one or more energy groups and subdomains.
|
||||
This method constructs a 4D NumPy array for the requested
|
||||
multi-group cross section data for one or more subdomains
|
||||
(1st dimension), energy groups in (2nd dimension), energy groups out
|
||||
(3rd dimension), and nuclides (4th dimension).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -1735,6 +1739,9 @@ class MatrixMGXS(MGXS):
|
|||
Defaults to 'inout'.
|
||||
value : {'mean', 'std_dev', 'rel_err'}
|
||||
A string for the type of value to return. Defaults to 'mean'.
|
||||
squeeze : bool
|
||||
A boolean representing whether to eliminate the extra dimensions
|
||||
of the multi-dimensional array to be returned. Defaults to True.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -1790,7 +1797,7 @@ class MatrixMGXS(MGXS):
|
|||
# Construct a collection of the nuclides to retrieve from the xs tally
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']:
|
||||
query_nuclides = self.get_all_nuclides()
|
||||
query_nuclides = self.get_nuclides()
|
||||
else:
|
||||
query_nuclides = nuclides
|
||||
else:
|
||||
|
|
@ -1806,8 +1813,6 @@ class MatrixMGXS(MGXS):
|
|||
filter_bins=filter_bins,
|
||||
nuclides=query_nuclides, value=value)
|
||||
|
||||
xs = np.nan_to_num(xs)
|
||||
|
||||
# Divide by atom number densities for microscopic cross sections
|
||||
if xs_type == 'micro':
|
||||
if self.by_nuclide:
|
||||
|
|
@ -1817,33 +1822,36 @@ class MatrixMGXS(MGXS):
|
|||
if value == 'mean' or value == 'std_dev':
|
||||
xs /= densities[np.newaxis, :, np.newaxis]
|
||||
|
||||
# Eliminate the trivial score dimension
|
||||
xs = np.squeeze(xs, axis=len(xs.shape) - 1)
|
||||
xs = np.nan_to_num(xs)
|
||||
|
||||
if in_groups == 'all':
|
||||
num_in_groups = self.num_groups
|
||||
else:
|
||||
num_in_groups = len(in_groups)
|
||||
|
||||
if out_groups == 'all':
|
||||
num_out_groups = self.num_groups
|
||||
else:
|
||||
num_out_groups = len(out_groups)
|
||||
|
||||
# Reshape tally data array with separate axes for domain and energy
|
||||
num_subdomains = int(xs.shape[0] / (num_in_groups * num_out_groups))
|
||||
new_shape = (num_subdomains, num_in_groups, num_out_groups)
|
||||
new_shape += xs.shape[1:]
|
||||
xs = np.reshape(xs, new_shape)
|
||||
|
||||
# Transpose the matrix if requested by user
|
||||
if row_column == 'outin':
|
||||
xs = np.swapaxes(xs, 1, 2)
|
||||
|
||||
# Reverse data if user requested increasing energy groups since
|
||||
# tally data is stored in order of increasing energies
|
||||
if order_groups == 'increasing':
|
||||
if in_groups == 'all':
|
||||
num_in_groups = self.num_groups
|
||||
else:
|
||||
num_in_groups = len(in_groups)
|
||||
if out_groups == 'all':
|
||||
num_out_groups = self.num_groups
|
||||
else:
|
||||
num_out_groups = len(out_groups)
|
||||
|
||||
# Reshape tally data array with separate axes for domain and energy
|
||||
num_subdomains = int(xs.shape[0] /
|
||||
(num_in_groups * num_out_groups))
|
||||
new_shape = (num_subdomains, num_in_groups, num_out_groups)
|
||||
new_shape += xs.shape[1:]
|
||||
xs = np.reshape(xs, new_shape)
|
||||
|
||||
# Transpose the matrix if requested by user
|
||||
if row_column == 'outin':
|
||||
xs = np.swapaxes(xs, 1, 2)
|
||||
|
||||
# Reverse energies to align with increasing energy groups
|
||||
xs = xs[:, ::-1, ::-1, :]
|
||||
|
||||
# Eliminate trivial dimensions
|
||||
if squeeze:
|
||||
xs = np.squeeze(xs)
|
||||
xs = np.atleast_2d(xs)
|
||||
|
||||
|
|
@ -1937,7 +1945,7 @@ class MatrixMGXS(MGXS):
|
|||
# Construct a collection of the nuclides to report
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all':
|
||||
nuclides = self.get_all_nuclides()
|
||||
nuclides = self.get_nuclides()
|
||||
if nuclides == 'sum':
|
||||
nuclides = ['sum']
|
||||
else:
|
||||
|
|
@ -2075,7 +2083,7 @@ class TotalXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -2193,7 +2201,7 @@ class TransportXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : 'analog'
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -2236,6 +2244,8 @@ class TransportXS(MGXS):
|
|||
super(TransportXS, self).__init__(domain, domain_type,
|
||||
groups, by_nuclide, name)
|
||||
self._rxn_type = 'transport'
|
||||
self._estimator = 'analog'
|
||||
self._valid_estimators = ['analog']
|
||||
|
||||
@property
|
||||
def scores(self):
|
||||
|
|
@ -2248,10 +2258,6 @@ class TransportXS(MGXS):
|
|||
energyout_filter = openmc.Filter('energyout', group_edges)
|
||||
return [[energy_filter], [energy_filter], [energyout_filter]]
|
||||
|
||||
@property
|
||||
def estimator(self):
|
||||
return 'analog'
|
||||
|
||||
@property
|
||||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
|
|
@ -2323,7 +2329,7 @@ class NuTransportXS(TransportXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : 'analog'
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -2444,7 +2450,7 @@ class AbsorptionXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -2461,7 +2467,8 @@ class AbsorptionXS(MGXS):
|
|||
The number of subdomains is unity for 'material', 'cell' and 'universe'
|
||||
domain types. 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).
|
||||
tally data from a statepoint file) and the number of mesh cells for
|
||||
'mesh' domain types.
|
||||
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.
|
||||
|
|
@ -2560,7 +2567,7 @@ class CaptureXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -2682,7 +2689,7 @@ class FissionXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -2793,7 +2800,7 @@ class NuFissionXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -2837,7 +2844,6 @@ class NuFissionXS(MGXS):
|
|||
groups, by_nuclide, name)
|
||||
self._rxn_type = 'nu-fission'
|
||||
|
||||
|
||||
class KappaFissionXS(MGXS):
|
||||
r"""A recoverable fission energy production rate multi-group cross section.
|
||||
|
||||
|
|
@ -2909,7 +2915,7 @@ class KappaFissionXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -3022,7 +3028,7 @@ class ScatterXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -3137,7 +3143,7 @@ class NuScatterXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -3180,10 +3186,8 @@ class NuScatterXS(MGXS):
|
|||
super(NuScatterXS, self).__init__(domain, domain_type,
|
||||
groups, by_nuclide, name)
|
||||
self._rxn_type = 'nu-scatter'
|
||||
|
||||
@property
|
||||
def estimator(self):
|
||||
return 'analog'
|
||||
self._estimator = 'analog'
|
||||
self._valid_estimators = ['analog']
|
||||
|
||||
|
||||
class ScatterMatrixXS(MatrixMGXS):
|
||||
|
|
@ -3271,7 +3275,7 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : 'analog'
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -3317,6 +3321,8 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
self._correction = 'P0'
|
||||
self._legendre_order = 0
|
||||
self._hdf5_key = 'scatter matrix'
|
||||
self._estimator = 'analog'
|
||||
self._valid_estimators = ['analog']
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
clone = super(ScatterMatrixXS, self).__deepcopy__(memo)
|
||||
|
|
@ -3520,11 +3526,13 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
def get_xs(self, in_groups='all', out_groups='all',
|
||||
subdomains='all', nuclides='all', moment='all',
|
||||
xs_type='macro', order_groups='increasing',
|
||||
row_column='inout', value='mean'):
|
||||
row_column='inout', value='mean', squeeze=True):
|
||||
r"""Returns an array of multi-group cross sections.
|
||||
|
||||
This method constructs a 2D NumPy array for the requested scattering
|
||||
matrix data data for one or more energy groups and subdomains.
|
||||
This method constructs a 5D NumPy array for the requested
|
||||
multi-group cross section data for one or more subdomains
|
||||
(1st dimension), energy groups in (2nd dimension), energy groups out
|
||||
(3rd dimension), nuclides (4th dimension), and moments (5th dimension).
|
||||
|
||||
NOTE: The scattering moments are not multiplied by the :math:`(2l+1)/2`
|
||||
prefactor in the expansion of the scattering source into Legendre
|
||||
|
|
@ -3560,6 +3568,9 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
Defaults to 'inout'.
|
||||
value : {'mean', 'std_dev', 'rel_err'}
|
||||
A string for the type of value to return. Defaults to 'mean'.
|
||||
squeeze : bool
|
||||
A boolean representing whether to eliminate the extra dimensions
|
||||
of the multi-dimensional array to be returned. Defaults to True.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -3622,7 +3633,7 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
# Construct a collection of the nuclides to retrieve from the xs tally
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all' or nuclides == 'sum' or nuclides == ['sum']:
|
||||
query_nuclides = self.get_all_nuclides()
|
||||
query_nuclides = self.get_nuclides()
|
||||
else:
|
||||
query_nuclides = nuclides
|
||||
else:
|
||||
|
|
@ -3638,8 +3649,6 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
filter_bins=filter_bins,
|
||||
nuclides=query_nuclides, value=value)
|
||||
|
||||
xs = np.nan_to_num(xs)
|
||||
|
||||
# Divide by atom number densities for microscopic cross sections
|
||||
if xs_type == 'micro':
|
||||
if self.by_nuclide:
|
||||
|
|
@ -3649,32 +3658,35 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
if value == 'mean' or value == 'std_dev':
|
||||
xs /= densities[np.newaxis, :, np.newaxis]
|
||||
|
||||
# Convert and nans to zero
|
||||
xs = np.nan_to_num(xs)
|
||||
|
||||
if in_groups == 'all':
|
||||
num_in_groups = self.num_groups
|
||||
else:
|
||||
num_in_groups = len(in_groups)
|
||||
|
||||
if out_groups == 'all':
|
||||
num_out_groups = self.num_groups
|
||||
else:
|
||||
num_out_groups = len(out_groups)
|
||||
|
||||
# Reshape tally data array with separate axes for domain and energy
|
||||
num_subdomains = int(xs.shape[0] / (num_in_groups * num_out_groups))
|
||||
new_shape = (num_subdomains, num_in_groups, num_out_groups)
|
||||
new_shape += xs.shape[1:]
|
||||
xs = np.reshape(xs, new_shape)
|
||||
|
||||
# Transpose the scattering matrix if requested by user
|
||||
if row_column == 'outin':
|
||||
xs = np.swapaxes(xs, 1, 2)
|
||||
|
||||
# Reverse data if user requested increasing energy groups since
|
||||
# tally data is stored in order of increasing energies
|
||||
if order_groups == 'increasing':
|
||||
if in_groups == 'all':
|
||||
num_in_groups = self.num_groups
|
||||
else:
|
||||
num_in_groups = len(in_groups)
|
||||
if out_groups == 'all':
|
||||
num_out_groups = self.num_groups
|
||||
else:
|
||||
num_out_groups = len(out_groups)
|
||||
|
||||
# Reshape tally data array with separate axes for domain and energy
|
||||
num_subdomains = int(xs.shape[0] / (num_in_groups * num_out_groups))
|
||||
new_shape = (num_subdomains, num_in_groups, num_out_groups)
|
||||
new_shape += xs.shape[1:]
|
||||
xs = np.reshape(xs, new_shape)
|
||||
|
||||
# Transpose the scattering matrix if requested by user
|
||||
if row_column == 'outin':
|
||||
xs = np.swapaxes(xs, 1, 2)
|
||||
|
||||
# Reverse energies to align with increasing energy groups
|
||||
xs = xs[:, ::-1, ::-1, :]
|
||||
|
||||
# Eliminate trivial dimensions
|
||||
if squeeze:
|
||||
xs = np.squeeze(xs)
|
||||
xs = np.atleast_2d(xs)
|
||||
|
||||
|
|
@ -3731,7 +3743,7 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
if self.legendre_order > 0:
|
||||
# Insert a column corresponding to the Legendre moments
|
||||
moments = ['P{}'.format(i) for i in range(self.legendre_order+1)]
|
||||
moments = np.tile(moments, df.shape[0] / len(moments))
|
||||
moments = np.tile(moments, int(df.shape[0] / len(moments)))
|
||||
df['moment'] = moments
|
||||
|
||||
# Place the moment column before the mean column
|
||||
|
|
@ -3789,7 +3801,7 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
# Construct a collection of the nuclides to report
|
||||
if self.by_nuclide:
|
||||
if nuclides == 'all':
|
||||
nuclides = self.get_all_nuclides()
|
||||
nuclides = self.get_nuclides()
|
||||
if nuclides == 'sum':
|
||||
nuclides = ['sum']
|
||||
else:
|
||||
|
|
@ -3932,7 +3944,7 @@ class NuScatterMatrixXS(ScatterMatrixXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : 'analog'
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -4054,7 +4066,7 @@ class MultiplicityMatrixXS(MatrixMGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : 'analog'
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -4097,6 +4109,8 @@ class MultiplicityMatrixXS(MatrixMGXS):
|
|||
super(MultiplicityMatrixXS, self).__init__(domain, domain_type, groups,
|
||||
by_nuclide, name)
|
||||
self._rxn_type = 'multiplicity matrix'
|
||||
self._estimator = 'analog'
|
||||
self._valid_estimators = ['analog']
|
||||
|
||||
@property
|
||||
def scores(self):
|
||||
|
|
@ -4201,7 +4215,7 @@ class NuFissionMatrixXS(MatrixMGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : 'analog'
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -4245,6 +4259,8 @@ class NuFissionMatrixXS(MatrixMGXS):
|
|||
groups, by_nuclide, name)
|
||||
self._rxn_type = 'nu-fission'
|
||||
self._hdf5_key = 'nu-fission matrix'
|
||||
self._estimator = 'analog'
|
||||
self._valid_estimators = ['analog']
|
||||
|
||||
|
||||
class Chi(MGXS):
|
||||
|
|
@ -4316,7 +4332,7 @@ class Chi(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : 'analog'
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -4358,6 +4374,8 @@ class Chi(MGXS):
|
|||
groups=None, by_nuclide=False, name=''):
|
||||
super(Chi, self).__init__(domain, domain_type, groups, by_nuclide, name)
|
||||
self._rxn_type = 'chi'
|
||||
self._estimator = 'analog'
|
||||
self._valid_estimators = ['analog']
|
||||
|
||||
@property
|
||||
def scores(self):
|
||||
|
|
@ -4375,10 +4393,6 @@ class Chi(MGXS):
|
|||
def tally_keys(self):
|
||||
return ['nu-fission-in', 'nu-fission-out']
|
||||
|
||||
@property
|
||||
def estimator(self):
|
||||
return 'analog'
|
||||
|
||||
@property
|
||||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
|
|
@ -4515,11 +4529,13 @@ class Chi(MGXS):
|
|||
|
||||
def get_xs(self, groups='all', subdomains='all', nuclides='all',
|
||||
xs_type='macro', order_groups='increasing',
|
||||
value='mean', **kwargs):
|
||||
value='mean', squeeze=True, **kwargs):
|
||||
"""Returns an array of the fission spectrum.
|
||||
|
||||
This method constructs a 2D NumPy array for the requested multi-group
|
||||
cross section data data for one or more energy groups and subdomains.
|
||||
This method constructs a 3D NumPy array for the requested
|
||||
multi-group cross section data for one or more subdomains
|
||||
(1st dimension), energy groups (2nd dimension), and nuclides
|
||||
(3rd dimension).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -4541,6 +4557,9 @@ class Chi(MGXS):
|
|||
Defaults to 'increasing'.
|
||||
value : {'mean', 'std_dev', 'rel_err'}
|
||||
A string for the type of value to return. Defaults to 'mean'.
|
||||
squeeze : bool
|
||||
A boolean representing whether to eliminate the extra dimensions
|
||||
of the multi-dimensional array to be returned. Defaults to True.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -4595,7 +4614,7 @@ class Chi(MGXS):
|
|||
nu_fission_out = self.tallies['nu-fission-out']
|
||||
|
||||
# Sum out all nuclides
|
||||
nuclides = self.get_all_nuclides()
|
||||
nuclides = self.get_nuclides()
|
||||
nu_fission_in = nu_fission_in.summation(nuclides=nuclides)
|
||||
nu_fission_out = nu_fission_out.summation(nuclides=nuclides)
|
||||
|
||||
|
|
@ -4615,7 +4634,7 @@ class Chi(MGXS):
|
|||
|
||||
# Get chi for all nuclides in the domain
|
||||
elif nuclides == 'all':
|
||||
nuclides = self.get_all_nuclides()
|
||||
nuclides = self.get_nuclides()
|
||||
xs = self.xs_tally.get_values(filters=filters,
|
||||
filter_bins=filter_bins,
|
||||
nuclides=nuclides, value=value)
|
||||
|
|
@ -4632,27 +4651,29 @@ class Chi(MGXS):
|
|||
xs = self.xs_tally.get_values(filters=filters,
|
||||
filter_bins=filter_bins, value=value)
|
||||
|
||||
# Eliminate the trivial score dimension
|
||||
xs = np.squeeze(xs, axis=len(xs.shape) - 1)
|
||||
xs = np.nan_to_num(xs)
|
||||
|
||||
# Reshape tally data array with separate axes for domain and energy
|
||||
if groups == 'all':
|
||||
num_groups = self.num_groups
|
||||
else:
|
||||
num_groups = len(groups)
|
||||
|
||||
num_subdomains = int(xs.shape[0] / num_groups)
|
||||
new_shape = (num_subdomains, num_groups) + xs.shape[1:]
|
||||
xs = np.reshape(xs, new_shape)
|
||||
|
||||
# Reverse data if user requested increasing energy groups since
|
||||
# tally data is stored in order of increasing energies
|
||||
if order_groups == 'increasing':
|
||||
|
||||
# Reshape tally data array with separate axes for domain and energy
|
||||
if groups == 'all':
|
||||
num_groups = self.num_groups
|
||||
else:
|
||||
num_groups = len(groups)
|
||||
num_subdomains = int(xs.shape[0] / num_groups)
|
||||
new_shape = (num_subdomains, num_groups) + xs.shape[1:]
|
||||
xs = np.reshape(xs, new_shape)
|
||||
|
||||
# Reverse energies to align with increasing energy groups
|
||||
xs = xs[:, ::-1, :]
|
||||
|
||||
# Eliminate trivial dimensions
|
||||
if squeeze:
|
||||
xs = np.squeeze(xs)
|
||||
xs = np.atleast_1d(xs)
|
||||
|
||||
xs = np.nan_to_num(xs)
|
||||
return xs
|
||||
|
||||
def get_pandas_dataframe(self, groups='all', nuclides='all',
|
||||
|
|
@ -4761,7 +4782,7 @@ class ChiPrompt(Chi):
|
|||
|
||||
\langle \nu^p \sigma_{f,g' \rightarrow g} \phi \rangle &= \int_{r \in V}
|
||||
dr \int_{4\pi} d\Omega' \int_0^\infty dE' \int_{E_g}^{E_{g-1}} dE \;
|
||||
\chi(E) \nu^p \sigma_f (r, E') \psi(r, E', \Omega')\\
|
||||
\chi(E)^p \nu^p \sigma_f (r, E') \psi(r, E', \Omega')\\
|
||||
\langle \nu^p \sigma_f \phi \rangle &= \int_{r \in V} dr \int_{4\pi}
|
||||
d\Omega' \int_0^\infty dE' \int_0^\infty dE \; \chi(E) \nu^p \sigma_f (r,
|
||||
E') \psi(r, E', \Omega') \\
|
||||
|
|
@ -4806,7 +4827,7 @@ class ChiPrompt(Chi):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : 'analog'
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -4921,7 +4942,7 @@ class InverseVelocity(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
@ -4938,7 +4959,8 @@ class InverseVelocity(MGXS):
|
|||
The number of subdomains is unity for 'material', 'cell' and 'universe'
|
||||
domain types. 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).
|
||||
tally data from a statepoint file) and the number of mesh cells for
|
||||
'mesh' domain types.
|
||||
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.
|
||||
|
|
@ -5055,7 +5077,7 @@ class PromptNuFissionXS(MGXS):
|
|||
tally_keys : list of str
|
||||
The keys into the tallies dictionary for each tally used to compute
|
||||
the multi-group cross section
|
||||
estimator : {'tracklength', 'analog'}
|
||||
estimator : {'tracklength', 'collision', 'analog'}
|
||||
The tally estimator used to compute the multi-group cross section
|
||||
tallies : collections.OrderedDict
|
||||
OpenMC tallies needed to compute the multi-group cross section. The keys
|
||||
|
|
|
|||
|
|
@ -99,9 +99,6 @@ class XSdata(object):
|
|||
Unique identifier for the xsdata object
|
||||
alias : str
|
||||
Separate unique identifier for the xsdata object
|
||||
zaid : int
|
||||
1000*(atomic number) + mass number. As an example, the zaid of U235
|
||||
would be 92235.
|
||||
awr : float
|
||||
Atomic weight ratio of an isotope. That is, the ratio of the mass
|
||||
of the isotope to the mass of a single neutron.
|
||||
|
|
@ -227,7 +224,6 @@ class XSdata(object):
|
|||
self._energy_groups = energy_groups
|
||||
self._representation = representation
|
||||
self._alias = None
|
||||
self._zaid = None
|
||||
self._awr = None
|
||||
self._kT = None
|
||||
self._fissionable = False
|
||||
|
|
@ -262,10 +258,6 @@ class XSdata(object):
|
|||
def alias(self):
|
||||
return self._alias
|
||||
|
||||
@property
|
||||
def zaid(self):
|
||||
return self._zaid
|
||||
|
||||
@property
|
||||
def awr(self):
|
||||
return self._awr
|
||||
|
|
@ -396,13 +388,6 @@ class XSdata(object):
|
|||
else:
|
||||
self._alias = self._name
|
||||
|
||||
@zaid.setter
|
||||
def zaid(self, zaid):
|
||||
# Check type and value
|
||||
check_type('zaid', zaid, Integral)
|
||||
check_greater_than('zaid', zaid, 0)
|
||||
self._zaid = zaid
|
||||
|
||||
@awr.setter
|
||||
def awr(self, awr):
|
||||
# Check validity of type and that the awr value is > 0
|
||||
|
|
@ -603,7 +588,8 @@ class XSdata(object):
|
|||
if np.sum(self._nu_fission) > 0.0:
|
||||
self._fissionable = True
|
||||
|
||||
def set_total_mgxs(self, total, nuclide='total', xs_type='macro'):
|
||||
def set_total_mgxs(self, total, nuclide='total', xs_type='macro',
|
||||
subdomain='all'):
|
||||
"""This method allows for an openmc.mgxs.TotalXS or
|
||||
openmc.mgxs.TransportXS to be used to set the total cross section
|
||||
for this XSdata object.
|
||||
|
|
@ -619,6 +605,9 @@ class XSdata(object):
|
|||
xs_type: {'macro', 'micro'}
|
||||
Provide the macro or micro cross section in units of cm^-1 or
|
||||
barns. Defaults to 'macro'.
|
||||
subdomain : iterable of int
|
||||
If the MGXS contains a mesh domain type, the subdomain parameter
|
||||
specifies which mesh cell (i.e., [i, j, k] index) to use.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
|
@ -631,16 +620,17 @@ class XSdata(object):
|
|||
openmc.mgxs.TransportXS))
|
||||
check_value('energy_groups', total.energy_groups, [self.energy_groups])
|
||||
check_value('domain_type', total.domain_type,
|
||||
['universe', 'cell', 'material'])
|
||||
['universe', 'cell', 'material', 'mesh'])
|
||||
|
||||
if self.representation is 'isotropic':
|
||||
self._total = total.get_xs(nuclides=nuclide, xs_type=xs_type)
|
||||
self._total = total.get_xs(nuclides=nuclide, xs_type=xs_type,
|
||||
subdomains=subdomain)
|
||||
elif self.representation is 'angle':
|
||||
msg = 'Angular-Dependent MGXS have not yet been implemented'
|
||||
raise ValueError(msg)
|
||||
|
||||
def set_absorption_mgxs(self, absorption, nuclide='total',
|
||||
xs_type='macro'):
|
||||
xs_type='macro', subdomain=None):
|
||||
"""This method allows for an openmc.mgxs.AbsorptionXS
|
||||
to be used to set the absorption cross section for this XSdata object.
|
||||
|
||||
|
|
@ -655,6 +645,9 @@ class XSdata(object):
|
|||
xs_type: {'macro', 'micro'}
|
||||
Provide the macro or micro cross section in units of cm^-1 or
|
||||
barns. Defaults to 'macro'.
|
||||
subdomain : iterable of int
|
||||
If the MGXS contains a mesh domain type, the subdomain parameter
|
||||
specifies which mesh cell (i.e., [i, j, k] index) to use.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
|
@ -667,16 +660,18 @@ class XSdata(object):
|
|||
check_value('energy_groups', absorption.energy_groups,
|
||||
[self.energy_groups])
|
||||
check_value('domain_type', absorption.domain_type,
|
||||
['universe', 'cell', 'material'])
|
||||
['universe', 'cell', 'material', 'mesh'])
|
||||
|
||||
if self.representation is 'isotropic':
|
||||
self._absorption = absorption.get_xs(nuclides=nuclide,
|
||||
xs_type=xs_type)
|
||||
xs_type=xs_type,
|
||||
subdomains=subdomain)
|
||||
elif self.representation is 'angle':
|
||||
msg = 'Angular-Dependent MGXS have not yet been implemented'
|
||||
raise ValueError(msg)
|
||||
|
||||
def set_fission_mgxs(self, fission, nuclide='total', xs_type='macro'):
|
||||
def set_fission_mgxs(self, fission, nuclide='total', xs_type='macro',
|
||||
subdomain=None):
|
||||
"""This method allows for an openmc.mgxs.FissionXS
|
||||
to be used to set the fission cross section for this XSdata object.
|
||||
|
||||
|
|
@ -691,6 +686,9 @@ class XSdata(object):
|
|||
xs_type: {'macro', 'micro'}
|
||||
Provide the macro or micro cross section in units of cm^-1 or
|
||||
barns. Defaults to 'macro'.
|
||||
subdomain : iterable of int
|
||||
If the MGXS contains a mesh domain type, the subdomain parameter
|
||||
specifies which mesh cell (i.e., [i, j, k] index) to use.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
|
@ -703,17 +701,18 @@ class XSdata(object):
|
|||
check_value('energy_groups', fission.energy_groups,
|
||||
[self.energy_groups])
|
||||
check_value('domain_type', fission.domain_type,
|
||||
['universe', 'cell', 'material'])
|
||||
['universe', 'cell', 'material', 'mesh'])
|
||||
|
||||
if self.representation is 'isotropic':
|
||||
self._fission = fission.get_xs(nuclides=nuclide,
|
||||
xs_type=xs_type)
|
||||
xs_type=xs_type,
|
||||
subdomains=subdomain)
|
||||
elif self.representation is 'angle':
|
||||
msg = 'Angular-Dependent MGXS have not yet been implemented'
|
||||
raise ValueError(msg)
|
||||
|
||||
def set_nu_fission_mgxs(self, nu_fission, nuclide='total',
|
||||
xs_type='macro'):
|
||||
xs_type='macro', subdomain=None):
|
||||
"""This method allows for an openmc.mgxs.NuFissionXS
|
||||
to be used to set the nu-fission cross section for this XSdata object.
|
||||
|
||||
|
|
@ -728,6 +727,9 @@ class XSdata(object):
|
|||
xs_type: {'macro', 'micro'}
|
||||
Provide the macro or micro cross section in units of cm^-1 or
|
||||
barns. Defaults to 'macro'.
|
||||
subdomain : iterable of int
|
||||
If the MGXS contains a mesh domain type, the subdomain parameter
|
||||
specifies which mesh cell (i.e., [i, j, k] index) to use.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
|
@ -741,11 +743,12 @@ class XSdata(object):
|
|||
check_value('energy_groups', nu_fission.energy_groups,
|
||||
[self.energy_groups])
|
||||
check_value('domain_type', nu_fission.domain_type,
|
||||
['universe', 'cell', 'material'])
|
||||
['universe', 'cell', 'material', 'mesh'])
|
||||
|
||||
if self.representation is 'isotropic':
|
||||
self._nu_fission = nu_fission.get_xs(nuclides=nuclide,
|
||||
xs_type=xs_type)
|
||||
xs_type=xs_type,
|
||||
subdomains=subdomain)
|
||||
elif self.representation is 'angle':
|
||||
msg = 'Angular-Dependent MGXS have not yet been implemented'
|
||||
raise ValueError(msg)
|
||||
|
|
@ -759,7 +762,7 @@ class XSdata(object):
|
|||
self._fissionable = True
|
||||
|
||||
def set_kappa_fission_mgxs(self, k_fission, nuclide='total',
|
||||
xs_type='macro'):
|
||||
xs_type='macro', subdomain=None):
|
||||
"""This method allows for an openmc.mgxs.KappaFissionXS
|
||||
to be used to set the kappa-fission cross section for this XSdata
|
||||
object.
|
||||
|
|
@ -775,6 +778,9 @@ class XSdata(object):
|
|||
xs_type: {'macro', 'micro'}
|
||||
Provide the macro or micro cross section in units of cm^-1 or
|
||||
barns. Defaults to 'macro'.
|
||||
subdomain : iterable of int
|
||||
If the MGXS contains a mesh domain type, the subdomain parameter
|
||||
specifies which mesh cell (i.e., [i, j, k] index) to use.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
|
@ -787,16 +793,18 @@ class XSdata(object):
|
|||
check_value('energy_groups', k_fission.energy_groups,
|
||||
[self.energy_groups])
|
||||
check_value('domain_type', k_fission.domain_type,
|
||||
['universe', 'cell', 'material'])
|
||||
['universe', 'cell', 'material', 'mesh'])
|
||||
|
||||
if self.representation is 'isotropic':
|
||||
self._kappa_fission = k_fission.get_xs(nuclides=nuclide,
|
||||
xs_type=xs_type)
|
||||
xs_type=xs_type,
|
||||
subdomains=subdomain)
|
||||
elif self.representation is 'angle':
|
||||
msg = 'Angular-Dependent MGXS have not yet been implemented'
|
||||
raise ValueError(msg)
|
||||
|
||||
def set_chi_mgxs(self, chi, nuclide='total', xs_type='macro'):
|
||||
def set_chi_mgxs(self, chi, nuclide='total', xs_type='macro',
|
||||
subdomain=None):
|
||||
"""This method allows for an openmc.mgxs.Chi
|
||||
to be used to set chi for this XSdata object.
|
||||
|
||||
|
|
@ -810,6 +818,9 @@ class XSdata(object):
|
|||
xs_type: {'macro', 'micro'}
|
||||
Provide the macro or micro cross section in units of cm^-1 or
|
||||
barns. Defaults to 'macro'.
|
||||
subdomain : iterable of int
|
||||
If the MGXS contains a mesh domain type, the subdomain parameter
|
||||
specifies which mesh cell (i.e., [i, j, k] index) to use.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
|
@ -827,11 +838,11 @@ class XSdata(object):
|
|||
check_type('chi', chi, openmc.mgxs.Chi)
|
||||
check_value('energy_groups', chi.energy_groups, [self.energy_groups])
|
||||
check_value('domain_type', chi.domain_type,
|
||||
['universe', 'cell', 'material'])
|
||||
['universe', 'cell', 'material', 'mesh'])
|
||||
|
||||
if self.representation is 'isotropic':
|
||||
self._chi = chi.get_xs(nuclides=nuclide,
|
||||
xs_type=xs_type)
|
||||
xs_type=xs_type, subdomains=subdomain)
|
||||
elif self.representation is 'angle':
|
||||
msg = 'Angular-Dependent MGXS have not yet been implemented'
|
||||
raise ValueError(msg)
|
||||
|
|
@ -839,7 +850,8 @@ class XSdata(object):
|
|||
if self.use_chi is not None:
|
||||
self.use_chi = True
|
||||
|
||||
def set_scatter_mgxs(self, scatter, nuclide='total', xs_type='macro'):
|
||||
def set_scatter_mgxs(self, scatter, nuclide='total', xs_type='macro',
|
||||
subdomain=None):
|
||||
"""This method allows for an openmc.mgxs.ScatterMatrixXS
|
||||
to be used to set the scatter matrix cross section for this XSdata
|
||||
object. If the XSdata.order attribute has not yet been set, then
|
||||
|
|
@ -856,6 +868,9 @@ class XSdata(object):
|
|||
xs_type: {'macro', 'micro'}
|
||||
Provide the macro or micro cross section in units of cm^-1 or
|
||||
barns. Defaults to 'macro'.
|
||||
subdomain : iterable of int
|
||||
If the MGXS contains a mesh domain type, the subdomain parameter
|
||||
specifies which mesh cell (i.e., [i, j, k] index) to use.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
|
@ -868,7 +883,7 @@ class XSdata(object):
|
|||
check_value('energy_groups', scatter.energy_groups,
|
||||
[self.energy_groups])
|
||||
check_value('domain_type', scatter.domain_type,
|
||||
['universe', 'cell', 'material'])
|
||||
['universe', 'cell', 'material', 'mesh'])
|
||||
|
||||
if self.scatt_type != 'legendre':
|
||||
msg = 'Anisotropic scattering representations other than ' \
|
||||
|
|
@ -892,16 +907,16 @@ class XSdata(object):
|
|||
self.energy_groups.num_groups,
|
||||
self.energy_groups.num_groups))
|
||||
for moment in range(self.num_orders):
|
||||
self._scatter[moment, :, :] = scatter.get_xs(nuclides=nuclide,
|
||||
xs_type=xs_type,
|
||||
moment=moment)
|
||||
self._scatter[moment, :, :] = \
|
||||
scatter.get_xs(nuclides=nuclide, xs_type=xs_type,
|
||||
moment=moment, subdomains=subdomain)
|
||||
|
||||
elif self.representation is 'angle':
|
||||
msg = 'Angular-Dependent MGXS have not yet been implemented'
|
||||
raise ValueError(msg)
|
||||
|
||||
def set_multiplicity_mgxs(self, nuscatter, scatter=None, nuclide='total',
|
||||
xs_type='macro'):
|
||||
xs_type='macro', subdomain=None):
|
||||
"""This method allows for either the direct use of only an
|
||||
openmc.mgxs.MultiplicityMatrixXS OR an openmc.mgxs.NuScatterMatrixXS and
|
||||
openmc.mgxs.ScatterMatrixXS to be used to set the scattering
|
||||
|
|
@ -926,6 +941,9 @@ class XSdata(object):
|
|||
xs_type: {'macro', 'micro'}
|
||||
Provide the macro or micro cross section in units of cm^-1 or
|
||||
barns. Defaults to 'macro'.
|
||||
subdomain : iterable of int
|
||||
If the MGXS contains a mesh domain type, the subdomain parameter
|
||||
specifies which mesh cell (i.e., [i, j, k] index) to use.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
|
@ -939,7 +957,7 @@ class XSdata(object):
|
|||
check_value('energy_groups', nuscatter.energy_groups,
|
||||
[self.energy_groups])
|
||||
check_value('domain_type', nuscatter.domain_type,
|
||||
['universe', 'cell', 'material'])
|
||||
['universe', 'cell', 'material', 'mesh'])
|
||||
if scatter is not None:
|
||||
check_type('scatter', scatter, openmc.mgxs.ScatterMatrixXS)
|
||||
if isinstance(nuscatter, openmc.mgxs.MultiplicityMatrixXS):
|
||||
|
|
@ -950,16 +968,18 @@ class XSdata(object):
|
|||
check_value('energy_groups', scatter.energy_groups,
|
||||
[self.energy_groups])
|
||||
check_value('domain_type', scatter.domain_type,
|
||||
['universe', 'cell', 'material'])
|
||||
['universe', 'cell', 'material', 'mesh'])
|
||||
|
||||
if self.representation is 'isotropic':
|
||||
nuscatt = nuscatter.get_xs(nuclides=nuclide,
|
||||
xs_type=xs_type, moment=0)
|
||||
xs_type=xs_type, moment=0,
|
||||
subdomains=subdomain)
|
||||
if isinstance(nuscatter, openmc.mgxs.MultiplicityMatrixXS):
|
||||
self._multiplicity = nuscatt
|
||||
else:
|
||||
scatt = scatter.get_xs(nuclides=nuclide,
|
||||
xs_type=xs_type, moment=0)
|
||||
xs_type=xs_type, moment=0,
|
||||
subdomains=subdomain)
|
||||
self._multiplicity = np.divide(nuscatt, scatt)
|
||||
elif self.representation is 'angle':
|
||||
msg = 'Angular-Dependent MGXS have not yet been implemented'
|
||||
|
|
@ -978,18 +998,10 @@ class XSdata(object):
|
|||
subelement = ET.SubElement(element, 'kT')
|
||||
subelement.text = str(self._kT)
|
||||
|
||||
if self._zaid is not None:
|
||||
subelement = ET.SubElement(element, 'zaid')
|
||||
subelement.text = str(self._zaid)
|
||||
|
||||
if self._awr is not None:
|
||||
subelement = ET.SubElement(element, 'awr')
|
||||
subelement.text = str(self._awr)
|
||||
|
||||
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)
|
||||
|
|
@ -1085,6 +1097,10 @@ class MGXSLibrary(object):
|
|||
def energy_groups(self):
|
||||
return self._energy_groups
|
||||
|
||||
@property
|
||||
def xsdatas(self):
|
||||
return self._xsdatas
|
||||
|
||||
@inverse_velocities.setter
|
||||
def inverse_velocities(self, inverse_velocities):
|
||||
check_type('inverse_velocities', inverse_velocities, Iterable, Real)
|
||||
|
|
|
|||
20
openmc/mixin.py
Normal file
20
openmc/mixin.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import numpy as np
|
||||
|
||||
|
||||
class EqualityMixin(object):
|
||||
"""A Class which provides generic __eq__ and __ne__ functionality which
|
||||
can easily be inherited by downstream classes.
|
||||
"""
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, type(self)):
|
||||
for key, value in self.__dict__.items():
|
||||
if not np.array_equal(value, other.__dict__.get(key)):
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
|
@ -1,13 +1,26 @@
|
|||
from __future__ import division
|
||||
import copy
|
||||
from collections import Iterable
|
||||
from numbers import Real
|
||||
import warnings
|
||||
import itertools
|
||||
import random
|
||||
from collections import Iterable, defaultdict
|
||||
from numbers import Real
|
||||
from random import uniform, gauss
|
||||
from heapq import heappush, heappop
|
||||
from math import pi, sin, cos, floor, log10, sqrt
|
||||
from abc import ABCMeta, abstractproperty, abstractmethod
|
||||
|
||||
import numpy as np
|
||||
try:
|
||||
import scipy.spatial
|
||||
_SCIPY_AVAILABLE = True
|
||||
except ImportError:
|
||||
_SCIPY_AVAILABLE = False
|
||||
|
||||
import openmc
|
||||
import openmc.checkvalue as cv
|
||||
|
||||
|
||||
class TRISO(openmc.Cell):
|
||||
"""Tristructural-isotopic (TRISO) micro fuel particle
|
||||
|
||||
|
|
@ -82,6 +95,377 @@ class TRISO(openmc.Cell):
|
|||
k_min:k_max+1, j_min:j_max+1, i_min:i_max+1]))
|
||||
|
||||
|
||||
class _Domain(object):
|
||||
"""Container in which to pack particles.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
particle_radius : float
|
||||
Radius of particles to be packed in container.
|
||||
center : Iterable of float
|
||||
Cartesian coordinates of the center of the container. Default is
|
||||
[0., 0., 0.]
|
||||
|
||||
Attributes
|
||||
----------
|
||||
particle_radius : float
|
||||
Radius of particles to be packed in container.
|
||||
center : list of float
|
||||
Cartesian coordinates of the center of the container. Default is
|
||||
[0., 0., 0.]
|
||||
cell_length : list of float
|
||||
Length in x-, y-, and z- directions of each cell in mesh overlaid on
|
||||
domain.
|
||||
limits : list of float
|
||||
Minimum and maximum position in x-, y-, and z-directions where particle
|
||||
center can be placed.
|
||||
volume : float
|
||||
Volume of the container.
|
||||
|
||||
"""
|
||||
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
def __init__(self, particle_radius, center=[0., 0., 0.]):
|
||||
self._cell_length = None
|
||||
self._limits = None
|
||||
|
||||
self.particle_radius = particle_radius
|
||||
self.center = center
|
||||
|
||||
@property
|
||||
def particle_radius(self):
|
||||
return self._particle_radius
|
||||
|
||||
@property
|
||||
def center(self):
|
||||
return self._center
|
||||
|
||||
@abstractproperty
|
||||
def limits(self):
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def cell_length(self):
|
||||
pass
|
||||
|
||||
@abstractproperty
|
||||
def volume(self):
|
||||
pass
|
||||
|
||||
@particle_radius.setter
|
||||
def particle_radius(self, particle_radius):
|
||||
self._particle_radius = float(particle_radius)
|
||||
self._limits = None
|
||||
self._cell_length = None
|
||||
|
||||
@center.setter
|
||||
def center(self, center):
|
||||
if np.asarray(center).size != 3:
|
||||
raise ValueError('Unable to set domain center to {} since it must '
|
||||
'be of length 3'.format(center))
|
||||
self._center = [float(x) for x in center]
|
||||
self._limits = None
|
||||
self._cell_length = None
|
||||
|
||||
def mesh_cell(self, p):
|
||||
"""Calculate the index of the cell in a mesh overlaid on the domain in
|
||||
which the given particle center falls.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
p : Iterable of float
|
||||
Cartesian coordinates of particle center.
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple of int
|
||||
Indices of mesh cell.
|
||||
|
||||
"""
|
||||
return tuple(int(p[i]/self.cell_length[i]) for i in range(3))
|
||||
|
||||
def nearby_mesh_cells(self, p):
|
||||
"""Calculates the indices of all cells in a mesh overlaid on the domain
|
||||
within one diameter of the given particle.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
p : Iterable of float
|
||||
Cartesian coordinates of particle center.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of tuple of int
|
||||
Indices of mesh cells.
|
||||
|
||||
"""
|
||||
d = 2*self.particle_radius
|
||||
r = [[a/self.cell_length[i] for a in [p[i]-d, p[i], p[i]+d]]
|
||||
for i in range(3)]
|
||||
return list(itertools.product(*({int(x) for x in y} for y in r)))
|
||||
|
||||
@abstractmethod
|
||||
def random_point(self):
|
||||
"""Generate Cartesian coordinates of center of a particle that is
|
||||
contained entirely within the domain with uniform probability.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of float
|
||||
Cartesian coordinates of particle center.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class _CubicDomain(_Domain):
|
||||
"""Cubic container in which to pack particles.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
length : float
|
||||
Length of each side of the cubic container.
|
||||
particle_radius : float
|
||||
Radius of particles to be packed in container.
|
||||
center : Iterable of float
|
||||
Cartesian coordinates of the center of the container. Default is
|
||||
[0., 0., 0.]
|
||||
|
||||
Attributes
|
||||
----------
|
||||
length : float
|
||||
Length of each side of the cubic container.
|
||||
particle_radius : float
|
||||
Radius of particles to be packed in container.
|
||||
center : list of float
|
||||
Cartesian coordinates of the center of the container. Default is
|
||||
[0., 0., 0.]
|
||||
cell_length : list of float
|
||||
Length in x-, y-, and z- directions of each cell in mesh overlaid on
|
||||
domain.
|
||||
limits : list of float
|
||||
Minimum and maximum position in x-, y-, and z-directions where particle
|
||||
center can be placed.
|
||||
volume : float
|
||||
Volume of the container.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, length, particle_radius, center=[0., 0., 0.]):
|
||||
super(_CubicDomain, self).__init__(particle_radius, center)
|
||||
self.length = length
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return self._length
|
||||
|
||||
@property
|
||||
def limits(self):
|
||||
if self._limits is None:
|
||||
xlim = self.length/2 - self.particle_radius
|
||||
self._limits = [[x - xlim for x in self.center],
|
||||
[x + xlim for x in self.center]]
|
||||
return self._limits
|
||||
|
||||
@property
|
||||
def cell_length(self):
|
||||
if self._cell_length is None:
|
||||
mesh_length = [self.length, self.length, self.length]
|
||||
self._cell_length = [x/int(x/(4*self.particle_radius))
|
||||
for x in mesh_length]
|
||||
return self._cell_length
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
return self.length**3
|
||||
|
||||
@length.setter
|
||||
def length(self, length):
|
||||
self._length = float(length)
|
||||
self._limits = None
|
||||
self._cell_length = None
|
||||
|
||||
@limits.setter
|
||||
def limits(self, limits):
|
||||
self._limits = limits
|
||||
|
||||
def random_point(self):
|
||||
return [uniform(self.limits[0][0], self.limits[1][0]),
|
||||
uniform(self.limits[0][1], self.limits[1][1]),
|
||||
uniform(self.limits[0][2], self.limits[1][2])]
|
||||
|
||||
|
||||
class _CylindricalDomain(_Domain):
|
||||
"""Cylindrical container in which to pack particles.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
length : float
|
||||
Length along z-axis of the cylindrical container.
|
||||
radius : float
|
||||
Radius of the cylindrical container.
|
||||
center : Iterable of float
|
||||
Cartesian coordinates of the center of the container. Default is
|
||||
[0., 0., 0.]
|
||||
|
||||
Attributes
|
||||
----------
|
||||
length : float
|
||||
Length along z-axis of the cylindrical container.
|
||||
radius : float
|
||||
Radius of the cylindrical container.
|
||||
particle_radius : float
|
||||
Radius of particles to be packed in container.
|
||||
center : list of float
|
||||
Cartesian coordinates of the center of the container. Default is
|
||||
[0., 0., 0.]
|
||||
cell_length : list of float
|
||||
Length in x-, y-, and z- directions of each cell in mesh overlaid on
|
||||
domain.
|
||||
limits : list of float
|
||||
Minimum and maximum position in x-, y-, and z-directions where particle
|
||||
center can be placed.
|
||||
volume : float
|
||||
Volume of the container.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, length, radius, particle_radius, center=[0., 0., 0.]):
|
||||
super(_CylindricalDomain, self).__init__(particle_radius, center)
|
||||
self.length = length
|
||||
self.radius = radius
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return self._length
|
||||
|
||||
@property
|
||||
def radius(self):
|
||||
return self._radius
|
||||
|
||||
@property
|
||||
def limits(self):
|
||||
if self._limits is None:
|
||||
xlim = self.length/2 - self.particle_radius
|
||||
rlim = self.radius - self.particle_radius
|
||||
self._limits = [[self.center[0] - rlim, self.center[1] - rlim,
|
||||
self.center[2] - xlim],
|
||||
[self.center[0] + rlim, self.center[1] + rlim,
|
||||
self.center[2] + xlim]]
|
||||
return self._limits
|
||||
|
||||
@property
|
||||
def cell_length(self):
|
||||
if self._cell_length is None:
|
||||
mesh_length = [2*self.radius, 2*self.radius, self.length]
|
||||
self._cell_length = [x/int(x/(4*self.particle_radius))
|
||||
for x in mesh_length]
|
||||
return self._cell_length
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
return self.length * pi * self.radius**2
|
||||
|
||||
@length.setter
|
||||
def length(self, length):
|
||||
self._length = float(length)
|
||||
self._limits = None
|
||||
self._cell_length = None
|
||||
|
||||
@radius.setter
|
||||
def radius(self, radius):
|
||||
self._radius = float(radius)
|
||||
self._limits = None
|
||||
self._cell_length = None
|
||||
|
||||
@limits.setter
|
||||
def limits(self, limits):
|
||||
self._limits = limits
|
||||
|
||||
def random_point(self):
|
||||
r = sqrt(uniform(0, (self.radius - self.particle_radius)**2))
|
||||
t = uniform(0, 2*pi)
|
||||
return [r*cos(t) + self.center[0], r*sin(t) + self.center[1],
|
||||
uniform(self.limits[0][2], self.limits[1][2])]
|
||||
|
||||
|
||||
class _SphericalDomain(_Domain):
|
||||
"""Spherical container in which to pack particles.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
radius : float
|
||||
Radius of the spherical container.
|
||||
center : Iterable of float
|
||||
Cartesian coordinates of the center of the container. Default is
|
||||
[0., 0., 0.]
|
||||
|
||||
Attributes
|
||||
----------
|
||||
radius : float
|
||||
Radius of the spherical container.
|
||||
particle_radius : float
|
||||
Radius of particles to be packed in container.
|
||||
center : list of float
|
||||
Cartesian coordinates of the center of the container. Default is
|
||||
[0., 0., 0.]
|
||||
cell_length : list of float
|
||||
Length in x-, y-, and z- directions of each cell in mesh overlaid on
|
||||
domain.
|
||||
limits : list of float
|
||||
Minimum and maximum position in x-, y-, and z-directions where particle
|
||||
center can be placed.
|
||||
volume : float
|
||||
Volume of the container.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, radius, particle_radius, center=[0., 0., 0.]):
|
||||
super(_SphericalDomain, self).__init__(particle_radius, center)
|
||||
self.radius = radius
|
||||
|
||||
@property
|
||||
def radius(self):
|
||||
return self._radius
|
||||
|
||||
@property
|
||||
def limits(self):
|
||||
if self._limits is None:
|
||||
rlim = self.radius - self.particle_radius
|
||||
self._limits = [[x - rlim for x in self.center],
|
||||
[x + rlim for x in self.center]]
|
||||
return self._limits
|
||||
|
||||
@property
|
||||
def cell_length(self):
|
||||
if self._cell_length is None:
|
||||
mesh_length = [2*self.radius, 2*self.radius, 2*self.radius]
|
||||
self._cell_length = [x/int(x/(4*self.particle_radius))
|
||||
for x in mesh_length]
|
||||
return self._cell_length
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
return 4/3 * pi * self.radius**3
|
||||
|
||||
@radius.setter
|
||||
def radius(self, radius):
|
||||
self._radius = float(radius)
|
||||
self._limits = None
|
||||
self._cell_length = None
|
||||
|
||||
@limits.setter
|
||||
def limits(self, limits):
|
||||
self._limits = limits
|
||||
|
||||
def random_point(self):
|
||||
x = (gauss(0, 1), gauss(0, 1), gauss(0, 1))
|
||||
r = (uniform(0, (self.radius - self.particle_radius)**3)**(1/3) /
|
||||
sqrt(x[0]**2 + x[1]**2 + x[2]**2))
|
||||
return [r*x[i] + self.center[i] for i in range(3)]
|
||||
|
||||
|
||||
def create_triso_lattice(trisos, lower_left, pitch, shape, background):
|
||||
"""Create a lattice containing TRISO particles for optimized tracking.
|
||||
|
||||
|
|
@ -153,3 +537,503 @@ def create_triso_lattice(trisos, lower_left, pitch, shape, background):
|
|||
lattice.outer = openmc.Universe(cells=[background_cell])
|
||||
|
||||
return lattice
|
||||
|
||||
|
||||
def _random_sequential_pack(domain, n_particles):
|
||||
"""Random sequential packing of particles within a container.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
domain : openmc.model._Domain
|
||||
Container in which to pack particles.
|
||||
n_particles : int
|
||||
Number of particles to pack.
|
||||
|
||||
Returns
|
||||
------
|
||||
numpy.ndarray
|
||||
Cartesian coordinates of centers of particles.
|
||||
|
||||
"""
|
||||
|
||||
sqd = (2*domain.particle_radius)**2
|
||||
particles = []
|
||||
mesh = defaultdict(list)
|
||||
|
||||
for i in range(n_particles):
|
||||
# Randomly sample new center coordinates while there are any overlaps
|
||||
while True:
|
||||
p = domain.random_point()
|
||||
idx = domain.mesh_cell(p)
|
||||
if any((p[0]-q[0])**2 + (p[1]-q[1])**2 + (p[2]-q[2])**2 < sqd
|
||||
for q in mesh[idx]):
|
||||
continue
|
||||
else:
|
||||
break
|
||||
particles.append(p)
|
||||
|
||||
for idx in domain.nearby_mesh_cells(p):
|
||||
mesh[idx].append(p)
|
||||
|
||||
return np.array(particles)
|
||||
|
||||
|
||||
def _close_random_pack(domain, particles, contraction_rate):
|
||||
"""Close random packing of particles using the Jodrey-Tory algorithm.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
domain : openmc.model._Domain
|
||||
Container in which to pack particles.
|
||||
particles : numpy.ndarray
|
||||
Initial Cartesian coordinates of centers of particles.
|
||||
contraction_rate : float
|
||||
Contraction rate of outer diameter.
|
||||
|
||||
"""
|
||||
|
||||
def add_rod(d, i, j):
|
||||
"""Add a new rod to the priority queue.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
d : float
|
||||
distance between centers of particles i and j.
|
||||
i, j : int
|
||||
Index of particles in particles array.
|
||||
|
||||
"""
|
||||
|
||||
rod = [d, i, j]
|
||||
rods_map[i] = (j, rod)
|
||||
rods_map[j] = (i, rod)
|
||||
heappush(rods, rod)
|
||||
|
||||
def remove_rod(i):
|
||||
"""Mark the rod containing particle i as removed.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
i : int
|
||||
Index of particle in particles array.
|
||||
|
||||
"""
|
||||
|
||||
if i in rods_map:
|
||||
j, rod = rods_map.pop(i)
|
||||
del rods_map[j]
|
||||
rod[1] = removed
|
||||
rod[2] = removed
|
||||
|
||||
def pop_rod():
|
||||
"""Remove and return the shortest rod.
|
||||
|
||||
Returns
|
||||
-------
|
||||
d : float
|
||||
distance between centers of particles i and j.
|
||||
i, j : int
|
||||
Index of particles in particles array.
|
||||
|
||||
"""
|
||||
|
||||
while rods:
|
||||
d, i, j = heappop(rods)
|
||||
if i != removed and j != removed:
|
||||
del rods_map[i]
|
||||
del rods_map[j]
|
||||
return d, i, j
|
||||
|
||||
def create_rod_list():
|
||||
"""Generate sorted list of rods (distances between particle centers).
|
||||
|
||||
Rods are arranged in a heap where each element contains the rod length
|
||||
and the particle indices. A rod between particles p and q is only
|
||||
included if the distance between p and q could not be changed by the
|
||||
elimination of a greater overlap, i.e. q has no nearer neighbors than p.
|
||||
|
||||
A mapping of particle ids to rods is maintained in 'rods_map'. Each key
|
||||
in the dict is the id of a particle that is in the rod list, and the
|
||||
value is the id of its nearest neighbor and the rod that contains them.
|
||||
The dict is used to find rods in the priority queue and to mark removed
|
||||
rods so rods can be "removed" without breaking the heap structure
|
||||
invariant.
|
||||
|
||||
"""
|
||||
|
||||
# Create KD tree for quick nearest neighbor search
|
||||
tree = scipy.spatial.cKDTree(particles)
|
||||
|
||||
# Find distance to nearest neighbor and index of nearest neighbor for
|
||||
# all particles
|
||||
d, n = tree.query(particles, k=2)
|
||||
d = d[:,1]
|
||||
n = n[:,1]
|
||||
|
||||
# Array of particle indices, indices of nearest neighbors, and
|
||||
# distances to nearest neighbors
|
||||
a = np.vstack((list(range(n.size)), n, d)).T
|
||||
|
||||
# Sort along second column and swap first and second columns to create
|
||||
# array of nearest neighbor indices, indices of particles they are
|
||||
# nearest neighbors of, and distances between them
|
||||
b = a[a[:,1].argsort()]
|
||||
b[:,[0, 1]] = b[:,[1, 0]]
|
||||
|
||||
# Find the intersection between 'a' and 'b': a list of particles who
|
||||
# are each other's nearest neighbors and the distance between them
|
||||
r = list({tuple(x) for x in a} & {tuple(x) for x in b})
|
||||
|
||||
# Remove duplicate rods and sort by distance
|
||||
r = map(list, set([(x[2], int(min(x[0:2])), int(max(x[0:2])))
|
||||
for x in r]))
|
||||
|
||||
# Clear priority queue and add rods
|
||||
del rods[:]
|
||||
rods_map.clear()
|
||||
for d, i, j in r:
|
||||
add_rod(d, i, j)
|
||||
|
||||
# Inner diameter is set initially to the shortest center-to-center
|
||||
# distance between any two particles
|
||||
if rods:
|
||||
inner_diameter[0] = rods[0][0]
|
||||
|
||||
def update_mesh(i):
|
||||
"""Update which mesh cells the particle is in based on new particle
|
||||
center coordinates.
|
||||
|
||||
'mesh'/'mesh_map' is a two way dictionary used to look up which
|
||||
particles are located within one diameter of a given mesh cell and
|
||||
which mesh cells a given particle center is within one diameter of.
|
||||
This is used to speed up the nearest neighbor search.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
i : int
|
||||
Index of particle in particles array.
|
||||
|
||||
"""
|
||||
|
||||
# Determine which mesh cells the particle is in and remove the
|
||||
# particle id from those cells
|
||||
for idx in mesh_map[i]:
|
||||
mesh[idx].remove(i)
|
||||
del mesh_map[i]
|
||||
|
||||
# Determine which mesh cells are within one diameter of particle's
|
||||
# center and add this particle to the list of particles in those cells
|
||||
for idx in domain.nearby_mesh_cells(particles[i]):
|
||||
mesh[idx].add(i)
|
||||
mesh_map[i].add(idx)
|
||||
|
||||
def reduce_outer_diameter():
|
||||
"""Reduce the outer diameter so that at the (i+1)-st iteration it is:
|
||||
|
||||
d_out^(i+1) = d_out^(i) - (1/2)^(j) * d_out0 * k / n,
|
||||
|
||||
where k is the contraction rate, n is the number of particles, and
|
||||
|
||||
j = floor(-log10(pf_out - pf_in)).
|
||||
|
||||
"""
|
||||
|
||||
inner_pf = (4/3 * pi * (inner_diameter[0]/2)**3 * n_particles /
|
||||
domain.volume)
|
||||
outer_pf = (4/3 * pi * (outer_diameter[0]/2)**3 * n_particles /
|
||||
domain.volume)
|
||||
|
||||
j = floor(-log10(outer_pf - inner_pf))
|
||||
outer_diameter[0] = (outer_diameter[0] - 0.5**j * contraction_rate *
|
||||
initial_outer_diameter / n_particles)
|
||||
|
||||
|
||||
def repel_particles(i, j, d):
|
||||
"""Move particles p and q apart according to the following
|
||||
transformation (accounting for reflective boundary conditions on
|
||||
domain):
|
||||
|
||||
r_i^(n+1) = r_i^(n) + 1/2(d_out^(n+1) - d^(n))
|
||||
r_j^(n+1) = r_j^(n) - 1/2(d_out^(n+1) - d^(n))
|
||||
|
||||
Parameters
|
||||
----------
|
||||
i, j : int
|
||||
Index of particles in particles array.
|
||||
d : float
|
||||
distance between centers of particles i and j.
|
||||
|
||||
"""
|
||||
|
||||
# Moving each particle distance 'r' away from the other along the line
|
||||
# joining the particle centers will ensure their final distance is equal
|
||||
# to the outer diameter
|
||||
r = (outer_diameter[0] - d)/2
|
||||
|
||||
v = (particles[i] - particles[j])/d
|
||||
particles[i] += r*v
|
||||
particles[j] -= r*v
|
||||
|
||||
# Apply reflective boundary conditions
|
||||
particles[i] = particles[i].clip(domain.limits[0], domain.limits[1])
|
||||
particles[j] = particles[j].clip(domain.limits[0], domain.limits[1])
|
||||
|
||||
update_mesh(i)
|
||||
update_mesh(j)
|
||||
|
||||
def nearest(i):
|
||||
"""Find index of nearest neighbor of particle i.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
i : int
|
||||
Index in particles array of particle for which to find nearest
|
||||
neighbor.
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
Index in particles array of nearest neighbor of i
|
||||
float
|
||||
distance between i and nearest neighbor.
|
||||
|
||||
"""
|
||||
|
||||
# Need the second nearest neighbor of i since the nearest neighbor
|
||||
# will be itself. Using argpartition, the k-th nearest neighbor is
|
||||
# placed at index k.
|
||||
idx = list(mesh[domain.mesh_cell(particles[i])])
|
||||
dists = scipy.spatial.distance.cdist([particles[i]], particles[idx])[0]
|
||||
if dists.size > 1:
|
||||
j = dists.argpartition(1)[1]
|
||||
return idx[j], dists[j]
|
||||
else:
|
||||
return None, None
|
||||
|
||||
def update_rod_list(i, j):
|
||||
"""Update the rod list with the new nearest neighbors of particles i
|
||||
and j since their overlap was eliminated.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
i, j : int
|
||||
Index of particles in particles array.
|
||||
|
||||
"""
|
||||
|
||||
# If the nearest neighbor k of particle i has no nearer neighbors,
|
||||
# remove the rod currently containing k from the rod list and add rod
|
||||
# k-i, keeping the rod list sorted
|
||||
k, d_ik = nearest(i)
|
||||
if k and nearest(k)[0] == i:
|
||||
remove_rod(k)
|
||||
add_rod(d_ik, i, k)
|
||||
l, d_jl = nearest(j)
|
||||
if l and nearest(l)[0] == j:
|
||||
remove_rod(l)
|
||||
add_rod(d_jl, j, l)
|
||||
|
||||
# Set inner diameter to the shortest distance between two particle
|
||||
# centers
|
||||
if rods:
|
||||
inner_diameter[0] = rods[0][0]
|
||||
|
||||
if not _SCIPY_AVAILABLE:
|
||||
raise ImportError('SciPy must be installed to perform '
|
||||
'close random packing.')
|
||||
|
||||
n_particles = len(particles)
|
||||
diameter = 2*domain.particle_radius
|
||||
|
||||
# Flag for marking rods that have been removed from priority queue
|
||||
removed = -1
|
||||
|
||||
# Outer diameter initially set to arbitrary value that yields pf of 1
|
||||
initial_outer_diameter = 2*(domain.volume/(n_particles*4/3*pi))**(1/3)
|
||||
|
||||
# Inner and outer diameter of particles will change during packing
|
||||
outer_diameter = [initial_outer_diameter]
|
||||
inner_diameter = [0]
|
||||
|
||||
rods = []
|
||||
rods_map = {}
|
||||
mesh = defaultdict(set)
|
||||
mesh_map = defaultdict(set)
|
||||
|
||||
for i in range(n_particles):
|
||||
for idx in domain.nearby_mesh_cells(particles[i]):
|
||||
mesh[idx].add(i)
|
||||
mesh_map[i].add(idx)
|
||||
|
||||
while True:
|
||||
create_rod_list()
|
||||
if inner_diameter[0] >= diameter:
|
||||
break
|
||||
while True:
|
||||
d, i, j = pop_rod()
|
||||
reduce_outer_diameter()
|
||||
repel_particles(i, j, d)
|
||||
update_rod_list(i, j)
|
||||
if inner_diameter[0] >= diameter or not rods:
|
||||
break
|
||||
|
||||
|
||||
def pack_trisos(radius, fill, domain_shape='cylinder', domain_length=None,
|
||||
domain_radius=None, domain_center=[0., 0., 0.],
|
||||
n_particles=None, packing_fraction=None,
|
||||
initial_packing_fraction=0.3, contraction_rate=1/400, seed=1):
|
||||
"""Generate a random, non-overlapping configuration of TRISO particles
|
||||
within a container.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
radius : float
|
||||
Outer radius of TRISO particles.
|
||||
fill : openmc.Universe
|
||||
Universe which contains all layers of the TRISO particle.
|
||||
domain_shape : {'cube', 'cylinder', or 'sphere'}
|
||||
Geometry of the container in which the TRISO particles are packed.
|
||||
domain_length : float
|
||||
Length of the container (if cube or cylinder).
|
||||
domain_radius : float
|
||||
Radius of the container (if cylinder or sphere).
|
||||
domain_center : Iterable of float
|
||||
Cartesian coordinates of the center of the container.
|
||||
n_particles : int
|
||||
Number of TRISO particles to pack in the domain. Exactly one of
|
||||
'n_particles' and 'packing_fraction' should be specified -- the other
|
||||
will be calculated.
|
||||
packing_fraction : float
|
||||
Packing fraction of particles. Exactly one of 'n_particles' and
|
||||
'packing_fraction' should be specified -- the other will be calculated.
|
||||
initial_packing_fraction : float, optional
|
||||
Packing fraction used to initialize the configuration of particles in
|
||||
the domain. Default value is 0.3. It is not recommended to set the
|
||||
initial packing fraction much higher than 0.3 as the random sequential
|
||||
packing algorithm becomes prohibitively slow as it approaches its limit
|
||||
(~0.38).
|
||||
contraction_rate : float, optional
|
||||
Contraction rate of outer diameter. This can affect the speed of the
|
||||
close random packing algorithm. Default value is 1/400.
|
||||
seed : int, optional
|
||||
RNG seed.
|
||||
|
||||
Returns
|
||||
-------
|
||||
trisos : list of openmc.model.TRISO
|
||||
List of TRISO particles in the domain.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The particle configuration is generated using a combination of random
|
||||
sequential packing (RSP) and close random packing (CRP). RSP performs
|
||||
better than CRP for lower packing fractions (pf), but it becomes
|
||||
prohibitively slow as it approaches its packing limit (~0.38). CRP can
|
||||
achieve higher pf of up to ~0.64 and scales better with increasing pf.
|
||||
|
||||
If the desired pf is below some threshold for which RSP will be faster than
|
||||
CRP ('initial_packing_fraction'), only RSP is used. If a higher pf is
|
||||
required, particles with a radius smaller than the desired final radius
|
||||
(and therefore with a smaller pf) are initialized within the domain using
|
||||
RSP. This initial configuration of particles is then used as a starting
|
||||
point for CRP using Jodrey and Tory's algorithm [1]_.
|
||||
|
||||
In RSP, particle centers are placed one by one at random, and placement
|
||||
attempts for a particle are made until the particle is not overlapping any
|
||||
others. This implementation of the algorithm uses a mesh over the domain
|
||||
to speed up the nearest neighbor search by only searching for a particle's
|
||||
neighbors within that mesh cell.
|
||||
|
||||
In CRP, each particle is assigned two diameters, and inner and an outer,
|
||||
which approach each other during the simulation. The inner diameter,
|
||||
defined as the minimum center-to-center distance, is the true diameter of
|
||||
the particles and defines the pf. At each iteration the worst overlap
|
||||
between particles based on outer diameter is eliminated by moving the
|
||||
particles apart along the line joining their centers. Iterations continue
|
||||
until the two diameters converge or until the desired pf is reached.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] W. S. Jodrey and E. M. Tory, "Computer simulation of close random
|
||||
packing of equal spheres", Phys. Rev. A 32 (1985) 2347-2351.
|
||||
|
||||
"""
|
||||
|
||||
# Check for valid container geometry and dimensions
|
||||
if domain_shape not in ['cube', 'cylinder', 'sphere']:
|
||||
raise ValueError('Unable to set domain_shape to "{}". Only "cube", '
|
||||
'"cylinder", and "sphere" are '
|
||||
'supported."'.format(domain_shape))
|
||||
if not domain_length and domain_shape in ['cube', 'cylinder']:
|
||||
raise ValueError('"domain_length" must be specified for {} domain '
|
||||
'geometry '.format(domain_shape))
|
||||
if not domain_radius and domain_shape in ['cylinder', 'sphere']:
|
||||
raise ValueError('"domain_radius" must be specified for {} domain '
|
||||
'geometry '.format(domain_shape))
|
||||
|
||||
if domain_shape is 'cube':
|
||||
domain = _CubicDomain(length=domain_length, particle_radius=radius,
|
||||
center=domain_center)
|
||||
elif domain_shape is 'cylinder':
|
||||
domain = _CylindricalDomain(length=domain_length, radius=domain_radius,
|
||||
particle_radius=radius, center=domain_center)
|
||||
elif domain_shape is 'sphere':
|
||||
domain = _SphericalDomain(radius=domain_radius, particle_radius=radius,
|
||||
center=domain_center)
|
||||
|
||||
# Calculate the packing fraction if the number of particles is specified;
|
||||
# otherwise, calculate the number of particles from the packing fraction.
|
||||
if ((n_particles is None and packing_fraction is None) or
|
||||
(n_particles is not None and packing_fraction is not None)):
|
||||
raise ValueError('Exactly one of "n_particles" and "packing_fraction" '
|
||||
'must be specified.')
|
||||
elif packing_fraction is None:
|
||||
n_particles = int(n_particles)
|
||||
packing_fraction = 4/3*pi*radius**3*n_particles / domain.volume
|
||||
elif n_particles is None:
|
||||
packing_fraction = float(packing_fraction)
|
||||
n_particles = int(packing_fraction*domain.volume // (4/3*pi*radius**3))
|
||||
|
||||
# Check for valid packing fractions for each algorithm
|
||||
if packing_fraction >= 0.64:
|
||||
raise ValueError('Packing fraction of {} is greater than the '
|
||||
'packing fraction limit for close random '
|
||||
'packing (0.64)'.format(packing_fraction))
|
||||
if initial_packing_fraction >= 0.38:
|
||||
raise ValueError('Initial packing fraction of {} is greater than the '
|
||||
'packing fraction limit for random sequential'
|
||||
'packing (0.38)'.format(initial_packing_fraction))
|
||||
if initial_packing_fraction > packing_fraction:
|
||||
initial_packing_fraction = packing_fraction
|
||||
if packing_fraction > 0.3:
|
||||
initial_packing_fraction = 0.3
|
||||
|
||||
random.seed(seed)
|
||||
|
||||
# Calculate the particle radius used in the initial random sequential
|
||||
# packing from the initial packing fraction
|
||||
initial_radius = (3/4 * initial_packing_fraction * domain.volume /
|
||||
(pi * n_particles))**(1/3)
|
||||
domain.particle_radius = initial_radius
|
||||
|
||||
# Recalculate the limits for the initial random sequential packing using
|
||||
# the desired final particle radius to ensure particles are fully contained
|
||||
# within the domain during the close random pack
|
||||
domain.limits = [[x - initial_radius + radius for x in domain.limits[0]],
|
||||
[x + initial_radius - radius for x in domain.limits[1]]]
|
||||
|
||||
# Generate non-overlapping particles for an initial inner radius using
|
||||
# random sequential packing algorithm
|
||||
particles = _random_sequential_pack(domain, n_particles)
|
||||
|
||||
# Use the particle configuration produced in random sequential packing as a
|
||||
# starting point for close random pack with the desired final particle
|
||||
# radius
|
||||
if initial_packing_fraction != packing_fraction:
|
||||
domain.particle_radius = radius
|
||||
_close_random_pack(domain, particles, contraction_rate)
|
||||
|
||||
trisos = []
|
||||
for p in particles:
|
||||
trisos.append(TRISO(radius, fill, p))
|
||||
return trisos
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from numbers import Integral
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from openmc.checkvalue import check_type
|
||||
|
||||
|
|
@ -14,42 +15,28 @@ class Nuclide(object):
|
|||
----------
|
||||
name : str
|
||||
Name of the nuclide, e.g. U235
|
||||
xs : str
|
||||
Cross section identifier, e.g. 71c
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name : str
|
||||
Name of the nuclide, e.g. U235
|
||||
xs : str
|
||||
Cross section identifier, e.g. 71c
|
||||
zaid : int
|
||||
1000*(atomic number) + mass number. As an example, the zaid of U235
|
||||
would be 92235.
|
||||
scattering : 'data' or 'iso-in-lab' or None
|
||||
The type of angular scattering distribution to use
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name='', xs=None):
|
||||
def __init__(self, name=''):
|
||||
# Initialize class attributes
|
||||
self._name = ''
|
||||
self._xs = None
|
||||
self._zaid = None
|
||||
self._scattering = None
|
||||
|
||||
# Set the Material class attributes
|
||||
self.name = name
|
||||
|
||||
if xs is not None:
|
||||
self.xs = xs
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Nuclide):
|
||||
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:
|
||||
|
|
@ -71,9 +58,6 @@ class Nuclide(object):
|
|||
|
||||
def __repr__(self):
|
||||
string = 'Nuclide - {0}\n'.format(self._name)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self.xs)
|
||||
if self.zaid is not None:
|
||||
string += '{0: <16}{1}{2}\n'.format('\tZAID', '=\t', self.zaid)
|
||||
if self.scattering is not None:
|
||||
string += '{0: <16}{1}{2}\n'.format('\tscattering', '=\t',
|
||||
self.scattering)
|
||||
|
|
@ -83,14 +67,6 @@ class Nuclide(object):
|
|||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def xs(self):
|
||||
return self._xs
|
||||
|
||||
@property
|
||||
def zaid(self):
|
||||
return self._zaid
|
||||
|
||||
@property
|
||||
def scattering(self):
|
||||
return self._scattering
|
||||
|
|
@ -100,19 +76,18 @@ class Nuclide(object):
|
|||
check_type('name', name, basestring)
|
||||
self._name = name
|
||||
|
||||
@xs.setter
|
||||
def xs(self, xs):
|
||||
check_type('cross-section identifier', xs, basestring)
|
||||
self._xs = xs
|
||||
if '-' in name:
|
||||
self._name = name.replace('-', '')
|
||||
self._name = self._name.replace('Nat', '0')
|
||||
if self._name.endswith('m'):
|
||||
self._name = self._name[:-1] + '_m1'
|
||||
|
||||
@zaid.setter
|
||||
def zaid(self, zaid):
|
||||
check_type('zaid', zaid, Integral)
|
||||
self._zaid = zaid
|
||||
msg = 'OpenMC nuclides follow the GND naming convention. Nuclide ' \
|
||||
'"{}" is being renamed as "{}".'.format(name, self._name)
|
||||
warnings.warn(msg)
|
||||
|
||||
@scattering.setter
|
||||
def scattering(self, scattering):
|
||||
|
||||
if not scattering in ['data', 'iso-in-lab']:
|
||||
msg = 'Unable to set scattering for Nuclide to {0} ' \
|
||||
'which is not "data" or "iso-in-lab"'.format(scattering)
|
||||
|
|
|
|||
|
|
@ -689,13 +689,14 @@ def get_openmc_cell(opencg_cell):
|
|||
translation = np.asarray(opencg_cell.translation, dtype=np.float64)
|
||||
openmc_cell.translation = translation
|
||||
|
||||
surfaces = []
|
||||
operators = []
|
||||
for surface, halfspace in opencg_cell.surfaces.values():
|
||||
surfaces.append(get_openmc_surface(surface))
|
||||
operators.append(operator.neg if halfspace == -1 else operator.pos)
|
||||
openmc_cell.region = openmc.Intersection(
|
||||
*[op(s) for op, s in zip(operators, surfaces)])
|
||||
if opencg_cell.surfaces:
|
||||
surfaces = []
|
||||
operators = []
|
||||
for surface, halfspace in opencg_cell.surfaces.values():
|
||||
surfaces.append(get_openmc_surface(surface))
|
||||
operators.append(operator.neg if halfspace == -1 else operator.pos)
|
||||
openmc_cell.region = openmc.Intersection(
|
||||
*[op(s) for op, s in zip(operators, surfaces)])
|
||||
|
||||
# Add the OpenMC Cell to the global collection of all OpenMC Cells
|
||||
OPENMC_CELLS[cell_id] = openmc_cell
|
||||
|
|
@ -911,6 +912,7 @@ def get_openmc_lattice(opencg_lattice):
|
|||
if lattice_id in OPENMC_LATTICES:
|
||||
return OPENMC_LATTICES[lattice_id]
|
||||
|
||||
name = opencg_lattice.name
|
||||
dimension = opencg_lattice.dimension
|
||||
width = opencg_lattice.width
|
||||
offset = opencg_lattice.offset
|
||||
|
|
@ -941,7 +943,7 @@ def get_openmc_lattice(opencg_lattice):
|
|||
((np.array(width, dtype=np.float64) *
|
||||
np.array(dimension, dtype=np.float64))) / -2.0
|
||||
|
||||
openmc_lattice = openmc.RectLattice(lattice_id=lattice_id)
|
||||
openmc_lattice = openmc.RectLattice(lattice_id=lattice_id, name=name)
|
||||
openmc_lattice.pitch = width
|
||||
openmc_lattice.universes = universe_array
|
||||
openmc_lattice.lower_left = lower_left
|
||||
|
|
@ -995,13 +997,17 @@ def get_opencg_geometry(openmc_geometry):
|
|||
return opencg_geometry
|
||||
|
||||
|
||||
def get_openmc_geometry(opencg_geometry):
|
||||
def get_openmc_geometry(opencg_geometry, compatible=False):
|
||||
"""Return an OpenMC geometry corresponding to an OpenCG geometry.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
opencg_geometry : opencg.Geometry
|
||||
OpenCG geometry
|
||||
compatible : bool
|
||||
Whether the OpenCG geometry is compatible with OpenMC's geometric
|
||||
primitives. This should be set to False if the OpenCG geometry
|
||||
uses SquarePrism surfaces. False by default.
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -1017,9 +1023,6 @@ def get_openmc_geometry(opencg_geometry):
|
|||
opencg_geometry.assign_auto_ids()
|
||||
opencg_geometry = copy.deepcopy(opencg_geometry)
|
||||
|
||||
# Update Cell bounding boxes in Geometry
|
||||
opencg_geometry.update_bounding_boxes()
|
||||
|
||||
# Clear dictionaries and auto-generated ID
|
||||
OPENMC_SURFACES.clear()
|
||||
OPENCG_SURFACES.clear()
|
||||
|
|
@ -1031,12 +1034,13 @@ def get_openmc_geometry(opencg_geometry):
|
|||
OPENCG_LATTICES.clear()
|
||||
|
||||
# Make the entire geometry "compatible" before assigning auto IDs
|
||||
universes = opencg_geometry.get_all_universes()
|
||||
for universe in universes.values():
|
||||
if not isinstance(universe, opencg.Lattice):
|
||||
make_opencg_cells_compatible(universe)
|
||||
if not compatible:
|
||||
universes = opencg_geometry.get_all_universes()
|
||||
for universe in universes.values():
|
||||
if not isinstance(universe, opencg.Lattice):
|
||||
make_opencg_cells_compatible(universe)
|
||||
|
||||
opencg_geometry.assign_auto_ids()
|
||||
opencg_geometry.assign_auto_ids()
|
||||
|
||||
opencg_root_universe = opencg_geometry.root_universe
|
||||
openmc_root_universe = get_openmc_universe(opencg_root_universe)
|
||||
|
|
|
|||
|
|
@ -67,6 +67,11 @@ class Plot(object):
|
|||
col_spec : dict
|
||||
Dictionary indicating that certain cells/materials (keys) should be
|
||||
colored with a specific RGB (values)
|
||||
level : int
|
||||
Universe depth to plot at
|
||||
meshlines : dict
|
||||
Dictionary defining type, id, linewidth and color of a regular mesh
|
||||
to be plotted on top of a plot
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -81,10 +86,12 @@ class Plot(object):
|
|||
self._color = 'cell'
|
||||
self._type = 'slice'
|
||||
self._basis = 'xy'
|
||||
self._background = [0, 0, 0]
|
||||
self._background = None
|
||||
self._mask_components = None
|
||||
self._mask_background = None
|
||||
self._col_spec = None
|
||||
self._level = None
|
||||
self._meshlines = None
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
|
|
@ -138,6 +145,14 @@ class Plot(object):
|
|||
def col_spec(self):
|
||||
return self._col_spec
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
return self._level
|
||||
|
||||
@property
|
||||
def meshlines(self):
|
||||
return self._meshlines
|
||||
|
||||
@id.setter
|
||||
def id(self, plot_id):
|
||||
if plot_id is None:
|
||||
|
|
@ -231,9 +246,9 @@ class Plot(object):
|
|||
|
||||
@mask_components.setter
|
||||
def mask_components(self, mask_components):
|
||||
cv.check_type('plot mask_components', mask_components, Iterable, Integral)
|
||||
cv.check_type('plot mask components', mask_components, Iterable, Integral)
|
||||
for component in mask_components:
|
||||
cv.check_greater_than('plot mask_components', component, 0, True)
|
||||
cv.check_greater_than('plot mask components', component, 0, True)
|
||||
self._mask_components = mask_components
|
||||
|
||||
@mask_background.setter
|
||||
|
|
@ -245,6 +260,45 @@ class Plot(object):
|
|||
cv.check_less_than('plot mask background', rgb, 256)
|
||||
self._mask_background = mask_background
|
||||
|
||||
@level.setter
|
||||
def level(self, plot_level):
|
||||
cv.check_type('plot level', plot_level, Integral)
|
||||
cv.check_greater_than('plot level', plot_level, 0, equality=True)
|
||||
self._level = plot_level
|
||||
|
||||
@meshlines.setter
|
||||
def meshlines(self, meshlines):
|
||||
cv.check_type('plot meshlines', meshlines, dict)
|
||||
if 'type' not in meshlines:
|
||||
msg = 'Unable to set on plot the meshlines "{0}" which ' \
|
||||
'does not have a "type" key'.format(meshlines)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif meshlines['type'] not in ['tally', 'entropy', 'ufs', 'cmfd']:
|
||||
msg = 'Unable to set the meshlines with ' \
|
||||
'type "{0}"'.format(meshlines['type'])
|
||||
raise ValueError(msg)
|
||||
|
||||
if 'id' in meshlines:
|
||||
cv.check_type('plot meshlines id', meshlines['id'], Integral)
|
||||
cv.check_greater_than('plot meshlines id', meshlines['id'], 0,
|
||||
equality=True)
|
||||
|
||||
if 'linewidth' in meshlines:
|
||||
cv.check_type('plot mesh linewidth', meshlines['linewidth'], Integral)
|
||||
cv.check_greater_than('plot mesh linewidth', meshlines['linewidth'],
|
||||
0, equality=True)
|
||||
|
||||
if 'color' in meshlines:
|
||||
cv.check_type('plot meshlines color', meshlines['color'], Iterable,
|
||||
Integral)
|
||||
cv.check_length('plot meshlines color', meshlines['color'], 3)
|
||||
for rgb in meshlines['color']:
|
||||
cv.check_greater_than('plot meshlines color', rgb, 0, True)
|
||||
cv.check_less_than('plot meshlines color', rgb, 256)
|
||||
|
||||
self._meshlines = meshlines
|
||||
|
||||
def __repr__(self):
|
||||
string = 'Plot\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
|
||||
|
|
@ -256,11 +310,16 @@ class Plot(object):
|
|||
string += '{0: <16}{1}{2}\n'.format('\tOrigin', '=\t', self._origin)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tPixels', '=\t', self._origin)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tColor', '=\t', self._color)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tMask', '=\t',
|
||||
string += '{0: <16}{1}{2}\n'.format('\tBackground', '=\t',
|
||||
self._background)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tMask components', '=\t',
|
||||
self._mask_components)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tMask', '=\t',
|
||||
string += '{0: <16}{1}{2}\n'.format('\tMask background', '=\t',
|
||||
self._mask_background)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tCol Spec', '=\t', self._col_spec)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tLevel', '=\t', self._level)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tMeshlines', '=\t',
|
||||
self._meshlines)
|
||||
return string
|
||||
|
||||
def colorize(self, geometry, seed=1):
|
||||
|
|
@ -382,7 +441,7 @@ class Plot(object):
|
|||
subelement = ET.SubElement(element, "pixels")
|
||||
subelement.text = ' '.join(map(str, self._pixels))
|
||||
|
||||
if self._mask_background is not None:
|
||||
if self._background is not None:
|
||||
subelement = ET.SubElement(element, "background")
|
||||
subelement.text = ' '.join(map(str, self._background))
|
||||
|
||||
|
|
@ -400,6 +459,21 @@ class Plot(object):
|
|||
subelement.set("background", ' '.join(map(
|
||||
str, self._mask_background)))
|
||||
|
||||
if self._level is not None:
|
||||
subelement = ET.SubElement(element, "level")
|
||||
subelement.text = str(self._level)
|
||||
|
||||
if self._meshlines is not None:
|
||||
subelement = ET.SubElement(element, "meshlines")
|
||||
subelement.set("meshtype", self._meshlines['type'])
|
||||
if self._meshlines['id'] is not None:
|
||||
subelement.set("id", str(self._meshlines['id']))
|
||||
if self._meshlines['linewidth'] is not None:
|
||||
subelement.set("linewidth", str(self._meshlines['linewidth']))
|
||||
if self._meshlines['color'] is not None:
|
||||
subelement.set("color", ' '.join(map(
|
||||
str, self._meshlines['color'])))
|
||||
|
||||
return element
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from collections import Iterable
|
||||
from collections import Iterable, MutableSequence, Mapping
|
||||
from numbers import Real, Integral
|
||||
import warnings
|
||||
from xml.etree import ElementTree as ET
|
||||
|
|
@ -7,10 +7,8 @@ import sys
|
|||
import numpy as np
|
||||
|
||||
from openmc.clean_xml import clean_xml_indentation
|
||||
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
|
||||
import openmc.checkvalue as cv
|
||||
from openmc import Nuclide, VolumeCalculation, Source
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
|
@ -80,8 +78,6 @@ class Settings(object):
|
|||
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
|
||||
|
|
@ -105,6 +101,14 @@ class Settings(object):
|
|||
Coordinates of the lower-left point of the Shannon entropy mesh
|
||||
entropy_upper_right : tuple or list
|
||||
Coordinates of the upper-right point of the Shannon entropy mesh
|
||||
temperature : dict
|
||||
Defines a default temperature and method for treating intermediate
|
||||
temperatures at which nuclear data doesn't exist. Accepted keys are
|
||||
'default', 'method', and 'tolerance'. The value for 'default' should be
|
||||
a float representing the default temperature in Kelvin. The value for
|
||||
'method' should be 'nearest' or 'multipole'. If the method is
|
||||
'nearest', 'tolerance' indicates a range of temperature within which
|
||||
cross sections may be used.
|
||||
trigger_active : bool
|
||||
Indicate whether tally triggers are used
|
||||
trigger_max_batches : int
|
||||
|
|
@ -132,11 +136,10 @@ class Settings(object):
|
|||
Coordinates of the lower-left point of the UFS mesh
|
||||
ufs_upper_right : tuple or list
|
||||
Coordinates of the upper-right point of the UFS mesh
|
||||
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
|
||||
volume_calculations : VolumeCalculation or iterable of VolumeCalculation
|
||||
Stochastic volume calculation specifications
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -155,12 +158,11 @@ class Settings(object):
|
|||
self._max_order = None
|
||||
|
||||
# Source subelement
|
||||
self._source = None
|
||||
self._source = cv.CheckedList(Source, 'source distributions')
|
||||
|
||||
self._confidence_intervals = None
|
||||
self._cross_sections = None
|
||||
self._multipole_library = None
|
||||
self._energy_grid = None
|
||||
self._ptables = None
|
||||
self._run_cmfd = None
|
||||
self._seed = None
|
||||
|
|
@ -197,6 +199,8 @@ class Settings(object):
|
|||
self._trace = None
|
||||
self._track = None
|
||||
|
||||
self._temperature = {}
|
||||
|
||||
# Cutoff subelement
|
||||
self._weight = None
|
||||
self._weight_avg = None
|
||||
|
|
@ -216,10 +220,11 @@ class Settings(object):
|
|||
|
||||
self._settings_file = ET.Element("settings")
|
||||
self._run_mode_subelement = None
|
||||
self._source_element = None
|
||||
self._multipole_active = None
|
||||
|
||||
self._resonance_scattering = None
|
||||
self._resonance_scattering = cv.CheckedList(
|
||||
ResonanceScattering, 'resonance scattering models')
|
||||
self._volume_calculations = cv.CheckedList(
|
||||
VolumeCalculation, 'volume calculations')
|
||||
|
||||
@property
|
||||
def run_mode(self):
|
||||
|
|
@ -269,10 +274,6 @@ class Settings(object):
|
|||
def multipole_library(self):
|
||||
return self._multipole_library
|
||||
|
||||
@property
|
||||
def energy_grid(self):
|
||||
return self._energy_grid
|
||||
|
||||
@property
|
||||
def ptables(self):
|
||||
return self._ptables
|
||||
|
|
@ -361,6 +362,10 @@ class Settings(object):
|
|||
def verbosity(self):
|
||||
return self._verbosity
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
return self._temperature
|
||||
|
||||
@property
|
||||
def trace(self):
|
||||
return self._trace
|
||||
|
|
@ -413,14 +418,14 @@ class Settings(object):
|
|||
def dd_count_interactions(self):
|
||||
return self._dd_count_interactions
|
||||
|
||||
@property
|
||||
def use_windowed_multipole(self):
|
||||
return self._multipole_active
|
||||
|
||||
@property
|
||||
def resonance_scattering(self):
|
||||
return self._resonance_scattering
|
||||
|
||||
@property
|
||||
def volume_calculations(self):
|
||||
return self._volume_calculations
|
||||
|
||||
@run_mode.setter
|
||||
def run_mode(self, run_mode):
|
||||
if run_mode not in ['eigenvalue', 'fixed source']:
|
||||
|
|
@ -431,26 +436,26 @@ class Settings(object):
|
|||
|
||||
@batches.setter
|
||||
def batches(self, batches):
|
||||
check_type('batches', batches, Integral)
|
||||
check_greater_than('batches', batches, 0)
|
||||
cv.check_type('batches', batches, Integral)
|
||||
cv.check_greater_than('batches', batches, 0)
|
||||
self._batches = batches
|
||||
|
||||
@generations_per_batch.setter
|
||||
def generations_per_batch(self, generations_per_batch):
|
||||
check_type('generations per patch', generations_per_batch, Integral)
|
||||
check_greater_than('generations per batch', generations_per_batch, 0)
|
||||
cv.check_type('generations per patch', generations_per_batch, Integral)
|
||||
cv.check_greater_than('generations per batch', generations_per_batch, 0)
|
||||
self._generations_per_batch = generations_per_batch
|
||||
|
||||
@inactive.setter
|
||||
def inactive(self, inactive):
|
||||
check_type('inactive batches', inactive, Integral)
|
||||
check_greater_than('inactive batches', inactive, 0, True)
|
||||
cv.check_type('inactive batches', inactive, Integral)
|
||||
cv.check_greater_than('inactive batches', inactive, 0, True)
|
||||
self._inactive = inactive
|
||||
|
||||
@particles.setter
|
||||
def particles(self, particles):
|
||||
check_type('particles', particles, Integral)
|
||||
check_greater_than('particles', particles, 0)
|
||||
cv.check_type('particles', particles, Integral)
|
||||
cv.check_greater_than('particles', particles, 0)
|
||||
self._particles = particles
|
||||
|
||||
@keff_trigger.setter
|
||||
|
|
@ -484,23 +489,21 @@ class Settings(object):
|
|||
|
||||
@energy_mode.setter
|
||||
def energy_mode(self, energy_mode):
|
||||
check_value('energy mode', energy_mode,
|
||||
cv.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)
|
||||
cv.check_type('maximum scattering order', max_order, Integral)
|
||||
cv.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):
|
||||
self._source = [source,]
|
||||
else:
|
||||
check_type('source distribution', source, Iterable, Source)
|
||||
self._source = source
|
||||
if not isinstance(source, MutableSequence):
|
||||
source = [source]
|
||||
self._source = cv.CheckedList(Source, 'source distributions', source)
|
||||
|
||||
@output.setter
|
||||
def output(self, output):
|
||||
|
|
@ -525,197 +528,206 @@ class Settings(object):
|
|||
|
||||
@output_path.setter
|
||||
def output_path(self, output_path):
|
||||
check_type('output path', output_path, basestring)
|
||||
cv.check_type('output path', output_path, basestring)
|
||||
self._output_path = output_path
|
||||
|
||||
@verbosity.setter
|
||||
def verbosity(self, verbosity):
|
||||
check_type('verbosity', verbosity, Integral)
|
||||
check_greater_than('verbosity', verbosity, 1, True)
|
||||
check_less_than('verbosity', verbosity, 10, True)
|
||||
cv.check_type('verbosity', verbosity, Integral)
|
||||
cv.check_greater_than('verbosity', verbosity, 1, True)
|
||||
cv.check_less_than('verbosity', verbosity, 10, True)
|
||||
self._verbosity = verbosity
|
||||
|
||||
@statepoint_batches.setter
|
||||
def statepoint_batches(self, batches):
|
||||
check_type('statepoint batches', batches, Iterable, Integral)
|
||||
cv.check_type('statepoint batches', batches, Iterable, Integral)
|
||||
for batch in batches:
|
||||
check_greater_than('statepoint batch', batch, 0)
|
||||
cv.check_greater_than('statepoint batch', batch, 0)
|
||||
self._statepoint_batches = batches
|
||||
|
||||
@statepoint_interval.setter
|
||||
def statepoint_interval(self, interval):
|
||||
check_type('statepoint interval', interval, Integral)
|
||||
cv.check_type('statepoint interval', interval, Integral)
|
||||
self._statepoint_interval = interval
|
||||
|
||||
@sourcepoint_batches.setter
|
||||
def sourcepoint_batches(self, batches):
|
||||
check_type('sourcepoint batches', batches, Iterable, Integral)
|
||||
cv.check_type('sourcepoint batches', batches, Iterable, Integral)
|
||||
for batch in batches:
|
||||
check_greater_than('sourcepoint batch', batch, 0)
|
||||
cv.check_greater_than('sourcepoint batch', batch, 0)
|
||||
self._sourcepoint_batches = batches
|
||||
|
||||
@sourcepoint_interval.setter
|
||||
def sourcepoint_interval(self, interval):
|
||||
check_type('sourcepoint interval', interval, Integral)
|
||||
cv.check_type('sourcepoint interval', interval, Integral)
|
||||
self._sourcepoint_interval = interval
|
||||
|
||||
@sourcepoint_separate.setter
|
||||
def sourcepoint_separate(self, source_separate):
|
||||
check_type('sourcepoint separate', source_separate, bool)
|
||||
cv.check_type('sourcepoint separate', source_separate, bool)
|
||||
self._sourcepoint_separate = source_separate
|
||||
|
||||
@sourcepoint_write.setter
|
||||
def sourcepoint_write(self, source_write):
|
||||
check_type('sourcepoint write', source_write, bool)
|
||||
cv.check_type('sourcepoint write', source_write, bool)
|
||||
self._sourcepoint_write = source_write
|
||||
|
||||
@sourcepoint_overwrite.setter
|
||||
def sourcepoint_overwrite(self, source_overwrite):
|
||||
check_type('sourcepoint overwrite', source_overwrite, bool)
|
||||
cv.check_type('sourcepoint overwrite', source_overwrite, bool)
|
||||
self._sourcepoint_overwrite = source_overwrite
|
||||
|
||||
@confidence_intervals.setter
|
||||
def confidence_intervals(self, confidence_intervals):
|
||||
check_type('confidence interval', confidence_intervals, bool)
|
||||
cv.check_type('confidence interval', confidence_intervals, bool)
|
||||
self._confidence_intervals = confidence_intervals
|
||||
|
||||
@cross_sections.setter
|
||||
def cross_sections(self, cross_sections):
|
||||
check_type('cross sections', cross_sections, basestring)
|
||||
cv.check_type('cross sections', cross_sections, basestring)
|
||||
self._cross_sections = cross_sections
|
||||
|
||||
@multipole_library.setter
|
||||
def multipole_library(self, multipole_library):
|
||||
check_type('cross sections', multipole_library, basestring)
|
||||
cv.check_type('cross sections', multipole_library, basestring)
|
||||
self._multipole_library = multipole_library
|
||||
|
||||
@energy_grid.setter
|
||||
def energy_grid(self, energy_grid):
|
||||
check_value('energy grid', energy_grid,
|
||||
['nuclide', 'logarithm', 'material-union'])
|
||||
self._energy_grid = energy_grid
|
||||
|
||||
@ptables.setter
|
||||
def ptables(self, ptables):
|
||||
check_type('probability tables', ptables, bool)
|
||||
cv.check_type('probability tables', ptables, bool)
|
||||
self._ptables = ptables
|
||||
|
||||
@run_cmfd.setter
|
||||
def run_cmfd(self, run_cmfd):
|
||||
check_type('run_cmfd', run_cmfd, bool)
|
||||
cv.check_type('run_cmfd', run_cmfd, bool)
|
||||
self._run_cmfd = run_cmfd
|
||||
|
||||
@seed.setter
|
||||
def seed(self, seed):
|
||||
check_type('random number generator seed', seed, Integral)
|
||||
check_greater_than('random number generator seed', seed, 0)
|
||||
cv.check_type('random number generator seed', seed, Integral)
|
||||
cv.check_greater_than('random number generator seed', seed, 0)
|
||||
self._seed = seed
|
||||
|
||||
@survival_biasing.setter
|
||||
def survival_biasing(self, survival_biasing):
|
||||
check_type('survival biasing', survival_biasing, bool)
|
||||
cv.check_type('survival biasing', survival_biasing, bool)
|
||||
self._survival_biasing = survival_biasing
|
||||
|
||||
@weight.setter
|
||||
def weight(self, weight):
|
||||
check_type('weight cutoff', weight, Real)
|
||||
check_greater_than('weight cutoff', weight, 0.0)
|
||||
cv.check_type('weight cutoff', weight, Real)
|
||||
cv.check_greater_than('weight cutoff', weight, 0.0)
|
||||
self._weight = weight
|
||||
|
||||
@weight_avg.setter
|
||||
def weight_avg(self, weight_avg):
|
||||
check_type('average survival weight', weight_avg, Real)
|
||||
check_greater_than('average survival weight', weight_avg, 0.0)
|
||||
cv.check_type('average survival weight', weight_avg, Real)
|
||||
cv.check_greater_than('average survival weight', weight_avg, 0.0)
|
||||
self._weight_avg = weight_avg
|
||||
|
||||
@entropy_dimension.setter
|
||||
def entropy_dimension(self, dimension):
|
||||
check_type('entropy mesh dimension', dimension, Iterable, Integral)
|
||||
check_length('entropy mesh dimension', dimension, 3)
|
||||
cv.check_type('entropy mesh dimension', dimension, Iterable, Integral)
|
||||
cv.check_length('entropy mesh dimension', dimension, 3)
|
||||
self._entropy_dimension = dimension
|
||||
|
||||
@entropy_lower_left.setter
|
||||
def entropy_lower_left(self, lower_left):
|
||||
check_type('entropy mesh lower left corner', lower_left,
|
||||
cv.check_type('entropy mesh lower left corner', lower_left,
|
||||
Iterable, Real)
|
||||
check_length('entropy mesh lower left corner', lower_left, 3)
|
||||
cv.check_length('entropy mesh lower left corner', lower_left, 3)
|
||||
self._entropy_lower_left = lower_left
|
||||
|
||||
@entropy_upper_right.setter
|
||||
def entropy_upper_right(self, upper_right):
|
||||
check_type('entropy mesh upper right corner', upper_right,
|
||||
cv.check_type('entropy mesh upper right corner', upper_right,
|
||||
Iterable, Real)
|
||||
check_length('entropy mesh upper right corner', upper_right, 3)
|
||||
cv.check_length('entropy mesh upper right corner', upper_right, 3)
|
||||
self._entropy_upper_right = upper_right
|
||||
|
||||
@trigger_active.setter
|
||||
def trigger_active(self, trigger_active):
|
||||
check_type('trigger active', trigger_active, bool)
|
||||
cv.check_type('trigger active', trigger_active, bool)
|
||||
self._trigger_active = trigger_active
|
||||
|
||||
@trigger_max_batches.setter
|
||||
def trigger_max_batches(self, trigger_max_batches):
|
||||
check_type('trigger maximum batches', trigger_max_batches, Integral)
|
||||
check_greater_than('trigger maximum batches', trigger_max_batches, 0)
|
||||
cv.check_type('trigger maximum batches', trigger_max_batches, Integral)
|
||||
cv.check_greater_than('trigger maximum batches', trigger_max_batches, 0)
|
||||
self._trigger_max_batches = trigger_max_batches
|
||||
|
||||
@trigger_batch_interval.setter
|
||||
def trigger_batch_interval(self, trigger_batch_interval):
|
||||
check_type('trigger batch interval', trigger_batch_interval, Integral)
|
||||
check_greater_than('trigger batch interval', trigger_batch_interval, 0)
|
||||
cv.check_type('trigger batch interval', trigger_batch_interval, Integral)
|
||||
cv.check_greater_than('trigger batch interval', trigger_batch_interval, 0)
|
||||
self._trigger_batch_interval = trigger_batch_interval
|
||||
|
||||
@no_reduce.setter
|
||||
def no_reduce(self, no_reduce):
|
||||
check_type('no reduction option', no_reduce, bool)
|
||||
cv.check_type('no reduction option', no_reduce, bool)
|
||||
self._no_reduce = no_reduce
|
||||
|
||||
@temperature.setter
|
||||
def temperature(self, temperature):
|
||||
cv.check_type('temperature settings', temperature, Mapping)
|
||||
for key, value in temperature.items():
|
||||
cv.check_value('temperature key', key,
|
||||
['default', 'method', 'tolerance'])
|
||||
if key == 'default':
|
||||
cv.check_type('default temperature', value, Real)
|
||||
elif key == 'method':
|
||||
cv.check_value('temperature method', value,
|
||||
['nearest', 'interpolation', 'multipole'])
|
||||
elif key == 'tolerance':
|
||||
cv.check_type('temperature tolerance', value, Real)
|
||||
self._temperature = temperature
|
||||
|
||||
@threads.setter
|
||||
def threads(self, threads):
|
||||
check_type('number of threads', threads, Integral)
|
||||
check_greater_than('number of threads', threads, 0)
|
||||
cv.check_type('number of threads', threads, Integral)
|
||||
cv.check_greater_than('number of threads', threads, 0)
|
||||
self._threads = threads
|
||||
|
||||
@trace.setter
|
||||
def trace(self, trace):
|
||||
check_type('trace', trace, Iterable, Integral)
|
||||
check_length('trace', trace, 3)
|
||||
check_greater_than('trace batch', trace[0], 0)
|
||||
check_greater_than('trace generation', trace[1], 0)
|
||||
check_greater_than('trace particle', trace[2], 0)
|
||||
cv.check_type('trace', trace, Iterable, Integral)
|
||||
cv.check_length('trace', trace, 3)
|
||||
cv.check_greater_than('trace batch', trace[0], 0)
|
||||
cv.check_greater_than('trace generation', trace[1], 0)
|
||||
cv.check_greater_than('trace particle', trace[2], 0)
|
||||
self._trace = trace
|
||||
|
||||
@track.setter
|
||||
def track(self, track):
|
||||
check_type('track', track, Iterable, Integral)
|
||||
cv.check_type('track', track, Iterable, Integral)
|
||||
if len(track) % 3 != 0:
|
||||
msg = 'Unable to set the track to "{0}" since its length is ' \
|
||||
'not a multiple of 3'.format(track)
|
||||
raise ValueError(msg)
|
||||
for t in zip(track[::3], track[1::3], track[2::3]):
|
||||
check_greater_than('track batch', t[0], 0)
|
||||
check_greater_than('track generation', t[0], 0)
|
||||
check_greater_than('track particle', t[0], 0)
|
||||
cv.check_greater_than('track batch', t[0], 0)
|
||||
cv.check_greater_than('track generation', t[0], 0)
|
||||
cv.check_greater_than('track particle', t[0], 0)
|
||||
self._track = track
|
||||
|
||||
@ufs_dimension.setter
|
||||
def ufs_dimension(self, dimension):
|
||||
check_type('UFS mesh dimension', dimension, Iterable, Integral)
|
||||
check_length('UFS mesh dimension', dimension, 3)
|
||||
cv.check_type('UFS mesh dimension', dimension, Iterable, Integral)
|
||||
cv.check_length('UFS mesh dimension', dimension, 3)
|
||||
for dim in dimension:
|
||||
check_greater_than('UFS mesh dimension', dim, 1, True)
|
||||
cv.check_greater_than('UFS mesh dimension', dim, 1, True)
|
||||
self._ufs_dimension = dimension
|
||||
|
||||
@ufs_lower_left.setter
|
||||
def ufs_lower_left(self, lower_left):
|
||||
check_type('UFS mesh lower left corner', lower_left, Iterable, Real)
|
||||
check_length('UFS mesh lower left corner', lower_left, 3)
|
||||
cv.check_type('UFS mesh lower left corner', lower_left, Iterable, Real)
|
||||
cv.check_length('UFS mesh lower left corner', lower_left, 3)
|
||||
self._ufs_lower_left = lower_left
|
||||
|
||||
@ufs_upper_right.setter
|
||||
def ufs_upper_right(self, upper_right):
|
||||
check_type('UFS mesh upper right corner', upper_right, Iterable, Real)
|
||||
check_length('UFS mesh upper right corner', upper_right, 3)
|
||||
cv.check_type('UFS mesh upper right corner', upper_right, Iterable, Real)
|
||||
cv.check_length('UFS mesh upper right corner', upper_right, 3)
|
||||
self._ufs_upper_right = upper_right
|
||||
|
||||
@dd_mesh_dimension.setter
|
||||
|
|
@ -724,8 +736,8 @@ class Settings(object):
|
|||
warnings.warn('This feature is not yet implemented in a release '
|
||||
'version of openmc')
|
||||
|
||||
check_type('DD mesh dimension', dimension, Iterable, Integral)
|
||||
check_length('DD mesh dimension', dimension, 3)
|
||||
cv.check_type('DD mesh dimension', dimension, Iterable, Integral)
|
||||
cv.check_length('DD mesh dimension', dimension, 3)
|
||||
|
||||
self._dd_mesh_dimension = dimension
|
||||
|
||||
|
|
@ -735,8 +747,8 @@ class Settings(object):
|
|||
warnings.warn('This feature is not yet implemented in a release '
|
||||
'version of openmc')
|
||||
|
||||
check_type('DD mesh lower left corner', lower_left, Iterable, Real)
|
||||
check_length('DD mesh lower left corner', lower_left, 3)
|
||||
cv.check_type('DD mesh lower left corner', lower_left, Iterable, Real)
|
||||
cv.check_length('DD mesh lower left corner', lower_left, 3)
|
||||
|
||||
self._dd_mesh_lower_left = lower_left
|
||||
|
||||
|
|
@ -746,8 +758,8 @@ class Settings(object):
|
|||
warnings.warn('This feature is not yet implemented in a release '
|
||||
'version of openmc')
|
||||
|
||||
check_type('DD mesh upper right corner', upper_right, Iterable, Real)
|
||||
check_length('DD mesh upper right corner', upper_right, 3)
|
||||
cv.check_type('DD mesh upper right corner', upper_right, Iterable, Real)
|
||||
cv.check_length('DD mesh upper right corner', upper_right, 3)
|
||||
|
||||
self._dd_mesh_upper_right = upper_right
|
||||
|
||||
|
|
@ -757,7 +769,7 @@ class Settings(object):
|
|||
warnings.warn('This feature is not yet implemented in a release '
|
||||
'version of openmc')
|
||||
|
||||
check_type('DD nodemap', nodemap, Iterable)
|
||||
cv.check_type('DD nodemap', nodemap, Iterable)
|
||||
|
||||
nodemap = np.array(nodemap).flatten()
|
||||
|
||||
|
|
@ -782,7 +794,7 @@ class Settings(object):
|
|||
warnings.warn('This feature is not yet implemented in a release '
|
||||
'version of openmc')
|
||||
|
||||
check_type('DD allow leakage', allow, bool)
|
||||
cv.check_type('DD allow leakage', allow, bool)
|
||||
|
||||
self._dd_allow_leakage = allow
|
||||
|
||||
|
|
@ -793,24 +805,23 @@ class Settings(object):
|
|||
warnings.warn('This feature is not yet implemented in a release '
|
||||
'version of openmc')
|
||||
|
||||
check_type('DD count interactions', interactions, bool)
|
||||
cv.check_type('DD count interactions', interactions, bool)
|
||||
|
||||
self._dd_count_interactions = interactions
|
||||
|
||||
@use_windowed_multipole.setter
|
||||
def use_windowed_multipole(self, active):
|
||||
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]
|
||||
if not isinstance(res, MutableSequence):
|
||||
res = [res]
|
||||
self._resonance_scattering = cv.CheckedList(
|
||||
ResonanceScattering, 'resonance scattering models', res)
|
||||
|
||||
@volume_calculations.setter
|
||||
def volume_calculations(self, vol_calcs):
|
||||
if not isinstance(vol_calcs, MutableSequence):
|
||||
vol_calcs = [vol_calcs]
|
||||
self._volume_calculations = cv.CheckedList(
|
||||
VolumeCalculation, 'stochastic volume calculations', vol_calcs)
|
||||
|
||||
def _create_run_mode_subelement(self):
|
||||
|
||||
|
|
@ -869,9 +880,12 @@ class Settings(object):
|
|||
element.text = str(self._max_order)
|
||||
|
||||
def _create_source_subelement(self):
|
||||
if self.source is not None:
|
||||
for source in self.source:
|
||||
self._settings_file.append(source.to_xml())
|
||||
for source in self.source:
|
||||
self._settings_file.append(source.to_xml_element())
|
||||
|
||||
def _create_volume_calcs_subelement(self):
|
||||
for calc in self.volume_calculations:
|
||||
self._settings_file.append(calc.to_xml_element())
|
||||
|
||||
def _create_output_subelement(self):
|
||||
if self._output is not None:
|
||||
|
|
@ -952,11 +966,6 @@ class Settings(object):
|
|||
element = ET.SubElement(self._settings_file, "multipole_library")
|
||||
element.text = str(self._multipole_library)
|
||||
|
||||
def _create_energy_grid_subelement(self):
|
||||
if self._energy_grid is not None:
|
||||
element = ET.SubElement(self._settings_file, "energy_grid")
|
||||
element.text = str(self._energy_grid)
|
||||
|
||||
def _create_ptables_subelement(self):
|
||||
if self._ptables is not None:
|
||||
element = ET.SubElement(self._settings_file, "ptables")
|
||||
|
|
@ -1039,6 +1048,13 @@ class Settings(object):
|
|||
element = ET.SubElement(self._settings_file, "no_reduce")
|
||||
element.text = str(self._no_reduce).lower()
|
||||
|
||||
def _create_temperature_subelements(self):
|
||||
if self.temperature:
|
||||
for key, value in self.temperature.items():
|
||||
element = ET.SubElement(self._settings_file,
|
||||
"temperature_{}".format(key))
|
||||
element.text = str(value)
|
||||
|
||||
def _create_threads_subelement(self):
|
||||
if self._threads is not None:
|
||||
element = ET.SubElement(self._settings_file, "threads")
|
||||
|
|
@ -1096,24 +1112,11 @@ class Settings(object):
|
|||
subelement = ET.SubElement(element, "count_interactions")
|
||||
subelement.text = str(self._dd_count_interactions).lower()
|
||||
|
||||
def _create_use_multipole_subelement(self):
|
||||
if self._multipole_active is not None:
|
||||
element = ET.SubElement(self._settings_file,
|
||||
"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 _create_resonance_scattering_subelement(self):
|
||||
if len(self.resonance_scattering) > 0:
|
||||
elem = ET.SubElement(self._settings_file, 'resonance_scattering')
|
||||
for r in self.resonance_scattering:
|
||||
elem.append(r.to_xml_element())
|
||||
|
||||
def export_to_xml(self):
|
||||
"""Create a settings.xml file that can be used for a simulation.
|
||||
|
|
@ -1125,7 +1128,6 @@ class Settings(object):
|
|||
self._source_subelement = None
|
||||
self._trigger_subelement = None
|
||||
self._run_mode_subelement = None
|
||||
self._source_element = None
|
||||
|
||||
self._create_run_mode_subelement()
|
||||
self._create_source_subelement()
|
||||
|
|
@ -1135,7 +1137,6 @@ class Settings(object):
|
|||
self._create_confidence_intervals()
|
||||
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()
|
||||
|
|
@ -1148,12 +1149,13 @@ class Settings(object):
|
|||
self._create_no_reduce_subelement()
|
||||
self._create_threads_subelement()
|
||||
self._create_verbosity_subelement()
|
||||
self._create_temperature_subelements()
|
||||
self._create_trace_subelement()
|
||||
self._create_track_subelement()
|
||||
self._create_ufs_subelement()
|
||||
self._create_dd_subelement()
|
||||
self._create_use_multipole_subelement()
|
||||
self._create_resonance_scattering_element()
|
||||
self._create_resonance_scattering_subelement()
|
||||
self._create_volume_calcs_subelement()
|
||||
|
||||
# Clean the indentation in the file to be user-readable
|
||||
clean_xml_indentation(self._settings_file)
|
||||
|
|
@ -1167,14 +1169,26 @@ class Settings(object):
|
|||
class ResonanceScattering(object):
|
||||
"""Specification of the elastic scattering model for resonant isotopes
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclide : openmc.Nuclide
|
||||
The nuclide affected by this resonance scattering treatment.
|
||||
method : {'ARES', 'CXS', 'DBRC', 'WCM'}
|
||||
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.
|
||||
|
||||
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
|
||||
method : {'ARES', 'CXS', 'DBRC', 'WCM'}
|
||||
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).
|
||||
|
|
@ -1187,21 +1201,20 @@ class ResonanceScattering(object):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._nuclide = None
|
||||
self._nuclide_0K = None
|
||||
self._method = None
|
||||
def __init__(self, nuclide, method='CXS', E_min=None, E_max=None):
|
||||
self._E_min = None
|
||||
self._E_max = None
|
||||
self.nuclide = nuclide
|
||||
self.method = method
|
||||
if E_min is not None:
|
||||
self.E_min = E_min
|
||||
if E_max is not None:
|
||||
self.E_max = E_max
|
||||
|
||||
@property
|
||||
def nuclide(self):
|
||||
return self._nuclide
|
||||
|
||||
@property
|
||||
def nuclide_0K(self):
|
||||
return self._nuclide_0K
|
||||
|
||||
@property
|
||||
def method(self):
|
||||
return self._method
|
||||
|
|
@ -1216,45 +1229,45 @@ class ResonanceScattering(object):
|
|||
|
||||
@nuclide.setter
|
||||
def nuclide(self, nuc):
|
||||
check_type('nuclide', nuc, Nuclide)
|
||||
cv.check_type('nuclide', nuc, Nuclide)
|
||||
self._nuclide = nuc
|
||||
|
||||
@nuclide_0K.setter
|
||||
def nuclide_0K(self, nuc):
|
||||
check_type('nuclide_0K', nuc, Nuclide)
|
||||
self._nuclide_0K = nuc
|
||||
|
||||
@method.setter
|
||||
def method(self, m):
|
||||
check_value('method', m, ('ARES', 'CXS', 'DBRC', 'WCM'))
|
||||
cv.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)
|
||||
cv.check_type('E_min', E, Real)
|
||||
cv.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)
|
||||
cv.check_type('E_max', E, Real)
|
||||
cv.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")
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the resonance scattering model
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing resonance scattering model
|
||||
|
||||
"""
|
||||
scatterer = ET.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 = '{0.name}.{0.xs}'.format(self.nuclide)
|
||||
subelement = ET.SubElement(scatterer, 'xs_label_0K')
|
||||
subelement.text = '{0.name}.{0.xs}'.format(self.nuclide_0K)
|
||||
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)
|
||||
return scatterer
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ class Source(object):
|
|||
cv.check_greater_than('source strength', strength, 0.0, True)
|
||||
self._strength = strength
|
||||
|
||||
def to_xml(self):
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the source
|
||||
|
||||
Returns
|
||||
|
|
@ -117,9 +117,9 @@ class Source(object):
|
|||
if self.file is not None:
|
||||
element.set("file", self.file)
|
||||
if self.space is not None:
|
||||
element.append(self.space.to_xml())
|
||||
element.append(self.space.to_xml_element())
|
||||
if self.angle is not None:
|
||||
element.append(self.angle.to_xml())
|
||||
element.append(self.angle.to_xml_element())
|
||||
if self.energy is not None:
|
||||
element.append(self.energy.to_xml('energy'))
|
||||
element.append(self.energy.to_xml_element('energy'))
|
||||
return element
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import sys
|
|||
import re
|
||||
import os
|
||||
import warnings
|
||||
import glob
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
|
@ -22,8 +23,9 @@ class StatePoint(object):
|
|||
filename : str
|
||||
Path to file to load
|
||||
autolink : bool, optional
|
||||
Whether to automatically link in metadata from a summary.h5
|
||||
file. Defaults to True.
|
||||
Whether to automatically link in metadata from a summary.h5 file and
|
||||
stochastic volume calculation results from volume_*.h5 files. Defaults
|
||||
to True.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
@ -143,6 +145,12 @@ class StatePoint(object):
|
|||
su = openmc.Summary(path_summary)
|
||||
self.link_with_summary(su)
|
||||
|
||||
path_volume = os.path.join(os.path.dirname(filename), 'volume_*.h5')
|
||||
for path_i in glob.glob(path_volume):
|
||||
if re.search(r'volume_\d+\.h5', path_i):
|
||||
vol = openmc.VolumeCalculation.from_hdf5(path_i)
|
||||
self.add_volume_information(vol)
|
||||
|
||||
def close(self):
|
||||
self._f.close()
|
||||
|
||||
|
|
@ -481,6 +489,18 @@ class StatePoint(object):
|
|||
for tally_id in self.tallies:
|
||||
self.tallies[tally_id].sparse = self.sparse
|
||||
|
||||
def add_volume_information(self, volume_calc):
|
||||
"""Add volume information to the geometry within the file
|
||||
|
||||
Parameters
|
||||
----------
|
||||
volume_calc : openmc.VolumeCalculation
|
||||
Results from a stochastic volume calculation
|
||||
|
||||
"""
|
||||
if self.summary is not None:
|
||||
self.summary.add_volume_information(volume_calc)
|
||||
|
||||
def get_tally(self, scores=[], filters=[], nuclides=[],
|
||||
name=None, id=None, estimator=None, exact_filters=False,
|
||||
exact_nuclides=False, exact_scores=False):
|
||||
|
|
@ -662,7 +682,7 @@ class StatePoint(object):
|
|||
if isinstance(tally_filter, openmc.SurfaceFilter):
|
||||
surface_ids = []
|
||||
for bin in tally_filter.bins:
|
||||
surface_ids.append(summary.surfaces[bin].id)
|
||||
surface_ids.append(bin)
|
||||
tally_filter.bins = surface_ids
|
||||
|
||||
if isinstance(tally_filter, (openmc.CellFilter,
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class UnitSphere(object):
|
|||
self._reference_uvw = uvw/np.linalg.norm(uvw)
|
||||
|
||||
@abstractmethod
|
||||
def to_xml(self):
|
||||
def to_xml_element(self):
|
||||
return ''
|
||||
|
||||
|
||||
|
|
@ -109,13 +109,21 @@ class PolarAzimuthal(UnitSphere):
|
|||
cv.check_type('azimuthal angle', phi, Univariate)
|
||||
self._phi = phi
|
||||
|
||||
def to_xml(self):
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the angular distribution
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing angular distribution data
|
||||
|
||||
"""
|
||||
element = ET.Element('angle')
|
||||
element.set("type", "mu-phi")
|
||||
if self.reference_uvw is not None:
|
||||
element.set("reference_uvw", ' '.join(map(str, self.reference_uvw)))
|
||||
element.append(self.mu.to_xml('mu'))
|
||||
element.append(self.phi.to_xml('phi'))
|
||||
element.append(self.mu.to_xml_element('mu'))
|
||||
element.append(self.phi.to_xml_element('phi'))
|
||||
return element
|
||||
|
||||
|
||||
|
|
@ -127,7 +135,15 @@ class Isotropic(UnitSphere):
|
|||
def __init__(self):
|
||||
super(Isotropic, self).__init__()
|
||||
|
||||
def to_xml(self):
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the isotropic distribution
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing isotropic distribution data
|
||||
|
||||
"""
|
||||
element = ET.Element('angle')
|
||||
element.set("type", "isotropic")
|
||||
return element
|
||||
|
|
@ -152,7 +168,15 @@ class Monodirectional(UnitSphere):
|
|||
def __init__(self, reference_uvw=[1., 0., 0.]):
|
||||
super(Monodirectional, self).__init__(reference_uvw)
|
||||
|
||||
def to_xml(self):
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the monodirectional distribution
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing monodirectional distribution data
|
||||
|
||||
"""
|
||||
element = ET.Element('angle')
|
||||
element.set("type", "monodirectional")
|
||||
if self.reference_uvw is not None:
|
||||
|
|
@ -174,7 +198,7 @@ class Spatial(object):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
def to_xml(self):
|
||||
def to_xml_element(self):
|
||||
return ''
|
||||
|
||||
|
||||
|
|
@ -238,12 +262,20 @@ class CartesianIndependent(Spatial):
|
|||
cv.check_type('z coordinate', z, Univariate)
|
||||
self._z = z
|
||||
|
||||
def to_xml(self):
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the spatial distribution
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing spatial distribution data
|
||||
|
||||
"""
|
||||
element = ET.Element('space')
|
||||
element.set('type', 'cartesian')
|
||||
element.append(self.x.to_xml('x'))
|
||||
element.append(self.y.to_xml('y'))
|
||||
element.append(self.z.to_xml('z'))
|
||||
element.append(self.x.to_xml_element('x'))
|
||||
element.append(self.y.to_xml_element('y'))
|
||||
element.append(self.z.to_xml_element('z'))
|
||||
return element
|
||||
|
||||
|
||||
|
|
@ -308,7 +340,15 @@ class Box(Spatial):
|
|||
cv.check_type('only fissionable', only_fissionable, bool)
|
||||
self._only_fissionable = only_fissionable
|
||||
|
||||
def to_xml(self):
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the box distribution
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing box distribution data
|
||||
|
||||
"""
|
||||
element = ET.Element('space')
|
||||
if self.only_fissionable:
|
||||
element.set("type", "fission")
|
||||
|
|
@ -352,7 +392,15 @@ class Point(Spatial):
|
|||
cv.check_length('coordinate', xyz, 3)
|
||||
self._xyz = xyz
|
||||
|
||||
def to_xml(self):
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the point distribution
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing point distribution location
|
||||
|
||||
"""
|
||||
element = ET.Element('space')
|
||||
element.set("type", "point")
|
||||
params = ET.SubElement(element, "parameters")
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from xml.etree import ElementTree as ET
|
|||
import numpy as np
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
|
@ -15,7 +16,7 @@ _INTERPOLATION_SCHEMES = ['histogram', 'linear-linear', 'linear-log',
|
|||
'log-linear', 'log-log']
|
||||
|
||||
|
||||
class Univariate(object):
|
||||
class Univariate(EqualityMixin):
|
||||
"""Probability distribution of a single random variable.
|
||||
|
||||
The Univariate class is an abstract class that can be derived to implement a
|
||||
|
|
@ -29,7 +30,7 @@ class Univariate(object):
|
|||
pass
|
||||
|
||||
@abstractmethod
|
||||
def to_xml(self, element_name):
|
||||
def to_xml_element(self, element_name):
|
||||
return ''
|
||||
|
||||
@abstractmethod
|
||||
|
|
@ -92,7 +93,20 @@ class Discrete(Univariate):
|
|||
cv.check_greater_than('discrete probability', pk, 0.0, True)
|
||||
self._p = p
|
||||
|
||||
def to_xml(self, element_name):
|
||||
def to_xml_element(self, element_name):
|
||||
"""Return XML representation of the discrete distribution
|
||||
|
||||
Parameters
|
||||
----------
|
||||
element_name : str
|
||||
XML element name
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing discrete distribution data
|
||||
|
||||
"""
|
||||
element = ET.Element(element_name)
|
||||
element.set("type", "discrete")
|
||||
|
||||
|
|
@ -153,7 +167,20 @@ class Uniform(Univariate):
|
|||
t.c = [0., 1.]
|
||||
return t
|
||||
|
||||
def to_xml(self, element_name):
|
||||
def to_xml_element(self, element_name):
|
||||
"""Return XML representation of the uniform distribution
|
||||
|
||||
Parameters
|
||||
----------
|
||||
element_name : str
|
||||
XML element name
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing uniform distribution data
|
||||
|
||||
"""
|
||||
element = ET.Element(element_name)
|
||||
element.set("type", "uniform")
|
||||
element.set("parameters", '{} {}'.format(self.a, self.b))
|
||||
|
|
@ -196,7 +223,20 @@ class Maxwell(Univariate):
|
|||
cv.check_greater_than('Maxwell temperature', theta, 0.0)
|
||||
self._theta = theta
|
||||
|
||||
def to_xml(self, element_name):
|
||||
def to_xml_element(self, element_name):
|
||||
"""Return XML representation of the Maxwellian distribution
|
||||
|
||||
Parameters
|
||||
----------
|
||||
element_name : str
|
||||
XML element name
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing Maxwellian distribution data
|
||||
|
||||
"""
|
||||
element = ET.Element(element_name)
|
||||
element.set("type", "maxwell")
|
||||
element.set("parameters", str(self.theta))
|
||||
|
|
@ -254,7 +294,20 @@ class Watt(Univariate):
|
|||
cv.check_greater_than('Watt b', b, 0.0)
|
||||
self._b = b
|
||||
|
||||
def to_xml(self, element_name):
|
||||
def to_xml_element(self, element_name):
|
||||
"""Return XML representation of the Watt distribution
|
||||
|
||||
Parameters
|
||||
----------
|
||||
element_name : str
|
||||
XML element name
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing Watt distribution data
|
||||
|
||||
"""
|
||||
element = ET.Element(element_name)
|
||||
element.set("type", "watt")
|
||||
element.set("parameters", '{} {}'.format(self.a, self.b))
|
||||
|
|
@ -333,7 +386,20 @@ class Tabular(Univariate):
|
|||
cv.check_value('interpolation', interpolation, _INTERPOLATION_SCHEMES)
|
||||
self._interpolation = interpolation
|
||||
|
||||
def to_xml(self, element_name):
|
||||
def to_xml_element(self, element_name):
|
||||
"""Return XML representation of the tabular distribution
|
||||
|
||||
Parameters
|
||||
----------
|
||||
element_name : str
|
||||
XML element name
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing tabular distribution data
|
||||
|
||||
"""
|
||||
element = ET.Element(element_name)
|
||||
element.set("type", "tabular")
|
||||
element.set("interpolation", self.interpolation)
|
||||
|
|
@ -386,7 +452,7 @@ class Legendre(Univariate):
|
|||
self._legendre_polynomial = np.polynomial.legendre.Legendre(
|
||||
coefficients)
|
||||
|
||||
def to_xml(self, element_name):
|
||||
def to_xml_element(self, element_name):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
|
@ -440,5 +506,5 @@ class Mixture(Univariate):
|
|||
Iterable, Univariate)
|
||||
self._distribution = distribution
|
||||
|
||||
def to_xml(self, element_name):
|
||||
def to_xml_element(self, element_name):
|
||||
raise NotImplementedError
|
||||
|
|
|
|||
|
|
@ -83,11 +83,10 @@ class Summary(object):
|
|||
n_nuclides = self._f['nuclides/n_nuclides_total'].value
|
||||
names = self._f['nuclides/names'].value
|
||||
awrs = self._f['nuclides/awrs'].value
|
||||
zaids = self._f['nuclides/zaids'].value
|
||||
for n in range(n_nuclides):
|
||||
name = names[n].decode()
|
||||
name = name[:name.find('.')]
|
||||
self.nuclides[name] = (zaids[n], awrs[n])
|
||||
self.nuclides[name] = awrs[n]
|
||||
|
||||
def _read_geometry(self):
|
||||
# Read in and initialize the Materials and Geometry
|
||||
|
|
@ -124,22 +123,21 @@ class Summary(object):
|
|||
if 'sab_names' in self._f['materials'][key]:
|
||||
sab_tables = self._f['materials'][key]['sab_names'].value
|
||||
for sab_table in sab_tables:
|
||||
name, xs = sab_table.decode().split('.')
|
||||
material.add_s_alpha_beta(name, xs)
|
||||
name = sab_table.decode()
|
||||
material.add_s_alpha_beta(name)
|
||||
|
||||
# Set the Material's density to atom/b-cm as used by OpenMC
|
||||
material.set_density(density=density, units='atom/b-cm')
|
||||
|
||||
# Add all nuclides to the Material
|
||||
for fullname, density in zip(nuclides, nuc_densities):
|
||||
fullname = fullname.decode().strip()
|
||||
name, xs = fullname.split('.')
|
||||
name = fullname.decode().strip()
|
||||
|
||||
if 'nat' in name:
|
||||
material.add_element(openmc.Element(name=name, xs=xs),
|
||||
material.add_element(openmc.Element(name=name),
|
||||
percent=density, percent_type='ao')
|
||||
else:
|
||||
material.add_nuclide(openmc.Nuclide(name=name, xs=xs),
|
||||
material.add_nuclide(openmc.Nuclide(name=name),
|
||||
percent=density, percent_type='ao')
|
||||
|
||||
# Add the Material to the global dictionary of all Materials
|
||||
|
|
@ -576,6 +574,17 @@ class Summary(object):
|
|||
# Add Tally to the global dictionary of all Tallies
|
||||
self.tallies[tally_id] = tally
|
||||
|
||||
def add_volume_information(self, volume_calc):
|
||||
"""Add volume information to the geometry within the summary file
|
||||
|
||||
Parameters
|
||||
----------
|
||||
volume_calc : openmc.VolumeCalculation
|
||||
Results from a stochastic volume calculation
|
||||
|
||||
"""
|
||||
self.openmc_geometry.add_volume_information(volume_calc)
|
||||
|
||||
def get_material_by_id(self, material_id):
|
||||
"""Return a Material object given the material id
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ _SCORE_CLASSES = (basestring, CrossScore, AggregateScore)
|
|||
_NUCLIDE_CLASSES = (basestring, Nuclide, CrossNuclide, AggregateNuclide)
|
||||
_FILTER_CLASSES = (Filter, CrossFilter, AggregateFilter)
|
||||
|
||||
# Valid types of estimators
|
||||
ESTIMATOR_TYPES = ['tracklength', 'collision', 'analog']
|
||||
|
||||
|
||||
def reset_auto_tally_id():
|
||||
"""Reset counter for auto-generated tally IDs."""
|
||||
|
|
@ -387,8 +390,7 @@ class Tally(object):
|
|||
|
||||
@estimator.setter
|
||||
def estimator(self, estimator):
|
||||
cv.check_value('estimator', estimator,
|
||||
['analog', 'tracklength', 'collision'])
|
||||
cv.check_value('estimator', estimator, ESTIMATOR_TYPES)
|
||||
self._estimator = estimator
|
||||
|
||||
@triggers.setter
|
||||
|
|
@ -795,6 +797,9 @@ class Tally(object):
|
|||
else:
|
||||
no_scores_match = False
|
||||
|
||||
if score == 'current' and score not in self.scores:
|
||||
return False
|
||||
|
||||
# Nuclides cannot be specified on 'flux' scores
|
||||
if 'flux' in self.scores or 'flux' in other.scores:
|
||||
if self.nuclides != other.nuclides:
|
||||
|
|
@ -2189,8 +2194,8 @@ class Tally(object):
|
|||
|
||||
"""
|
||||
|
||||
cv.check_type('filter1', filter1, (Filter, CrossFilter, AggregateFilter))
|
||||
cv.check_type('filter2', filter2, (Filter, CrossFilter, AggregateFilter))
|
||||
cv.check_type('filter1', filter1, _FILTER_CLASSES)
|
||||
cv.check_type('filter2', filter2, _FILTER_CLASSES)
|
||||
|
||||
# Check that the filters exist in the tally and are not the same
|
||||
if filter1 == filter2:
|
||||
|
|
@ -2272,8 +2277,8 @@ class Tally(object):
|
|||
'since it does not contain any results.'.format(self.id)
|
||||
raise ValueError(msg)
|
||||
|
||||
cv.check_type('nuclide1', nuclide1, Nuclide)
|
||||
cv.check_type('nuclide2', nuclide2, Nuclide)
|
||||
cv.check_type('nuclide1', nuclide1, _NUCLIDE_CLASSES)
|
||||
cv.check_type('nuclide2', nuclide2, _NUCLIDE_CLASSES)
|
||||
|
||||
# Check that the nuclides exist in the tally and are not the same
|
||||
if nuclide1 == nuclide2:
|
||||
|
|
@ -3310,7 +3315,7 @@ class Tally(object):
|
|||
|
||||
"""
|
||||
|
||||
cv.check_type('new_filter', new_filter, Filter)
|
||||
cv.check_type('new_filter', new_filter, _FILTER_CLASSES)
|
||||
|
||||
if new_filter in self.filters:
|
||||
msg = 'Unable to diagonalize Tally ID="{0}" which already ' \
|
||||
|
|
|
|||
|
|
@ -349,7 +349,27 @@ class Universe(object):
|
|||
# Return the offset computed at all nested Universe levels
|
||||
return offset
|
||||
|
||||
def get_all_nuclides(self):
|
||||
def get_nuclides(self):
|
||||
"""Returns all nuclides in the universe
|
||||
|
||||
Returns
|
||||
-------
|
||||
nuclides : list of str
|
||||
List of nuclide names
|
||||
|
||||
"""
|
||||
|
||||
nuclides = []
|
||||
|
||||
# Append all Nuclides in each Cell in the Universe to the dictionary
|
||||
for cell in self.cells.values():
|
||||
for nuclide in cell.get_nuclides():
|
||||
if nuclide not in nuclides:
|
||||
nuclides.append(nuclide)
|
||||
|
||||
return nuclides
|
||||
|
||||
def get_nuclide_densities(self):
|
||||
"""Return all nuclides contained in the universe
|
||||
|
||||
Returns
|
||||
|
|
@ -360,13 +380,8 @@ class Universe(object):
|
|||
|
||||
"""
|
||||
|
||||
nuclides = OrderedDict()
|
||||
|
||||
# Append all Nuclides in each Cell in the Universe to the dictionary
|
||||
for cell in self._cells.values():
|
||||
nuclides.update(cell.get_all_nuclides())
|
||||
|
||||
return nuclides
|
||||
raise NotImplementedError('Determining average nuclide densities over '
|
||||
'an entire universe not yet supported.')
|
||||
|
||||
def get_all_cells(self):
|
||||
"""Return all cells that are contained within the universe
|
||||
|
|
|
|||
248
openmc/volume.py
Normal file
248
openmc/volume.py
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
from collections import Iterable, Mapping
|
||||
from numbers import Real, Integral
|
||||
from xml.etree import ElementTree as ET
|
||||
from warnings import warn
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import openmc
|
||||
import openmc.checkvalue as cv
|
||||
|
||||
|
||||
class VolumeCalculation(object):
|
||||
"""Stochastic volume calculation specifications and results.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
domains : Iterable of openmc.Cell, openmc.Material, or openmc.Universe
|
||||
Domains to find volumes of
|
||||
samples : int
|
||||
Number of samples used to generate volume estimates
|
||||
lower_left : Iterable of float
|
||||
Lower-left coordinates of bounding box used to sample points. If this
|
||||
argument is not supplied, an attempt is made to automatically determine
|
||||
a bounding box.
|
||||
upper_right : Iterable of float
|
||||
Upper-right coordinates of bounding box used to sample points. If this
|
||||
argument is not supplied, an attempt is made to automatically determine
|
||||
a bounding box.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
ids : Iterable of int
|
||||
IDs of domains to find volumes of
|
||||
domain_type : {'cell', 'material', 'universe'}
|
||||
Type of each domain
|
||||
samples : int
|
||||
Number of samples used to generate volume estimates
|
||||
lower_left : Iterable of float
|
||||
Lower-left coordinates of bounding box used to sample points
|
||||
upper_right : Iterable of float
|
||||
Upper-right coordinates of bounding box used to sample points
|
||||
results : dict
|
||||
Dictionary whose keys are unique IDs of domains and values are
|
||||
dictionaries with calculated volumes and total number of atoms for each
|
||||
nuclide present in the domain.
|
||||
volumes : dict
|
||||
Dictionary whose keys are unique IDs of domains and values are the
|
||||
estimated volumes
|
||||
atoms_dataframe : pandas.DataFrame
|
||||
DataFrame showing the estimated number of atoms for each nuclide present
|
||||
in each domain specified.
|
||||
|
||||
"""
|
||||
def __init__(self, domains, samples, lower_left=None,
|
||||
upper_right=None):
|
||||
self._results = None
|
||||
|
||||
cv.check_type('domains', domains, Iterable,
|
||||
(openmc.Cell, openmc.Material, openmc.Universe))
|
||||
if isinstance(domains[0], openmc.Cell):
|
||||
self._domain_type = 'cell'
|
||||
elif isinstance(domains[0], openmc.Material):
|
||||
self._domain_type = 'material'
|
||||
elif isinstance(domains[0], openmc.Universe):
|
||||
self._domain_type = 'universe'
|
||||
self.ids = [d.id for d in domains]
|
||||
|
||||
self.samples = samples
|
||||
|
||||
if lower_left is not None:
|
||||
if upper_right is None:
|
||||
raise ValueError('Both lower-left and upper-right coordinates '
|
||||
'should be specified')
|
||||
|
||||
# For cell domains, try to compute bounding box and make sure
|
||||
# user-specified one is valid
|
||||
if self.domain_type == 'cell':
|
||||
for c in domains:
|
||||
if c.region is None:
|
||||
continue
|
||||
ll, ur = c.region.bounding_box
|
||||
if np.any(np.isinf(ll)) or np.any(np.isinf(ur)):
|
||||
continue
|
||||
if (np.any(np.asarray(lower_left) > ll) or
|
||||
np.any(np.asarray(upper_right) < ur)):
|
||||
warn("Specified bounding box is smaller than computed "
|
||||
"bounding box for cell {}. Volume calculation may "
|
||||
"be incorrect!".format(c.id))
|
||||
|
||||
self.lower_left = lower_left
|
||||
self.upper_right = upper_right
|
||||
else:
|
||||
if self.domain_type == 'cell':
|
||||
ll, ur = openmc.Union(*[c.region for c in domains]).bounding_box
|
||||
if np.any(np.isinf(ll)) or np.any(np.isinf(ur)):
|
||||
raise ValueError('Could not automatically determine bounding '
|
||||
'box for stochastic volume calculation.')
|
||||
else:
|
||||
self.lower_left = ll
|
||||
self.upper_right = ur
|
||||
else:
|
||||
raise ValueError('Could not automatically determine bounding box '
|
||||
'for stochastic volume calculation.')
|
||||
|
||||
@property
|
||||
def ids(self):
|
||||
return self._ids
|
||||
|
||||
@property
|
||||
def samples(self):
|
||||
return self._samples
|
||||
|
||||
@property
|
||||
def lower_left(self):
|
||||
return self._lower_left
|
||||
|
||||
@property
|
||||
def upper_right(self):
|
||||
return self._upper_right
|
||||
|
||||
@property
|
||||
def results(self):
|
||||
return self._results
|
||||
|
||||
@property
|
||||
def domain_type(self):
|
||||
return self._domain_type
|
||||
|
||||
@property
|
||||
def volumes(self):
|
||||
return {uid: results['volume'] for uid, results in self.results.items()}
|
||||
|
||||
@property
|
||||
def atoms_dataframe(self):
|
||||
items = []
|
||||
columns = [self.domain_type.capitalize(), 'Nuclide', 'Atoms',
|
||||
'Uncertainty']
|
||||
for uid, results in self.results.items():
|
||||
for name, atoms in results['atoms']:
|
||||
items.append((uid, name, atoms[0], atoms[1]))
|
||||
|
||||
return pd.DataFrame.from_records(items, columns=columns)
|
||||
|
||||
@ids.setter
|
||||
def ids(self, ids):
|
||||
cv.check_type('domain IDs', ids, Iterable, Real)
|
||||
self._ids = ids
|
||||
|
||||
@samples.setter
|
||||
def samples(self, samples):
|
||||
cv.check_type('number of samples', samples, Integral)
|
||||
cv.check_greater_than('number of samples', samples, 0)
|
||||
self._samples = samples
|
||||
|
||||
@lower_left.setter
|
||||
def lower_left(self, lower_left):
|
||||
name = 'lower-left bounding box coordinates',
|
||||
cv.check_type(name, lower_left, Iterable, Real)
|
||||
cv.check_length(name, lower_left, 3)
|
||||
self._lower_left = lower_left
|
||||
|
||||
@upper_right.setter
|
||||
def upper_right(self, upper_right):
|
||||
name = 'upper-right bounding box coordinates'
|
||||
cv.check_type(name, upper_right, Iterable, Real)
|
||||
cv.check_length(name, upper_right, 3)
|
||||
self._upper_right = upper_right
|
||||
|
||||
@results.setter
|
||||
def results(self, results):
|
||||
cv.check_type('results', results, Mapping)
|
||||
self._results = results
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, filename):
|
||||
"""Load stochastic volume calculation results from HDF5 file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
Path to volume.h5 file
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.VolumeCalculation
|
||||
Results of the stochastic volume calculation
|
||||
|
||||
"""
|
||||
import h5py
|
||||
|
||||
with h5py.File(filename, 'r') as f:
|
||||
domain_type = f.attrs['domain_type'].decode()
|
||||
samples = f.attrs['samples']
|
||||
lower_left = f.attrs['lower_left']
|
||||
upper_right = f.attrs['upper_right']
|
||||
|
||||
results = {}
|
||||
ids = []
|
||||
for obj_name in f:
|
||||
if obj_name.startswith('domain_'):
|
||||
domain_id = int(obj_name[7:])
|
||||
ids.append(domain_id)
|
||||
group = f[obj_name]
|
||||
volume = tuple(group['volume'].value)
|
||||
nucnames = group['nuclides'].value
|
||||
atoms = group['atoms'].value
|
||||
|
||||
atom_list = []
|
||||
for name_i, atoms_i in zip(nucnames, atoms):
|
||||
atom_list.append((name_i.decode(), tuple(atoms_i)))
|
||||
results[domain_id] = {'volume': volume, 'atoms': atom_list}
|
||||
|
||||
# Instantiate some throw-away domains that are used by the constructor
|
||||
# to assign IDs
|
||||
if domain_type == 'cell':
|
||||
domains = [openmc.Cell(uid) for uid in ids]
|
||||
elif domain_type == 'material':
|
||||
domains = [openmc.Material(uid) for uid in ids]
|
||||
elif domain_type == 'universe':
|
||||
domains = [openmc.Universe(uid) for uid in ids]
|
||||
|
||||
# Instantiate the class and assign results
|
||||
vol = cls(domains, samples, lower_left, upper_right)
|
||||
vol.results = results
|
||||
return vol
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the volume calculation
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing volume calculation data
|
||||
|
||||
"""
|
||||
element = ET.Element("volume_calc")
|
||||
dt_elem = ET.SubElement(element, "domain_type")
|
||||
dt_elem.text = self.domain_type
|
||||
id_elem = ET.SubElement(element, "domain_ids")
|
||||
id_elem.text = ' '.join(str(uid) for uid in self.ids)
|
||||
samples_elem = ET.SubElement(element, "samples")
|
||||
samples_elem.text = str(self.samples)
|
||||
ll_elem = ET.SubElement(element, "lower_left")
|
||||
ll_elem.text = ' '.join(str(x) for x in self.lower_left)
|
||||
ur_elem = ET.SubElement(element, "upper_right")
|
||||
ur_elem.text = ' '.join(str(x) for x in self.upper_right)
|
||||
return element
|
||||
|
|
@ -7,7 +7,7 @@ OpenMC Monte Carlo Particle Transport Code
|
|||
|
||||
The OpenMC project aims to provide a fully-featured Monte Carlo particle
|
||||
transport code based on modern methods. It is a constructive solid geometry,
|
||||
continuous-energy transport code that uses ACE format cross sections. The
|
||||
continuous-energy transport code that uses HDF5 format cross sections. The
|
||||
project started under the Computational Reactor Physics Group at MIT.
|
||||
|
||||
Complete documentation on the usage of OpenMC is hosted on Read the Docs at
|
||||
|
|
|
|||
|
|
@ -25,6 +25,13 @@ follows the NNDC data convention (1000*Z + A + 300 + 100*m), or the MCNP data
|
|||
convention (essentially the same as NNDC, except that the first metastable state
|
||||
of Am242 is 95242 and the ground state is 95642).
|
||||
|
||||
The optional --fission_energy_release argument will accept an HDF5 file
|
||||
containing a library of fission energy release (ENDF MF=1 MT=458) data. A
|
||||
library built from ENDF/B-VII.1 data is released with OpenMC and can be found at
|
||||
openmc/data/fission_Q_data_endb71.h5. This data is necessary for
|
||||
'fission-q-prompt' and 'fission-q-recoverable' tallies, but is not needed
|
||||
otherwise.
|
||||
|
||||
"""
|
||||
|
||||
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter,
|
||||
|
|
@ -47,6 +54,8 @@ parser.add_argument('--xsdir', help='MCNP xsdir file that lists '
|
|||
'ACE libraries')
|
||||
parser.add_argument('--xsdata', help='Serpent xsdata file that lists '
|
||||
'ACE libraries')
|
||||
parser.add_argument('--fission_energy_release', help='HDF5 file containing '
|
||||
'fission energy release data')
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.isdir(args.destination):
|
||||
|
|
@ -106,6 +115,7 @@ elif args.xsdata is not None:
|
|||
else:
|
||||
ace_libraries = args.libraries
|
||||
|
||||
nuclides = {}
|
||||
library = openmc.data.DataLibrary()
|
||||
|
||||
for filename in ace_libraries:
|
||||
|
|
@ -116,36 +126,87 @@ for filename in ace_libraries:
|
|||
|
||||
lib = openmc.data.ace.Library(filename)
|
||||
for table in lib.tables:
|
||||
if table.name.endswith('c'):
|
||||
name, xs = table.name.split('.')
|
||||
if xs.endswith('c'):
|
||||
# Continuous-energy neutron data
|
||||
try:
|
||||
neutron = openmc.data.IncidentNeutron.from_ace(
|
||||
table, args.metastable)
|
||||
except Exception as e:
|
||||
print('Failed to convert {}: {}'.format(table.name, e))
|
||||
continue
|
||||
print('Converting {} (ACE) to {} (HDF5)'.format(table.name, neutron.name))
|
||||
if name not in nuclides:
|
||||
try:
|
||||
neutron = openmc.data.IncidentNeutron.from_ace(
|
||||
table, args.metastable)
|
||||
except Exception as e:
|
||||
print('Failed to convert {}: {}'.format(table.name, e))
|
||||
continue
|
||||
|
||||
# Determine filename
|
||||
outfile = os.path.join(args.destination,
|
||||
neutron.name.replace('.', '_') + '.h5')
|
||||
neutron.export_to_hdf5(outfile, 'w')
|
||||
# Fission energy release data, if available
|
||||
if args.fission_energy_release is not None:
|
||||
fer = openmc.data.FissionEnergyRelease.from_compact_hdf5(
|
||||
args.fission_energy_release, neutron)
|
||||
if fer is not None:
|
||||
neutron.fission_energy = fer
|
||||
|
||||
# Register with library
|
||||
library.register_file(outfile)
|
||||
print('Converting {} (ACE) to {} (HDF5)'.format(table.name,
|
||||
neutron.name))
|
||||
|
||||
elif table.name.endswith('t'):
|
||||
# Determine filename
|
||||
outfile = os.path.join(args.destination,
|
||||
neutron.name.replace('.', '_') + '.h5')
|
||||
neutron.export_to_hdf5(outfile, 'w')
|
||||
|
||||
# Register with library
|
||||
library.register_file(outfile)
|
||||
|
||||
# Add nuclide to list
|
||||
nuclides[name] = outfile
|
||||
else:
|
||||
# Then we only need to append the data
|
||||
try:
|
||||
neutron = \
|
||||
openmc.data.IncidentNeutron.from_hdf5(nuclides[name])
|
||||
print('Converting {} (ACE) to {} (HDF5)'.format(table.name,
|
||||
neutron.name))
|
||||
neutron.add_temperature_from_ace(table, args.metastable)
|
||||
neutron.export_to_hdf5(nuclides[name] + '_1', 'w')
|
||||
os.rename(nuclides[name] + '_1', nuclides[name])
|
||||
except Exception as e:
|
||||
print('Failed to convert {}: {}'.format(table.name, e))
|
||||
continue
|
||||
|
||||
elif xs.endswith('t'):
|
||||
# Adjust name to be the new thermal scattering name
|
||||
name = openmc.data.get_thermal_name(name)
|
||||
# Thermal scattering data
|
||||
thermal = openmc.data.ThermalScattering.from_ace(table)
|
||||
print('Converting {} (ACE) to {} (HDF5)'.format(table.name, thermal.name))
|
||||
if name not in nuclides:
|
||||
try:
|
||||
thermal = openmc.data.ThermalScattering.from_ace(table)
|
||||
except Exception as e:
|
||||
print('Failed to convert {}: {}'.format(table.name, e))
|
||||
continue
|
||||
print('Converting {} (ACE) to {} (HDF5)'.format(table.name,
|
||||
thermal.name))
|
||||
|
||||
# Determine filename
|
||||
outfile = os.path.join(args.destination,
|
||||
thermal.name.replace('.', '_') + '.h5')
|
||||
thermal.export_to_hdf5(outfile, 'w')
|
||||
# Determine filename
|
||||
outfile = os.path.join(args.destination,
|
||||
thermal.name.replace('.', '_') + '.h5')
|
||||
thermal.export_to_hdf5(outfile, 'w')
|
||||
|
||||
# Register with library
|
||||
library.register_file(outfile, 'thermal')
|
||||
# Register with library
|
||||
library.register_file(outfile, 'thermal')
|
||||
|
||||
# Add data to list
|
||||
nuclides[name] = outfile
|
||||
|
||||
else:
|
||||
# Then we only need to append the data
|
||||
try:
|
||||
thermal = openmc.data.ThermalScattering.from_hdf5(nuclides[name])
|
||||
print('Converting {} (ACE) to {} (HDF5)'.format(table.name,
|
||||
thermal.name))
|
||||
thermal.add_temperature_from_ace(table)
|
||||
thermal.export_to_hdf5(nuclides[name] + '_1', 'w')
|
||||
os.rename(nuclides[name] + '_1', nuclides[name])
|
||||
except Exception as e:
|
||||
print('Failed to convert {}: {}'.format(table.name, e))
|
||||
continue
|
||||
|
||||
# Write cross_sections.xml
|
||||
libpath = os.path.join(args.destination, 'cross_sections.xml')
|
||||
|
|
|
|||
|
|
@ -253,23 +253,6 @@ def update_geometry(geometry_root):
|
|||
|
||||
return was_updated
|
||||
|
||||
def get_thermal_name(name):
|
||||
"""Get proper S(a,b) table name, e.g. 'HH2O' -> 'c_H_in_H2O'"""
|
||||
if name.lower() in _THERMAL_NAMES:
|
||||
return _THERMAL_NAMES[name.lower()]
|
||||
else:
|
||||
# Make an educated guess?? This actually works well for
|
||||
# JEFF-3.2 which stupidly uses names like lw00.32t,
|
||||
# lw01.32t, etc. for different temperatures
|
||||
matches = get_close_matches(
|
||||
name.lower(), _THERMAL_NAMES.keys(), cutoff=0.5)
|
||||
if len(matches) > 0:
|
||||
return _THERMAL_NAMES[matches[0]] + '.' + xs
|
||||
else:
|
||||
# OK, we give up. Just use the ACE name.
|
||||
return 'c_' + name
|
||||
return name
|
||||
|
||||
def update_materials(root):
|
||||
"""Update the given XML materials tree. Return True if changes were made."""
|
||||
was_updated = False
|
||||
|
|
@ -298,13 +281,13 @@ def update_materials(root):
|
|||
for sab in material.findall('sab'):
|
||||
if 'name' in sab.attrib:
|
||||
sabname = sab.attrib['name']
|
||||
sab.set('name', get_thermal_name(sabname))
|
||||
sab.set('name', openmc.data.get_thermal_name(sabname))
|
||||
was_updated = True
|
||||
|
||||
elif sab.find('name') is not None:
|
||||
name_elem = sab.find('name')
|
||||
sabname = name_elem.text
|
||||
name_elem.text = get_thermal(sabname)
|
||||
name_elem.text = openmc.data.get_thermal_name(sabname)
|
||||
was_updated = True
|
||||
|
||||
return was_updated
|
||||
|
|
|
|||
275
src/algorithm.F90
Normal file
275
src/algorithm.F90
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
module algorithm
|
||||
|
||||
use constants
|
||||
use stl_vector, only: VectorInt, VectorReal
|
||||
|
||||
implicit none
|
||||
|
||||
integer, parameter :: MAX_ITERATION = 64
|
||||
|
||||
interface binary_search
|
||||
module procedure binary_search_real, binary_search_int4, binary_search_int8
|
||||
end interface binary_search
|
||||
|
||||
interface sort
|
||||
module procedure sort_int, sort_real, sort_vector_int, sort_vector_real
|
||||
end interface sort
|
||||
|
||||
interface find
|
||||
module procedure find_int, find_real, find_vector_int, find_vector_real
|
||||
end interface find
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! BINARY_SEARCH performs a binary search of an array to find where a specific
|
||||
! value lies in the array. This is used extensively for energy grid searching
|
||||
!===============================================================================
|
||||
|
||||
pure function binary_search_real(array, n, val) result(array_index)
|
||||
|
||||
integer, intent(in) :: n
|
||||
real(8), intent(in) :: array(n)
|
||||
real(8), intent(in) :: val
|
||||
integer :: array_index
|
||||
|
||||
integer :: L
|
||||
integer :: R
|
||||
integer :: n_iteration
|
||||
|
||||
L = 1
|
||||
R = n
|
||||
|
||||
if (val < array(L) .or. val > array(R)) then
|
||||
array_index = -1
|
||||
return
|
||||
end if
|
||||
|
||||
n_iteration = 0
|
||||
do while (R - L > 1)
|
||||
! Find values at midpoint
|
||||
array_index = L + (R - L)/2
|
||||
if (val >= array(array_index)) then
|
||||
L = array_index
|
||||
else
|
||||
R = array_index
|
||||
end if
|
||||
|
||||
! check for large number of iterations
|
||||
n_iteration = n_iteration + 1
|
||||
if (n_iteration == MAX_ITERATION) then
|
||||
array_index = -2
|
||||
return
|
||||
end if
|
||||
end do
|
||||
|
||||
array_index = L
|
||||
|
||||
end function binary_search_real
|
||||
|
||||
pure function binary_search_int4(array, n, val) result(array_index)
|
||||
|
||||
integer, intent(in) :: n
|
||||
integer, intent(in) :: array(n)
|
||||
integer, intent(in) :: val
|
||||
integer :: array_index
|
||||
|
||||
integer :: L
|
||||
integer :: R
|
||||
integer :: n_iteration
|
||||
|
||||
L = 1
|
||||
R = n
|
||||
|
||||
if (val < array(L) .or. val > array(R)) then
|
||||
array_index = -1
|
||||
return
|
||||
end if
|
||||
|
||||
n_iteration = 0
|
||||
do while (R - L > 1)
|
||||
! Find values at midpoint
|
||||
array_index = L + (R - L)/2
|
||||
if (val >= array(array_index)) then
|
||||
L = array_index
|
||||
else
|
||||
R = array_index
|
||||
end if
|
||||
|
||||
! check for large number of iterations
|
||||
n_iteration = n_iteration + 1
|
||||
if (n_iteration == MAX_ITERATION) then
|
||||
array_index = -2
|
||||
return
|
||||
end if
|
||||
end do
|
||||
|
||||
array_index = L
|
||||
|
||||
end function binary_search_int4
|
||||
|
||||
pure function binary_search_int8(array, n, val) result(array_index)
|
||||
|
||||
integer, intent(in) :: n
|
||||
integer(8), intent(in) :: array(n)
|
||||
integer(8), intent(in) :: val
|
||||
integer :: array_index
|
||||
|
||||
integer :: L
|
||||
integer :: R
|
||||
integer :: n_iteration
|
||||
|
||||
L = 1
|
||||
R = n
|
||||
|
||||
if (val < array(L) .or. val > array(R)) then
|
||||
array_index = -1
|
||||
return
|
||||
end if
|
||||
|
||||
n_iteration = 0
|
||||
do while (R - L > 1)
|
||||
! Find values at midpoint
|
||||
array_index = L + (R - L)/2
|
||||
if (val >= array(array_index)) then
|
||||
L = array_index
|
||||
else
|
||||
R = array_index
|
||||
end if
|
||||
|
||||
! check for large number of iterations
|
||||
n_iteration = n_iteration + 1
|
||||
if (n_iteration == MAX_ITERATION) then
|
||||
array_index = -2
|
||||
return
|
||||
end if
|
||||
end do
|
||||
|
||||
array_index = L
|
||||
|
||||
end function binary_search_int8
|
||||
|
||||
!===============================================================================
|
||||
! SORT sorts an array in place using an insertion sort.
|
||||
!===============================================================================
|
||||
|
||||
pure subroutine sort_int(array)
|
||||
integer, intent(inout) :: array(:)
|
||||
|
||||
integer :: k, m
|
||||
integer :: temp
|
||||
|
||||
if (size(array) > 1) then
|
||||
SORT: do k = 2, size(array)
|
||||
! Save value to move
|
||||
m = k
|
||||
temp = array(k)
|
||||
|
||||
MOVE_OVER: do while (m > 1)
|
||||
! Check if insertion value is greater than (m-1)th value
|
||||
if (temp >= array(m - 1)) exit
|
||||
|
||||
! Move values over until hitting one that's not larger
|
||||
array(m) = array(m - 1)
|
||||
m = m - 1
|
||||
end do MOVE_OVER
|
||||
|
||||
! Put the original value into its new position
|
||||
array(m) = temp
|
||||
end do SORT
|
||||
end if
|
||||
end subroutine sort_int
|
||||
|
||||
pure subroutine sort_real(array)
|
||||
real(8), intent(inout) :: array(:)
|
||||
|
||||
integer :: k, m
|
||||
real(8) :: temp
|
||||
|
||||
if (size(array) > 1) then
|
||||
SORT: do k = 2, size(array)
|
||||
! Save value to move
|
||||
m = k
|
||||
temp = array(k)
|
||||
|
||||
MOVE_OVER: do while (m > 1)
|
||||
! Check if insertion value is greater than (m-1)th value
|
||||
if (temp >= array(m - 1)) exit
|
||||
|
||||
! Move values over until hitting one that's not larger
|
||||
array(m) = array(m - 1)
|
||||
m = m - 1
|
||||
end do MOVE_OVER
|
||||
|
||||
! Put the original value into its new position
|
||||
array(m) = temp
|
||||
end do SORT
|
||||
end if
|
||||
end subroutine sort_real
|
||||
|
||||
pure subroutine sort_vector_int(vec)
|
||||
type(VectorInt), intent(inout) :: vec
|
||||
|
||||
call sort_int(vec % data(1:vec%size()))
|
||||
end subroutine sort_vector_int
|
||||
|
||||
pure subroutine sort_vector_real(vec)
|
||||
type(VectorReal), intent(inout) :: vec
|
||||
|
||||
call sort_real(vec % data(1:vec%size()))
|
||||
end subroutine sort_vector_real
|
||||
|
||||
!===============================================================================
|
||||
! FIND determines the index of the first occurrence of a value in an array. If
|
||||
! the value does not appear in the array, -1 is returned.
|
||||
!===============================================================================
|
||||
|
||||
pure function find_int(array, val) result(index)
|
||||
integer, intent(in) :: array(:)
|
||||
integer, intent(in) :: val
|
||||
integer :: index
|
||||
|
||||
integer :: i
|
||||
|
||||
index = -1
|
||||
do i = 1, size(array)
|
||||
if (array(i) == val) then
|
||||
index = i
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
end function find_int
|
||||
|
||||
pure function find_real(array, val) result(index)
|
||||
real(8), intent(in) :: array(:)
|
||||
real(8), intent(in) :: val
|
||||
integer :: index
|
||||
|
||||
integer :: i
|
||||
|
||||
index = -1
|
||||
do i = 1, size(array)
|
||||
if (array(i) == val) then
|
||||
index = i
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
end function find_real
|
||||
|
||||
pure function find_vector_int(vec, val) result(index)
|
||||
type(VectorInt), intent(in) :: vec
|
||||
integer, intent(in) :: val
|
||||
integer :: index
|
||||
|
||||
index = find_int(vec % data(1:vec % size()), val)
|
||||
end function find_vector_int
|
||||
|
||||
pure function find_vector_real(vec, val) result(index)
|
||||
type(VectorReal), intent(in) :: vec
|
||||
real(8), intent(in) :: val
|
||||
integer :: index
|
||||
|
||||
index = find_real(vec % data(1:vec % size()), val)
|
||||
end function find_vector_real
|
||||
|
||||
end module algorithm
|
||||
|
|
@ -2,12 +2,12 @@ module angle_distribution
|
|||
|
||||
use hdf5, only: HID_T, HSIZE_T
|
||||
|
||||
use algorithm, only: binary_search
|
||||
use constants, only: ZERO, ONE, HISTOGRAM, LINEAR_LINEAR
|
||||
use distribution_univariate, only: DistributionContainer, Tabular
|
||||
use hdf5_interface, only: read_attribute, get_shape, read_dataset, &
|
||||
open_dataset, close_dataset
|
||||
use random_lcg, only: prn
|
||||
use search, only: binary_search
|
||||
|
||||
implicit none
|
||||
private
|
||||
|
|
|
|||
|
|
@ -51,9 +51,10 @@ 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
|
||||
FILTER_SURFACE, OUT_LEFT, OUT_RIGHT, OUT_BACK, &
|
||||
OUT_FRONT, OUT_BOTTOM, OUT_TOP, IN_LEFT, IN_RIGHT, &
|
||||
IN_BACK, IN_FRONT, IN_BOTTOM, IN_TOP, CMFD_NOACCEL, &
|
||||
ZERO, ONE, TINY_BIT
|
||||
use error, only: fatal_error
|
||||
use global, only: cmfd, n_cmfd_tallies, cmfd_tallies, meshes,&
|
||||
matching_bins
|
||||
|
|
@ -234,76 +235,74 @@ contains
|
|||
matching_bins(i_filter_ein) = ng - h + 1
|
||||
end if
|
||||
|
||||
! Left surface
|
||||
! Get the bin for this mesh cell
|
||||
matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, &
|
||||
(/ i-1, j, k /) + 1, .true.)
|
||||
matching_bins(i_filter_surf) = IN_RIGHT
|
||||
(/ i, j, k /))
|
||||
|
||||
! Left surface
|
||||
matching_bins(i_filter_surf) = OUT_LEFT
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t%stride) + 1 ! outgoing
|
||||
* t % stride) + 1
|
||||
cmfd % current(1,h,i,j,k) = t % results(1,score_index) % sum
|
||||
matching_bins(i_filter_surf) = OUT_RIGHT
|
||||
|
||||
matching_bins(i_filter_surf) = IN_LEFT
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! incoming
|
||||
* t % stride) + 1
|
||||
cmfd % current(2,h,i,j,k) = t % results(1,score_index) % sum
|
||||
|
||||
! Right surface
|
||||
matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, &
|
||||
(/ i, j, k /) + 1, .true.)
|
||||
matching_bins(i_filter_surf) = IN_RIGHT
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! incoming
|
||||
* t % stride) + 1
|
||||
cmfd % current(3,h,i,j,k) = t % results(1,score_index) % sum
|
||||
|
||||
matching_bins(i_filter_surf) = OUT_RIGHT
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! outgoing
|
||||
* t % stride) + 1
|
||||
cmfd % current(4,h,i,j,k) = t % results(1,score_index) % sum
|
||||
|
||||
! Back surface
|
||||
matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, &
|
||||
(/ i, j-1, k /) + 1, .true.)
|
||||
matching_bins(i_filter_surf) = IN_FRONT
|
||||
matching_bins(i_filter_surf) = OUT_BACK
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! outgoing
|
||||
* t % stride) + 1
|
||||
cmfd % current(5,h,i,j,k) = t % results(1,score_index) % sum
|
||||
matching_bins(i_filter_surf) = OUT_FRONT
|
||||
|
||||
matching_bins(i_filter_surf) = IN_BACK
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! incoming
|
||||
* t % stride) + 1
|
||||
cmfd % current(6,h,i,j,k) = t % results(1,score_index) % sum
|
||||
|
||||
! Front surface
|
||||
matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, &
|
||||
(/ i, j, k /) + 1, .true.)
|
||||
matching_bins(i_filter_surf) = IN_FRONT
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! incoming
|
||||
* t % stride) + 1
|
||||
cmfd % current(7,h,i,j,k) = t % results(1,score_index) % sum
|
||||
|
||||
matching_bins(i_filter_surf) = OUT_FRONT
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! outgoing
|
||||
* t % stride) + 1
|
||||
cmfd % current(8,h,i,j,k) = t % results(1,score_index) % sum
|
||||
|
||||
! Bottom surface
|
||||
matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, &
|
||||
(/ i, j, k-1 /) + 1, .true.)
|
||||
matching_bins(i_filter_surf) = IN_TOP
|
||||
matching_bins(i_filter_surf) = OUT_BOTTOM
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! outgoing
|
||||
* t % stride) + 1
|
||||
cmfd % current(9,h,i,j,k) = t % results(1,score_index) % sum
|
||||
matching_bins(i_filter_surf) = OUT_TOP
|
||||
|
||||
matching_bins(i_filter_surf) = IN_BOTTOM
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! incoming
|
||||
* t % stride) + 1
|
||||
cmfd % current(10,h,i,j,k) = t % results(1,score_index) % sum
|
||||
|
||||
! Top surface
|
||||
matching_bins(i_filter_mesh) = mesh_indices_to_bin(m, &
|
||||
(/ i, j, k /) + 1, .true.)
|
||||
matching_bins(i_filter_surf) = IN_TOP
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! incoming
|
||||
* t % stride) + 1
|
||||
cmfd % current(11,h,i,j,k) = t % results(1,score_index) % sum
|
||||
|
||||
matching_bins(i_filter_surf) = OUT_TOP
|
||||
score_index = sum((matching_bins(1:size(t % filters)) - 1) &
|
||||
* t % stride) + 1 ! outgoing
|
||||
* t % stride) + 1
|
||||
cmfd % current(12,h,i,j,k) = t % results(1,score_index) % sum
|
||||
|
||||
end if TALLY
|
||||
|
|
@ -448,7 +447,6 @@ contains
|
|||
! Get leakage
|
||||
leakage = ZERO
|
||||
LEAK: do l = 1, 3
|
||||
|
||||
leakage = leakage + ((cmfd % current(4*l,g,i,j,k) - &
|
||||
cmfd % current(4*l-1,g,i,j,k))) - &
|
||||
((cmfd % current(4*l-2,g,i,j,k) - &
|
||||
|
|
|
|||
|
|
@ -213,13 +213,13 @@ contains
|
|||
|
||||
subroutine cmfd_reweight(new_weights)
|
||||
|
||||
use algorithm, only: binary_search
|
||||
use constants, only: ZERO, ONE
|
||||
use error, only: warning, fatal_error
|
||||
use global, only: meshes, source_bank, work, n_user_meshes, cmfd, &
|
||||
master
|
||||
use mesh_header, only: RegularMesh
|
||||
use mesh, only: count_bank_sites, get_mesh_indices
|
||||
use search, only: binary_search
|
||||
use string, only: to_str
|
||||
|
||||
#ifdef MPI
|
||||
|
|
|
|||
|
|
@ -531,13 +531,15 @@ contains
|
|||
allocate(SurfaceFilter :: filters(n_filters) % obj)
|
||||
select type(filt => filters(n_filters) % obj)
|
||||
type is(SurfaceFilter)
|
||||
filt % n_bins = 2 * m % n_dimension
|
||||
allocate(filt % surfaces(2 * m % n_dimension))
|
||||
filt % n_bins = 4 * m % n_dimension
|
||||
allocate(filt % surfaces(4 * m % n_dimension))
|
||||
if (m % n_dimension == 2) then
|
||||
filt % surfaces = (/ IN_RIGHT, OUT_RIGHT, IN_FRONT, OUT_FRONT /)
|
||||
filt % surfaces = (/ OUT_LEFT, IN_LEFT, IN_RIGHT, OUT_RIGHT, &
|
||||
OUT_BACK, IN_BACK, IN_FRONT, OUT_FRONT /)
|
||||
elseif (m % n_dimension == 3) then
|
||||
filt % surfaces = (/ IN_RIGHT, OUT_RIGHT, IN_FRONT, OUT_FRONT, &
|
||||
IN_TOP, OUT_TOP /)
|
||||
filt % surfaces = (/ OUT_LEFT, IN_LEFT, IN_RIGHT, OUT_RIGHT, &
|
||||
OUT_BACK, IN_BACK, IN_FRONT, OUT_FRONT, &
|
||||
OUT_BOTTOM, IN_BOTTOM, IN_TOP, OUT_TOP /)
|
||||
end if
|
||||
end select
|
||||
t % find_filter(FILTER_SURFACE) = n_filters
|
||||
|
|
|
|||
|
|
@ -54,7 +54,8 @@ module constants
|
|||
|
||||
! Maximum number of external source spatial resamples to encounter before an
|
||||
! error is thrown.
|
||||
integer, parameter :: MAX_EXTSRC_RESAMPLES = 10000
|
||||
integer, parameter :: EXTSRC_REJECT_THRESHOLD = 10000
|
||||
real(8), parameter :: EXTSRC_REJECT_FRACTION = 0.05
|
||||
|
||||
! ============================================================================
|
||||
! PHYSICAL CONSTANTS
|
||||
|
|
@ -267,6 +268,12 @@ module constants
|
|||
JENDL_33 = 7, &
|
||||
JENDL_40 = 8
|
||||
|
||||
! Temperature treatment method
|
||||
integer, parameter :: &
|
||||
TEMPERATURE_NEAREST = 1, &
|
||||
TEMPERATURE_INTERPOLATION = 2, &
|
||||
TEMPERATURE_MULTIPOLE = 3
|
||||
|
||||
! ============================================================================
|
||||
! TALLY-RELATED CONSTANTS
|
||||
|
||||
|
|
@ -289,7 +296,7 @@ module constants
|
|||
EVENT_ABSORB = 2
|
||||
|
||||
! Tally score type
|
||||
integer, parameter :: N_SCORE_TYPES = 21
|
||||
integer, parameter :: N_SCORE_TYPES = 23
|
||||
integer, parameter :: &
|
||||
SCORE_FLUX = -1, & ! flux
|
||||
SCORE_TOTAL = -2, & ! total reaction rate
|
||||
|
|
@ -311,7 +318,9 @@ module constants
|
|||
SCORE_EVENTS = -18, & ! number of events
|
||||
SCORE_DELAYED_NU_FISSION = -19, & ! delayed neutron production rate
|
||||
SCORE_PROMPT_NU_FISSION = -20, & ! prompt neutron production rate
|
||||
SCORE_INVERSE_VELOCITY = -21 ! flux-weighted inverse velocity
|
||||
SCORE_INVERSE_VELOCITY = -21, & ! flux-weighted inverse velocity
|
||||
SCORE_FISS_Q_PROMPT = -22, & ! prompt fission Q-value
|
||||
SCORE_FISS_Q_RECOV = -23 ! recoverable fission Q-value
|
||||
|
||||
! Maximum scattering order supported
|
||||
integer, parameter :: MAX_ANG_ORDER = 10
|
||||
|
|
@ -356,12 +365,18 @@ module constants
|
|||
|
||||
! Tally surface current directions
|
||||
integer, parameter :: &
|
||||
IN_RIGHT = 1, &
|
||||
OUT_RIGHT = 2, &
|
||||
IN_FRONT = 3, &
|
||||
OUT_FRONT = 4, &
|
||||
IN_TOP = 5, &
|
||||
OUT_TOP = 6
|
||||
OUT_LEFT = 1, & ! x min
|
||||
OUT_RIGHT = 2, & ! x max
|
||||
OUT_BACK = 3, & ! y min
|
||||
OUT_FRONT = 4, & ! y max
|
||||
OUT_BOTTOM = 5, & ! z min
|
||||
OUT_TOP = 6, & ! z max
|
||||
IN_LEFT = 7, & ! x min
|
||||
IN_RIGHT = 8, & ! x max
|
||||
IN_BACK = 9, & ! y min
|
||||
IN_FRONT = 10, & ! y max
|
||||
IN_BOTTOM = 11, & ! z min
|
||||
IN_TOP = 12 ! z max
|
||||
|
||||
! Tally trigger types and threshold
|
||||
integer, parameter :: &
|
||||
|
|
@ -380,11 +395,12 @@ module constants
|
|||
! ============================================================================
|
||||
! RANDOM NUMBER STREAM CONSTANTS
|
||||
|
||||
integer, parameter :: N_STREAMS = 4
|
||||
integer, parameter :: N_STREAMS = 5
|
||||
integer, parameter :: STREAM_TRACKING = 1
|
||||
integer, parameter :: STREAM_TALLIES = 2
|
||||
integer, parameter :: STREAM_SOURCE = 3
|
||||
integer, parameter :: STREAM_URR_PTABLE = 4
|
||||
integer, parameter :: STREAM_VOLUME = 5
|
||||
|
||||
! ============================================================================
|
||||
! MISCELLANEOUS CONSTANTS
|
||||
|
|
@ -397,12 +413,6 @@ module constants
|
|||
integer, parameter :: ERROR_INT = -huge(0)
|
||||
real(8), parameter :: ERROR_REAL = -huge(0.0_8) * 0.917826354_8
|
||||
|
||||
! Energy grid methods
|
||||
integer, parameter :: &
|
||||
GRID_NUCLIDE = 1, & ! unique energy grid for each nuclide
|
||||
GRID_MAT_UNION = 2, & ! material union grids with pointers
|
||||
GRID_LOGARITHM = 3 ! lethargy mapping
|
||||
|
||||
! Running modes
|
||||
integer, parameter :: &
|
||||
MODE_FIXEDSOURCE = 1, & ! Fixed source mode
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
module cross_section
|
||||
|
||||
use algorithm, only: binary_search
|
||||
use constants
|
||||
use energy_grid, only: grid_method, log_spacing
|
||||
use energy_grid, only: log_spacing
|
||||
use error, only: fatal_error
|
||||
use global
|
||||
use list_header, only: ListElemInt
|
||||
|
|
@ -14,7 +15,6 @@ module cross_section
|
|||
use particle_header, only: Particle
|
||||
use random_lcg, only: prn, future_prn, prn_set_stream
|
||||
use sab_header, only: SAlphaBeta
|
||||
use search, only: binary_search
|
||||
|
||||
implicit none
|
||||
|
||||
|
|
@ -37,7 +37,6 @@ contains
|
|||
! union grid
|
||||
real(8) :: atom_density ! atom density of a nuclide
|
||||
logical :: check_sab ! should we check for S(a,b) table?
|
||||
type(Material), pointer :: mat ! current material
|
||||
|
||||
! Set all material macroscopic cross sections to zero
|
||||
material_xs % total = ZERO
|
||||
|
|
@ -49,89 +48,83 @@ contains
|
|||
! Exit subroutine if material is void
|
||||
if (p % material == MATERIAL_VOID) return
|
||||
|
||||
mat => materials(p % material)
|
||||
|
||||
! Find energy index on energy grid
|
||||
if (grid_method == GRID_MAT_UNION) then
|
||||
i_grid = find_energy_index(mat, p % E)
|
||||
else if (grid_method == GRID_LOGARITHM) then
|
||||
associate (mat => materials(p % material))
|
||||
! Find energy index on energy grid
|
||||
i_grid = int(log(p % E/energy_min_neutron)/log_spacing)
|
||||
end if
|
||||
|
||||
! Determine if this material has S(a,b) tables
|
||||
check_sab = (mat % n_sab > 0)
|
||||
! Determine if this material has S(a,b) tables
|
||||
check_sab = (mat % n_sab > 0)
|
||||
|
||||
! Initialize position in i_sab_nuclides
|
||||
j = 1
|
||||
! Initialize position in i_sab_nuclides
|
||||
j = 1
|
||||
|
||||
! Add contribution from each nuclide in material
|
||||
do i = 1, mat % n_nuclides
|
||||
! ========================================================================
|
||||
! CHECK FOR S(A,B) TABLE
|
||||
! Add contribution from each nuclide in material
|
||||
do i = 1, mat % n_nuclides
|
||||
! ========================================================================
|
||||
! CHECK FOR S(A,B) TABLE
|
||||
|
||||
i_sab = 0
|
||||
i_sab = 0
|
||||
|
||||
! Check if this nuclide matches one of the S(a,b) tables specified -- this
|
||||
! relies on i_sab_nuclides being in sorted order
|
||||
if (check_sab) then
|
||||
if (i == mat % i_sab_nuclides(j)) then
|
||||
! Get index in sab_tables
|
||||
i_sab = mat % i_sab_tables(j)
|
||||
! Check if this nuclide matches one of the S(a,b) tables specified -- this
|
||||
! relies on i_sab_nuclides being in sorted order
|
||||
if (check_sab) then
|
||||
if (i == mat % i_sab_nuclides(j)) then
|
||||
! Get index in sab_tables
|
||||
i_sab = mat % i_sab_tables(j)
|
||||
|
||||
! If particle energy is greater than the highest energy for the S(a,b)
|
||||
! table, don't use the S(a,b) table
|
||||
if (p % E > sab_tables(i_sab) % threshold_inelastic) i_sab = 0
|
||||
! If particle energy is greater than the highest energy for the S(a,b)
|
||||
! table, don't use the S(a,b) table
|
||||
if (p % E > sab_tables(i_sab) % data(1) % threshold_inelastic) i_sab = 0
|
||||
|
||||
! Increment position in i_sab_nuclides
|
||||
j = j + 1
|
||||
! Increment position in i_sab_nuclides
|
||||
j = j + 1
|
||||
|
||||
! Don't check for S(a,b) tables if there are no more left
|
||||
if (j > mat % n_sab) check_sab = .false.
|
||||
! Don't check for S(a,b) tables if there are no more left
|
||||
if (j > mat % n_sab) check_sab = .false.
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
|
||||
! ========================================================================
|
||||
! CALCULATE MICROSCOPIC CROSS SECTION
|
||||
! ========================================================================
|
||||
! CALCULATE MICROSCOPIC CROSS SECTION
|
||||
|
||||
! Determine microscopic cross sections for this nuclide
|
||||
i_nuclide = mat % nuclide(i)
|
||||
! Determine microscopic cross sections for this nuclide
|
||||
i_nuclide = mat % nuclide(i)
|
||||
|
||||
! Calculate microscopic cross section for this nuclide
|
||||
if (p % E /= micro_xs(i_nuclide) % last_E &
|
||||
.or. p % sqrtkT /= micro_xs(i_nuclide) % last_sqrtkT) then
|
||||
call calculate_nuclide_xs(i_nuclide, i_sab, p % E, p % material, i, &
|
||||
i_grid, p % sqrtkT)
|
||||
else if (i_sab /= micro_xs(i_nuclide) % last_index_sab) then
|
||||
call calculate_nuclide_xs(i_nuclide, i_sab, p % E, p % material, i, &
|
||||
i_grid, p % sqrtkT)
|
||||
end if
|
||||
! Calculate microscopic cross section for this nuclide
|
||||
if (p % E /= micro_xs(i_nuclide) % last_E &
|
||||
.or. p % sqrtkT /= micro_xs(i_nuclide) % last_sqrtkT) then
|
||||
call calculate_nuclide_xs(i_nuclide, i_sab, p % E, i_grid, p % sqrtkT)
|
||||
else if (i_sab /= micro_xs(i_nuclide) % last_index_sab) then
|
||||
call calculate_nuclide_xs(i_nuclide, i_sab, p % E, i_grid, p % sqrtkT)
|
||||
end if
|
||||
|
||||
! ========================================================================
|
||||
! ADD TO MACROSCOPIC CROSS SECTION
|
||||
! ========================================================================
|
||||
! ADD TO MACROSCOPIC CROSS SECTION
|
||||
|
||||
! Copy atom density of nuclide in material
|
||||
atom_density = mat % atom_density(i)
|
||||
! Copy atom density of nuclide in material
|
||||
atom_density = mat % atom_density(i)
|
||||
|
||||
! Add contributions to material macroscopic total cross section
|
||||
material_xs % total = material_xs % total + &
|
||||
atom_density * micro_xs(i_nuclide) % total
|
||||
! Add contributions to material macroscopic total cross section
|
||||
material_xs % total = material_xs % total + &
|
||||
atom_density * micro_xs(i_nuclide) % total
|
||||
|
||||
! Add contributions to material macroscopic scattering cross section
|
||||
material_xs % elastic = material_xs % elastic + &
|
||||
atom_density * micro_xs(i_nuclide) % elastic
|
||||
! Add contributions to material macroscopic scattering cross section
|
||||
material_xs % elastic = material_xs % elastic + &
|
||||
atom_density * micro_xs(i_nuclide) % elastic
|
||||
|
||||
! Add contributions to material macroscopic absorption cross section
|
||||
material_xs % absorption = material_xs % absorption + &
|
||||
atom_density * micro_xs(i_nuclide) % absorption
|
||||
! Add contributions to material macroscopic absorption cross section
|
||||
material_xs % absorption = material_xs % absorption + &
|
||||
atom_density * micro_xs(i_nuclide) % absorption
|
||||
|
||||
! Add contributions to material macroscopic fission cross section
|
||||
material_xs % fission = material_xs % fission + &
|
||||
atom_density * micro_xs(i_nuclide) % fission
|
||||
! Add contributions to material macroscopic fission cross section
|
||||
material_xs % fission = material_xs % fission + &
|
||||
atom_density * micro_xs(i_nuclide) % fission
|
||||
|
||||
! Add contributions to material macroscopic nu-fission cross section
|
||||
material_xs % nu_fission = material_xs % nu_fission + &
|
||||
atom_density * micro_xs(i_nuclide) % nu_fission
|
||||
end do
|
||||
! Add contributions to material macroscopic nu-fission cross section
|
||||
material_xs % nu_fission = material_xs % nu_fission + &
|
||||
atom_density * micro_xs(i_nuclide) % nu_fission
|
||||
end do
|
||||
end associate
|
||||
|
||||
end subroutine calculate_xs
|
||||
|
||||
|
|
@ -140,169 +133,162 @@ contains
|
|||
! given index in the nuclides array at the energy of the given particle
|
||||
!===============================================================================
|
||||
|
||||
subroutine calculate_nuclide_xs(i_nuclide, i_sab, E, i_mat, i_nuc_mat, &
|
||||
i_log_union, sqrtkT)
|
||||
subroutine calculate_nuclide_xs(i_nuclide, i_sab, E, i_log_union, sqrtkT)
|
||||
integer, intent(in) :: i_nuclide ! index into nuclides array
|
||||
integer, intent(in) :: i_sab ! index into sab_tables array
|
||||
real(8), intent(in) :: E ! energy
|
||||
integer, intent(in) :: i_mat ! index into materials array
|
||||
integer, intent(in) :: i_nuc_mat ! index into nuclides array for a material
|
||||
integer, intent(in) :: i_log_union ! index into logarithmic mapping array or
|
||||
! material union energy grid
|
||||
real(8), intent(in) :: sqrtkT ! Square root of kT, material dependent
|
||||
|
||||
logical :: use_mp ! true if XS can be calculated with windowed multipole
|
||||
integer :: i_temp ! index for temperature
|
||||
integer :: i_grid ! index on nuclide energy grid
|
||||
integer :: i_low ! lower logarithmic mapping index
|
||||
integer :: i_high ! upper logarithmic mapping index
|
||||
real(8) :: f ! interp factor on nuclide energy grid
|
||||
real(8) :: kT ! temperature in MeV
|
||||
real(8) :: sigT, sigA, sigF ! Intermediate multipole variables
|
||||
type(Nuclide), pointer :: nuc
|
||||
type(Material), pointer :: mat
|
||||
|
||||
! Set pointer to nuclide and material
|
||||
nuc => nuclides(i_nuclide)
|
||||
mat => materials(i_mat)
|
||||
|
||||
! Check to see if there is multipole data present at this energy
|
||||
use_mp = .false.
|
||||
if (nuc % mp_present) then
|
||||
if (E >= nuc % multipole % start_E/1.0e6_8 .and. &
|
||||
E <= nuc % multipole % end_E/1.0e6_8) then
|
||||
use_mp = .true.
|
||||
end if
|
||||
end if
|
||||
|
||||
! Evaluate multipole or interpolate
|
||||
if (use_mp) then
|
||||
! Call multipole kernel
|
||||
call multipole_eval(nuc % multipole, E, sqrtkT, sigT, sigA, sigF)
|
||||
|
||||
micro_xs(i_nuclide) % total = sigT
|
||||
micro_xs(i_nuclide) % absorption = sigA
|
||||
micro_xs(i_nuclide) % elastic = sigT - sigA
|
||||
|
||||
if (nuc % fissionable) then
|
||||
micro_xs(i_nuclide) % fission = sigF
|
||||
micro_xs(i_nuclide) % nu_fission = sigF * nuc % nu(E, EMISSION_TOTAL)
|
||||
associate (nuc => nuclides(i_nuclide))
|
||||
! Check to see if there is multipole data present at this energy
|
||||
use_mp = .false.
|
||||
if (nuc % mp_present) then
|
||||
if (E >= nuc % multipole % start_E/1.0e6_8 .and. &
|
||||
E <= nuc % multipole % end_E/1.0e6_8) then
|
||||
use_mp = .true.
|
||||
else
|
||||
! If using multipole data but outside the RRR, pick the nearest
|
||||
! temperature. Note that there is no tolerance here, so this
|
||||
! temperature could be very far off!
|
||||
kT = sqrtkT**2
|
||||
i_temp = minloc(abs(nuclides(i_nuclide) % kTs - kT), dim=1)
|
||||
end if
|
||||
else
|
||||
micro_xs(i_nuclide) % fission = ZERO
|
||||
micro_xs(i_nuclide) % nu_fission = ZERO
|
||||
! If not using multipole data, do a linear search on temperature
|
||||
kT = sqrtkT**2
|
||||
do i_temp = 1, size(nuclides(i_nuclide) % kTs)
|
||||
if (abs(nuclides(i_nuclide) % kTs(i_temp) - kT) < &
|
||||
K_BOLTZMANN*temperature_tolerance) exit
|
||||
end do
|
||||
end if
|
||||
|
||||
! Ensure these values are set
|
||||
! Note, the only time either is used is in one of 4 places:
|
||||
! 1. physics.F90 - scatter - For inelastic scatter.
|
||||
! 2. physics.F90 - sample_fission - For partial fissions.
|
||||
! 3. tally.F90 - score_general - For tallying on MTxxx reactions.
|
||||
! 4. cross_section.F90 - calculate_urr_xs - For unresolved purposes.
|
||||
! It is worth noting that none of these occur in the resolved
|
||||
! resonance range, so the value here does not matter.
|
||||
micro_xs(i_nuclide) % index_grid = 0
|
||||
micro_xs(i_nuclide) % interp_factor = ZERO
|
||||
else
|
||||
! Determine index on nuclide energy grid
|
||||
select case (grid_method)
|
||||
case (GRID_MAT_UNION)
|
||||
! Evaluate multipole or interpolate
|
||||
if (use_mp) then
|
||||
! Call multipole kernel
|
||||
call multipole_eval(nuc % multipole, E, sqrtkT, sigT, sigA, sigF)
|
||||
|
||||
i_grid = mat % nuclide_grid_index(i_nuc_mat, i_log_union)
|
||||
micro_xs(i_nuclide) % total = sigT
|
||||
micro_xs(i_nuclide) % absorption = sigA
|
||||
micro_xs(i_nuclide) % elastic = sigT - sigA
|
||||
|
||||
case (GRID_LOGARITHM)
|
||||
! Determine the energy grid index using a logarithmic mapping to reduce
|
||||
! the energy range over which a binary search needs to be performed
|
||||
|
||||
if (E < nuc % energy(1)) then
|
||||
i_grid = 1
|
||||
elseif (E > nuc % energy(nuc % n_grid)) then
|
||||
i_grid = nuc % n_grid - 1
|
||||
if (nuc % fissionable) then
|
||||
micro_xs(i_nuclide) % fission = sigF
|
||||
micro_xs(i_nuclide) % nu_fission = sigF * nuc % nu(E, EMISSION_TOTAL)
|
||||
else
|
||||
! Determine bounding indices based on which equal log-spaced interval
|
||||
! the energy is in
|
||||
i_low = nuc % grid_index(i_log_union)
|
||||
i_high = nuc % grid_index(i_log_union + 1) + 1
|
||||
|
||||
! Perform binary search over reduced range
|
||||
i_grid = binary_search(nuc % energy(i_low:i_high), &
|
||||
i_high - i_low + 1, E) + i_low - 1
|
||||
micro_xs(i_nuclide) % fission = ZERO
|
||||
micro_xs(i_nuclide) % nu_fission = ZERO
|
||||
end if
|
||||
|
||||
case (GRID_NUCLIDE)
|
||||
! Perform binary search on the nuclide energy grid in order to determine
|
||||
! which points to interpolate between
|
||||
! Ensure these values are set
|
||||
! Note, the only time either is used is in one of 4 places:
|
||||
! 1. physics.F90 - scatter - For inelastic scatter.
|
||||
! 2. physics.F90 - sample_fission - For partial fissions.
|
||||
! 3. tally.F90 - score_general - For tallying on MTxxx reactions.
|
||||
! 4. cross_section.F90 - calculate_urr_xs - For unresolved purposes.
|
||||
! It is worth noting that none of these occur in the resolved
|
||||
! resonance range, so the value here does not matter.
|
||||
micro_xs(i_nuclide) % index_temp = i_temp
|
||||
micro_xs(i_nuclide) % index_grid = 0
|
||||
micro_xs(i_nuclide) % interp_factor = ZERO
|
||||
else
|
||||
associate (grid => nuc % grid(i_temp), xs => nuc % sum_xs(i_temp))
|
||||
! Determine the energy grid index using a logarithmic mapping to reduce
|
||||
! the energy range over which a binary search needs to be performed
|
||||
|
||||
if (E <= nuc % energy(1)) then
|
||||
i_grid = 1
|
||||
elseif (E > nuc % energy(nuc % n_grid)) then
|
||||
i_grid = nuc % n_grid - 1
|
||||
else
|
||||
i_grid = binary_search(nuc % energy, nuc % n_grid, E)
|
||||
if (E < grid % energy(1)) then
|
||||
i_grid = 1
|
||||
elseif (E > grid % energy(size(grid % energy))) then
|
||||
i_grid = size(grid % energy) - 1
|
||||
else
|
||||
! Determine bounding indices based on which equal log-spaced interval
|
||||
! the energy is in
|
||||
i_low = grid % grid_index(i_log_union)
|
||||
i_high = grid % grid_index(i_log_union + 1) + 1
|
||||
|
||||
! Perform binary search over reduced range
|
||||
i_grid = binary_search(grid % energy(i_low:i_high), &
|
||||
i_high - i_low + 1, E) + i_low - 1
|
||||
end if
|
||||
|
||||
! check for rare case where two energy points are the same
|
||||
if (grid % energy(i_grid) == grid % energy(i_grid + 1)) &
|
||||
i_grid = i_grid + 1
|
||||
|
||||
! calculate interpolation factor
|
||||
f = (E - grid % energy(i_grid)) / &
|
||||
(grid % energy(i_grid + 1) - grid % energy(i_grid))
|
||||
|
||||
micro_xs(i_nuclide) % index_temp = i_temp
|
||||
micro_xs(i_nuclide) % index_grid = i_grid
|
||||
micro_xs(i_nuclide) % interp_factor = f
|
||||
|
||||
! Initialize nuclide cross-sections to zero
|
||||
micro_xs(i_nuclide) % fission = ZERO
|
||||
micro_xs(i_nuclide) % nu_fission = ZERO
|
||||
|
||||
! Calculate microscopic nuclide total cross section
|
||||
micro_xs(i_nuclide) % total = (ONE - f) * xs % total(i_grid) &
|
||||
+ f * xs % total(i_grid + 1)
|
||||
|
||||
! Calculate microscopic nuclide elastic cross section
|
||||
micro_xs(i_nuclide) % elastic = (ONE - f) * xs % elastic(i_grid) &
|
||||
+ f * xs % elastic(i_grid + 1)
|
||||
|
||||
! Calculate microscopic nuclide absorption cross section
|
||||
micro_xs(i_nuclide) % absorption = (ONE - f) * xs % absorption( &
|
||||
i_grid) + f * xs % absorption(i_grid + 1)
|
||||
|
||||
if (nuc % fissionable) then
|
||||
! Calculate microscopic nuclide total cross section
|
||||
micro_xs(i_nuclide) % fission = (ONE - f) * xs % fission(i_grid) &
|
||||
+ f * xs % fission(i_grid + 1)
|
||||
|
||||
! Calculate microscopic nuclide nu-fission cross section
|
||||
micro_xs(i_nuclide) % nu_fission = (ONE - f) * xs % nu_fission( &
|
||||
i_grid) + f * xs % nu_fission(i_grid + 1)
|
||||
end if
|
||||
end associate
|
||||
end if
|
||||
|
||||
! Initialize sab treatment to false
|
||||
micro_xs(i_nuclide) % index_sab = NONE
|
||||
micro_xs(i_nuclide) % elastic_sab = ZERO
|
||||
|
||||
! Initialize URR probability table treatment to false
|
||||
micro_xs(i_nuclide) % use_ptable = .false.
|
||||
|
||||
! If there is S(a,b) data for this nuclide, we need to do a few
|
||||
! things. Since the total cross section was based on non-S(a,b) data, we
|
||||
! need to correct it by subtracting the non-S(a,b) elastic cross section and
|
||||
! then add back in the calculated S(a,b) elastic+inelastic cross section.
|
||||
|
||||
if (i_sab > 0) call calculate_sab_xs(i_nuclide, i_sab, E, sqrtkT)
|
||||
|
||||
! if the particle is in the unresolved resonance range and there are
|
||||
! probability tables, we need to determine cross sections from the table
|
||||
|
||||
if (urr_ptables_on .and. nuc % urr_present .and. .not. use_mp) then
|
||||
if (E > nuc % urr_data(i_temp) % energy(1) .and. E < nuc % &
|
||||
urr_data(i_temp) % energy(nuc % urr_data(i_temp) % n_energy)) then
|
||||
call calculate_urr_xs(i_nuclide, i_temp, E)
|
||||
end if
|
||||
|
||||
end select
|
||||
|
||||
! check for rare case where two energy points are the same
|
||||
if (nuc % energy(i_grid) == nuc % energy(i_grid+1)) i_grid = i_grid + 1
|
||||
|
||||
! calculate interpolation factor
|
||||
f = (E - nuc%energy(i_grid))/(nuc%energy(i_grid+1) - nuc%energy(i_grid))
|
||||
|
||||
micro_xs(i_nuclide) % index_grid = i_grid
|
||||
micro_xs(i_nuclide) % interp_factor = f
|
||||
|
||||
! Initialize nuclide cross-sections to zero
|
||||
micro_xs(i_nuclide) % fission = ZERO
|
||||
micro_xs(i_nuclide) % nu_fission = ZERO
|
||||
|
||||
! Calculate microscopic nuclide total cross section
|
||||
micro_xs(i_nuclide) % total = (ONE - f) * nuc % total(i_grid) &
|
||||
+ f * nuc % total(i_grid+1)
|
||||
|
||||
! Calculate microscopic nuclide elastic cross section
|
||||
micro_xs(i_nuclide) % elastic = (ONE - f) * nuc % elastic(i_grid) &
|
||||
+ f * nuc % elastic(i_grid+1)
|
||||
|
||||
! Calculate microscopic nuclide absorption cross section
|
||||
micro_xs(i_nuclide) % absorption = (ONE - f) * nuc % absorption( &
|
||||
i_grid) + f * nuc % absorption(i_grid+1)
|
||||
|
||||
if (nuc % fissionable) then
|
||||
! Calculate microscopic nuclide total cross section
|
||||
micro_xs(i_nuclide) % fission = (ONE - f) * nuc % fission(i_grid) &
|
||||
+ f * nuc % fission(i_grid+1)
|
||||
|
||||
! Calculate microscopic nuclide nu-fission cross section
|
||||
micro_xs(i_nuclide) % nu_fission = (ONE - f) * nuc % nu_fission( &
|
||||
i_grid) + f * nuc % nu_fission(i_grid+1)
|
||||
end if
|
||||
end if
|
||||
|
||||
! Initialize sab treatment to false
|
||||
micro_xs(i_nuclide) % index_sab = NONE
|
||||
micro_xs(i_nuclide) % elastic_sab = ZERO
|
||||
|
||||
! Initialize URR probability table treatment to false
|
||||
micro_xs(i_nuclide) % use_ptable = .false.
|
||||
|
||||
! If there is S(a,b) data for this nuclide, we need to do a few
|
||||
! things. Since the total cross section was based on non-S(a,b) data, we
|
||||
! need to correct it by subtracting the non-S(a,b) elastic cross section and
|
||||
! then add back in the calculated S(a,b) elastic+inelastic cross section.
|
||||
|
||||
if (i_sab > 0) call calculate_sab_xs(i_nuclide, i_sab, E)
|
||||
|
||||
! if the particle is in the unresolved resonance range and there are
|
||||
! probability tables, we need to determine cross sections from the table
|
||||
|
||||
if (urr_ptables_on .and. nuc % urr_present) then
|
||||
if (E > nuc % urr_data % energy(1) .and. &
|
||||
E < nuc % urr_data % energy(nuc % urr_data % n_energy)) then
|
||||
call calculate_urr_xs(i_nuclide, E)
|
||||
end if
|
||||
end if
|
||||
|
||||
micro_xs(i_nuclide) % last_E = E
|
||||
micro_xs(i_nuclide) % last_index_sab = i_sab
|
||||
micro_xs(i_nuclide) % last_sqrtkT = sqrtkT
|
||||
micro_xs(i_nuclide) % last_E = E
|
||||
micro_xs(i_nuclide) % last_index_sab = i_sab
|
||||
micro_xs(i_nuclide) % last_sqrtkT = sqrtkT
|
||||
end associate
|
||||
|
||||
end subroutine calculate_nuclide_xs
|
||||
|
||||
|
|
@ -312,75 +298,85 @@ contains
|
|||
! whatever data were taken from the normal Nuclide table.
|
||||
!===============================================================================
|
||||
|
||||
subroutine calculate_sab_xs(i_nuclide, i_sab, E)
|
||||
subroutine calculate_sab_xs(i_nuclide, i_sab, E, sqrtkT)
|
||||
|
||||
integer, intent(in) :: i_nuclide ! index into nuclides array
|
||||
integer, intent(in) :: i_sab ! index into sab_tables array
|
||||
real(8), intent(in) :: E ! energy
|
||||
real(8), intent(in) :: sqrtkT ! temperature
|
||||
|
||||
integer :: i_grid ! index on S(a,b) energy grid
|
||||
integer :: i_temp ! temperature index
|
||||
real(8) :: f ! interp factor on S(a,b) energy grid
|
||||
real(8) :: inelastic ! S(a,b) inelastic cross section
|
||||
real(8) :: elastic ! S(a,b) elastic cross section
|
||||
type(SAlphaBeta), pointer :: sab
|
||||
real(8) :: kT
|
||||
|
||||
! Set flag that S(a,b) treatment should be used for scattering
|
||||
micro_xs(i_nuclide) % index_sab = i_sab
|
||||
|
||||
! Determine temperature for S(a,b) table
|
||||
kT = sqrtkT**2
|
||||
do i_temp = 1, size(sab_tables(i_sab) % kTs)
|
||||
if (abs(sab_tables(i_sab) % kTs(i_temp) - kT) < &
|
||||
K_BOLTZMANN*temperature_tolerance) exit
|
||||
end do
|
||||
|
||||
! Get pointer to S(a,b) table
|
||||
sab => sab_tables(i_sab)
|
||||
associate (sab => sab_tables(i_sab) % data(i_temp))
|
||||
|
||||
! Get index and interpolation factor for inelastic grid
|
||||
if (E < sab % inelastic_e_in(1)) then
|
||||
i_grid = 1
|
||||
f = ZERO
|
||||
else
|
||||
i_grid = binary_search(sab % inelastic_e_in, sab % n_inelastic_e_in, E)
|
||||
f = (E - sab%inelastic_e_in(i_grid)) / &
|
||||
(sab%inelastic_e_in(i_grid+1) - sab%inelastic_e_in(i_grid))
|
||||
end if
|
||||
! Get index and interpolation factor for inelastic grid
|
||||
if (E < sab % inelastic_e_in(1)) then
|
||||
i_grid = 1
|
||||
f = ZERO
|
||||
else
|
||||
i_grid = binary_search(sab % inelastic_e_in, sab % n_inelastic_e_in, E)
|
||||
f = (E - sab%inelastic_e_in(i_grid)) / &
|
||||
(sab%inelastic_e_in(i_grid+1) - sab%inelastic_e_in(i_grid))
|
||||
end if
|
||||
|
||||
! Calculate S(a,b) inelastic scattering cross section
|
||||
inelastic = (ONE - f) * sab % inelastic_sigma(i_grid) + &
|
||||
f * sab % inelastic_sigma(i_grid + 1)
|
||||
! Calculate S(a,b) inelastic scattering cross section
|
||||
inelastic = (ONE - f) * sab % inelastic_sigma(i_grid) + &
|
||||
f * sab % inelastic_sigma(i_grid + 1)
|
||||
|
||||
! Check for elastic data
|
||||
if (E < sab % threshold_elastic) then
|
||||
! Determine whether elastic scattering is given in the coherent or
|
||||
! incoherent approximation. For coherent, the cross section is
|
||||
! represented as P/E whereas for incoherent, it is simply P
|
||||
! Check for elastic data
|
||||
if (E < sab % threshold_elastic) then
|
||||
! Determine whether elastic scattering is given in the coherent or
|
||||
! incoherent approximation. For coherent, the cross section is
|
||||
! represented as P/E whereas for incoherent, it is simply P
|
||||
|
||||
if (sab % elastic_mode == SAB_ELASTIC_EXACT) then
|
||||
if (E < sab % elastic_e_in(1)) then
|
||||
! If energy is below that of the lowest Bragg peak, the elastic
|
||||
! cross section will be zero
|
||||
elastic = ZERO
|
||||
if (sab % elastic_mode == SAB_ELASTIC_EXACT) then
|
||||
if (E < sab % elastic_e_in(1)) then
|
||||
! If energy is below that of the lowest Bragg peak, the elastic
|
||||
! cross section will be zero
|
||||
elastic = ZERO
|
||||
else
|
||||
i_grid = binary_search(sab % elastic_e_in, &
|
||||
sab % n_elastic_e_in, E)
|
||||
elastic = sab % elastic_P(i_grid) / E
|
||||
end if
|
||||
else
|
||||
i_grid = binary_search(sab % elastic_e_in, &
|
||||
sab % n_elastic_e_in, E)
|
||||
elastic = sab % elastic_P(i_grid) / E
|
||||
! Determine index on elastic energy grid
|
||||
if (E < sab % elastic_e_in(1)) then
|
||||
i_grid = 1
|
||||
else
|
||||
i_grid = binary_search(sab % elastic_e_in, &
|
||||
sab % n_elastic_e_in, E)
|
||||
end if
|
||||
|
||||
! Get interpolation factor for elastic grid
|
||||
f = (E - sab%elastic_e_in(i_grid))/(sab%elastic_e_in(i_grid+1) - &
|
||||
sab%elastic_e_in(i_grid))
|
||||
|
||||
! Calculate S(a,b) elastic scattering cross section
|
||||
elastic = (ONE - f) * sab % elastic_P(i_grid) + &
|
||||
f * sab % elastic_P(i_grid + 1)
|
||||
end if
|
||||
else
|
||||
! Determine index on elastic energy grid
|
||||
if (E < sab % elastic_e_in(1)) then
|
||||
i_grid = 1
|
||||
else
|
||||
i_grid = binary_search(sab % elastic_e_in, &
|
||||
sab % n_elastic_e_in, E)
|
||||
end if
|
||||
|
||||
! Get interpolation factor for elastic grid
|
||||
f = (E - sab%elastic_e_in(i_grid))/(sab%elastic_e_in(i_grid+1) - &
|
||||
sab%elastic_e_in(i_grid))
|
||||
|
||||
! Calculate S(a,b) elastic scattering cross section
|
||||
elastic = (ONE - f) * sab % elastic_P(i_grid) + &
|
||||
f * sab % elastic_P(i_grid + 1)
|
||||
! No elastic data
|
||||
elastic = ZERO
|
||||
end if
|
||||
else
|
||||
! No elastic data
|
||||
elastic = ZERO
|
||||
end if
|
||||
end associate
|
||||
|
||||
! Correct total and elastic cross sections
|
||||
micro_xs(i_nuclide) % total = micro_xs(i_nuclide) % total - &
|
||||
|
|
@ -390,6 +386,9 @@ contains
|
|||
! Store S(a,b) elastic cross section for sampling later
|
||||
micro_xs(i_nuclide) % elastic_sab = elastic
|
||||
|
||||
! Save temperature index
|
||||
micro_xs(i_nuclide) % index_temp_sab = i_temp
|
||||
|
||||
end subroutine calculate_sab_xs
|
||||
|
||||
!===============================================================================
|
||||
|
|
@ -397,9 +396,9 @@ contains
|
|||
! from probability tables
|
||||
!===============================================================================
|
||||
|
||||
subroutine calculate_urr_xs(i_nuclide, E)
|
||||
|
||||
subroutine calculate_urr_xs(i_nuclide, i_temp, E)
|
||||
integer, intent(in) :: i_nuclide ! index into nuclides array
|
||||
integer, intent(in) :: i_temp ! temperature index
|
||||
real(8), intent(in) :: E ! energy
|
||||
|
||||
integer :: i_energy ! index for energy
|
||||
|
|
@ -414,7 +413,7 @@ contains
|
|||
|
||||
micro_xs(i_nuclide) % use_ptable = .true.
|
||||
|
||||
associate (nuc => nuclides(i_nuclide), urr => nuclides(i_nuclide) % urr_data)
|
||||
associate (nuc => nuclides(i_nuclide), urr => nuclides(i_nuclide) % urr_data(i_temp))
|
||||
! determine energy table
|
||||
i_energy = 1
|
||||
do
|
||||
|
|
@ -433,7 +432,7 @@ contains
|
|||
! random number for the same nuclide at different temperatures, therefore
|
||||
! preserving correlation of temperature in probability tables.
|
||||
call prn_set_stream(STREAM_URR_PTABLE)
|
||||
r = future_prn(int(nuc_zaid_dict % get_key(nuc % zaid), 8))
|
||||
r = future_prn(int(i_nuclide, 8))
|
||||
call prn_set_stream(STREAM_TRACKING)
|
||||
|
||||
i_low = 1
|
||||
|
|
@ -497,10 +496,10 @@ contains
|
|||
f = micro_xs(i_nuclide) % interp_factor
|
||||
|
||||
! Determine inelastic scattering cross section
|
||||
associate (rxn => nuc % reactions(nuc % urr_inelastic))
|
||||
if (i_energy >= rxn % threshold) then
|
||||
inelastic = (ONE - f) * rxn % sigma(i_energy - rxn%threshold + 1) + &
|
||||
f * rxn % sigma(i_energy - rxn%threshold + 2)
|
||||
associate (xs => nuc % reactions(nuc % urr_inelastic) % xs(i_temp))
|
||||
if (i_energy >= xs % threshold) then
|
||||
inelastic = (ONE - f) * xs % value(i_energy - xs % threshold + 1) + &
|
||||
f * xs % value(i_energy - xs % threshold + 2)
|
||||
end if
|
||||
end associate
|
||||
end if
|
||||
|
|
|
|||
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