Merge remote-tracking branch 'upstream/develop' into diff_tally6

This commit is contained in:
Sterling Harper 2016-10-21 01:52:55 -04:00
commit d602dc0e6a
130 changed files with 8251 additions and 17146 deletions

5
.gitignore vendored
View file

@ -89,3 +89,8 @@ docs/source/pythonapi/examples/mgxs
docs/source/pythonapi/examples/tracks
docs/source/pythonapi/examples/fission-rates
docs/source/pythonapi/examples/plots
# Cython files
*.c
*.html
*.so

View file

@ -1,4 +1,5 @@
sudo: false
sudo: required
dist: trusty
language: python
python:
- "2.7"
@ -28,7 +29,7 @@ before_install:
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
- conda info -a
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy h5py=2.5 pandas
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION six numpy scipy h5py=2.5 pandas
- source activate test-environment
# Install GCC, MPICH, HDF5, PHDF5
@ -46,14 +47,12 @@ before_script:
fi
- export OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml
- cd data
- git clone --branch=master git://github.com/smharper/windowed_multipole_library.git wmp_lib
- tar xzvf wmp_lib/multipole_lib.tar.gz
- export OPENMC_MULTIPOLE_LIBRARY=$PWD/multipole_lib
- cd ..
script:
- cd tests
- export OMP_NUM_THREADS=3
- export OMP_NUM_THREADS=2
- ./travis.sh
- cd ..

View file

@ -1,37 +0,0 @@
========================
cross_sections.xml Files
========================
As a reminder, in order to run a simulation with OpenMC, you will need cross
section data for each nuclide in your problem. OpenMC is not currently
distributed with cross section data, so you will have to obtain cross section
data by other means. The `user's guide`_ offers some helpful advice on how you
can obtain cross sections.
When OpenMC starts up, it needs a cross_sections.xml file that tells it where to
find ACE format cross sections. The files in this directory are configured to
work with a few common cross section sources.
- **cross_sections_ascii.xml** -- This file matches ENDF/B-VII.0 cross sections
distributed with MCNP5 / MCNP6 beta.
- **cross_sections_nndc.xml** -- This file matches ENDF/B-VII.1 cross sections
distributed from the `NNDC website`_.
- **cross_sections_serpent.xml** -- This file matches ENDF/B-VII.0 cross
sections distributed with Serpent 1.1.7.
- **cross_sections.xml** - This file matches ENDF/B-VII.0 cross sections
distributed with MCNP5 / MCNP6 beta *that have been converted to binary*.
To use any of these files, you need to follow two steps:
1. Change the path on the ``<directory>`` element in the cross_sections.xml file
to the directory containing the ACE files.
2. Enter the absolute path of the cross_sections.xml on the ``<cross_sections>``
element in your settings.xml, or set the CROSS_SECTIONS environment variable to
the full path of the cross_sections.xml file.
.. _user's guide: http://mit-crpg.github.io/openmc/usersguide/install.html#cross-section-configuration
.. _NNDC website: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html

View file

@ -6,21 +6,19 @@ Multi-Group Cross Section Library Format
OpenMC can be run in continuous-energy mode or multi-group mode, provided the
nuclear data is available. In continuous-energy mode, the
``cross_sections.xml`` file contains necessary meta-data for each data set,
``cross_sections.xml`` file contains necessary meta-data for each dataset,
including the name and a file system location where the complete library
can be found. In multi-group mode, this ``mgxs.xml`` file contains
this same meta-data describing the nuclide or material, but also contains the
group-wise nuclear data. This portion of the manual describes the format of
the multi-group data library required to be used in the ``mgxs.xml``
file.
can be found. In multi-group mode, the multi-group meta-data and the
nuclear data itself is contained within an ``mgxs.h5`` file. This portion of
the manual describes the format of the multi-group data library required
to be used in the ``mgxs.h5`` file.
Similar to the other input file types, the multi-group library is provided in
the XML_ format. This library must provide some meta-data about the library
itself (such as the number of groups and the group structure, etc.) as well as
the actual cross section data itself for each of the necessary nuclides or
materials.
The multi-group library is provided in the HDF5_ format. This library must
provide some meta-data about the library itself (such as the number of
groups and the group structure, etc.) as well as the actual cross section
data itself for each of the necessary nuclides or materials.
.. _XML: http://www.w3.org/XML/
.. _HDF5: http://www.hdfgroup.org/HDF5/
.. _mgxs_lib_spec:
@ -28,277 +26,139 @@ materials.
MGXS Library Specification
--------------------------
The multi-group library meta-data is contained within the groups_,
group_structure_, and inverse_velocities_ elements.
The actual multi-group data itself is contained within the xsdata_ element.
**/**
.. _groups:
:Attributes: - **groups** (*int*) -- Number of energy groups
- **group structure** (*double[]*) -- Monotonically increasing
list of group boundaries, in units of MeV. The length of this
array should be the number of groups plus 1.
``<groups>`` Element
--------------------
**/<library name>/**
The ``<groups>`` element has no attributes and simply provides the number of
energy groups contained within the library.
The data within <library name> contains the temperature-dependent multi-group
data for the nuclide or material that it represents.
*Default*: None, this must be provided.
:Attributes: - **atomic_weight_ratio** (*double*) -- The atomic weight ratio
(optional, i.e. it is not meaningful for material-wise data).
- **fissionable** (*bool*) -- Whether the dataset is fissionable
(True) or not (False).
- **representation** (*char[]*) -- The method used to generate and
represent the multi-group cross sections. That is, whether they
were generated with scalar flux weighting (or reduced to a
similar representation) and thus are angle-independent, or if the
data was generated with angular dependent fluxes and thus the
data is angle-dependent. Valid values are either "isotropic" or
"angle".
- **num_azimuthal** (*int*) -- Number of equal width angular bins
that the azimuthal angular domain is subdivided if the
`representation` attribute is "angle". This parameter is
ignored otherwise.
- **num_polar** (*int*) -- Number of equal width angular bins
that the polar angular domain is subdivided if the
`representation` attribute is "angle". This parameter is
ignored otherwise.
- **scatter_format** (*char[]*) -- The representation of the
scattering angular distribution. The options are either
"legendre", "histogram", or "tabular". If not provided, the
default of "legendre" will be assumed.
- **order** (*int*) -- Either the Legendre order, number of bins,
or number of points (depending on the value of `scatter_format`)
used to describe the angular distribution associated with each
group-to-group transfer probability.
- **scatter_shape** (*char[]*) -- The shape of the provided
scatter and multiplicity matrix. The values provided are strings
describing the ordering the scattering array is provided in
row-major (i.e., C/C++ and Python) indexing. Valid values are
"[Order][G][G']" or "[Order][G'][G]" where "G'" denotes the
secondary/outgoing energy groups, "G" denotes the incoming
energy groups, and "Order" is the angular distribution index.
This value is not required; if not the default value of
"[Order][G][G']" will be assumed.
.. _group_structure:
**/<library name>/kTs/**
``<group_structure>`` Element
-----------------------------
:Datasets:
- **<TTT>K** (*double*) -- kT values (in MeV) for each Temperature
TTT (in Kelvin), rounded to the nearest integer
The ``<group_structure>`` element has no attributes and should be provided as a
monotonically increasing list of bounding energies, in MeV, for a number of
groups. To provide proper energy boundaries, the length of the data within the
``<group_structure>`` element should be one more than the number of groups in
the problem. For example, a two-group problem could be specified as:
**/<library name>/<TTT>K/**
.. code-block:: xml
Temperature-dependent data, provided for temperature <TTT>K.
<group_structure> 0.0 0.625E-6 20.0 </group_structure>
:Datasets: - **total** (*double[]* or *double[][][]*) -- Total cross section.
This is a 1-D vector if `representation` is "isotropic", or a 3-D
vector if `representation` is "angle" with dimensions of
[groups][azimuthal][polar].
- **absorption** (*double[]* or *double[][][]*) -- Absorption
cross section.
This is a 1-D vector if `representation` is "isotropic", or a 3-D
vector if `representation` is "angle" with dimensions of
[groups][azimuthal][polar].
- **fission** (*double[]* or *double[][][]*) -- Fission
cross section.
This is a 1-D vector if `representation` is "isotropic", or a 3-D
vector if `representation` is "angle" with dimensions of
[groups][azimuthal][polar]. This is only required if the dataset
is fissionable and fission-tallies are expected to be used.
- **kappa-fission** (*double[]* or *double[][][]*) -- Kappa-Fission
(energy-release from fission) cross section.
This is a 1-D vector if `representation` is "isotropic", or a 3-D
vector if `representation` is "angle" with dimensions of
[groups][azimuthal][polar]. This is only required if the dataset
is fissionable and fission-tallies are expected to be used.
- **chi** (*double[]* or *double[][][]*) -- Fission neutron energy
spectra.
This is a 1-D vector if `representation` is "isotropic", or a 3-D
vector if `representation` is "angle" with dimensions of
[groups][azimuthal][polar]. This is only required if the dataset
is fissionable and fission-tallies are expected to be used.
- **nu-fission** (*double[]* to *double[][][][]*) -- Nu-Fission
cross section.
If **chi** is provided, then `nu-fission` has the same
dimensionality as `fission`. If **chi** is not provided, then
the `nu-fission` data must represent the fission neutron energy
spectra as well and thus will have one additional dimension
for the outgoing energy group. In this case, `nu-fission` has the
same dimensionality as `multiplicity_matrix`.
- **inverse_velocities** (*double[]*) -- Average inverse velocity
for each of the groups in the library. This dataset is optional.
*Default*: None, this must be provided.
**/<library name>/<TTT>K/scatter_data/**
.. _inverse_velocities:
``<inverse_velocities>`` Element
--------------------------------
The ``<inverse_velocities>`` element optionally indicates the average
inverse velocity corresponding to each of the groups in the problem.
This element should therefore be an array with a length which matches the
number of groups set in the groups_ element.
*Default*: Should this be needed by the presence of an ``inverse-velocity``
score in the ``tallies.xml`` file and not provided in this element, OpenMC
will simply convert the group mid-point energy to an inverse of the velocity
and use this information for tallying.
.. _xsdata:
``<xsdata>`` Element
--------------------
The ``<xsdata>`` element contains the nuclide or material-specific meta-data as
well as the actual cross section data. The following are the
attributes/sub-elements required to describe the meta-data:
:name:
The name of the microscopic or macroscopic data set. An extension to the
name must be provided (e.g., the ``.300K`` in ``UO2.300K``). The name and
extension together must be twelve or less characters in length. This
extension must follow a period and be five characters or less in length.
similar to the equivalent in the continuous-energy ``cross_sections.xml``
file, is used to denote variants of the particular nuclide or material of
interest (i.e. the ``UO2`` data in this example could have been generated
at a temperature of 300K).
*Default*: None, this must be provided.
:alias:
An alternative name to use for the microscopic or macroscopic data set.
*Default*: If no alias is provided, it will adopt the value of ``name``.
:kT:
The temperature times Boltzmann's constant (in units of MeV) at which the
data was generated.
*Default*: Room temperature, 2.53E-8 MeV
:fissionable:
This element states whether or not the data in question is fissionable.
Accepted values are "true" or "false".
*Default*: None, this element must be provided.
:representation:
This element provides the method used to generate and represent the
multi-group cross sections. That is, whether they were generated with
scalar flux weighting (or reduced to an equivalent representation)
and thus are angle-independent, or if the data was generated with angular
dependent fluxes and thus the data is angle-dependent. The options are
either "isotropic" or "angle".
*Default*: "isotropic"
:num_azimuthal:
This element provides the number of equal width angular bins that the
azimuthal angular domain is subdivided in the case of angle-dependent
cross sections (i.e., "angle" is passed to the ``representation`` element).
Note that these bins are equal in azimuthal angle widths, not equal in the
cosine of the azimuthal angle widths.
*Default*: If ``representation`` is "angle", this must be provided. This
parameter is not used for other ``representation`` types.
:num_polar:
This element provides the number of equal width angular bins that the
polar angular domain is subdivided in the case of angle-dependent
cross sections (i.e., "angle" is passed to the ``representation`` element).
Note that these bins are equal in polar angle widths, not equal in the
cosine of the polar angle widths.
*Default*: If ``representation`` is "angle", this must be provided. This
parameter is not used for other ``representation`` types.
:scatt_type:
This element provides the representation of the angular distribution
associated with each group-to-group transfer probability. The options are
either "legendre", "histogram", or "tabular".
The "legendre" option means the angular distribution has been
expanded via Legendre polynomials of the order provided in the "order"
element.
The "histogram" option means the angular distribution is provided in
an equi-width histogram format with a number of bins as provided in the
"order" element. This is useful when the angular distribution was
obtained from a Monte Carlo tally and thus is natively in the histogram
format.
The "tabular" option means the angular distribution is provided in an
equi-spaced point-wise representation.
*Default*: "legendre"
:order:
This element provides either the Legendre order, number of bins, or number
of points used to describe the angular distribution associated with each
group-to-group transfer probability. The specific meaning of this bin
depends upon the value of ``scatt_type`` as discussed above.
*Default*: None, this element must be provided.
:tabular_legendre:
This optional element is used to set how the Legendre scattering kernel, if
provided via the ``scatt_type`` element above, is represented and thus used
during the scattering process. Specifically, the options are to either
convert the Legendre expansion to a tabular representation or leave it as
a set of Legendre coefficients. Converting to a tabular representation
will cost memory but can allow for a decrease in runtime compared to
leaving as a set of Legendre coefficients. This element has the following
attributes/sub-elements:
:enable:
This attribute/sub-element denotes whether or not the conversion to the
tabular format should be performed or not. A value of "true" means
the conversion should be performed, "false" means it should not.
*Default*: "true"
:num_points:
If the conversion is to take place the number of tabular points is
required. This attribute/sub-element allows the user to set the desired
number of points.
*Default*: 33
The following attributes/sub-elements are the cross section values to
be used during the transport process.
:total:
This element requires the group-wise total cross section ordered by
increasing group index (i.e., fast to thermal). If ``representation`` is
"isotropic", then the length of this list should equal the number of
groups described in the ``groups`` element. If ``representation`` is
"angle", then the length of this list should equal the number of groups
times the number of azimuthal angles times the number of polar angles,
with the inner-dimension being groups, intermediate-dimension being
azimuthal angles and outer-dimension being the polar angles.
*Default*: If not provided, it will be determined by summing the
absorption and scattering cross sections.
:absorption:
This element requires the group-wise absorption cross section ordered by
increasing group index (i.e., fast to thermal). If ``representation`` is
"isotropic", then the length of this list should equal the number of
groups described in the ``groups`` element. If ``representation`` is
"angle", then the length of this list should equal the number of groups
times the number of azimuthal angles times the number of polar angles,
with the inner-dimension being groups, intermediate-dimension being
azimuthal angles and outer-dimension being the polar angles.
*Default*: None, this must be provided.
:scatter:
This element requires the scattering moment matrices presented with the
columns representing incoming group and rows representing the outgoing
group. That is, down-scatter will be above the diagonal of the resultant
matrix. This matrix is repeated for every Legendre order (in order of
increasing orders) if ``scatt_type`` is "legendre"; otherwise, this
matrix is repeated for every bin of the histogram or tabular
representation. Finally, if ``representation`` is "angle", the above
is repeated for every azimuthal angle and every polar angle, in that
order.
*Default*: None, this must be provided.
:multiplicity:
This element provides the ratio of neutrons produced in scattering
collisions to the neutrons which undergo scattering collisions; that is,
the multiplicity provides the code with a scaling factor to account for
neutrons being produced in (n,xn) reactions. This information is assumed
isotropic and therefore does not need to be repeated for every Legendre
moment or histogram/tabular bin. This matrix follows the same arrangement
as described for the ``scatter`` element, with the exception of the
data needed to provide the scattering type information.
*Default*: Multiplicities of 1.0 are assumed (i.e., (n,xn) reactions are
neglected).
The following fission-specific data are only needed should ``fissionable``
be "true".
:fission:
This element requires the group-wise fission cross section ordered by
increasing group index (i.e., fast to thermal). If ``representation`` is
"isotropic", then the length of this list should equal the number of
groups described in the ``groups`` element. If ``representation`` is
"angle", then the length of this list should equal the number of groups
times the number of azimuthal angles times the number of polar angles,
with the inner-dimension being groups, intermediate-dimension being
azimuthal angles and outer-dimension being the polar angles.
*Default*: None, this is required only if fission tallies are
requested and the material is fissionable.
:kappa_fission:
This element requires the group-wise kappa-fission cross section ordered by
increasing group index (i.e., fast to thermal). If ``representation`` is
"isotropic", then the length of this list should equal the number of
groups described in the ``groups`` element. If ``representation`` is
"angle", then the length of this list should equal the number of groups
times the number of azimuthal angles times the number of polar angles,
with the inner-dimension being groups, intermediate-dimension being
azimuthal angles and outer-dimension being the polar angles.
*Default*: None, this is required only if kappa_fission tallies are
requested and the material is fissionable.
:chi:
This element requires the group-wise fission spectra ordered by
increasing group index (i.e., fast to thermal). This element should be
used if making the common approximation that the fission spectra does
not depend on incoming energy. If the user does not wish to make this
approximation, then this should not be provided and this information
included in the ``nu_fission`` element instead. If ``representation`` is
"isotropic", then the length of this list should equal the number of
groups described in the ``groups`` element. If ``representation`` is
"angle", then the length of this list should equal the number of groups
times the number of azimuthal angles times the number of polar angles,
with the inner-dimension being groups, intermediate-dimension being
azimuthal angles and outer-dimension being the polar angles.
*Default*: None, either this element is provided or ``nu_fission`` is
provided in fission matrix form, or the material is not fissionable.
:nu_fission:
This element provides either the group-wise fission production cross
section vector (i.e., if ``chi`` is provided), or is the group-wise fission
production matrix. If providing the vector, it should be ordered the same
as the ``fission`` data. If providing the matrix, it should be ordered
the same as the ``multiplicity`` matrix.
*Default*: None, either this element must be provided if the material
is fissionable.
Data specific to neutron scattering for the temperature <TTT>K
:Datasets: - **g_min** (*int[]* or *int[][][]*) --
Minimum (most energetic) groups with non-zero values of
the scattering matrix provided. If `scatter_shape` is
"[Order][G][G']" then `g_min` will describe the minimum values
of "G'" for each "G"; if `scatter_shape` is "[Order][G'][G]"
then `g_min` will describe the minimum values of "G" for each "G'".
These group numbers use the standard
ordering where the fastest neutron energy group is group 1 while
the slowest neutron energy group is group G.
The dimensionality of `g_min` is:
`g_min[g]`, or `g_min[num_polar][num_azimuthal][g]`.
The former is used when `representation` is "isotropic", and the
latter when `representation` is "angle".
- **g_max** (*int[]* or *int[][][]*) --
Similar to `g_min`, except this dataset describes the maximum
(least energetic) groups with non-zero values of
the scattering matrix.
- **scatter_matrix** (*double[]*) -- Flattened representation of the
scattering moment matrices. The pre-flattened array corresponds to
the shape provied in `scatter_shape`, but if `representation` is
"angle" the dimensionality in `scatter_shape` is prepended by
"[num_polar][num_azimuthal]" dimensions. The right-most energy
group dimension will only include the entries between `g_min` and
`g_max`.
dimension has a dimensionality of `g_min` to `g_max`.
- **multiplicity_matrix** (*double[]*) -- Flattened representation of
the scattering moment matrices. This dataset provides the code with
a scaling factor to account for neutrons being produced in (n,xn)
reactions. This is assumed isotropic and therefore is not repeated
for every Legendre moment or histogram/tabular bin. This dataset is
optional, if it is not provided no multiplication (i.e., values of
1.0) will be assumed.
The pre-flattened array is shapes consistent with `scatter_matrix`
except the "[Order]" dimension in `scatter_shape` is ignored since
this data is assumed isotropic.

File diff suppressed because one or more lines are too long

View file

@ -345,6 +345,7 @@ Functions
.. autosummary::
:toctree: generated
:nosignatures:
:template: myfunction.rst
openmc.model.create_triso_lattice
openmc.model.pack_trisos
@ -353,16 +354,6 @@ Functions
:mod:`openmc.data` -- Nuclear Data Interface
--------------------------------------------
Physical Data
-------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myfunction.rst
openmc.data.atomic_mass
Core Classes
------------
@ -375,11 +366,23 @@ Core Classes
openmc.data.Reaction
openmc.data.Product
openmc.data.Tabulated1D
openmc.data.FissionEnergyRelease
openmc.data.ThermalScattering
openmc.data.CoherentElastic
openmc.data.FissionEnergyRelease
openmc.data.DataLibrary
Core Functions
--------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myfunction.rst
openmc.data.atomic_mass
openmc.data.write_compact_458_library
Angle-Energy Distributions
--------------------------
@ -393,6 +396,7 @@ Angle-Energy Distributions
openmc.data.CorrelatedAngleEnergy
openmc.data.UncorrelatedAngleEnergy
openmc.data.NBodyPhaseSpace
openmc.data.LaboratoryAngleEnergy
openmc.data.AngleDistribution
openmc.data.EnergyDistribution
openmc.data.ArbitraryTabulated
@ -405,6 +409,24 @@ Angle-Energy Distributions
openmc.data.LevelInelastic
openmc.data.ContinuousTabular
Resonance Data
--------------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.data.Resonances
openmc.data.ResonanceRange
openmc.data.SingleLevelBreitWigner
openmc.data.MultiLevelBreitWigner
openmc.data.ReichMoore
openmc.data.RMatrixLimited
openmc.data.ParticlePair
openmc.data.SpinGroup
openmc.data.Unresolved
ACE Format
----------
@ -425,9 +447,37 @@ Functions
.. autosummary::
:toctree: generated
:nosignatures:
:template: myfunction.rst
openmc.data.ace.ascii_to_binary
openmc.data.write_compact_458_library
ENDF Format
-----------
Classes
+++++++
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.data.endf.Evaluation
Functions
+++++++++
.. autosummary::
:toctree: generated
:nosignatures:
:template: myfunction.rst
openmc.data.endf.float_endf
openmc.data.endf.get_cont_record
openmc.data.endf.get_head_record
openmc.data.endf.get_tab1_record
openmc.data.endf.get_tab2_record
openmc.data.endf.get_text_record
.. _Jupyter: https://jupyter.org/
.. _NumPy: http://www.numpy.org/

View file

@ -8,6 +8,26 @@ This quick install guide outlines the basic steps needed to install OpenMC on
your computer. For more detailed instructions on configuring and installing
OpenMC, see :ref:`usersguide_install` in the User's Manual.
----------------------------------------
Installing on Linux/Mac with conda-forge
----------------------------------------
`Conda <http://conda.pydata.org/docs/>`_ is an open source package management
system and environment management system for installing multiple versions of
software packages and their dependencies and switching easily between them. If
you have `conda` installed on your system, OpenMC can be installed via the
`conda-forge` channel. First, add the `conda-forge` channel with:
.. code-block:: sh
conda config --add channels conda-forge
OpenMC can then be installed with:
.. code-block:: sh
conda install openmc
--------------------------------
Installing on Ubuntu through PPA
--------------------------------

View file

@ -121,10 +121,13 @@ multi-group mode.
``<cutoff>`` Element
--------------------
The ``<cutoff>`` element indicates the weight cutoff used below which particles
undergo Russian roulette. Surviving particles are assigned a user-determined
weight. Note that weight cutoffs and Russian rouletting are not turned on by
default. This element has the following attributes/sub-elements:
The ``<cutoff>`` element indicates two kinds of cutoffs. The first is the weight
cutoff used below which particles undergo Russian roulette. Surviving particles
are assigned a user-determined weight. Note that weight cutoffs and Russian
rouletting are not turned on by default. The second is the energy cutoff which
is used to kill particles under certain energy. The energy cutoff should not be
used unless you know particles under the energy are of no importance to results
you care. This element has the following attributes/sub-elements:
:weight:
The weight below which particles undergo Russian roulette.
@ -137,6 +140,11 @@ default. This element has the following attributes/sub-elements:
*Default*: 1.0
:energy:
The energy under which particles will be killed.
*Default*: 0.0
.. _eigenvalue:
``<eigenvalue>`` Element
@ -709,6 +717,36 @@ survival biasing, otherwise known as implicit capture or absorption.
*Default*: false
.. _tabular_legendre:
``<tabular_legendre>`` Element
---------------------------------
The optional ``<tabular_legendre>`` element specifies how the multi-group
Legendre scattering kernel is represented if encountered in a multi-group
problem. Specifically, the options are to either convert the Legendre
expansion to a tabular representation or leave it as a set of Legendre
coefficients. Converting to a tabular representation will cost memory but can
allow for a decrease in runtime compared to leaving as a set of Legendre
coefficients. This element has the following attributes/sub-elements:
:enable:
This attribute/sub-element denotes whether or not the conversion of a
Legendre scattering expansion to the tabular format should be performed or
not. A value of “true” means the conversion should be performed, “false”
means it will not.
*Default*: true
:num_points:
If the conversion is to take place the number of tabular points is
required. This attribute/sub-element allows the user to set the desired
number of points.
*Default*: 33
.. note:: This element is only used in the multi-group :ref:`energy_mode`.
.. _temperature_default:
``<temperature_default>`` Element
@ -872,6 +910,19 @@ displayed. This element takes the following attributes:
*Default*: 5
``<create_fission_neutrons>`` Element
-------------------------------------
The ``<create_fission_neutrons>`` element indicates whether fission neutrons
should be created or not. If this element is set to "true", fission neutrons
will be created; otherwise the fission is treated as capture and no fission
neutron will be created. Note that this option is only applied to fixed source
calculation. For eigenvalue calculation, fission will always be treated as real
fission.
*Default*: true
``<volume_calc>`` Element
-------------------------

View file

@ -4,6 +4,38 @@
Installation and Configuration
==============================
----------------------------------------
Installing on Linux/Mac with conda-forge
----------------------------------------
`Conda <http://conda.pydata.org/docs/>`_ is an open source package management
system and environment management system for installing multiple versions of
software packages and their dependencies and switching easily between
them. `conda-forge <https://conda-forge.github.io/>`_ is a community-led conda
channel of installable packages. For instructions on installing conda, please
consult their `documentation
<http://conda.pydata.org/docs/install/quick.html>`_.
Once you have `conda` installed on your system, add the `conda-forge` channel to
your configuration with:
.. code-block:: sh
conda config --add channels conda-forge
Once the `conda-forge` channel has been enabled, OpenMC can then be installed
with:
.. code-block:: sh
conda install openmc
It is possible to list all of the versions of OpenMC available on your platform with:
.. code-block:: sh
conda search openmc --channel conda-forge
-----------------------------
Installing on Ubuntu with PPA
-----------------------------
@ -51,7 +83,7 @@ Prerequisites
installed on your machine. Since a number of Fortran 2003/2008 features
are used in the code, it is recommended that you use the latest version of
whatever compiler you choose. For gfortran_, it is necessary to use
version 4.6.0 or above.
version 4.8.0 or above.
If you are using Debian or a Debian derivative such as Ubuntu, you can
install the gfortran compiler using the following command::
@ -407,13 +439,11 @@ extract the ACE data, fix any deficiencies, and create an HDF5 library:
.. code-block:: sh
cd openmc/data
python get_nndc_data.py
openmc-get-nndc-data
At this point, you should set the :envvar:`OPENMC_CROSS_SECTIONS` environment
variable to the absolute path of the file
``openmc/data/nndc_hdf5/cross_sections.xml``. This cross section set is used by
the test suite.
variable to the absolute path of the file ``nndc_hdf5/cross_sections.xml``. This
cross section set is used by the test suite.
Using JEFF Cross Sections from OECD/NEA
---------------------------------------
@ -424,12 +454,10 @@ and extract the ACE data, fix any deficiencies, and create an HDF5 library.
.. code-block:: sh
cd openmc/data
python get_jeff_data.py
openmc-get-jeff-data
At this point, you should set the :envvar:`OPENMC_CROSS_SECTIONS` environment
variable to the absolute path of the file
``openmc/data/jeff-3.2-hdf5/cross_sections.xml``.
variable to the absolute path of the file ``jeff-3.2-hdf5/cross_sections.xml``.
Using Cross Sections from MCNP
------------------------------
@ -441,8 +469,7 @@ format, run the following:
.. code-block:: sh
cd openmc/data
python convert_mcnp_endf70.py /path/to/mcnpdata/
openmc-convert-mcnp70-data /path/to/mcnpdata/
where ``/path/to/mcnpdata`` is the directory containing the ``endf70[a-k]``
files.
@ -452,8 +479,7 @@ the following script:
.. code-block:: sh
cd openmc/data
python convert_mcnp_endf71.py /path/to/mcnpdata
openmc-convert-mcnp71-data /path/to/mcnpdata
where ``/path/to/mcnpdata`` is the directory containing the ``endf71x`` and
``ENDF71SaB`` directories.
@ -470,16 +496,16 @@ that are to be converted:
1. List each ACE library as a positional argument. This is very useful in
conjunction with the usual shell utilities (ls, find, etc.).
2. Use the --xml option to specify a pre-v0.9 cross_sections.xml file.
3. Use the --xsdir option to specify a MCNP xsdir file.
4. Use the --xsdata option to specify a Serpent xsdata file.
2. Use the ``--xml`` option to specify a pre-v0.9 cross_sections.xml file.
3. Use the ``--xsdir` option to specify a MCNP xsdir file.
4. Use the ``--xsdata`` option to specify a Serpent xsdata file.
The script does not use any extra information from cross_sections.xml/ xsdir/
xsdata files to determine whether the nuclide is metastable. Instead, the
--metastable argument can be used to specify whether the ZAID naming convention
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).
``--metastable`` argument can be used to specify whether the ZAID naming
convention 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 ``openmc-ace-to-hdf5`` script has the following command-line flags:

View file

@ -21,44 +21,47 @@ groups = openmc.mgxs.EnergyGroups(group_edges=[1E-11, 0.0635E-6, 10.0E-6,
# Instantiate the 7-group (C5G7) cross section data
uo2_xsdata = openmc.XSdata('UO2', groups)
uo2_xsdata.order = 0
uo2_xsdata.total = [0.1779492, 0.3298048, 0.4803882, 0.5543674,
0.3118013, 0.3951678, 0.5644058]
uo2_xsdata.absorption = [8.0248E-03, 3.7174E-03, 2.6769E-02, 9.6236E-02,
3.0020E-02, 1.1126E-01, 2.8278E-01]
uo2_xsdata.scatter = [[[0.1275370, 0.0423780, 0.0000094, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
[0.0000000, 0.3244560, 0.0016314, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
[0.0000000, 0.0000000, 0.4509400, 0.0026792, 0.0000000, 0.0000000, 0.0000000],
[0.0000000, 0.0000000, 0.0000000, 0.4525650, 0.0055664, 0.0000000, 0.0000000],
[0.0000000, 0.0000000, 0.0000000, 0.0001253, 0.2714010, 0.0102550, 0.0000000],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0012968, 0.2658020, 0.0168090],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0085458, 0.2730800]]]
uo2_xsdata.fission = [7.21206E-03, 8.19301E-04, 6.45320E-03,
1.85648E-02, 1.78084E-02, 8.30348E-02,
2.16004E-01]
uo2_xsdata.nu_fission = [2.005998E-02, 2.027303E-03, 1.570599E-02,
4.518301E-02, 4.334208E-02, 2.020901E-01,
5.257105E-01]
uo2_xsdata.chi = [5.8791E-01, 4.1176E-01, 3.3906E-04, 1.1761E-07,
0.0000E+00, 0.0000E+00, 0.0000E+00]
uo2_xsdata.set_total(
[0.1779492, 0.3298048, 0.4803882, 0.5543674, 0.3118013, 0.3951678,
0.5644058])
uo2_xsdata.set_absorption([8.0248E-03, 3.7174E-03, 2.6769E-02, 9.6236E-02,
3.0020E-02, 1.1126E-01, 2.8278E-01])
uo2_xsdata.set_scatter_matrix(
[[[0.1275370, 0.0423780, 0.0000094, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
[0.0000000, 0.3244560, 0.0016314, 0.0000000, 0.0000000, 0.0000000, 0.0000000],
[0.0000000, 0.0000000, 0.4509400, 0.0026792, 0.0000000, 0.0000000, 0.0000000],
[0.0000000, 0.0000000, 0.0000000, 0.4525650, 0.0055664, 0.0000000, 0.0000000],
[0.0000000, 0.0000000, 0.0000000, 0.0001253, 0.2714010, 0.0102550, 0.0000000],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0012968, 0.2658020, 0.0168090],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0085458, 0.2730800]]])
uo2_xsdata.set_fission([7.21206E-03, 8.19301E-04, 6.45320E-03,
1.85648E-02, 1.78084E-02, 8.30348E-02,
2.16004E-01])
uo2_xsdata.set_nu_fission([2.005998E-02, 2.027303E-03, 1.570599E-02,
4.518301E-02, 4.334208E-02, 2.020901E-01,
5.257105E-01])
uo2_xsdata.set_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', groups)
h2o_xsdata.order = 0
h2o_xsdata.total = [0.15920605, 0.412969593, 0.59030986, 0.58435,
0.718, 1.2544497, 2.650379]
h2o_xsdata.absorption = [6.0105E-04, 1.5793E-05, 3.3716E-04,
1.9406E-03, 5.7416E-03, 1.5001E-02,
3.7239E-02]
h2o_xsdata.scatter = [[[0.0444777, 0.1134000, 0.0007235, 0.0000037, 0.0000001, 0.0000000, 0.0000000],
[0.0000000, 0.2823340, 0.1299400, 0.0006234, 0.0000480, 0.0000074, 0.0000010],
[0.0000000, 0.0000000, 0.3452560, 0.2245700, 0.0169990, 0.0026443, 0.0005034],
[0.0000000, 0.0000000, 0.0000000, 0.0910284, 0.4155100, 0.0637320, 0.0121390],
[0.0000000, 0.0000000, 0.0000000, 0.0000714, 0.1391380, 0.5118200, 0.0612290],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0022157, 0.6999130, 0.5373200],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.1324400, 2.4807000]]]
h2o_xsdata.set_total([0.15920605, 0.412969593, 0.59030986, 0.58435,
0.718, 1.2544497, 2.650379])
h2o_xsdata.set_absorption([6.0105E-04, 1.5793E-05, 3.3716E-04,
1.9406E-03, 5.7416E-03, 1.5001E-02,
3.7239E-02])
h2o_xsdata.set_scatter_matrix(
[[[0.0444777, 0.1134000, 0.0007235, 0.0000037, 0.0000001, 0.0000000, 0.0000000],
[0.0000000, 0.2823340, 0.1299400, 0.0006234, 0.0000480, 0.0000074, 0.0000010],
[0.0000000, 0.0000000, 0.3452560, 0.2245700, 0.0169990, 0.0026443, 0.0005034],
[0.0000000, 0.0000000, 0.0000000, 0.0910284, 0.4155100, 0.0637320, 0.0121390],
[0.0000000, 0.0000000, 0.0000000, 0.0000714, 0.1391380, 0.5118200, 0.0612290],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0022157, 0.6999130, 0.5373200],
[0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.1324400, 2.4807000]]])
mg_cross_sections_file = openmc.MGXSLibrary(groups)
mg_cross_sections_file.add_xsdatas([uo2_xsdata, h2o_xsdata])
mg_cross_sections_file.export_to_xml()
mg_cross_sections_file.export_to_hdf5()
###############################################################################
@ -129,7 +132,7 @@ geometry.export_to_xml()
# Instantiate a Settings object, set all runtime parameters, and export to XML
settings_file = openmc.Settings()
settings_file.energy_mode = "multi-group"
settings_file.cross_sections = "./mgxs.xml"
settings_file.cross_sections = "./mgxs.h5"
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles

View file

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

Binary file not shown.

View file

@ -28,13 +28,12 @@
</source>
<output>
<cross_sections>true</cross_sections>
<summary>true</summary>
<tallies>true</tallies>
</output>
<survival_biasing>false</survival_biasing>
<cross_sections>./mg_cross_sections.xml</cross_sections>
<cross_sections>./mgxs.h5</cross_sections>
</settings>

View file

@ -2,14 +2,13 @@ import sys
import copy
from collections import Iterable
from six import string_types
import numpy as np
import openmc
from openmc.filter import _FILTER_TYPES
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
# Acceptable tally arithmetic binary operations
_TALLY_ARITHMETIC_OPS = ['+', '-', '*', '/', '^']
@ -86,18 +85,18 @@ class CrossScore(object):
@left_score.setter
def left_score(self, left_score):
cv.check_type('left_score', left_score,
(basestring, CrossScore, AggregateScore))
string_types + (CrossScore, AggregateScore))
self._left_score = left_score
@right_score.setter
def right_score(self, right_score):
cv.check_type('right_score', right_score,
(basestring, CrossScore, AggregateScore))
string_types + (CrossScore, AggregateScore))
self._right_score = right_score
@binary_op.setter
def binary_op(self, binary_op):
cv.check_type('binary_op', binary_op, basestring)
cv.check_type('binary_op', binary_op, string_types)
cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS)
self._binary_op = binary_op
@ -202,7 +201,7 @@ class CrossNuclide(object):
@binary_op.setter
def binary_op(self, binary_op):
cv.check_type('binary_op', binary_op, basestring)
cv.check_type('binary_op', binary_op, string_types)
cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS)
self._binary_op = binary_op
@ -343,7 +342,7 @@ class CrossFilter(object):
@binary_op.setter
def binary_op(self, binary_op):
cv.check_type('binary_op', binary_op, basestring)
cv.check_type('binary_op', binary_op, string_types)
cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS)
self._binary_op = binary_op
@ -495,12 +494,12 @@ class AggregateScore(object):
@scores.setter
def scores(self, scores):
cv.check_iterable_type('scores', scores, basestring)
cv.check_iterable_type('scores', scores, string_types)
self._scores = scores
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, (basestring, CrossScore))
cv.check_type('aggregate_op', aggregate_op, string_types +(CrossScore,))
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
@ -575,12 +574,12 @@ class AggregateNuclide(object):
@nuclides.setter
def nuclides(self, nuclides):
cv.check_iterable_type('nuclides', nuclides,
(basestring, openmc.Nuclide, CrossNuclide))
string_types + (openmc.Nuclide, CrossNuclide))
self._nuclides = nuclides
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, basestring)
cv.check_type('aggregate_op', aggregate_op, string_types)
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op
@ -711,7 +710,7 @@ class AggregateFilter(object):
@aggregate_op.setter
def aggregate_op(self, aggregate_op):
cv.check_type('aggregate_op', aggregate_op, basestring)
cv.check_type('aggregate_op', aggregate_op, string_types)
cv.check_value('aggregate_op', aggregate_op, _TALLY_AGGREGATE_OPS)
self._aggregate_op = aggregate_op

View file

@ -5,6 +5,7 @@ from xml.etree import ElementTree as ET
import sys
import warnings
from six import string_types
import numpy as np
import openmc
@ -12,9 +13,6 @@ import openmc.checkvalue as cv
from openmc.surface import Halfspace
from openmc.region import Region, Intersection, Complement
if sys.version_info[0] >= 3:
basestring = str
# A static variable for auto-generated Cell IDs
AUTO_CELL_ID = 10000
@ -243,7 +241,7 @@ class Cell(object):
@name.setter
def name(self, name):
if name is not None:
cv.check_type('cell name', name, basestring)
cv.check_type('cell name', name, string_types)
self._name = name
else:
self._name = ''
@ -251,7 +249,7 @@ class Cell(object):
@fill.setter
def fill(self, fill):
if fill is not None:
if isinstance(fill, basestring):
if isinstance(fill, string_types):
if fill.strip().lower() != 'void':
msg = 'Unable to set Cell ID="{0}" to use a non-Material ' \
'or Universe fill "{1}"'.format(self._id, fill)
@ -336,7 +334,7 @@ class Cell(object):
@distribcell_paths.setter
def distribcell_paths(self, distribcell_paths):
cv.check_iterable_type('distribcell_paths', distribcell_paths,
basestring)
string_types)
self._distribcell_paths = distribcell_paths
def add_surface(self, surface, halfspace):

View file

@ -15,13 +15,12 @@ from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
from six import string_types
from openmc.clean_xml import clean_xml_indentation
from openmc.checkvalue import (check_type, check_length, check_value,
check_greater_than, check_less_than)
if sys.version_info[0] >= 3:
basestring = str
class CMFDMesh(object):
"""A structured Cartesian mesh used for Coarse Mesh Finite Difference (CMFD)
@ -339,7 +338,7 @@ class CMFD(object):
@display.setter
def display(self, display):
check_type('CMFD display', display, basestring)
check_type('CMFD display', display, string_types)
check_value('CMFD display', display,
['balance', 'dominance', 'entropy', 'source'])
self._display = display

View file

@ -4,6 +4,7 @@ from .reaction import *
from .ace import *
from .angle_distribution import *
from .function import *
from .endf import *
from .energy_distribution import *
from .product import *
from .angle_energy import *
@ -15,3 +16,4 @@ from .thermal import *
from .urr import *
from .library import *
from .fission_energy import *
from .resonance import *

View file

@ -20,15 +20,12 @@ from os import SEEK_CUR
import struct
import sys
from six import string_types
import numpy as np
from openmc.mixin import EqualityMixin
if sys.version_info[0] >= 3:
basestring = str
def ascii_to_binary(ascii_file, binary_file):
"""Convert an ACE file in ASCII format (type 1) to binary format (type 2).
@ -156,7 +153,7 @@ class Library(EqualityMixin):
"""
def __init__(self, filename, table_names=None, verbose=False):
if isinstance(table_names, basestring):
if isinstance(table_names, string_types):
table_names = [table_names]
if table_names is not None:
table_names = set(table_names)

View file

@ -1,12 +1,16 @@
from collections import Iterable
from io import StringIO
from numbers import Real
from warnings import warn
import numpy as np
import openmc.checkvalue as cv
from openmc.mixin import EqualityMixin
from openmc.stats import Univariate, Tabular, Uniform
from openmc.stats import Univariate, Tabular, Uniform, Legendre
from .function import INTERPOLATION_SCHEME
from .endf import get_head_record, get_cont_record, get_tab1_record, \
get_list_record, get_tab2_record
class AngleDistribution(EqualityMixin):
@ -199,3 +203,107 @@ class AngleDistribution(EqualityMixin):
mu.append(mu_i)
return cls(energy, mu)
@classmethod
def from_endf(cls, ev, mt):
"""Generate an angular distribution from an ENDF evaluation
Parameters
----------
ev : openmc.data.endf.Evaluation
ENDF evaluation
mt : int
The MT value of the reaction to get angular distributions for
Returns
-------
openmc.data.AngleDistribution
Angular distribution
"""
file_obj = StringIO(ev.section[4, mt])
# Read HEAD record
items = get_head_record(file_obj)
lvt = items[2]
ltt = items[3]
# Read CONT record
items = get_cont_record(file_obj)
li = items[2]
nk = items[4]
center_of_mass = (items[3] == 2)
# Check for obsolete energy transformation matrix. If present, just skip
# it and keep reading
if lvt > 0:
warn('Obsolete energy transformation matrix in MF=4 angular '
'distribution.')
for _ in range((nk + 5)//6):
file_obj.readline()
if ltt == 0 and li == 1:
# Purely isotropic
energy = np.array([0., ev.info['energy_max']])
mu = [Uniform(-1., 1.), Uniform(-1., 1.)]
elif ltt == 1 and li == 0:
# Legendre polynomial coefficients
params, tab2 = get_tab2_record(file_obj)
n_energy = params[5]
energy = np.zeros(n_energy)
mu = []
for i in range(n_energy):
items, al = get_list_record(file_obj)
temperature = items[0]
energy[i] = items[1]
coefficients = np.asarray([1.0] + al)
mu.append(Legendre(coefficients))
elif ltt == 2 and li == 0:
# Tabulated probability distribution
params, tab2 = get_tab2_record(file_obj)
n_energy = params[5]
energy = np.zeros(n_energy)
mu = []
for i in range(n_energy):
params, f = get_tab1_record(file_obj)
temperature = params[0]
energy[i] = params[1]
if f.n_regions > 1:
raise NotImplementedError('Angular distribution with multiple '
'interpolation regions not supported.')
mu.append(Tabular(f.x, f.y, INTERPOLATION_SCHEME[f.interpolation[0]]))
elif ltt == 3 and li == 0:
# Legendre for low energies / tabulated for high energies
params, tab2 = get_tab2_record(file_obj)
n_energy_legendre = params[5]
energy_legendre = np.zeros(n_energy_legendre)
mu = []
for i in range(n_energy_legendre):
items, al = get_list_record(file_obj)
temperature = items[0]
energy_legendre[i] = items[1]
coefficients = np.asarray([1.0] + al)
mu.append(Legendre(coefficients))
params, tab2 = get_tab2_record(file_obj)
n_energy_tabulated = params[5]
energy_tabulated = np.zeros(n_energy_tabulated)
for i in range(n_energy_tabulated):
params, f = get_tab1_record(file_obj)
temperature = params[0]
energy_tabulated[i] = params[1]
if f.n_regions > 1:
raise NotImplementedError('Angular distribution with multiple '
'interpolation regions not supported.')
mu.append(Tabular(f.x, f.y, INTERPOLATION_SCHEME[f.interpolation[0]]))
energy = np.concatenate((energy_legendre, energy_tabulated))
return AngleDistribution(energy, mu)

View file

@ -1,14 +1,15 @@
from abc import ABCMeta, abstractmethod
from io import StringIO
from six import add_metaclass
import openmc.data
from openmc.mixin import EqualityMixin
@add_metaclass(ABCMeta)
class AngleEnergy(EqualityMixin):
"""Distribution in angle and energy of a secondary particle."""
__metaclass = ABCMeta
@abstractmethod
def to_hdf5(self, group):
pass
@ -40,7 +41,7 @@ class AngleEnergy(EqualityMixin):
@staticmethod
def from_ace(ace, location_dist, location_start, rx=None):
"""Generate an AngleEnergy object from ACE data
"""Generate an angle-energy distribution from ACE data
Parameters
----------

View file

@ -5,9 +5,11 @@ from warnings import warn
import numpy as np
import openmc.checkvalue as cv
from openmc.stats import Tabular, Univariate, Discrete, Mixture, Uniform
from openmc.stats import Tabular, Univariate, Discrete, Mixture, \
Uniform, Legendre
from .function import INTERPOLATION_SCHEME
from .angle_energy import AngleEnergy
from .endf import get_list_record, get_tab2_record
class CorrelatedAngleEnergy(AngleEnergy):
@ -405,3 +407,52 @@ class CorrelatedAngleEnergy(AngleEnergy):
mu.append(mu_i)
return cls(breakpoints, interpolation, energy, energy_out, mu)
@classmethod
def from_endf(cls, file_obj):
"""Generate correlated angle-energy distribution from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positioned at the start of a section for a correlated
angle-energy distribution
Returns
-------
openmc.data.CorrelatedAngleEnergy
Correlated angle-energy distribution
"""
params, tab2 = get_tab2_record(file_obj)
lep = params[3]
ne = params[5]
energy = np.zeros(ne)
n_discrete_energies = np.zeros(ne, dtype=int)
energy_out = []
mu = []
for i in range(ne):
items, values = get_list_record(file_obj)
energy[i] = items[1]
n_discrete_energies[i] = items[2]
# TODO: separate out discrete lines
n_angle = items[3]
n_energy_out = items[5]
values = np.asarray(values)
values.shape = (n_energy_out, n_angle + 2)
# Outgoing energy distribution at the i-th incoming energy
eout_i = values[:,0]
eout_p_i = values[:,1]
energy_out_i = Tabular(eout_i, eout_p_i, INTERPOLATION_SCHEME[lep],
ignore_negative=True)
energy_out.append(energy_out_i)
# Legendre coefficients used for angular distributions
mu_i = []
for j in range(n_energy_out):
mu_i.append(Legendre(values[j,1:]))
mu.append(mu_i)
return cls(tab2.breakpoints, tab2.interpolation, energy,
energy_out, mu)

View file

@ -104,8 +104,8 @@ NATURAL_ABUNDANCE = {
'U234': 5.4e-05, 'U235': 0.007204, 'U238': 0.992742
}
ATOMIC_SYMBOL = {1: 'H', 2: 'He', 3: 'Li', 4: 'Be', 5: 'B', 6: 'C', 7: 'N',
8: 'O', 9: 'F', 10: 'Ne', 11: 'Na', 12: 'Mg', 13: 'Al',
ATOMIC_SYMBOL = {0: 'n', 1: 'H', 2: 'He', 3: 'Li', 4: 'Be', 5: 'B', 6: 'C',
7: 'N', 8: 'O', 9: 'F', 10: 'Ne', 11: 'Na', 12: 'Mg', 13: 'Al',
14: 'Si', 15: 'P', 16: 'S', 17: 'Cl', 18: 'Ar', 19: 'K',
20: 'Ca', 21: 'Sc', 22: 'Ti', 23: 'V', 24: 'Cr', 25: 'Mn',
26: 'Fe', 27: 'Co', 28: 'Ni', 29: 'Cu', 30: 'Zn', 31: 'Ga',
@ -129,60 +129,6 @@ ATOMIC_NUMBER = {value: key for key, value in ATOMIC_SYMBOL.items()}
_ATOMIC_MASS = {}
REACTION_NAME = {1: '(n,total)', 2: '(n,elastic)', 4: '(n,level)',
5: '(n,misc)', 11: '(n,2nd)', 16: '(n,2n)', 17: '(n,3n)',
18: '(n,fission)', 19: '(n,f)', 20: '(n,nf)', 21: '(n,2nf)',
22: '(n,na)', 23: '(n,n3a)', 24: '(n,2na)', 25: '(n,3na)',
27: '(n,absorption)', 28: '(n,np)', 29: '(n,n2a)',
30: '(n,2n2a)', 32: '(n,nd)', 33: '(n,nt)', 34: '(n,nHe-3)',
35: '(n,nd2a)', 36: '(n,nt2a)', 37: '(n,4n)', 38: '(n,3nf)',
41: '(n,2np)', 42: '(n,3np)', 44: '(n,n2p)', 45: '(n,npa)',
91: '(n,nc)', 101: '(n,disappear)', 102: '(n,gamma)',
103: '(n,p)', 104: '(n,d)', 105: '(n,t)', 106: '(n,3He)',
107: '(n,a)', 108: '(n,2a)', 109: '(n,3a)', 111: '(n,2p)',
112: '(n,pa)', 113: '(n,t2a)', 114: '(n,d2a)', 115: '(n,pd)',
116: '(n,pt)', 117: '(n,da)', 152: '(n,5n)', 153: '(n,6n)',
154: '(n,2nt)', 155: '(n,ta)', 156: '(n,4np)', 157: '(n,3nd)',
158: '(n,nda)', 159: '(n,2npa)', 160: '(n,7n)', 161: '(n,8n)',
162: '(n,5np)', 163: '(n,6np)', 164: '(n,7np)', 165: '(n,4na)',
166: '(n,5na)', 167: '(n,6na)', 168: '(n,7na)', 169: '(n,4nd)',
170: '(n,5nd)', 171: '(n,6nd)', 172: '(n,3nt)', 173: '(n,4nt)',
174: '(n,5nt)', 175: '(n,6nt)', 176: '(n,2n3He)',
177: '(n,3n3He)', 178: '(n,4n3He)', 179: '(n,3n2p)',
180: '(n,3n3a)', 181: '(n,3npa)', 182: '(n,dt)',
183: '(n,npd)', 184: '(n,npt)', 185: '(n,ndt)',
186: '(n,np3He)', 187: '(n,nd3He)', 188: '(n,nt3He)',
189: '(n,nta)', 190: '(n,2n2p)', 191: '(n,p3He)',
192: '(n,d3He)', 193: '(n,3Hea)', 194: '(n,4n2p)',
195: '(n,4n2a)', 196: '(n,4npa)', 197: '(n,3p)',
198: '(n,n3p)', 199: '(n,3n2pa)', 200: '(n,5n2p)', 444: '(n,damage)',
649: '(n,pc)', 699: '(n,dc)', 749: '(n,tc)', 799: '(n,3Hec)',
849: '(n,ac)'}
REACTION_NAME.update({i: '(n,n{})'.format(i-50) for i in range(50, 91)})
REACTION_NAME.update({i: '(n,p{})'.format(i-600) for i in range(600, 649)})
REACTION_NAME.update({i: '(n,d{})'.format(i-650) for i in range(650, 699)})
REACTION_NAME.update({i: '(n,t{})'.format(i-700) for i in range(700, 749)})
REACTION_NAME.update({i: '(n,3He{})'.format(i-750) for i in range(750, 799)})
REACTION_NAME.update({i: '(n,a{})'.format(i-800) for i in range(800, 849)})
SUM_RULES = {1: [2, 3],
3: [4, 5, 11, 16, 17, 22, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35,
36, 37, 41, 42, 44, 45, 152, 153, 154, 156, 157, 158, 159, 160,
161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
173, 174, 175, 176, 177, 178, 179, 180, 181, 183, 184, 185,
186, 187, 188, 189, 190, 194, 195, 196, 198, 199, 200],
4: list(range(50, 92)),
16: list(range(875, 892)),
18: [19, 20, 21, 38],
27: [18, 101],
101: [102, 103, 104, 105, 106, 107, 108, 109, 111, 112, 113, 114,
115, 116, 117, 155, 182, 191, 192, 193, 197],
103: list(range(600, 650)),
104: list(range(650, 700)),
105: list(range(700, 750)),
106: list(range(750, 800)),
107: list(range(800, 850))}
def atomic_mass(isotope):
"""Return atomic mass of isotope in atomic mass units.
@ -207,7 +153,7 @@ def atomic_mass(isotope):
mass_file = os.path.join(os.path.dirname(__file__), 'mass.mas12')
with open(mass_file, 'r') as ame:
# Read lines in file starting at line 40
for line in itertools.islice(ame, 40, None):
for line in itertools.islice(ame, 39, None):
name = '{}{}'.format(line[20:22].strip(), int(line[16:19]))
mass = float(line[96:99]) + 1e-6*float(
line[100:106] + '.' + line[107:112])

419
openmc/data/endf.py Normal file
View file

@ -0,0 +1,419 @@
"""Module for parsing and manipulating data from ENDF evaluations.
All the classes and functions in this module are based on document
ENDF-102 titled "Data Formats and Procedures for the Evaluated Nuclear
Data File ENDF-6". The latest version from June 2009 can be found at
http://www-nds.iaea.org/ndspub/documents/endf/endf102/endf102.pdf
"""
from __future__ import print_function, division, unicode_literals
import io
import re
import os
from math import pi
from collections import OrderedDict, Iterable
import numpy as np
from numpy.polynomial.polynomial import Polynomial
from .function import Tabulated1D, INTERPOLATION_SCHEME
from openmc.stats.univariate import Uniform, Tabular, Legendre
LIBRARIES = {0: 'ENDF/B', 1: 'ENDF/A', 2: 'JEFF', 3: 'EFF',
4: 'ENDF/B High Energy', 5: 'CENDL', 6: 'JENDL',
31: 'INDL/V', 32: 'INDL/A', 33: 'FENDL', 34: 'IRDF',
35: 'BROND', 36: 'INGDB-90', 37: 'FENDL/A', 41: 'BROND'}
SUM_RULES = {1: [2, 3],
3: [4, 5, 11, 16, 17, 22, 23, 24, 25, 28, 29, 30, 32, 33, 34, 35,
36, 37, 41, 42, 44, 45, 152, 153, 154, 156, 157, 158, 159, 160,
161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
173, 174, 175, 176, 177, 178, 179, 180, 181, 183, 184, 185,
186, 187, 188, 189, 190, 194, 195, 196, 198, 199, 200],
4: list(range(50, 92)),
16: list(range(875, 892)),
18: [19, 20, 21, 38],
27: [8, 101],
101: [102, 103, 104, 105, 106, 107, 108, 109, 111, 112, 113, 114,
115, 116, 117, 155, 182, 191, 192, 193, 197],
103: list(range(600, 650)),
104: list(range(650, 700)),
105: list(range(700, 750)),
106: list(range(750, 800)),
107: list(range(800, 850))}
_ENDF_FLOAT_RE = re.compile(r'([\s\-\+]?\d*\.\d+)([\+\-]\d+)')
def radiation_type(value):
p = {0: 'gamma', 1: 'beta-', 2: 'ec/beta+', 3: 'IT',
4: 'alpha', 5: 'neutron', 6: 'sf', 7: 'proton',
8: 'e-', 9: 'xray', 10: 'unknown'}
if value % 1.0 == 0:
return p[int(value)]
else:
return (p[int(value)], p[int(10*value % 10)])
def float_endf(s):
"""Convert string of floating point number in ENDF to float.
The ENDF-6 format uses an 'e-less' floating point number format,
e.g. -1.23481+10. Trying to convert using the float built-in won't work
because of the lack of an 'e'. This function allows such strings to be
converted while still allowing numbers that are not in exponential notation
to be converted as well.
Parameters
----------
s : str
Floating-point number from an ENDF file
Returns
-------
float
The number
"""
return float(_ENDF_FLOAT_RE.sub(r'\1e\2', s))
def get_text_record(file_obj):
"""Return data from a TEXT record in an ENDF-6 file.
Parameters
----------
file_obj : file-like object
ENDF-6 file to read from
Returns
-------
str
Text within the TEXT record
"""
return file_obj.readline()[:66]
def get_cont_record(file_obj, skipC=False):
"""Return data from a CONT record in an ENDF-6 file.
Parameters
----------
file_obj : file-like object
ENDF-6 file to read from
skipC : bool
Determine whether to skip the first two quantities (C1, C2) of the CONT
record.
Returns
-------
list
The six items within the CONT record
"""
line = file_obj.readline()
if skipC:
C1 = None
C2 = None
else:
C1 = float_endf(line[:11])
C2 = float_endf(line[11:22])
L1 = int(line[22:33])
L2 = int(line[33:44])
N1 = int(line[44:55])
N2 = int(line[55:66])
return [C1, C2, L1, L2, N1, N2]
def get_head_record(file_obj):
"""Return data from a HEAD record in an ENDF-6 file.
Parameters
----------
file_obj : file-like object
ENDF-6 file to read from
Returns
-------
list
The six items within the HEAD record
"""
line = file_obj.readline()
ZA = int(float_endf(line[:11]))
AWR = float_endf(line[11:22])
L1 = int(line[22:33])
L2 = int(line[33:44])
N1 = int(line[44:55])
N2 = int(line[55:66])
return [ZA, AWR, L1, L2, N1, N2]
def get_list_record(file_obj):
"""Return data from a LIST record in an ENDF-6 file.
Parameters
----------
file_obj : file-like object
ENDF-6 file to read from
Returns
-------
list
The six items within the header
list
The values within the list
"""
# determine how many items are in list
items = get_cont_record(file_obj)
NPL = items[4]
# read items
b = []
for i in range((NPL - 1)//6 + 1):
line = file_obj.readline()
n = min(6, NPL - 6*i)
for j in range(n):
b.append(float_endf(line[11*j:11*(j + 1)]))
return (items, b)
def get_tab1_record(file_obj):
"""Return data from a TAB1 record in an ENDF-6 file.
Parameters
----------
file_obj : file-like object
ENDF-6 file to read from
Returns
-------
list
The six items within the header
openmc.data.Tabulated1D
The tabulated function
"""
# Determine how many interpolation regions and total points there are
line = file_obj.readline()
C1 = float_endf(line[:11])
C2 = float_endf(line[11:22])
L1 = int(line[22:33])
L2 = int(line[33:44])
n_regions = int(line[44:55])
n_pairs = int(line[55:66])
params = [C1, C2, L1, L2]
# Read the interpolation region data, namely NBT and INT
breakpoints = np.zeros(n_regions, dtype=int)
interpolation = np.zeros(n_regions, dtype=int)
m = 0
for i in range((n_regions - 1)//3 + 1):
line = file_obj.readline()
to_read = min(3, n_regions - m)
for j in range(to_read):
breakpoints[m] = int(line[0:11])
interpolation[m] = int(line[11:22])
line = line[22:]
m += 1
# Read tabulated pairs x(n) and y(n)
x = np.zeros(n_pairs)
y = np.zeros(n_pairs)
m = 0
for i in range((n_pairs - 1)//3 + 1):
line = file_obj.readline()
to_read = min(3, n_pairs - m)
for j in range(to_read):
x[m] = float_endf(line[:11])
y[m] = float_endf(line[11:22])
line = line[22:]
m += 1
return params, Tabulated1D(x, y, breakpoints, interpolation)
def get_tab2_record(file_obj):
# Determine how many interpolation regions and total points there are
params = get_cont_record(file_obj)
n_regions = params[4]
# Read the interpolation region data, namely NBT and INT
breakpoints = np.zeros(n_regions, dtype=int)
interpolation = np.zeros(n_regions, dtype=int)
m = 0
for i in range((n_regions - 1)//3 + 1):
line = file_obj.readline()
to_read = min(3, n_regions - m)
for j in range(to_read):
breakpoints[m] = int(line[0:11])
interpolation[m] = int(line[11:22])
line = line[22:]
m += 1
return params, Tabulated2D(breakpoints, interpolation)
class Evaluation(object):
"""ENDF material evaluation with multiple files/sections
Parameters
----------
filename : str
Path to ENDF file to read
Attributes
----------
info : dict
Miscallaneous information about the evaluation.
target : dict
Information about the target material, such as its mass, isomeric state,
whether it's stable, and whether it's fissionable.
projectile : dict
Information about the projectile such as its mass.
reaction_list : list of 4-tuples
List of sections in the evaluation. The entries of the tuples are the
file (MF), section (MT), number of records (NC), and modification
indicator (MOD).
"""
def __init__(self, filename):
fh = open(filename, 'r')
self.section = {}
self.info = {}
self.target = {}
self.projectile = {}
self.reaction_list = []
# Determine MAT number for this evaluation
MF = 0
while MF == 0:
position = fh.tell()
line = fh.readline()
MF = int(line[70:72])
self.material = int(line[66:70])
fh.seek(position)
while True:
# Find next section
while True:
position = fh.tell()
line = fh.readline()
MAT = int(line[66:70])
MF = int(line[70:72])
MT = int(line[72:75])
if MT > 0 or MAT == 0:
fh.seek(position)
break
# If end of material reached, exit loop
if MAT == 0:
break
section_data = ''
while True:
line = fh.readline()
if line[72:75] == ' 0':
break
else:
section_data += line
self.section[MF, MT] = section_data
self._read_header()
def _read_header(self):
file_obj = io.StringIO(self.section[1, 451])
# Information about target/projectile
items = get_head_record(file_obj)
self.target['atomic_number'] = items[0] // 1000
self.target['mass_number'] = items[0] % 1000
self.target['mass'] = items[1]
self._LRP = items[2]
self.target['fissionable'] = (items[3] == 1)
try:
global LIBRARIES
library = LIBRARIES[items[4]]
except KeyError:
library = 'Unknown'
self.info['modification'] = items[5]
# Control record 1
items = get_cont_record(file_obj)
self.target['excitation_energy'] = items[0]
self.target['stable'] = (int(items[1]) == 0)
self.target['state'] = items[2]
self.target['isomeric_state'] = items[3]
self.info['format'] = items[5]
assert self.info['format'] == 6
# Control record 2
items = get_cont_record(file_obj)
self.projectile['mass'] = items[0]
self.info['energy_max'] = items[1]
library_release = items[2]
self.info['sublibrary'] = items[4]
library_version = items[5]
self.info['library'] = (library, library_version, library_release)
# Control record 3
items = get_cont_record(file_obj)
self.target['temperature'] = items[0]
self.info['derived'] = (items[2] > 0)
NWD = items[4]
NXC = items[5]
# Text records
text = [get_text_record(file_obj) for i in range(NWD)]
if len(text) >= 5:
self.target['zsymam'] = text[0][0:11]
self.info['laboratory'] = text[0][11:22]
self.info['date'] = text[0][22:32]
self.info['author'] = text[0][32:66]
self.info['reference'] = text[1][1:22]
self.info['date_distribution'] = text[1][22:32]
self.info['date_release'] = text[1][33:43]
self.info['date_entry'] = text[1][55:63]
self.info['identifier'] = text[2:5]
self.info['description'] = text[5:]
# File numbers, reaction designations, and number of records
for i in range(NXC):
line = file_obj.readline()
mf = int(line[22:33])
mt = int(line[33:44])
nc = int(line[44:55])
try:
mod = int(line[55:66])
except ValueError:
# In JEFF 3.2, a few isotopes of U have MOD values that are
# missing. This prevents failure on these isotopes.
mod = 0
self.reaction_list.append((mf, mt, nc, mod))
class Tabulated2D(object):
"""Metadata for a two-dimensional function.
This is a dummy class that is not really used other than to store the
interpolation information for a two-dimensional function. Once we refactor
to adopt GND-like data containers, this will probably be removed or
extended.
Parameters
----------
breakpoints : Iterable of int
Breakpoints for interpolation regions
interpolation : Iterable of int
Interpolation scheme identification number, e.g., 3 means y is linear in
ln(x).
"""
def __init__(self, breakpoints, interpolation):
self.breakpoints = breakpoints
self.interpolation = interpolation

View file

@ -1,44 +0,0 @@
"""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}

View file

@ -3,19 +3,19 @@ from collections import Iterable
from numbers import Integral, Real
from warnings import warn
from six import add_metaclass
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
from .endf import get_tab1_record, get_tab2_record
@add_metaclass(ABCMeta)
class EnergyDistribution(EqualityMixin):
"""Abstract superclass for all energy distributions."""
__metaclass__ = ABCMeta
def __init__(self):
pass
@ -57,6 +57,40 @@ class EnergyDistribution(EqualityMixin):
raise ValueError("Unknown energy distribution type: {}"
.format(energy_type))
@staticmethod
def from_endf(file_obj, params):
"""Generate energy distribution from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positioned at the start of a section for an energy
distribution.
params : list
List of parameters at the start of the energy distribution that
includes the LF value indicating what type of energy distribution is
present.
Returns
-------
openmc.data.EnergyDistribution
A sub-class of :class:`openmc.data.EnergyDistribution`
"""
lf = params[3]
if lf == 1:
return ArbitraryTabulated.from_endf(file_obj, params)
elif lf == 5:
return GeneralEvaporation.from_endf(file_obj, params)
elif lf == 7:
return MaxwellEnergy.from_endf(file_obj, params)
elif lf == 9:
return Evaporation.from_endf(file_obj, params)
elif lf == 11:
return WattEnergy.from_endf(file_obj, params)
elif lf == 12:
return MadlandNix.from_endf(file_obj, params)
class ArbitraryTabulated(EnergyDistribution):
r"""Arbitrary tabulated function given in ENDF MF=5, LF=1 represented as
@ -88,6 +122,37 @@ class ArbitraryTabulated(EnergyDistribution):
def to_hdf5(self, group):
raise NotImplementedError
@classmethod
def from_endf(cls, file_obj, params):
"""Generate arbitrary tabulated distribution from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positioned at the start of a section for an energy
distribution.
params : list
List of parameters at the start of the energy distribution that
includes the LF value indicating what type of energy distribution is
present.
Returns
-------
openmc.data.ArbitraryTabulated
Arbitrary tabulated distribution
"""
params, tab2 = get_tab2_record(file_obj)
n_energies = params[5]
energy = np.zeros(n_energies)
pdf = []
for j in range(n_energies):
params, func = get_tab1_record(file_obj)
energy[j] = params[1]
pdf.append(func)
return cls(energy, pdf)
class GeneralEvaporation(EnergyDistribution):
r"""General evaporation spectrum given in ENDF MF=5, LF=5 represented as
@ -130,6 +195,31 @@ class GeneralEvaporation(EnergyDistribution):
def from_ace(cls, ace, idx=0):
raise NotImplementedError
@classmethod
def from_endf(cls, file_obj, params):
"""Generate general evaporation spectrum from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positioned at the start of a section for an energy
distribution.
params : list
List of parameters at the start of the energy distribution that
includes the LF value indicating what type of energy distribution is
present.
Returns
-------
openmc.data.GeneralEvaporation
General evaporation spectrum
"""
u = params[0]
params, theta = get_tab1_record(file_obj)
params, g = get_tab1_record(file_obj)
return cls(theta, g, u)
class MaxwellEnergy(EnergyDistribution):
r"""Simple Maxwellian fission spectrum represented as
@ -238,6 +328,30 @@ class MaxwellEnergy(EnergyDistribution):
return cls(theta, u)
@classmethod
def from_endf(cls, file_obj, params):
"""Generate Maxwell distribution from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positioned at the start of a section for an energy
distribution.
params : list
List of parameters at the start of the energy distribution that
includes the LF value indicating what type of energy distribution is
present.
Returns
-------
openmc.data.MaxwellEnergy
Maxwell distribution
"""
u = params[0]
params, theta = get_tab1_record(file_obj)
return cls(theta, u)
class Evaporation(EnergyDistribution):
r"""Evaporation spectrum represented as
@ -346,6 +460,30 @@ class Evaporation(EnergyDistribution):
return cls(theta, u)
@classmethod
def from_endf(cls, file_obj, params):
"""Generate evaporation spectrum from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positioned at the start of a section for an energy
distribution.
params : list
List of parameters at the start of the energy distribution that
includes the LF value indicating what type of energy distribution is
present.
Returns
-------
openmc.data.Evaporation
Evaporation spectrum
"""
u = params[0]
params, theta = get_tab1_record(file_obj)
return cls(theta, u)
class WattEnergy(EnergyDistribution):
r"""Energy-dependent Watt spectrum represented as
@ -480,6 +618,31 @@ class WattEnergy(EnergyDistribution):
return cls(a, b, u)
@classmethod
def from_endf(cls, file_obj, params):
"""Generate Watt fission spectrum from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positioned at the start of a section for an energy
distribution.
params : list
List of parameters at the start of the energy distribution that
includes the LF value indicating what type of energy distribution is
present.
Returns
-------
openmc.data.WattEnergy
Watt fission spectrum
"""
u = params[0]
params, a = get_tab1_record(file_obj)
params, b = get_tab1_record(file_obj)
return cls(a, b, u)
class MadlandNix(EnergyDistribution):
r"""Energy-dependent fission neutron spectrum (Madland and Nix) given in
@ -587,6 +750,31 @@ class MadlandNix(EnergyDistribution):
tm = Tabulated1D.from_hdf5(group['tm'])
return cls(efl, efh, tm)
@classmethod
def from_endf(cls, file_obj, params):
"""Generate Madland-Nix fission spectrum from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positioned at the start of a section for an energy
distribution.
params : list
List of parameters at the start of the energy distribution that
includes the LF value indicating what type of energy distribution is
present.
Returns
-------
openmc.data.MadlandNix
Madland-Nix fission spectrum
"""
params, tm = get_tab1_record(file_obj)
efl, efh = params[0:2]
return cls(efl, efh, tm)
class DiscretePhoton(EnergyDistribution):
"""Discrete photon energy distribution

View file

@ -1,27 +1,27 @@
from collections import Callable
from copy import deepcopy
from io import StringIO
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 .endf import get_cont_record, get_list_record, Evaluation
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):
def _extract_458_data(ev, units='eV'):
"""Read an ENDF file and extract the MF=1, MT=458 values.
Parameters
----------
filename : str
Path to and ENDF file
ev : openmc.data.Evaluation
ENDF evaluation
units : {'eV', 'MeV'}
The units that are used in values returned.
Returns
-------
@ -37,33 +37,25 @@ def _extract_458_data(filename):
caution.
"""
ident = identify_nuclide(filename)
cv.check_type('evaluation', ev, Evaluation)
cv.check_value('energy units', units, ('eV', 'MeV'))
if not ident['LFI']:
if not ev.target['fissionable']:
# 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:
if (1, 458) not in ev.section:
# No 458 data here.
return None
file_obj = StringIO(ev.section[1, 458])
# Read the number of coefficients in this LIST record.
NPL = read_CONT_line(lines[1])[4]
items = get_cont_record(file_obj)
NPL = items[3]
# 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)]))
items, data = get_list_record(file_obj)
# 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
@ -96,12 +88,13 @@ def _extract_458_data(filename):
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)
if units == '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
@ -161,20 +154,22 @@ def write_compact_458_library(endf_files, output_name='fission_Q_data.h5',
for fname in endf_files:
if verbose: print(fname)
ident = identify_nuclide(fname)
ev = Evaluation(fname)
# Skip non-fissionable nuclides.
if not ident['LFI']: continue
if not ev.target['fissionable']:
continue
# Get the important bits.
data = _extract_458_data(fname)
data = _extract_458_data(ev, 'MeV')
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'])
name = ATOMIC_SYMBOL[ev.target['atomic_number']] + \
str(ev.target['mass_number'])
if ev.target['isomeric_state'] != 0:
name += '_m' + str(ev.target['isomeric_state'])
nuclide_group = out.create_group(name)
# Write all the coefficients into one array. The first dimension gives
@ -361,7 +356,7 @@ class FissionEnergyRelease(EqualityMixin):
self._neutrinos = energy_release
@classmethod
def _from_dictionary(cls, energy_release, incident_neutron):
def _from_dictionary(cls, energy_release, incident_neutron, units='eV'):
"""Generate fission energy release data from a dictionary.
Parameters
@ -371,9 +366,10 @@ class FissionEnergyRelease(EqualityMixin):
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
units : {'eV', 'MeV'}
The energy units used in the returned object.
Returns
-------
@ -381,6 +377,8 @@ class FissionEnergyRelease(EqualityMixin):
Fission energy release data
"""
cv.check_value('energy units', units, ('eV', 'MeV'))
out = cls()
# How many coefficients are given for each component? If we only find
@ -414,42 +412,55 @@ class FissionEnergyRelease(EqualityMixin):
# 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']
nu = [p.yield_ for p in incident_neutron[18].products
if p.particle == 'neutron'
and p.emission_mode in ('prompt', 'total')]
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']
nu = [p.yield_ for p in incident_neutron[19].products
if p.particle == 'neutron'
and p.emission_mode in ('prompt', 'total')]
else:
raise ValueError('IncidentNeutron data has no fission '
'reaction.')
if len(nu_prompt) == 0:
if len(nu) == 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]))
if len(nu) > 1:
raise ValueError('Ambiguous prompt/total nu value.')
nu = nu[0]
if units == 'eV':
nu_const = 8.07e6
else:
nu_const = 8.07
if isinstance(nu, Tabulated1D):
ENP = deepcopy(nu)
ENP.y = (energy_release['ENP'] + 1.307 * nu.x
- nu_const * (nu.y - nu.y[0]))
elif isinstance(nu, Polynomial):
if len(nu) == 1:
ENP = Polynomial([energy_release['ENP'][0], 1.307])
else:
ENP = Polynomial(
[energy_release['ENP'][0], 1.307 - nu_const*nu.coef[1]]
+ [-nu_const*c for c in nu.coef[2:]])
out.prompt_neutrons = ENP
return out
@classmethod
def from_endf(cls, filename, incident_neutron):
def from_endf(cls, ev, incident_neutron, units='eV'):
"""Generate fission energy release data from an ENDF file.
Parameters
----------
filename : str
Name of the ENDF file containing fission energy release data
ev : openmc.data.endf.Evaluation
ENDF evaluation
incident_neutron : openmc.data.IncidentNeutron
Corresponding incident neutron dataset
units : {'eV', 'MeV'}
The energy units used in the returned object.
Returns
-------
@ -457,26 +468,26 @@ class FissionEnergyRelease(EqualityMixin):
Fission energy release data
"""
cv.check_type('evaluation', ev, Evaluation)
# Check to make sure this ENDF file matches the expected isomer.
ident = identify_nuclide(filename)
if ident['Z'] != incident_neutron.atomic_number:
if ev.target['atomic_number'] != 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:
if ev.target['mass_number'] != 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:
if ev.target['isomeric_state'] != incident_neutron.metastable:
raise ValueError('The metastable state of the ENDF evaluation does '
'not match the given IncidentNeutron.')
if not ident['LFI']:
if not ev.target['fissionable']:
raise ValueError('The ENDF evaluation is not fissionable.')
# Read the 458 data from the ENDF file.
value, uncertainty = _extract_458_data(filename)
value, uncertainty = _extract_458_data(ev, units)
# Build the object.
return cls._from_dictionary(value, incident_neutron)
return cls._from_dictionary(value, incident_neutron, units)
@classmethod
def from_hdf5(cls, group):
@ -516,7 +527,6 @@ class FissionEnergyRelease(EqualityMixin):
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

View file

@ -2,8 +2,10 @@ from abc import ABCMeta, abstractmethod
from collections import Iterable, Callable
from numbers import Real, Integral
from six import add_metaclass
import numpy as np
import openmc.data
import openmc.checkvalue as cv
from openmc.mixin import EqualityMixin
@ -11,11 +13,9 @@ INTERPOLATION_SCHEME = {1: 'histogram', 2: 'linear-linear', 3: 'linear-log',
4: 'log-linear', 5: 'log-log'}
@add_metaclass(ABCMeta)
class Function1D(EqualityMixin):
"""A function of one independent variable with HDF5 support."""
__metaclass__ = ABCMeta
@abstractmethod
def __call__(self): pass
@ -423,3 +423,83 @@ class Sum(EqualityMixin):
def functions(self, functions):
cv.check_type('functions', functions, Iterable, Callable)
self._functions = functions
class ResonancesWithBackground(EqualityMixin):
"""Cross section in resolved resonance region.
Parameters
----------
resonances : openmc.data.Resonances
Resolved resonance parameter data
background : Callable
Background cross section as a function of energy
mt : int
MT value of the reaction
Attributes
----------
resonances : openmc.data.Resonances
Resolved resonance parameter data
background : Callable
Background cross section as a function of energy
mt : int
MT value of the reaction
"""
def __init__(self, resonances, background, mt):
self.resonances = resonances
self.background = background
self.mt = mt
def __call__(self, x):
# Get background cross section
xs = self.background(x)
for r in self.resonances:
if not isinstance(r, openmc.data.resonance._RESOLVED):
continue
if isinstance(x, Iterable):
# Determine which energies are within resolved resonance range
within = (r.energy_min <= x) & (x <= r.energy_max)
# Get resonance cross sections and add to background
resonant_xs = r.reconstruct(x[within])
xs[within] += resonant_xs[self.mt]
else:
if r.energy_min <= x <= r.energy_max:
resonant_xs = r.reconstruct(x)
xs += resonant_xs[self.mt]
return xs
@property
def background(self):
return self._background
@property
def mt(self):
return self._mt
@property
def resonances(self):
return self._resonances
@background.setter
def background(self, background):
cv.check_type('background cross section', background, Callable)
self._background = background
@mt.setter
def mt(self, mt):
cv.check_type('MT value', mt, Integral)
self._mt = mt
@resonances.setter
def resonances(self, resonances):
cv.check_type('resolved resonance parameters', resonances,
openmc.data.Resonances)
self._resonances = resonances

View file

@ -8,6 +8,7 @@ import openmc.checkvalue as cv
from openmc.stats import Tabular, Univariate, Discrete, Mixture
from .function import Tabulated1D, INTERPOLATION_SCHEME
from .angle_energy import AngleEnergy
from .endf import get_list_record, get_tab2_record
class KalbachMann(AngleEnergy):
@ -346,3 +347,54 @@ class KalbachMann(AngleEnergy):
km_a.append(Tabulated1D(data[0], data[4]))
return cls(breakpoints, interpolation, energy, energy_out, km_r, km_a)
@classmethod
def from_endf(cls, file_obj):
"""Generate Kalbach-Mann distribution from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positioned at the start of the Kalbach-Mann distribution
Returns
-------
openmc.data.KalbachMann
Kalbach-Mann energy-angle distribution
"""
params, tab2 = get_tab2_record(file_obj)
lep = params[3]
ne = params[5]
energy = np.zeros(ne)
n_discrete_energies = np.zeros(ne, dtype=int)
energy_out = []
precompound = []
slope = []
for i in range(ne):
items, values = get_list_record(file_obj)
energy[i] = items[1]
n_discrete_energies[i] = items[2]
# TODO: split out discrete energies
n_angle = items[3]
n_energy_out = items[5]
values = np.asarray(values)
values.shape = (n_energy_out, n_angle + 2)
# Outgoing energy distribution at the i-th incoming energy
eout_i = values[:,0]
eout_p_i = values[:,1]
energy_out_i = Tabular(eout_i, eout_p_i, INTERPOLATION_SCHEME[lep])
energy_out.append(energy_out_i)
# Precompound and slope factors for Kalbach-Mann
r_i = values[:,2]
if n_angle == 2:
a_i = values[:,3]
else:
a_i = np.zeros_like(r_i)
precompound.append(Tabulated1D(eout_i, r_i))
slope.append(Tabulated1D(eout_i, a_i))
return cls(tab2.breakpoints, tab2.interpolation, energy,
energy_out, precompound, slope)

139
openmc/data/laboratory.py Normal file
View file

@ -0,0 +1,139 @@
from collections import Iterable
from numbers import Real, Integral
import numpy as np
import openmc.checkvalue as cv
from openmc.stats import Tabular, Univariate, Discrete, Mixture
from .angle_energy import AngleEnergy
from .function import INTERPOLATION_SCHEME
from .endf import get_tab2_record, get_tab1_record
class LaboratoryAngleEnergy(AngleEnergy):
"""Laboratory angle-energy distribution
Parameters
----------
breakpoints : Iterable of int
Breakpoints defining interpolation regions
interpolation : Iterable of int
Interpolation codes
energy : Iterable of float
Incoming energies at which distributions exist
mu : Iterable of openmc.stats.Univariate
Distribution of scattering cosines for each incoming energy
energy_out : Iterable of Iterable of openmc.stats.Univariate
Distribution of outgoing energies for each incoming energy/scattering
cosine
Attributes
----------
breakpoints : Iterable of int
Breakpoints defining interpolation regions
interpolation : Iterable of int
Interpolation codes
energy : Iterable of float
Incoming energies at which distributions exist
mu : Iterable of openmc.stats.Univariate
Distribution of scattering cosines for each incoming energy
energy_out : Iterable of Iterable of openmc.stats.Univariate
Distribution of outgoing energies for each incoming energy/scattering
cosine
"""
def __init__(self, breakpoints, interpolation, energy, mu, energy_out):
super(LaboratoryAngleEnergy).__init__()
self.breakpoints = breakpoints
self.interpolation = interpolation
self.energy = energy
self.mu = mu
self.energy_out = energy_out
@property
def breakpoints(self):
return self._breakpoints
@property
def interpolation(self):
return self._interpolation
@property
def energy(self):
return self._energy
@property
def mu(self):
return self._mu
@property
def energy_out(self):
return self._energy_out
@breakpoints.setter
def breakpoints(self, breakpoints):
cv.check_type('laboratory angle-energy breakpoints', breakpoints,
Iterable, Integral)
self._breakpoints = breakpoints
@interpolation.setter
def interpolation(self, interpolation):
cv.check_type('laboratory angle-energy interpolation', interpolation,
Iterable, Integral)
self._interpolation = interpolation
@energy.setter
def energy(self, energy):
cv.check_type('laboratory angle-energy incoming energy', energy,
Iterable, Real)
self._energy = energy
@mu.setter
def mu(self, mu):
cv.check_type('laboratory angle-energy outgoing cosine', mu,
Iterable, Univariate)
self._mu = mu
@energy_out.setter
def energy_out(self, energy_out):
cv.check_iterable_type('laboratory angle-energy outgoing energy',
energy_out, Univariate, 2, 2)
self._energy_out = energy_out
@classmethod
def from_endf(cls, file_obj):
"""Generate laboratory angle-energy distribution from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positioned at the start of a section for a correlated
angle-energy distribution
Returns
-------
openmc.data.LaboratoryAngleEnergy
Laboratory angle-energy distribution
"""
params, tab2 = get_tab2_record(file_obj)
ne = params[5]
energy = np.zeros(ne)
mu = []
energy_out = []
for i in range(ne):
params, tab2mu = get_tab2_record(file_obj)
energy[i] = params[1]
n_mu = params[5]
mu_i = np.zeros(n_mu)
p_mu_i = np.zeros(n_mu)
energy_out_i = []
for j in range(n_mu):
params, f = get_tab1_record(file_obj)
mu_i[j] = params[1]
p_mu_i[j] = sum(f.y)
energy_out_i.append(Tabular(f.x, f.y))
mu.append(Tabular(mu_i, p_mu_i))
energy_out.append(energy_out_i)
return cls(tab2.breakpoints, tab2.interpolation, energy, mu, energy_out)

View file

@ -4,6 +4,7 @@ import numpy as np
import openmc.checkvalue as cv
from .angle_energy import AngleEnergy
from .endf import get_cont_record
class NBodyPhaseSpace(AngleEnergy):
"""N-body phase space distribution
@ -17,7 +18,7 @@ class NBodyPhaseSpace(AngleEnergy):
atomic_weight_ratio : float
Atomic weight ratio of target nuclide
q_value : float
Q value for reaction in MeV
Q value for reaction in MeV or eV, depending on the data source.
Attributes
----------
@ -28,7 +29,7 @@ class NBodyPhaseSpace(AngleEnergy):
atomic_weight_ratio : float
Atomic weight ratio of target nuclide
q_value : float
Q value for reaction in MeV
Q value for reaction in MeV or eV, depending on the data source.
"""
@ -140,3 +141,25 @@ class NBodyPhaseSpace(AngleEnergy):
n_particles = int(ace.xss[idx])
total_mass = ace.xss[idx + 1]
return cls(total_mass, n_particles, ace.atomic_weight_ratio, q_value)
@classmethod
def from_endf(cls, file_obj):
"""Generate N-body phase space distribution from an ENDF evaluation
Parameters
----------
file_obj : file-like object
ENDF file positions at the start of the N-body phase space
distribution
Returns
-------
openmc.data.NBodyPhaseSpace
N-body phase space distribution
"""
items = get_cont_record(file_obj)
total_mass = items[0]
n_particles = items[5]
# TODO: get awr and Q value
return cls(total_mass, n_particles, 1.0, 0.0)

View file

@ -5,22 +5,22 @@ from itertools import chain
from numbers import Integral, Real
from warnings import warn
from six import string_types
import numpy as np
import h5py
from .data import ATOMIC_SYMBOL, SUM_RULES, K_BOLTZMANN
from .ace import Table, get_table
from .data import ATOMIC_SYMBOL, K_BOLTZMANN
from .fission_energy import FissionEnergyRelease
from .function import Tabulated1D, Sum
from .function import Tabulated1D, Sum, ResonancesWithBackground
from .endf import Evaluation, SUM_RULES
from .product import Product
from .reaction import Reaction, _get_photon_products
from .reaction import Reaction, _get_photon_products_ace
from .resonance import Resonances, _RESOLVED
from .urr import ProbabilityTables
import openmc.checkvalue as cv
from openmc.mixin import EqualityMixin
if sys.version_info[0] >= 3:
basestring = str
def _get_metadata(zaid, metastable_scheme='nndc'):
"""Return basic identifying data for a nuclide with a given ZAID.
@ -137,6 +137,8 @@ class IncidentNeutron(EqualityMixin):
Contains the cross sections, secondary angle and energy distributions,
and other associated data for each reaction. The keys are the MT values
and the values are Reaction objects.
resonances : openmc.data.Resonances or None
Resonance parameters
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.
@ -166,6 +168,7 @@ class IncidentNeutron(EqualityMixin):
self.reactions = OrderedDict()
self.summed_reactions = OrderedDict()
self._urr = {}
self._resonances = None
def __contains__(self, mt):
return mt in self.reactions or mt in self.summed_reactions
@ -212,6 +215,10 @@ class IncidentNeutron(EqualityMixin):
def reactions(self):
return self._reactions
@property
def resonances(self):
return self._resonances
@property
def summed_reactions(self):
return self._summed_reactions
@ -226,7 +233,7 @@ class IncidentNeutron(EqualityMixin):
@name.setter
def name(self, name):
cv.check_type('name', name, basestring)
cv.check_type('name', name, string_types)
self._name = name
@property
@ -236,7 +243,7 @@ class IncidentNeutron(EqualityMixin):
@atomic_number.setter
def atomic_number(self, atomic_number):
cv.check_type('atomic number', atomic_number, Integral)
cv.check_greater_than('atomic number', atomic_number, 0)
cv.check_greater_than('atomic number', atomic_number, 0, True)
self._atomic_number = atomic_number
@mass_number.setter
@ -268,6 +275,11 @@ class IncidentNeutron(EqualityMixin):
cv.check_type('reactions', reactions, Mapping)
self._reactions = reactions
@resonances.setter
def resonances(self, resonances):
cv.check_type('resonances', resonances, Resonances)
self._resonances = resonances
@summed_reactions.setter
def summed_reactions(self, summed_reactions):
cv.check_type('summed reactions', summed_reactions, Mapping)
@ -277,7 +289,7 @@ class IncidentNeutron(EqualityMixin):
def urr(self, urr):
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 table temperature', key, string_types)
cv.check_type('probability tables', value, ProbabilityTables)
self._urr = urr
@ -363,7 +375,7 @@ class IncidentNeutron(EqualityMixin):
return mts
def export_to_hdf5(self, path, mode='a'):
"""Export table to an HDF5 file.
"""Export incident neutron data to an HDF5 file.
Parameters
----------
@ -374,6 +386,10 @@ class IncidentNeutron(EqualityMixin):
to the :class:`h5py.File` constructor.
"""
# If data come from ENDF, don't allow exporting to HDF5
if hasattr(self, '_evaluation'):
raise NotImplementedError('Cannot export incident neutron data that '
'originated from an ENDF file.')
f = h5py.File(path, mode, libver='latest')
@ -587,7 +603,7 @@ class IncidentNeutron(EqualityMixin):
for mt_i in mts])
# Determine summed cross section
rx.products += _get_photon_products(ace, rx)
rx.products += _get_photon_products_ace(ace, rx)
data.summed_reactions[mt] = rx
# Read unresolved resonance probability tables
@ -596,3 +612,79 @@ class IncidentNeutron(EqualityMixin):
data.urr[strT] = urr
return data
@classmethod
def from_endf(cls, ev_or_filename):
"""Generate incident neutron continuous-energy data from an ENDF evaluation
Parameters
----------
ev_or_filename : openmc.data.endf.Evaluation or str
ENDF evaluation to read from. If given as a string, it is assumed to
be the filename for the ENDF file.
Returns
-------
openmc.data.IncidentNeutron
Incident neutron continuous-energy data
"""
if isinstance(ev_or_filename, Evaluation):
ev = ev_or_filename
else:
ev = Evaluation(ev_or_filename)
atomic_number = ev.target['atomic_number']
mass_number = ev.target['mass_number']
metastable = ev.target['isomeric_state']
atomic_weight_ratio = ev.target['mass']
temperature = ev.target['temperature']
# Determine name
element = ATOMIC_SYMBOL[atomic_number]
if metastable > 0:
name = '{}{}_m{}'.format(element, mass_number, metastable)
else:
name = '{}{}'.format(element, mass_number)
# Instantiate incident neutron data
data = cls(name, atomic_number, mass_number, metastable,
atomic_weight_ratio, temperature)
if (2, 151) in ev.section:
data.resonances = Resonances.from_endf(ev)
# Read each reaction
for mf, mt, nc, mod in ev.reaction_list:
if mf == 3:
data.reactions[mt] = Reaction.from_endf(ev, mt)
# Replace cross sections for elastic, capture, fission
try:
if any(isinstance(r, _RESOLVED) for r in data.resonances):
for mt in (2, 102, 18):
if mt in data.reactions:
rx = data.reactions[mt]
rx.xs['0K'] = ResonancesWithBackground(
data.resonances, rx.xs['0K'], mt)
except ValueError:
# Thrown if multiple resolved ranges (e.g. Pu239 in ENDF/B-VII.1)
pass
# If first-chance, second-chance, etc. fission are present, check
# whether energy distributions were specified in MF=5. If not, copy the
# energy distribution from MT=18.
for mt, rx in data.reactions.items():
if mt in (19, 20, 21, 38):
if (5, mt) not in ev.section:
neutron = data.reactions[18].products[0]
rx.products[0].applicability = neutron.applicability
rx.products[0].distribution = neutron.distribution
# Read fission energy release (requires that we already know nu for
# fission)
if (1, 458) in ev.section:
data.fission_energy = FissionEnergyRelease.from_endf(ev, data)
data._evaluation = ev
return data

View file

@ -1,16 +1,15 @@
from collections import Iterable
from io import StringIO
from numbers import Real
import sys
from six import string_types
import numpy as np
import openmc.checkvalue as cv
from openmc.mixin import EqualityMixin
from .function import Tabulated1D, Polynomial, Function1D
from .angle_energy import AngleEnergy
if sys.version_info[0] >= 3:
basestring = str
from .function import Tabulated1D, Polynomial, Function1D
class Product(EqualityMixin):
@ -114,7 +113,7 @@ class Product(EqualityMixin):
@particle.setter
def particle(self, particle):
cv.check_type('product particle type', particle, basestring)
cv.check_type('product particle type', particle, string_types)
self._particle = particle
@yield_.setter

View file

@ -3,21 +3,191 @@ from collections import Iterable, Callable, MutableMapping
from copy import deepcopy
from numbers import Real, Integral
from warnings import warn
from io import StringIO
from six import string_types
import numpy as np
import openmc.checkvalue as cv
from openmc.mixin import EqualityMixin
from openmc.stats import Uniform
from openmc.stats import Uniform, Tabular, Legendre
from .angle_distribution import AngleDistribution
from .angle_energy import AngleEnergy
from .function import Tabulated1D, Polynomial, Function1D
from .data import REACTION_NAME, K_BOLTZMANN
from .correlated import CorrelatedAngleEnergy
from .data import ATOMIC_SYMBOL, K_BOLTZMANN
from .endf import get_head_record, get_tab1_record, get_list_record, \
get_tab2_record, get_cont_record
from .energy_distribution import EnergyDistribution, LevelInelastic, \
DiscretePhoton
from .function import Tabulated1D, Polynomial
from .kalbach_mann import KalbachMann
from .laboratory import LaboratoryAngleEnergy
from .nbody import NBodyPhaseSpace
from .product import Product
from .uncorrelated import UncorrelatedAngleEnergy
def _get_fission_products(ace):
REACTION_NAME = {1: '(n,total)', 2: '(n,elastic)', 4: '(n,level)',
5: '(n,misc)', 11: '(n,2nd)', 16: '(n,2n)', 17: '(n,3n)',
18: '(n,fission)', 19: '(n,f)', 20: '(n,nf)', 21: '(n,2nf)',
22: '(n,na)', 23: '(n,n3a)', 24: '(n,2na)', 25: '(n,3na)',
27: '(n,absorption)', 28: '(n,np)', 29: '(n,n2a)',
30: '(n,2n2a)', 32: '(n,nd)', 33: '(n,nt)', 34: '(n,nHe-3)',
35: '(n,nd2a)', 36: '(n,nt2a)', 37: '(n,4n)', 38: '(n,3nf)',
41: '(n,2np)', 42: '(n,3np)', 44: '(n,n2p)', 45: '(n,npa)',
91: '(n,nc)', 101: '(n,disappear)', 102: '(n,gamma)',
103: '(n,p)', 104: '(n,d)', 105: '(n,t)', 106: '(n,3He)',
107: '(n,a)', 108: '(n,2a)', 109: '(n,3a)', 111: '(n,2p)',
112: '(n,pa)', 113: '(n,t2a)', 114: '(n,d2a)', 115: '(n,pd)',
116: '(n,pt)', 117: '(n,da)', 152: '(n,5n)', 153: '(n,6n)',
154: '(n,2nt)', 155: '(n,ta)', 156: '(n,4np)', 157: '(n,3nd)',
158: '(n,nda)', 159: '(n,2npa)', 160: '(n,7n)', 161: '(n,8n)',
162: '(n,5np)', 163: '(n,6np)', 164: '(n,7np)', 165: '(n,4na)',
166: '(n,5na)', 167: '(n,6na)', 168: '(n,7na)', 169: '(n,4nd)',
170: '(n,5nd)', 171: '(n,6nd)', 172: '(n,3nt)', 173: '(n,4nt)',
174: '(n,5nt)', 175: '(n,6nt)', 176: '(n,2n3He)',
177: '(n,3n3He)', 178: '(n,4n3He)', 179: '(n,3n2p)',
180: '(n,3n3a)', 181: '(n,3npa)', 182: '(n,dt)',
183: '(n,npd)', 184: '(n,npt)', 185: '(n,ndt)',
186: '(n,np3He)', 187: '(n,nd3He)', 188: '(n,nt3He)',
189: '(n,nta)', 190: '(n,2n2p)', 191: '(n,p3He)',
192: '(n,d3He)', 193: '(n,3Hea)', 194: '(n,4n2p)',
195: '(n,4n2a)', 196: '(n,4npa)', 197: '(n,3p)',
198: '(n,n3p)', 199: '(n,3n2pa)', 200: '(n,5n2p)', 444: '(n,damage)',
649: '(n,pc)', 699: '(n,dc)', 749: '(n,tc)', 799: '(n,3Hec)',
849: '(n,ac)'}
REACTION_NAME.update({i: '(n,n{})'.format(i-50) for i in range(50, 91)})
REACTION_NAME.update({i: '(n,p{})'.format(i-600) for i in range(600, 649)})
REACTION_NAME.update({i: '(n,d{})'.format(i-650) for i in range(650, 699)})
REACTION_NAME.update({i: '(n,t{})'.format(i-700) for i in range(700, 749)})
REACTION_NAME.update({i: '(n,3He{})'.format(i-750) for i in range(750, 799)})
REACTION_NAME.update({i: '(n,a{})'.format(i-800) for i in range(800, 849)})
def _get_products(ev, mt):
"""Generate products from MF=6 in an ENDF evaluation
Parameters
----------
ev : openmc.data.endf.Evaluation
ENDF evaluation to read from
mt : int
The MT value of the reaction to get products for
Returns
-------
products : list of openmc.data.Product
Products of the reaction
"""
file_obj = StringIO(ev.section[6, mt])
# Read HEAD record
items = get_head_record(file_obj)
reference_frame = {1: 'laboratory', 2: 'center-of-mass',
3: 'light-heavy'}[items[3]]
n_products = items[4]
products = []
for i in range(n_products):
# Get yield for this product
params, yield_ = get_tab1_record(file_obj)
za = params[0]
awr = params[1]
lip = params[2]
law = params[3]
if za == 0:
p = Product('photon')
elif za == 1:
p = Product('neutron')
elif za == 1000:
p = Product('electron')
else:
z = za // 1000
a = za % 1000
p = Product('{}{}'.format(ATOMIC_SYMBOL[z], a))
p.yield_ = yield_
"""
# Set reference frame
if reference_frame == 'laboratory':
p.center_of_mass = False
elif reference_frame == 'center-of-mass':
p.center_of_mass = True
elif reference_frame == 'light-heavy':
p.center_of_mass = (awr <= 4.0)
"""
if law == 0:
# No distribution given
pass
if law == 1:
# Continuum energy-angle distribution
# Peak ahead to determine type of distribution
position = file_obj.tell()
params = get_cont_record(file_obj)
file_obj.seek(position)
lang = params[2]
if lang == 1:
p.distribution = [CorrelatedAngleEnergy.from_endf(file_obj)]
elif lang == 2:
p.distribution = [KalbachMann.from_endf(file_obj)]
elif law == 2:
# Discrete two-body scattering
params, tab2 = get_tab2_record(file_obj)
ne = params[5]
energy = np.zeros(ne)
mu = []
for i in range(ne):
items, values = get_list_record(file_obj)
energy[i] = items[1]
lang = items[2]
if lang == 0:
mu.append(Legendre(values))
elif lang == 12:
mu.append(Tabular(values[::2], values[1::2]))
elif lang == 14:
mu.append(Tabular(values[::2], values[1::2],
'log-linear'))
angle_dist = AngleDistribution(energy, mu)
dist = UncorrelatedAngleEnergy(angle_dist)
p.distribution = [dist]
# TODO: Add level-inelastic info?
elif law == 3:
# Isotropic discrete emission
p.distribution = [UncorrelatedAngleEnergy()]
# TODO: Add level-inelastic info?
elif law == 4:
# Discrete two-body recoil
pass
elif law == 5:
# Charged particle elastic scattering
pass
elif law == 6:
# N-body phase-space distribution
p.distribution = [NBodyPhaseSpace.from_endf(file_obj)]
elif law == 7:
# Laboratory energy-angle distribution
p.distribution = [LaboratoryAngleEnergy.from_endf(file_obj)]
products.append(p)
return products
def _get_fission_products_ace(ace):
"""Generate fission products from an ACE table
Parameters
@ -148,7 +318,149 @@ def _get_fission_products(ace):
return products, derived_products
def _get_photon_products(ace, rx):
def _get_fission_products_endf(ev):
"""Generate fission products from an ENDF evaluation
Parameters
----------
ev : openmc.data.endf.Evaluation
Returns
-------
products : list of openmc.data.Product
Prompt and delayed fission neutrons
derived_products : list of openmc.data.Product
"Total" fission neutron
"""
products = []
derived_products = []
if (1, 456) in ev.section:
prompt_neutron = Product('neutron')
prompt_neutron.emission_mode = 'prompt'
# Prompt nu values
file_obj = StringIO(ev.section[1, 456])
lnu = get_head_record(file_obj)[3]
if lnu == 1:
# Polynomial representation
items, coefficients = get_list_record(file_obj)
prompt_neutron.yield_ = Polynomial(coefficients)
elif lnu == 2:
# Tabulated representation
params, prompt_neutron.yield_ = get_tab1_record(file_obj)
products.append(prompt_neutron)
if (1, 452) in ev.section:
total_neutron = Product('neutron')
total_neutron.emission_mode = 'total'
# Total nu values
file_obj = StringIO(ev.section[1, 452])
lnu = get_head_record(file_obj)[3]
if lnu == 1:
# Polynomial representation
items, coefficients = get_list_record(file_obj)
total_neutron.yield_ = Polynomial(coefficients)
elif lnu == 2:
# Tabulated representation
params, total_neutron.yield_ = get_tab1_record(file_obj)
if (1, 456) in ev.section:
derived_products.append(total_neutron)
else:
products.append(total_neutron)
if (1, 455) in ev.section:
file_obj = StringIO(ev.section[1, 455])
# Determine representation of delayed nu data
items = get_head_record(file_obj)
ldg = items[2]
lnu = items[3]
if ldg == 0:
# Delayed-group constants energy independent
items, decay_constants = get_list_record(file_obj)
for constant in decay_constants:
delayed_neutron = Product('neutron')
delayed_neutron.emission_mode = 'delayed'
delayed_neutron.decay_rate = constant
products.append(delayed_neutron)
elif ldg == 1:
# Delayed-group constants energy dependent
raise NotImplementedError('Delayed neutron with energy-dependent '
'group constants.')
# In MF=1, MT=455, the delayed-group abundances are actually not
# specified if the group constants are energy-independent. In this case,
# the abundances must be inferred from MF=5, MT=455 where multiple
# energy distributions are given.
if lnu == 1:
# Nu represented as polynomial
items, coefficients = get_list_record(file_obj)
yield_ = Polynomial(coefficients)
for neutron in products[-6:]:
neutron.yield_ = deepcopy(yield_)
elif lnu == 2:
# Nu represented by tabulation
params, yield_ = get_tab1_record(file_obj)
for neutron in products[-6:]:
neutron.yield_ = deepcopy(yield_)
if (5, 455) in ev.section:
file_obj = StringIO(ev.section[5, 455])
items = get_head_record(file_obj)
nk = items[4]
if nk != len(decay_constants):
raise ValueError(
'Number of delayed neutron fission spectra ({}) does not '
'match number of delayed neutron precursors ({}).'.format(
nk, len(decay_constants)))
for i in range(nk):
params, applicability = get_tab1_record(file_obj)
dist = UncorrelatedAngleEnergy()
dist.energy = EnergyDistribution.from_endf(file_obj, params)
delayed_neutron = products[1 + i]
yield_ = delayed_neutron.yield_
# Here we handle the fact that the delayed neutron yield is the
# product of the total delayed neutron yield and the
# "applicability" of the energy distribution law in file 5.
if isinstance(yield_, Tabulated1D):
if np.all(applicability.y == applicability.y[0]):
yield_.y *= applicability.y[0]
else:
# Get union energy grid and ensure energies are within
# interpolable range of both functions
max_energy = min(yield_.x[-1], applicability.x[-1])
energy = np.union1d(yield_.x, applicability.x)
energy = energy[energy <= max_energy]
# Calculate group yield
group_yield = yield_(energy) * applicability(energy)
delayed_neutron.yield_ = Tabulated1D(energy, group_yield)
elif isinstance(yield_, Polynomial):
if len(yield_) == 1:
delayed_neutron.yield_ = deepcopy(applicability)
delayed_neutron.yield_.y *= yield_.coef[0]
else:
if np.all(applicability.y == applicability.y[0]):
yield_.coef[0] *= applicability.y[0]
else:
raise NotImplementedError(
'Total delayed neutron yield and delayed group '
'probability are both energy-dependent.')
delayed_neutron.distribution.append(dist)
return products, derived_products
def _get_photon_products_ace(ace, rx):
"""Generate photon products from an ACE table
Parameters
@ -253,6 +565,117 @@ def _get_photon_products(ace, rx):
return photons
def _get_photon_products_endf(ev, rx):
"""Generate photon products from an ENDF evaluation
Parameters
----------
ev : openmc.data.endf.Evaluation
ENDF evaluation to read from
rx : openmc.data.Reaction
Reaction that generates photons
Returns
-------
photons : list of openmc.Products
Photons produced from reaction with given MT
"""
products = []
if (12, rx.mt) in ev.section:
file_obj = StringIO(ev.section[12, rx.mt])
items = get_head_record(file_obj)
option = items[2]
if option == 1:
# Multiplicities given
n_discrete_photon = items[4]
if n_discrete_photon > 1:
items, total_yield = get_tab1_record(file_obj)
for k in range(n_discrete_photon):
photon = Product('photon')
# Get photon yield
items, photon.yield_ = get_tab1_record(file_obj)
# Get photon energy distribution
law = items[3]
dist = UncorrelatedAngleEnergy()
if law == 1:
# TODO: Get file 15 distribution
pass
elif law == 2:
energy = items[1]
primary_flag = items[2]
dist.energy = DiscretePhoton(primary_flag, energy,
ev.target['mass'])
photon.distribution.append(dist)
products.append(photon)
elif option == 2:
# Transition probability arrays given
ppyield = {}
ppyield['type'] = 'transition'
ppyield['transition'] = transition = {}
# Determine whether simple (LG=1) or complex (LG=2) transitions
lg = items[3]
# Get transition data
items, values = get_list_record(file_obj)
transition['energy_start'] = items[0]
transition['energies'] = np.array(values[::lg + 1])
transition['direct_probability'] = np.array(values[1::lg + 1])
if lg == 2:
# Complex case
transition['conditional_probability'] = np.array(
values[2::lg + 1])
elif (13, rx.mt) in ev.section:
file_obj = StringIO(ev.section[13, rx.mt])
# Determine option
items = get_head_record(file_obj)
n_discrete_photon = items[4]
if n_discrete_photon > 1:
items, total_xs = get_tab1_record(file_obj)
for k in range(n_discrete_photon):
photon = Product('photon')
items, xs = get_tab1_record(file_obj)
# Re-interpolation photon production cross section and neutron cross
# section to union energy grid
energy = np.union1d(xs.x, rx.xs['0K'].x)
photon_prod_xs = xs(energy)
neutron_xs = rx.xs['0K'](energy)
idx = np.where(neutron_xs > 0)
# Calculate yield as ratio
yield_ = np.zeros_like(energy)
yield_[idx] = photon_prod_xs[idx] / neutron_xs[idx]
photon.yield_ = Tabulated1D(energy, yield_)
# Get photon energy distribution
law = items[3]
dist = UncorrelatedAngleEnergy()
if law == 1:
# TODO: Get file 15 distribution
pass
elif law == 2:
energy = items[1]
primary_flag = items[2]
dist.energy = DiscretePhoton(primary_flag, energy,
ev.target['mass'])
photon.distribution.append(dist)
products.append(photon)
return products
class Reaction(EqualityMixin):
"""A nuclear reaction
@ -274,9 +697,7 @@ class Reaction(EqualityMixin):
mt : int
The ENDF MT number for this reaction.
q_value : float
The Q-value of this reaction in MeV.
threshold : float
Threshold of the reaction in MeV
The Q-value of this reaction in MeV or eV, depending on the data source.
xs : dict of str to openmc.data.Function1D
Microscopic cross section for this reaction as a function of incident
energy; these cross sections are provided in a dictionary where the key
@ -349,8 +770,8 @@ class Reaction(EqualityMixin):
def xs(self, xs):
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)
cv.check_type('reaction cross section temperature', key, string_types)
cv.check_type('reaction cross section', value, Callable)
self._xs = xs
def to_hdf5(self, group):
@ -397,7 +818,7 @@ class Reaction(EqualityMixin):
Returns
-------
openmc.data.ace.Reaction
openmc.data.Reaction
Reaction data
"""
@ -500,7 +921,7 @@ class Reaction(EqualityMixin):
rx.products.append(neutron)
else:
assert mt in (18, 19, 20, 21, 38)
rx.products, rx.derived_products = _get_fission_products(ace)
rx.products, rx.derived_products = _get_fission_products_ace(ace)
for p in rx.products:
if p.emission_mode in ('prompt', 'total'):
@ -568,6 +989,97 @@ class Reaction(EqualityMixin):
# ======================================================================
# PHOTON PRODUCTION
rx.products += _get_photon_products(ace, rx)
rx.products += _get_photon_products_ace(ace, rx)
return rx
@classmethod
def from_endf(cls, ev, mt):
"""Generate a reaction from an ENDF evaluation
Parameters
----------
ev : openmc.data.endf.Evaluation
ENDF evaluation
mt : int
The MT value of the reaction to get angular distributions for
Returns
-------
rx : openmc.data.Reaction
Reaction data
"""
rx = Reaction(mt)
# Integrated cross section
if (3, mt) in ev.section:
file_obj = StringIO(ev.section[3, mt])
get_head_record(file_obj)
params, rx.xs['0K'] = get_tab1_record(file_obj)
rx.q_value = params[1]
# Get fission product yields (nu) as well as delayed neutron energy
# distributions
if mt in (18, 19, 20, 21, 38):
rx.products, rx.derived_products = _get_fission_products_endf(ev)
if (6, mt) in ev.section:
# Product angle-energy distribution
for product in _get_products(ev, mt):
if mt in (18, 19, 20, 21, 38) and product.particle == 'neutron':
rx.products[0].applicability = product.applicability
rx.products[0].distribution = product.distribution
else:
rx.products.append(product)
elif (4, mt) in ev.section or (5, mt) in ev.section:
# Uncorrelated angle-energy distribution
neutron = Product('neutron')
# Note that the energy distribution for MT=455 is read in
# _get_fission_products_endf rather than here
if (5, mt) in ev.section:
file_obj = StringIO(ev.section[5, mt])
items = get_head_record(file_obj)
nk = items[4]
for i in range(nk):
params, applicability = get_tab1_record(file_obj)
dist = UncorrelatedAngleEnergy()
dist.energy = EnergyDistribution.from_endf(file_obj, params)
neutron.applicability.append(applicability)
neutron.distribution.append(dist)
elif mt == 2:
# Elastic scattering -- no energy distribution is given since it
# can be calulcated analytically
dist = UncorrelatedAngleEnergy()
neutron.distribution.append(dist)
elif mt >= 51 and mt < 91:
# Level inelastic scattering -- no energy distribution is given
# since it can be calculated analytically. Here we determine the
# necessary parameters to create a LevelInelastic object
dist = UncorrelatedAngleEnergy()
A = ev.target['mass']
threshold = (A + 1.)/A*abs(rx.q_value)
mass_ratio = (A/(A + 1.))**2
dist.energy = LevelInelastic(threshold, mass_ratio)
neutron.distribution.append(dist)
if (4, mt) in ev.section:
for dist in neutron.distribution:
dist.angle = AngleDistribution.from_endf(ev, mt)
if mt in (18, 19, 20, 21, 38) and (5, mt) in ev.section:
# For fission reactions,
rx.products[0].applicability = neutron.applicability
rx.products[0].distribution = neutron.distribution
else:
rx.products.append(neutron)
if (12, mt) in ev.section or (13, mt) in ev.section:
rx.products += _get_photon_products_endf(ev, rx)
return rx

522
openmc/data/reconstruct.pyx Normal file
View file

@ -0,0 +1,522 @@
from libc.stdlib cimport malloc, calloc, free
from libc.math cimport cos, sin, sqrt, atan, M_PI
cimport numpy as np
import numpy as np
from numpy.linalg import inv
cimport cython
cdef extern from "complex.h":
double cabs(double complex)
double complex conj(double complex)
double creal(complex double)
double cimag(complex double)
double complex cexp(double complex)
# Physical constants are from CODATA 2014
cdef double NEUTRON_MASS_ENERGY = 939.5654133e6 # eV/c^2
cdef double HBAR_C = 197.3269788e5 # eV-b^0.5
@cython.cdivision(True)
def wave_number(double A, double E):
r"""Neutron wave number in center-of-mass system.
ENDF-102 defines the neutron wave number in the center-of-mass system in
Equation D.10 as
.. math::
k = \frac{2m_n}{\hbar} \frac{A}{A + 1} \sqrt{|E|}
Parameters
----------
A : double
Ratio of target mass to neutron mass
E : double
Energy in eV
Returns
-------
double
Neutron wave number in b^-0.5
"""
return A/(A + 1)*sqrt(2*NEUTRON_MASS_ENERGY*abs(E))/HBAR_C
@cython.cdivision(True)
cdef double _wave_number(double A, double E):
return A/(A + 1)*sqrt(2*NEUTRON_MASS_ENERGY*abs(E))/HBAR_C
@cython.cdivision(True)
cdef double phaseshift(int l, double rho):
"""Calculate hardsphere phase shift as given in ENDF-102, Equation D.13
Parameters
----------
l : int
Angular momentum quantum number
rho : float
Product of the wave number and the channel radius
Returns
-------
double
Hardsphere phase shift
"""
if l == 0:
return rho
elif l == 1:
return rho - atan(rho)
elif l == 2:
return rho - atan(3*rho/(3 - rho**2))
elif l == 3:
return rho - atan((15*rho - rho**3)/(15 - 6*rho**2))
elif l == 4:
return rho - atan((105*rho - 10*rho**3)/(105 - 45*rho**2 + rho**4))
@cython.cdivision(True)
def penetration_shift(int l, double rho):
r"""Calculate shift and penetration factors as given in ENDF-102, Equations D.11
and D.12.
Parameters
----------
l : int
Angular momentum quantum number
rho : float
Product of the wave number and the channel radius
Returns
-------
double
Penetration factor for given :math:`l`
double
Shift factor for given :math:`l`
"""
cdef double den
if l == 0:
return rho, 0.
elif l == 1:
den = 1 + rho**2
return rho**3/den, -1/den
elif l == 2:
den = 9 + 3*rho**2 + rho**4
return rho**5/den, -(18 + 3*rho**2)/den
elif l == 3:
den = 225 + 45*rho**2 + 6*rho**4 + rho**6
return rho**7/den, -(675 + 90*rho**2 + 6*rho**4)/den
elif l == 4:
den = 11025 + 1575*rho**2 + 135*rho**4 + 10*rho**6 + rho**8
return rho**9/den, -(44100 + 4725*rho**2 + 270*rho**4 + 10*rho**6)/den
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
def reconstruct_mlbw(mlbw, double E):
"""Evaluate cross section using MLBW data.
Parameters
----------
mlbw : openmc.data.MultiLevelBreitWigner
Multi-level Breit-Wigner resonance parameters
E : double
Energy in eV at which to evaluate the cross section
Returns
-------
elastic : double
Elastic scattering cross section in barns
capture : double
Radiative capture cross section in barns
fission : double
Fission cross section in barns
"""
cdef int i, nJ, ij, l, n_res, i_res
cdef double elastic, capture, fission
cdef double A, k, rho, rhohat, I
cdef double P, S, phi, cos2phi, sin2phi
cdef double Ex, Q, rhoc, rhochat, P_c, S_c
cdef double jmin, jmax, j, Dl
cdef double E_r, gt, gn, gg, gf, gx, P_r, S_r, P_rx
cdef double gnE, gtE, Eprime, x, f
cdef double *g
cdef double (*s)[2]
cdef double [:,:] params
I = mlbw.target_spin
A = mlbw.atomic_weight_ratio
k = _wave_number(A, E)
elastic = 0.
capture = 0.
fission = 0.
for i, l in enumerate(mlbw._l_values):
params = mlbw._parameter_matrix[l]
rho = k*mlbw.channel_radius[l](E)
rhohat = k*mlbw.scattering_radius[l](E)
P, S = penetration_shift(l, rho)
phi = phaseshift(l, rhohat)
cos2phi = cos(2*phi)
sin2phi = sin(2*phi)
# Determine shift and penetration at modified energy
if mlbw._competitive[i]:
Ex = E + mlbw.q_value[l]*(A + 1)/A
rhoc = mlbw.channel_radius[l](Ex)
rhochat = mlbw.scattering_radius[l](Ex)
P_c, S_c = penetration_shift(l, rhoc)
if Ex < 0:
P_c = 0
# Determine range of total angular momentum values based on equation
# 41 in LA-UR-12-27079
jmin = abs(abs(I - l) - 0.5)
jmax = I + l + 0.5
nJ = int(jmax - jmin + 1)
# Determine Dl factor using Equation 43 in LA-UR-12-27079
Dl = 2*l + 1
g = <double *> malloc(nJ*sizeof(double))
for ij in range(nJ):
j = jmin + ij
g[ij] = (2*j + 1)/(4*I + 2)
Dl -= g[ij]
s = <double (*)[2]> calloc(2*nJ, sizeof(double))
for i_res in range(params.shape[0]):
# Copy resonance parameters
E_r = params[i_res, 0]
j = params[i_res, 2]
ij = int(j - jmin)
gt = params[i_res, 3]
gn = params[i_res, 4]
gg = params[i_res, 5]
gf = params[i_res, 6]
gx = params[i_res, 7]
P_r = params[i_res, 8]
S_r = params[i_res, 9]
P_rx = params[i_res, 10]
# Calculate neutron and total width at energy E
gnE = P*gn/P_r # ENDF-102, Equation D.7
gtE = gnE + gg + gf
if gx > 0:
gtE += gx*P_c/P_rx
Eprime = E_r + (S_r - S)/(2*P_r)*gn # ENDF-102, Equation D.9
x = 2*(E - Eprime)/gtE # LA-UR-12-27079, Equation 26
f = 2*gnE/(gtE*(1 + x*x)) # Common factor in Equation 40
s[ij][0] += f # First sum in Equation 40
s[ij][1] += f*x # Second sum in Equation 40
capture += f*g[ij]*gg/gtE
if gf > 0:
fission += f*g[ij]*gf/gtE
for ij in range(nJ):
# Add all but last term of LA-UR-12-27079, Equation 40
elastic += g[ij]*((1 - cos2phi - s[ij][0])**2 +
(sin2phi + s[ij][1])**2)
# Add final term with Dl from Equation 40
elastic += 2*Dl*(1 - cos2phi)
# Free memory
free(g)
free(s)
capture *= 2*M_PI/(k*k)
fission *= 2*M_PI/(k*k)
elastic *= M_PI/(k*k)
return (elastic, capture, fission)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
def reconstruct_slbw(slbw, double E):
"""Evaluate cross section using SLBW data.
Parameters
----------
slbw : openmc.data.SingleLevelBreitWigner
Single-level Breit-Wigner resonance parameters
E : double
Energy in eV at which to evaluate the cross section
Returns
-------
elastic : double
Elastic scattering cross section in barns
capture : double
Radiative capture cross section in barns
fission : double
Fission cross section in barns
"""
cdef int i, l, i_res
cdef double elastic, capture, fission
cdef double A, k, rho, rhohat, I
cdef double P, S, phi, cos2phi, sin2phi, sinphi2
cdef double Ex, rhoc, rhochat, P_c, S_c
cdef double E_r, J, gt, gn, gg, gf, gx, P_r, S_r, P_rx
cdef double gnE, gtE, Eprime, f
cdef double x, theta, psi, chi
cdef double [:,:] params
I = slbw.target_spin
A = slbw.atomic_weight_ratio
k = _wave_number(A, E)
elastic = 0.
capture = 0.
fission = 0.
for i, l in enumerate(slbw._l_values):
params = slbw._parameter_matrix[l]
rho = k*slbw.channel_radius[l](E)
rhohat = k*slbw.scattering_radius[l](E)
P, S = penetration_shift(l, rho)
phi = phaseshift(l, rhohat)
cos2phi = cos(2*phi)
sin2phi = sin(2*phi)
sinphi2 = sin(phi)**2
# Add potential scattering -- first term in ENDF-102, Equation D.2
elastic += 4*M_PI/(k*k)*(2*l + 1)*sinphi2
# Determine shift and penetration at modified energy
if slbw._competitive[i]:
Ex = E + slbw.q_value[l]*(A + 1)/A
rhoc = slbw.channel_radius[l](Ex)
rhochat = slbw.scattering_radius[l](Ex)
P_c, S_c = penetration_shift(l, rhoc)
if Ex < 0:
P_c = 0
for i_res in range(params.shape[0]):
# Copy resonance parameters
E_r = params[i_res, 0]
J = params[i_res, 2]
gt = params[i_res, 3]
gn = params[i_res, 4]
gg = params[i_res, 5]
gf = params[i_res, 6]
gx = params[i_res, 7]
P_r = params[i_res, 8]
S_r = params[i_res, 9]
P_rx = params[i_res, 10]
# Calculate neutron and total width at energy E
gnE = P*gn/P_r # Equation D.7
gtE = gnE + gg + gf
if gx > 0:
gtE += gx*P_c/P_rx
Eprime = E_r + (S_r - S)/(2*P_r)*gn # Equation D.9
gJ = (2*J + 1)/(4*I + 2) # Mentioned in section D.1.1.4
# Calculate common factor for elastic, capture, and fission
# cross sections
f = M_PI/(k*k)*gJ*gnE/((E - Eprime)**2 + gtE**2/4)
# Add contribution to elastic per Equation D.2
elastic += f*(gnE*cos2phi - 2*(gg + gf)*sinphi2
+ 2*(E - Eprime)*sin2phi)
# Add contribution to capture per Equation D.3
capture += f*gg
# Add contribution to fission per Equation D.6
if gf > 0:
fission += f*gf
return (elastic, capture, fission)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
def reconstruct_rm(rm, double E):
"""Evaluate cross section using Reich-Moore data.
Parameters
----------
rm : openmc.data.ReichMoore
Reich-Moore resonance parameters
E : double
Energy in eV at which to evaluate the cross section
Returns
-------
elastic : double
Elastic scattering cross section in barns
capture : double
Radiative capture cross section in barns
fission : double
Fission cross section in barns
"""
cdef int i, l, m, n, i_res
cdef int i_s, num_s, i_J, num_J
cdef double elastic, capture, fission, total
cdef double A, k, rho, rhohat, I
cdef double P, S, phi
cdef double smin, smax, s, Jmin, Jmax, J, j
cdef double E_r, gn, gg, gfa, gfb, P_r
cdef double E_diff, abs_value, gJ
cdef double Kr, Ki, x
cdef double complex Ubar, U_, factor
cdef bint hasfission
cdef np.ndarray[double, ndim=2] one
cdef np.ndarray[double complex, ndim=2] K, Imat, U
cdef double [:,:] params
# Get nuclear spin
I = rm.target_spin
elastic = 0.
fission = 0.
total = 0.
A = rm.atomic_weight_ratio
k = _wave_number(A, E)
one = np.eye(3)
K = np.zeros((3,3), dtype=complex)
for i, l in enumerate(rm._l_values):
# Check for l-dependent scattering radius
rho = k*rm.channel_radius[l](E)
rhohat = k*rm.scattering_radius[l](E)
# Calculate shift and penetrability
P, S = penetration_shift(l, rho)
# Calculate phase shift
phi = phaseshift(l, rhohat)
# Calculate common factor on collision matrix terms (term outside curly
# braces in ENDF-102, Eq. D.27)
Ubar = cexp(-2j*phi)
# The channel spin is the vector sum of the target spin, I, and the
# neutron spin, 1/2, so can take on values of |I - 1/2| < s < I + 1/2
smin = abs(I - 0.5)
smax = I + 0.5
num_s = int(smax - smin + 1)
for i_s in range(num_s):
s = i_s + smin
# Total angular momentum is the vector sum of l and s and can assume
# values between |l - s| < J < l + s
Jmin = abs(l - s)
Jmax = l + s
num_J = int(Jmax - Jmin + 1)
for i_J in range(num_J):
J = i_J + Jmin
# Initialize K matrix
for m in range(3):
for n in range(3):
K[m,n] = 0.0
hasfission = False
if (l, J) in rm._parameter_matrix:
params = rm._parameter_matrix[l, J]
for i_res in range(params.shape[0]):
# Sometimes, the same (l, J) quantum numbers can occur
# for different values of the channel spin, s. In this
# case, the sign of the channel spin indicates which
# spin is to be used. If the spin is negative assume
# this resonance comes from the I - 1/2 channel and vice
# versa.
j = params[i_res, 2]
if l > 0:
if (j < 0 and s != smin) or (j > 0 and s != smax):
continue
# Copy resonance parameters
E_r = params[i_res, 0]
gn = params[i_res, 3]
gg = params[i_res, 4]
gfa = params[i_res, 5]
gfb = params[i_res, 6]
P_r = params[i_res, 7]
# Calculate neutron width at energy E
gn = sqrt(P*gn/P_r)
# Calculate j/2 * inverse of denominator of K matrix terms
factor = 0.5j/(E_r - E - 0.5j*gg)
# Upper triangular portion of K matrix -- see ENDF-102,
# Equation D.28
K[0,0] = K[0,0] + gn*gn*factor
if gfa != 0.0 or gfb != 0.0:
# Negate fission widths if necessary
gfa = (-1 if gfa < 0 else 1)*sqrt(abs(gfa))
gfb = (-1 if gfb < 0 else 1)*sqrt(abs(gfb))
K[0,1] = K[0,1] + gn*gfa*factor
K[0,2] = K[0,2] + gn*gfb*factor
K[1,1] = K[1,1] + gfa*gfa*factor
K[1,2] = K[1,2] + gfa*gfb*factor
K[2,2] = K[2,2] + gfb*gfb*factor
hasfission = True
# Get collision matrix
gJ = (2*J + 1)/(4*I + 2)
if hasfission:
# Copy upper triangular portion of K to lower triangular
K[1,0] = K[0,1]
K[2,0] = K[0,2]
K[2,1] = K[1,2]
Imat = inv(one - K)
U = Ubar*(2*Imat - one) # ENDF-102, Eq. D.27
elastic += gJ*cabs(1 - U[0,0])**2 # ENDF-102, Eq. D.24
total += 2*gJ*(1 - creal(U[0,0])) # ENDF-102, Eq. D.23
# Calculate fission from ENDF-102, Eq. D.26
fission += 4*gJ*(cabs(Imat[1,0])**2 + cabs(Imat[2,0])**2)
else:
U_ = Ubar*(2/(1 - K[0,0]) - 1)
if abs(creal(K[0,0])) < 3e-4 and abs(phi) < 3e-4:
# If K and phi are both very small, the calculated cross
# sections can lose precision because the real part of U
# ends up very close to unity. To get around this, we
# use Euler's formula to express Ubar by real and
# imaginary parts, expand cos(2phi) = 1 - 2phi^2 +
# O(phi^4), and then simplify
Kr = creal(K[0,0])
Ki = cimag(K[0,0])
x = 2*(-Kr + (Kr*Kr + Ki*Ki)*(1 - phi*phi) + phi*phi -
sin(2*phi)*Ki)/((1 - Kr)*(1 - Kr) + Ki*Ki)
total += 2*gJ*x
elastic += gJ*(x*x + cimag(U_)**2)
else:
total += 2*gJ*(1 - creal(U_)) # ENDF-102, Eq. D.23
elastic += gJ*cabs(1 - U_)**2 # ENDF-102, Eq. D.24
# Calculate capture as difference of other cross sections as per ENDF-102,
# Equation D.25
capture = total - elastic - fission
elastic *= M_PI/(k*k)
capture *= M_PI/(k*k)
fission *= M_PI/(k*k)
return (elastic, capture, fission)

1063
openmc/data/resonance.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,12 @@
import re
import sys
from six import string_types
import openmc
from openmc.checkvalue import check_type, check_length
from openmc.data import NATURAL_ABUNDANCE
if sys.version_info[0] >= 3:
basestring = str
class Element(object):
"""A natural element used in a material via <element>. Internally, OpenMC will
@ -43,7 +41,7 @@ class Element(object):
return False
else:
return True
elif isinstance(other, basestring) and other == self.name:
elif isinstance(other, string_types) and other == self.name:
return True
else:
return False
@ -78,7 +76,7 @@ class Element(object):
@name.setter
def name(self, name):
check_type('element name', name, basestring)
check_type('element name', name, string_types)
check_length('element name', name, 1, 2)
self._name = name

View file

@ -3,8 +3,7 @@ import subprocess
from numbers import Integral
import sys
if sys.version_info[0] >= 3:
basestring = str
from six import string_types
def _run(command, output, cwd):
@ -89,7 +88,7 @@ def run(particles=None, threads=None, geometry_debug=False,
if geometry_debug:
post_args += '-g '
if isinstance(restart_file, basestring):
if isinstance(restart_file, string_types):
post_args += '-r {0} '.format(restart_file)
if tracks:

View file

@ -1,21 +1,17 @@
from abc import ABCMeta, abstractproperty
from collections import Iterable, OrderedDict
import copy
from six import with_metaclass
from numbers import Real, Integral
import sys
from xml.etree import ElementTree as ET
from six import add_metaclass
import numpy as np
import openmc
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
_FILTER_TYPES = ['universe', 'material', 'cell', 'cellborn', 'surface',
'mesh', 'energy', 'energyout', 'mu', 'polar', 'azimuthal',
'distribcell', 'delayedgroup']
@ -37,7 +33,8 @@ class FilterMeta(ABCMeta):
**kwargs)
class Filter(with_metaclass(FilterMeta, object)):
@add_metaclass(FilterMeta)
class Filter(object):
"""Tally modifier that describes phase-space and other characteristics.
Parameters

View file

@ -1,21 +1,20 @@
from __future__ import division
import abc
from abc import ABCMeta
from collections import OrderedDict, Iterable
from math import sqrt, floor
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
from six import add_metaclass, string_types
import numpy as np
import openmc.checkvalue as cv
import openmc
if sys.version_info[0] >= 3:
basestring = str
@add_metaclass(ABCMeta)
class Lattice(object):
"""A repeating structure wherein each element is a universe.
@ -42,10 +41,6 @@ class Lattice(object):
of the lattice
"""
# This is an abstract class which cannot be instantiated
__metaclass__ = abc.ABCMeta
def __init__(self, lattice_id=None, name=''):
# Initialize Lattice class attributes
self.id = lattice_id
@ -106,7 +101,7 @@ class Lattice(object):
@name.setter
def name(self, name):
if name is not None:
cv.check_type('lattice name', name, basestring)
cv.check_type('lattice name', name, string_types)
self._name = name
else:
self._name = ''

View file

@ -1,9 +1,8 @@
import sys
from openmc.checkvalue import check_type
from six import string_types
if sys.version_info[0] >= 3:
basestring = str
from openmc.checkvalue import check_type
class Macroscopic(object):
@ -34,7 +33,7 @@ class Macroscopic(object):
return False
else:
return True
elif isinstance(other, basestring) and other == self.name:
elif isinstance(other, string_types) and other == self.name:
return True
else:
return False
@ -55,5 +54,5 @@ class Macroscopic(object):
@name.setter
def name(self, name):
check_type('name', name, basestring)
check_type('name', name, string_types)
self._name = name

View file

@ -5,14 +5,13 @@ import warnings
from xml.etree import ElementTree as ET
import sys
from six import string_types
import openmc
import openmc.data
import openmc.checkvalue as cv
from openmc.clean_xml import sort_xml_elements, clean_xml_indentation
if sys.version_info[0] >= 3:
basestring = str
# A static variable for auto-generated Material IDs
AUTO_MATERIAL_ID = 10000
@ -207,7 +206,7 @@ class Material(object):
def name(self, name):
if name is not None:
cv.check_type('name for Material ID="{0}"'.format(self._id),
name, basestring)
name, string_types)
self._name = name
else:
self._name = ''
@ -256,7 +255,7 @@ class Material(object):
warnings.warn('This feature is not yet implemented in a release '
'version of openmc')
if not isinstance(filename, basestring) and filename is not None:
if not isinstance(filename, string_types) and filename is not None:
msg = 'Unable to add OTF material file to Material ID="{0}" with a ' \
'non-string name "{1}"'.format(self._id, filename)
raise ValueError(msg)
@ -290,7 +289,7 @@ class Material(object):
'macroscopic data-set has already been added'.format(self._id)
raise ValueError(msg)
if not isinstance(nuclide, (openmc.Nuclide, basestring)):
if not isinstance(nuclide, string_types + (openmc.Nuclide,)):
msg = 'Unable to add a Nuclide to Material ID="{0}" with a ' \
'non-Nuclide value "{1}"'.format(self._id, nuclide)
raise ValueError(msg)
@ -355,7 +354,7 @@ class Material(object):
'has already been added'.format(self._id, macroscopic)
raise ValueError(msg)
if not isinstance(macroscopic, (openmc.Macroscopic, basestring)):
if not isinstance(macroscopic, string_types + (openmc.Macroscopic,)):
msg = 'Unable to add a Macroscopic to Material ID="{0}" with a ' \
'non-Macroscopic value "{1}"'.format(self._id, macroscopic)
raise ValueError(msg)
@ -425,7 +424,7 @@ class Material(object):
'macroscopic data-set has already been added'.format(self._id)
raise ValueError(msg)
if not isinstance(element, (openmc.Element, basestring)):
if not isinstance(element, string_types + (openmc.Element,)):
msg = 'Unable to add an Element to Material ID="{0}" with a ' \
'non-Element value "{1}"'.format(self._id, element)
raise ValueError(msg)
@ -490,7 +489,7 @@ class Material(object):
'macroscopic data-set has already been added'.format(self._id)
raise ValueError(msg)
if not isinstance(name, basestring):
if not isinstance(name, string_types):
msg = 'Unable to add an S(a,b) table to Material ID="{0}" with a ' \
'non-string table name "{1}"'.format(self._id, name)
raise ValueError(msg)

View file

@ -3,15 +3,13 @@ from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
from six import string_types
import numpy as np
import openmc.checkvalue as cv
import openmc
if sys.version_info[0] >= 3:
basestring = str
# "Static" variable for auto-generated and Mesh IDs
AUTO_MESH_ID = 10000
@ -131,7 +129,7 @@ class Mesh(object):
def name(self, name):
if name is not None:
cv.check_type('name for mesh ID="{0}"'.format(self._id),
name, basestring)
name, string_types)
self._name = name
else:
self._name = ''
@ -139,7 +137,7 @@ class Mesh(object):
@type.setter
def type(self, meshtype):
cv.check_type('type for mesh ID="{0}"'.format(self._id),
meshtype, basestring)
meshtype, string_types)
cv.check_value('type for mesh ID="{0}"'.format(self._id),
meshtype, ['regular'])
self._type = meshtype
@ -286,10 +284,8 @@ class Mesh(object):
openmc.XPlane(x0=self.upper_right[0],
boundary_type=bc[1])]
if len(self.dimension) == 1:
yplanes = [openmc.YPlane(y0=np.finfo(np.float).min,
boundary_type='reflective'),
openmc.YPlane(y0=np.finfo(np.float).max,
boundary_type='reflective')]
yplanes = [openmc.YPlane(y0=-1e10, boundary_type='reflective'),
openmc.YPlane(y0=1e10, boundary_type='reflective')]
else:
yplanes = [openmc.YPlane(y0=self.lower_left[1],
boundary_type=bc[2]),
@ -297,10 +293,15 @@ class Mesh(object):
boundary_type=bc[3])]
if len(self.dimension) <= 2:
zplanes = [openmc.ZPlane(z0=np.finfo(np.float).min,
boundary_type='reflective'),
openmc.ZPlane(z0=np.finfo(np.float).max,
boundary_type='reflective')]
# Would prefer to have the z ranges be the max supported float, but
# these values are apparently different between python and Fortran.
# Choosing a safe and sane default.
# Values of +/-1e10 are used here as there seems to be an
# inconsistency between what numpy uses as the max float and what
# Fortran expects for a real(8), so this avoids code complication
# and achieves the same goal.
zplanes = [openmc.ZPlane(z0=-1e10, boundary_type='reflective'),
openmc.ZPlane(z0=1e10, boundary_type='reflective')]
else:
zplanes = [openmc.ZPlane(z0=self.lower_left[2],
boundary_type=bc[4]),

View file

@ -8,10 +8,6 @@ import numpy as np
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
class EnergyGroups(object):
"""An energy groups structure used for multi-group cross-sections.

View file

@ -6,6 +6,7 @@ from numbers import Integral
from collections import OrderedDict
from warnings import warn
from six import string_types
import numpy as np
import openmc
@ -14,10 +15,6 @@ import openmc.checkvalue as cv
from openmc.tallies import ESTIMATOR_TYPES
if sys.version_info[0] >= 3:
basestring = str
class Library(object):
"""A multi-energy-group and multi-delayed-group cross section library for
some energy group structure.
@ -259,7 +256,7 @@ class Library(object):
@name.setter
def name(self, name):
cv.check_type('name', name, basestring)
cv.check_type('name', name, string_types)
self._name = name
@mgxs_types.setter
@ -268,7 +265,7 @@ class Library(object):
if mgxs_types == 'all':
self._mgxs_types = all_mgxs_types
else:
cv.check_iterable_type('mgxs_types', mgxs_types, basestring)
cv.check_iterable_type('mgxs_types', mgxs_types, string_types)
for mgxs_type in mgxs_types:
cv.check_value('mgxs_type', mgxs_type, all_mgxs_types)
self._mgxs_types = mgxs_types
@ -730,8 +727,8 @@ class Library(object):
'since a statepoint has not yet been loaded'
raise ValueError(msg)
cv.check_type('filename', filename, basestring)
cv.check_type('directory', directory, basestring)
cv.check_type('filename', filename, string_types)
cv.check_type('directory', directory, string_types)
import h5py
@ -773,8 +770,8 @@ class Library(object):
"""
cv.check_type('filename', filename, basestring)
cv.check_type('directory', directory, basestring)
cv.check_type('filename', filename, string_types)
cv.check_type('directory', directory, string_types)
# Make directory if it does not exist
if not os.path.exists(directory):
@ -808,8 +805,8 @@ class Library(object):
"""
cv.check_type('filename', filename, basestring)
cv.check_type('directory', directory, basestring)
cv.check_type('filename', filename, string_types)
cv.check_type('directory', directory, string_types)
# Make directory if it does not exist
if not os.path.exists(directory):
@ -822,11 +819,13 @@ class Library(object):
return pickle.load(open(full_filename, 'rb'))
def get_xsdata(self, domain, xsdata_name, nuclide='total', xs_type='macro',
order=None, tabular_legendre=None, tabular_points=33,
subdomain=None):
order=None, 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).
dataset for writing to an openmc.MGXSLibrary object.
Note that this method does not build an XSdata
object with nested temperature tables. The temperature of each
XSdata object will be left at the default value of 300K.
Parameters
----------
@ -845,18 +844,6 @@ class Library(object):
Scattering order for this data entry. Default is None,
which will set the XSdata object to use the order of the
Library.
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
representation by OpenMC. A value of `True` means that it is to be
converted while a value of `False` means that it will not be.
Defaults to `None` which leaves the default behavior of OpenMC in
place (the distribution is converted to a tabular representation).
tabular_points : int
This parameter is not used unless the ``tabular_legendre``
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
@ -867,7 +854,7 @@ class Library(object):
Returns
-------
xsdata : openmc.XSdata
Multi-Group Cross Section data set object.
Multi-Group Cross Section dataset object.
Raises
------
@ -883,17 +870,13 @@ class Library(object):
cv.check_type('domain', domain, (openmc.Material, openmc.Cell,
openmc.Universe, openmc.Mesh))
cv.check_type('xsdata_name', xsdata_name, basestring)
cv.check_type('nuclide', nuclide, basestring)
cv.check_type('xsdata_name', xsdata_name, string_types)
cv.check_type('nuclide', nuclide, string_types)
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
cv.check_type('order', order, (type(None), Integral))
if order is not None:
cv.check_greater_than('order', order, 0, equality=True)
cv.check_less_than('order', order, 10, equality=True)
cv.check_type('tabular_legendre', tabular_legendre,
(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)
@ -910,7 +893,7 @@ class Library(object):
# Build & add metadata to XSdata object
name = xsdata_name
if nuclide is not 'total':
if nuclide != 'total':
name += '_' + nuclide
xsdata = openmc.XSdata(name, self.energy_groups)
@ -922,14 +905,12 @@ class Library(object):
# the provided order or the Library's order.
xsdata.order = min(order, self.legendre_order)
# Set the tabular_legendre option if needed
if tabular_legendre is not None:
xsdata.tabular_legendre = {'enable': tabular_legendre,
'num_points': tabular_points}
# Right now only 'legendre' data and isotropic weighting is supported
self.scatter_format = 'legendre'
self.representation = 'isotropic'
if nuclide is not 'total':
xsdata.zaid = self._nuclides[nuclide][0]
xsdata.awr = self._nuclides[nuclide][1]
if nuclide != 'total':
xsdata.atomic_weight_ratio = self._nuclides[nuclide][1]
if subdomain is None:
subdomain = 'all'
@ -940,7 +921,7 @@ class Library(object):
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],
subdomains=subdomain)
subdomain=subdomain)
elif 'total' in self.mgxs_types:
mymgxs = self.get_mgxs(domain, 'total')
xsdata.set_total_mgxs(mymgxs, xs_type=xs_type, nuclide=[nuclide],
@ -979,49 +960,55 @@ class Library(object):
# 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],
subdomain=subdomain)
xsdata.set_multiplicity_matrix_mgxs(mymgxs, xs_type=xs_type,
nuclide=[nuclide],
subdomain=subdomain)
using_multiplicity = True
# 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],
subdomain=subdomain)
xsdata.set_multiplicity_matrix_mgxs(nuscatt_mgxs, scatt_mgxs,
xs_type=xs_type,
nuclide=[nuclide],
subdomain=subdomain)
using_multiplicity = True
else:
using_multiplicity = False
if using_multiplicity:
nuscatt_mgxs = self.get_mgxs(domain, 'nu-scatter matrix')
xsdata.set_scatter_mgxs(nuscatt_mgxs, xs_type=xs_type,
nuclide=[nuclide], subdomain=subdomain)
xsdata.set_scatter_matrix_mgxs(nuscatt_mgxs, xs_type=xs_type,
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],
subdomain=subdomain)
xsdata.set_scatter_matrix_mgxs(nuscatt_mgxs, xs_type=xs_type,
nuclide=[nuclide],
subdomain=subdomain)
# Since we are not using multiplicity, then
# scattering multiplication (nu-scatter) must be
# accounted for approximately by using an adjusted
# absorption cross section.
if 'total' in self.mgxs_types:
xsdata.absorption = \
np.subtract(xsdata.total,
np.sum(xsdata.scatter[0, :, :], axis=1))
if 'total' in self.mgxs_types or 'transport' in self.mgxs_types:
for i in range(len(xsdata.temperatures)):
xsdata._absorption[i] = \
np.subtract(xsdata._total[i], np.sum(
xsdata._scatter_matrix[i][0, :, :], axis=1))
return xsdata
def create_mg_library(self, xs_type='macro', xsdata_names=None,
tabular_legendre=None, tabular_points=33):
def create_mg_library(self, xs_type='macro', xsdata_names=None):
"""Creates an openmc.MGXSLibrary object to contain the MGXS data for the
Multi-Group mode of OpenMC.
Note that this library will not make use of nested temperature tables.
Every dataset in the library will be treated as if it was at the same
default temperature.
Parameters
----------
xs_type: {'macro', 'micro'}
@ -1031,18 +1018,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', ...
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
representation by OpenMC. A value of `True` means that it is to be
converted while a value of `False` means that it will not be.
Defaults to `None` which leaves the default behavior of OpenMC in
place (the distribution is converted to a tabular representation).
tabular_points : int
This parameter is not used unless the ``tabular_legendre``
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`.
Returns
-------
@ -1069,7 +1044,7 @@ 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)
cv.check_iterable_type('xsdata_names', xsdata_names, string_types)
# If gathering material-specific data, set the xs_type to macro
if not self.by_nuclide:
@ -1094,8 +1069,6 @@ class Library(object):
# 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
@ -1113,45 +1086,34 @@ class Library(object):
xsdata_name = 'set' + str(i + 1)
else:
xsdata_name = xsdata_names[i]
if nuclide is not 'total':
if nuclide != 'total':
xsdata_name += '_' + nuclide
xsdata = self.get_xsdata(domain, xsdata_name,
nuclide=nuclide, xs_type=xs_type,
tabular_legendre=tabular_legendre,
tabular_points=tabular_points)
nuclide=nuclide, xs_type=xs_type)
mgxs_file.add_xsdata(xsdata)
return mgxs_file
def create_mg_mode(self, xsdata_names=None, tabular_legendre=None,
tabular_points=33, bc=['reflective'] * 6):
def create_mg_mode(self, xsdata_names=None, 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
used to generate the MGXS data, with the only differences being
modifications to point to newly-created Materials which point to the
multi-group data. This method only creates a macroscopic
MGXS Library even if nuclidic tallies are specified in the Library.
and openmc.Geometry objects.
The created Geometry is the same as that used to generate the MGXS
data, with the only differences being modifications to point to
newly-created Materials which point to the multi-group data. This
method only creates a macroscopic MGXS Library even if nuclidic tallies
are specified in the Library. Note that this library will not make
use of nested temperature tables. Every dataset in the library will
be treated as if it was at the same default temperature.
Parameters
----------
xsdata_names : Iterable of str
List of names to apply to the "xsdata" entries in the
resultant mgxs data file. Defaults to 'set1', 'set2', ...
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
representation by OpenMC. A value of `True` means that it is to be
converted while a value of `False` means that it will not be.
Defaults to `None` which leaves the default behavior of OpenMC in
place (the distribution is converted to a tabular representation).
tabular_points : int
This parameter is not used unless the ``tabular_legendre``
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
@ -1198,8 +1160,7 @@ class Library(object):
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)
mgxs_file = self.create_mg_library('macro', xsdata_names)
# Now move on the creating the geometry and assigning materials
if self.domain_type == 'mesh':

View file

@ -6,8 +6,9 @@ import warnings
import os
import sys
import copy
import abc
from abc import ABCMeta
from six import add_metaclass, string_types
import numpy as np
import openmc
@ -15,8 +16,6 @@ from openmc.mgxs import MGXS
from openmc.mgxs.mgxs import _DOMAIN_TO_FILTER
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
# Supported cross section types
MDGXS_TYPES = ['delayed-nu-fission',
@ -28,6 +27,7 @@ MDGXS_TYPES = ['delayed-nu-fission',
MAX_DELAYED_GROUPS = 8
@add_metaclass(ABCMeta)
class MDGXS(MGXS):
"""An abstract multi-delayed-group cross section for some energy and delayed
group structures within some spatial domain.
@ -117,10 +117,6 @@ class MDGXS(MGXS):
The key used to index multi-group cross sections in an HDF5 data store
"""
# This is an abstract class which cannot be instantiated
__metaclass__ = abc.ABCMeta
def __init__(self, domain=None, domain_type=None, energy_groups=None,
delayed_groups=None, by_nuclide=False, name=''):
super(MDGXS, self).__init__(domain, domain_type, energy_groups,
@ -322,7 +318,7 @@ class MDGXS(MGXS):
filter_bins = []
# Construct a collection of the domain filter bins
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral,
max_depth=3)
for subdomain in subdomains:
@ -330,7 +326,7 @@ class MDGXS(MGXS):
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(groups, basestring):
if not isinstance(groups, string_types):
cv.check_iterable_type('groups', groups, Integral)
for group in groups:
filters.append(openmc.EnergyFilter)
@ -338,7 +334,7 @@ class MDGXS(MGXS):
(self.energy_groups.get_group_bounds(group),))
# Construct list of delayed group tuples for all requested groups
if not isinstance(delayed_groups, basestring):
if not isinstance(delayed_groups, string_types):
cv.check_type('delayed groups', delayed_groups, list, int)
for delayed_group in delayed_groups:
filters.append(openmc.DelayedGroupFilter)
@ -434,7 +430,7 @@ class MDGXS(MGXS):
"""
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
cv.check_iterable_type('energy_groups', groups, Integral)
cv.check_type('delayed groups', delayed_groups, list, int)
@ -544,7 +540,7 @@ class MDGXS(MGXS):
return
# Construct a collection of the subdomains to report
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
@ -561,7 +557,7 @@ class MDGXS(MGXS):
elif nuclides == 'sum':
nuclides = ['sum']
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
else:
nuclides = ['sum']
@ -651,8 +647,8 @@ class MDGXS(MGXS):
"""
cv.check_type('filename', filename, basestring)
cv.check_type('directory', directory, basestring)
cv.check_type('filename', filename, string_types)
cv.check_type('directory', directory, string_types)
cv.check_value('format', format, ['csv', 'excel', 'pickle', 'latex'])
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
@ -742,11 +738,11 @@ class MDGXS(MGXS):
"""
if not isinstance(groups, basestring):
if not isinstance(groups, string_types):
cv.check_iterable_type('groups', groups, Integral)
if nuclides != 'all' and nuclides != 'sum':
cv.check_iterable_type('nuclides', nuclides, basestring)
if not isinstance(delayed_groups, basestring):
cv.check_iterable_type('nuclides', nuclides, string_types)
if not isinstance(delayed_groups, string_types):
cv.check_type('delayed groups', delayed_groups, list, int)
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
@ -821,7 +817,7 @@ class MDGXS(MGXS):
columns = ['group in']
# Select out those groups the user requested
if not isinstance(groups, basestring):
if not isinstance(groups, string_types):
if 'group in' in df:
df = df[df['group in'].isin(groups)]
if 'group out' in df:
@ -1212,7 +1208,7 @@ class ChiDelayed(MDGXS):
filter_bins = []
# Construct a collection of the domain filter bins
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral,
max_depth=3)
for subdomain in subdomains:
@ -1220,7 +1216,7 @@ class ChiDelayed(MDGXS):
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(groups, basestring):
if not isinstance(groups, string_types):
cv.check_iterable_type('groups', groups, Integral)
for group in groups:
filters.append(openmc.EnergyoutFilter)
@ -1228,7 +1224,7 @@ class ChiDelayed(MDGXS):
(self.energy_groups.get_group_bounds(group),))
# Construct list of delayed group tuples for all requested groups
if not isinstance(delayed_groups, basestring):
if not isinstance(delayed_groups, string_types):
cv.check_type('delayed groups', delayed_groups, list, int)
for delayed_group in delayed_groups:
filters.append(openmc.DelayedGroupFilter)
@ -1276,7 +1272,7 @@ class ChiDelayed(MDGXS):
# Get chi delayed for user-specified nuclides in the domain
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
xs = self.xs_tally.get_values(filters=filters,
filter_bins=filter_bins,
nuclides=nuclides, value=value)

View file

@ -6,9 +6,10 @@ import warnings
import os
import sys
import copy
import abc
from abc import ABCMeta
import itertools
from six import add_metaclass, string_types
import numpy as np
import openmc
@ -16,9 +17,6 @@ import openmc.checkvalue as cv
from openmc.tallies import ESTIMATOR_TYPES
from openmc.mgxs import EnergyGroups
if sys.version_info[0] >= 3:
basestring = str
# Supported cross section types
MGXS_TYPES = ['total',
@ -61,6 +59,7 @@ _DOMAINS = (openmc.Cell,
openmc.Mesh)
@add_metaclass(ABCMeta)
class MGXS(object):
"""An abstract multi-group cross section for some energy group structure
within some spatial domain.
@ -145,10 +144,6 @@ class MGXS(object):
The key used to index multi-group cross sections in an HDF5 data store
"""
# This is an abstract class which cannot be instantiated
__metaclass__ = abc.ABCMeta
def __init__(self, domain=None, domain_type=None,
energy_groups=None, by_nuclide=False, name=''):
self._name = ''
@ -368,7 +363,7 @@ class MGXS(object):
@name.setter
def name(self, name):
cv.check_type('name', name, basestring)
cv.check_type('name', name, string_types)
self._name = name
@by_nuclide.setter
@ -378,7 +373,7 @@ class MGXS(object):
@nuclides.setter
def nuclides(self, nuclides):
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
self._nuclides = nuclides
@estimator.setter
@ -562,7 +557,7 @@ class MGXS(object):
"""
cv.check_type('nuclide', nuclide, basestring)
cv.check_type('nuclide', nuclide, string_types)
# Get list of all nuclides in the spatial domain
nuclides = self.domain.get_nuclide_densities()
@ -788,7 +783,7 @@ class MGXS(object):
filter_bins = []
# Construct a collection of the domain filter bins
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral,
max_depth=3)
for subdomain in subdomains:
@ -796,7 +791,7 @@ class MGXS(object):
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(groups, basestring):
if not isinstance(groups, string_types):
cv.check_iterable_type('groups', groups, Integral)
for group in groups:
filters.append(openmc.EnergyFilter)
@ -958,7 +953,7 @@ class MGXS(object):
"""
# Construct a collection of the subdomain filter bins to average across
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains)
@ -1013,7 +1008,7 @@ class MGXS(object):
"""
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
cv.check_iterable_type('energy_groups', groups, Integral)
# Build lists of filters and filter bins to slice
@ -1167,7 +1162,7 @@ class MGXS(object):
"""
# Construct a collection of the subdomains to report
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
@ -1184,7 +1179,7 @@ class MGXS(object):
elif nuclides == 'sum':
nuclides = ['sum']
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
else:
nuclides = ['sum']
@ -1302,7 +1297,7 @@ class MGXS(object):
xs_results = h5py.File(filename, 'w')
# Construct a collection of the subdomains to report
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
@ -1323,7 +1318,7 @@ class MGXS(object):
elif nuclides == 'sum':
nuclides = ['sum']
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
else:
nuclides = ['sum']
@ -1401,8 +1396,8 @@ class MGXS(object):
"""
cv.check_type('filename', filename, basestring)
cv.check_type('directory', directory, basestring)
cv.check_type('filename', filename, string_types)
cv.check_type('directory', directory, string_types)
cv.check_value('format', format, ['csv', 'excel', 'pickle', 'latex'])
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
@ -1488,10 +1483,10 @@ class MGXS(object):
"""
if not isinstance(groups, basestring):
if not isinstance(groups, string_types):
cv.check_iterable_type('groups', groups, Integral)
if nuclides != 'all' and nuclides != 'sum':
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
cv.check_value('xs_type', xs_type, ['macro', 'micro'])
# Get a Pandas DataFrame from the derived xs tally
@ -1530,7 +1525,7 @@ 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, len(query_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)
@ -1563,7 +1558,7 @@ class MGXS(object):
columns = ['group in']
# Select out those groups the user requested
if not isinstance(groups, basestring):
if not isinstance(groups, string_types):
if 'group in' in df:
df = df[df['group in'].isin(groups)]
if 'group out' in df:
@ -1616,6 +1611,7 @@ class MGXS(object):
return 'cm^-1' if xs_type == 'macro' else 'barns'
@add_metaclass(ABCMeta)
class MatrixMGXS(MGXS):
"""An abstract multi-group cross section for some energy group structure
within some spatial domain. This class is specifically intended for
@ -1703,10 +1699,6 @@ class MatrixMGXS(MGXS):
The key used to index multi-group cross sections in an HDF5 data store
"""
# This is an abstract class which cannot be instantiated
__metaclass__ = abc.ABCMeta
@property
def filters(self):
# Create the non-domain specific Filters for the Tallies
@ -1786,7 +1778,7 @@ class MatrixMGXS(MGXS):
filter_bins = []
# Construct a collection of the domain filter bins
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral,
max_depth=3)
for subdomain in subdomains:
@ -1794,7 +1786,7 @@ class MatrixMGXS(MGXS):
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(in_groups, basestring):
if not isinstance(in_groups, string_types):
cv.check_iterable_type('groups', in_groups, Integral)
for group in in_groups:
filters.append(openmc.EnergyFilter)
@ -1802,7 +1794,7 @@ class MatrixMGXS(MGXS):
self.energy_groups.get_group_bounds(group),))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(out_groups, basestring):
if not isinstance(out_groups, string_types):
cv.check_iterable_type('groups', out_groups, Integral)
for group in out_groups:
filters.append(openmc.EnergyoutFilter)
@ -1948,7 +1940,7 @@ class MatrixMGXS(MGXS):
"""
# Construct a collection of the subdomains to report
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
@ -1965,7 +1957,7 @@ class MatrixMGXS(MGXS):
if nuclides == 'sum':
nuclides = ['sum']
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
else:
nuclides = ['sum']
@ -3621,21 +3613,21 @@ class ScatterMatrixXS(MatrixMGXS):
filter_bins = []
# Construct a collection of the domain filter bins
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3)
for subdomain in subdomains:
filters.append(_DOMAIN_TO_FILTER[self.domain_type])
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(in_groups, basestring):
if not isinstance(in_groups, string_types):
cv.check_iterable_type('groups', in_groups, Integral)
for group in in_groups:
filters.append(openmc.EnergyFilter)
filter_bins.append((self.energy_groups.get_group_bounds(group),))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(out_groups, basestring):
if not isinstance(out_groups, string_types):
cv.check_iterable_type('groups', out_groups, Integral)
for group in out_groups:
filters.append(openmc.EnergyoutFilter)
@ -3809,7 +3801,7 @@ class ScatterMatrixXS(MatrixMGXS):
"""
# Construct a collection of the subdomains to report
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral)
elif self.domain_type == 'distribcell':
subdomains = np.arange(self.num_subdomains, dtype=np.int)
@ -3826,7 +3818,7 @@ class ScatterMatrixXS(MatrixMGXS):
if nuclides == 'sum':
nuclides = ['sum']
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
else:
nuclides = ['sum']
@ -4609,14 +4601,14 @@ class Chi(MGXS):
filter_bins = []
# Construct a collection of the domain filter bins
if not isinstance(subdomains, basestring):
if not isinstance(subdomains, string_types):
cv.check_iterable_type('subdomains', subdomains, Integral, max_depth=3)
for subdomain in subdomains:
filters.append(_DOMAIN_TO_FILTER[self.domain_type])
filter_bins.append((subdomain,))
# Construct list of energy group bounds tuples for all requested groups
if not isinstance(groups, basestring):
if not isinstance(groups, string_types):
cv.check_iterable_type('groups', groups, Integral)
for group in groups:
filters.append(openmc.EnergyoutFilter)
@ -4661,7 +4653,7 @@ class Chi(MGXS):
# Get chi for user-specified nuclides in the domain
else:
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
xs = self.xs_tally.get_values(filters=filters,
filter_bins=filter_bins,
nuclides=nuclides, value=value)

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@ from heapq import heappush, heappop
from math import pi, sin, cos, floor, log10, sqrt
from abc import ABCMeta, abstractproperty, abstractmethod
from six import add_metaclass
import numpy as np
try:
import scipy.spatial
@ -95,6 +96,7 @@ class TRISO(openmc.Cell):
k_min:k_max+1, j_min:j_max+1, i_min:i_max+1]))
@add_metaclass(ABCMeta)
class _Domain(object):
"""Container in which to pack particles.
@ -123,9 +125,6 @@ class _Domain(object):
Volume of the container.
"""
__metaclass__ = ABCMeta
def __init__(self, particle_radius, center=[0., 0., 0.]):
self._cell_length = None
self._limits = None

View file

@ -2,10 +2,9 @@ from numbers import Integral
import sys
import warnings
from openmc.checkvalue import check_type
from six import string_types
if sys.version_info[0] >= 3:
basestring = str
from openmc.checkvalue import check_type
class Nuclide(object):
@ -39,7 +38,7 @@ class Nuclide(object):
return False
else:
return True
elif isinstance(other, basestring) and other == self.name:
elif isinstance(other, string_types) and other == self.name:
return True
else:
return False
@ -73,7 +72,7 @@ class Nuclide(object):
@name.setter
def name(self, name):
check_type('name', name, basestring)
check_type('name', name, string_types)
self._name = name
if '-' in name:

View file

@ -4,14 +4,13 @@ from xml.etree import ElementTree as ET
import sys
import warnings
from six import string_types
import numpy as np
import openmc
import openmc.checkvalue as cv
from openmc.clean_xml import clean_xml_indentation
if sys.version_info[0] >= 3:
basestring = str
# A static variable for auto-generated Plot IDs
AUTO_PLOT_ID = 10000
@ -166,7 +165,7 @@ class Plot(object):
@name.setter
def name(self, name):
cv.check_type('plot name', name, basestring)
cv.check_type('plot name', name, string_types)
self._name = name
@width.setter
@ -191,24 +190,24 @@ class Plot(object):
@filename.setter
def filename(self, filename):
cv.check_type('filename', filename, basestring)
cv.check_type('filename', filename, string_types)
self._filename = filename
@color.setter
def color(self, color):
cv.check_type('plot color', color, basestring)
cv.check_type('plot color', color, string_types)
cv.check_value('plot color', color, ['cell', 'mat'])
self._color = color
@type.setter
def type(self, plottype):
cv.check_type('plot type', plottype, basestring)
cv.check_type('plot type', plottype, string_types)
cv.check_value('plot type', plottype, ['slice', 'voxel'])
self._type = plottype
@basis.setter
def basis(self, basis):
cv.check_type('plot basis', basis, basestring)
cv.check_type('plot basis', basis, string_types)
cv.check_value('plot basis', basis, ['xy', 'xz', 'yz'])
self._basis = basis
@ -387,7 +386,7 @@ class Plot(object):
cv.check_less_than('alpha', alpha, 1., equality=True)
# Get a background (R,G,B) tuple to apply in alpha compositing
if isinstance(background, basestring):
if isinstance(background, string_types):
if background == 'white':
background = (255, 255, 255)
elif background == 'black':

View file

@ -1,11 +1,13 @@
from abc import ABCMeta, abstractmethod
from collections import Iterable
from six import add_metaclass
import numpy as np
from openmc.checkvalue import check_type
@add_metaclass(ABCMeta)
class Region(object):
"""Region of space that can be assigned to a cell.
@ -16,9 +18,6 @@ class Region(object):
created through operators of the Surface and Region classes.
"""
__metaclass__ = ABCMeta
def __and__(self, other):
return Intersection(self, other)

View file

@ -4,15 +4,13 @@ import warnings
from xml.etree import ElementTree as ET
import sys
from six import string_types
import numpy as np
from openmc.clean_xml import clean_xml_indentation
import openmc.checkvalue as cv
from openmc import Nuclide, VolumeCalculation, Source
if sys.version_info[0] >= 3:
basestring = str
class Settings(object):
"""Settings used for an OpenMC simulation.
@ -89,10 +87,14 @@ class Settings(object):
Seed for the linear congruential pseudorandom number generator
survival_biasing : bool
Indicate whether survival biasing is to be used
weight : float
Weight cutoff below which particle undergo Russian roulette
weight_avg : float
Weight assigned to particles that are not killed after Russian roulette
cutoff : dict
Dictionary defining weight cutoff and energy cutoff. The dictionary may
have three keys, 'weight', 'weight_avg' and 'energy'. Value for 'weight'
should be a float indicating weight cutoff below which particle undergo
Russian roulette. Value for 'weight_avg' should be a float indicating
weight assigned to particles that are not killed after Russian
roulette. Value of energy should be a float indicating energy in MeV
below which particle will be killed.
entropy_dimension : tuple or list
Number of Shannon entropy mesh cells in the x, y, and z directions,
respectively
@ -100,6 +102,13 @@ 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
tabular_legendre : dict
Determines if a multi-group scattering moment kernel expanded via
Legendre polynomials is to be converted to a tabular distribution or
not. Accepted keys are 'enable' and 'num_points'. The value for
'enable' is a bool stating whether the conversion to tabular is
performed; the value for 'num_points' sets the number of points to use
in the tabular distribution, should 'enable' be True.
temperature : dict
Defines a default temperature and method for treating intermediate
temperatures at which nuclear data doesn't exist. Accepted keys are
@ -141,6 +150,8 @@ class Settings(object):
The elastic scattering model to use for resonant isotopes
volume_calculations : VolumeCalculation or iterable of VolumeCalculation
Stochastic volume calculation specifications
create_fission_neutrons : bool
Indicate whether fission neutrons should be created or not.
"""
@ -200,11 +211,12 @@ class Settings(object):
self._trace = None
self._track = None
self._tabular_legendre = {}
self._temperature = {}
# Cutoff subelement
self._weight = None
self._weight_avg = None
self._cutoff = None
# Uniform fission source subelement
self._ufs_dimension = 1
@ -227,6 +239,8 @@ class Settings(object):
self._volume_calculations = cv.CheckedList(
VolumeCalculation, 'volume calculations')
self._create_fission_neutrons = None
@property
def run_mode(self):
return self._run_mode
@ -363,6 +377,10 @@ class Settings(object):
def verbosity(self):
return self._verbosity
@property
def tabular_legendre(self):
return self._tabular_legendre
@property
def temperature(self):
return self._temperature
@ -376,12 +394,8 @@ class Settings(object):
return self._track
@property
def weight(self):
return self._weight
@property
def weight_avg(self):
return self._weight_avg
def cutoff(self):
return self._cutoff
@property
def ufs_dimension(self):
@ -427,6 +441,10 @@ class Settings(object):
def volume_calculations(self):
return self._volume_calculations
@property
def create_fission_neutrons(self):
return self._create_fission_neutrons
@run_mode.setter
def run_mode(self, run_mode):
if run_mode not in ['eigenvalue', 'fixed source']:
@ -529,7 +547,7 @@ class Settings(object):
@output_path.setter
def output_path(self, output_path):
cv.check_type('output path', output_path, basestring)
cv.check_type('output path', output_path, string_types)
self._output_path = output_path
@verbosity.setter
@ -585,12 +603,12 @@ class Settings(object):
@cross_sections.setter
def cross_sections(self, cross_sections):
cv.check_type('cross sections', cross_sections, basestring)
cv.check_type('cross sections', cross_sections, string_types)
self._cross_sections = cross_sections
@multipole_library.setter
def multipole_library(self, multipole_library):
cv.check_type('cross sections', multipole_library, basestring)
cv.check_type('cross sections', multipole_library, string_types)
self._multipole_library = multipole_library
@ptables.setter
@ -614,17 +632,29 @@ class Settings(object):
cv.check_type('survival biasing', survival_biasing, bool)
self._survival_biasing = survival_biasing
@weight.setter
def weight(self, weight):
cv.check_type('weight cutoff', weight, Real)
cv.check_greater_than('weight cutoff', weight, 0.0)
self._weight = weight
@cutoff.setter
def cutoff(self, cutoff):
if not isinstance(cutoff, Mapping):
msg = 'Unable to set cutoff from "{0}" which is not a '\
' Python dictionary'.format(cutoff)
raise ValueError(msg)
for key in cutoff:
if key == 'weight':
cv.check_type('weight cutoff', cutoff['weight'], Real)
cv.check_greater_than('weight cutoff', cutoff['weight'], 0.0)
elif key == 'weight_avg':
cv.check_type('average survival weight', cutoff['weight_avg'],
Real)
cv.check_greater_than('average survival weight',
cutoff['weight_avg'], 0.0)
elif key == 'energy':
cv.check_type('energy cutoff', cutoff['energy'], Real)
cv.check_greater_than('energy cutoff', cutoff['energy'], 0.0)
else:
msg = 'Unable to set cutoff to "{0}" which is unsupported by '\
'OpenMC'.format(key)
@weight_avg.setter
def weight_avg(self, weight_avg):
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
self._cutoff = cutoff
@entropy_dimension.setter
def entropy_dimension(self, dimension):
@ -668,6 +698,19 @@ class Settings(object):
cv.check_type('no reduction option', no_reduce, bool)
self._no_reduce = no_reduce
@tabular_legendre.setter
def tabular_legendre(self, tabular_legendre):
cv.check_type('tabular_legendre settings', tabular_legendre, Mapping)
for key, value in tabular_legendre.items():
cv.check_value('tabular_legendre key', key,
['enable', 'num_points'])
if key == 'enable':
cv.check_type('enable tabular_legendre', value, bool)
elif key == 'num_points':
cv.check_type('num_points tabular_legendre', value, Integral)
cv.check_greater_than('num_points tabular_legendre', value, 0)
self._tabular_legendre = tabular_legendre
@temperature.setter
def temperature(self, temperature):
cv.check_type('temperature settings', temperature, Mapping)
@ -826,6 +869,12 @@ class Settings(object):
self._volume_calculations = cv.CheckedList(
VolumeCalculation, 'stochastic volume calculations', vol_calcs)
@create_fission_neutrons.setter
def create_fission_neutrons(self, create_fission_neutrons):
cv.check_type('Whether create fission neutrons',
create_fission_neutrons, bool)
self._create_fission_neutrons = create_fission_neutrons
def _create_run_mode_subelement(self):
if self.run_mode == 'eigenvalue':
@ -990,14 +1039,19 @@ class Settings(object):
element.text = str(self._survival_biasing).lower()
def _create_cutoff_subelement(self):
if self._weight is not None:
if self._cutoff is not None:
element = ET.SubElement(self._settings_file, "cutoff")
if 'weight' in self._cutoff:
subelement = ET.SubElement(element, "weight")
subelement.text = str(self._cutoff['weight'])
subelement = ET.SubElement(element, "weight")
subelement.text = str(self._weight)
if 'weight_avg' in self._cutoff:
subelement = ET.SubElement(element, "weight_avg")
subelement.text = str(self._cutoff['weight_avg'])
subelement = ET.SubElement(element, "weight_avg")
subelement.text = str(self._weight_avg)
if 'energy' in self._cutoff:
subelement = ET.SubElement(element, "energy")
subelement.text = str(self._cutoff['energy'])
def _create_entropy_subelement(self):
if self._entropy_lower_left is not None and \
@ -1051,6 +1105,15 @@ class Settings(object):
element = ET.SubElement(self._settings_file, "no_reduce")
element.text = str(self._no_reduce).lower()
def _create_tabular_legendre_subelements(self):
if self.tabular_legendre:
element = ET.SubElement(self._settings_file, "tabular_legendre")
subelement = ET.SubElement(element, "enable")
subelement.text = str(self._tabular_legendre['enable']).lower()
if 'num_points' in self._tabular_legendre:
subelement = ET.SubElement(element, "num_points")
subelement.text = str(self._tabular_legendre['num_points'])
def _create_temperature_subelements(self):
if self.temperature:
for key, value in sorted(self.temperature.items()):
@ -1121,6 +1184,11 @@ class Settings(object):
for r in self.resonance_scattering:
elem.append(r.to_xml_element())
def _create_create_fission_neutrons_subelement(self):
if self._create_fission_neutrons is not None:
elem = ET.SubElement(self._settings_file, "create_fission_neutrons")
elem.text = str(self._create_fission_neutrons).lower()
def export_to_xml(self, path='settings.xml'):
"""Export simulation settings to an XML file.
@ -1157,6 +1225,7 @@ class Settings(object):
self._create_no_reduce_subelement()
self._create_threads_subelement()
self._create_verbosity_subelement()
self._create_tabular_legendre_subelements()
self._create_temperature_subelements()
self._create_trace_subelement()
self._create_track_subelement()
@ -1164,6 +1233,7 @@ class Settings(object):
self._create_dd_subelement()
self._create_resonance_scattering_subelement()
self._create_volume_calcs_subelement()
self._create_create_fission_neutrons_subelement()
# Clean the indentation in the file to be user-readable
clean_xml_indentation(self._settings_file)

View file

@ -2,13 +2,12 @@ from numbers import Real
import sys
from xml.etree import ElementTree as ET
from six import string_types
from openmc.stats.univariate import Univariate
from openmc.stats.multivariate import UnitSphere, Spatial
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
class Source(object):
"""Distribution of phase space coordinates for source sites.
@ -79,7 +78,7 @@ class Source(object):
@file.setter
def file(self, filename):
cv.check_type('source file', filename, basestring)
cv.check_type('source file', filename, string_types)
self._file = filename
@space.setter

View file

@ -9,9 +9,6 @@ import numpy as np
import openmc
import openmc.checkvalue as cv
if sys.version > '3':
long = int
class StatePoint(object):
"""State information on a simulation at a certain point in time (at the end

View file

@ -5,15 +5,14 @@ from numbers import Real
import sys
from xml.etree import ElementTree as ET
from six import add_metaclass
import numpy as np
import openmc.checkvalue as cv
from openmc.stats.univariate import Univariate, Uniform
if sys.version_info[0] >= 3:
basestring = str
@add_metaclass(ABCMeta)
class UnitSphere(object):
"""Distribution of points on the unit sphere.
@ -31,9 +30,6 @@ class UnitSphere(object):
Direction from which polar angle is measured
"""
__metaclass__ = ABCMeta
def __init__(self, reference_uvw=None):
self._reference_uvw = None
if reference_uvw is not None:
@ -184,6 +180,7 @@ class Monodirectional(UnitSphere):
return element
@add_metaclass(ABCMeta)
class Spatial(object):
"""Distribution of locations in three-dimensional Euclidean space.
@ -191,9 +188,6 @@ class Spatial(object):
distributions of source sites.
"""
__metaclass__ = ABCMeta
def __init__(self):
pass

View file

@ -4,18 +4,18 @@ from numbers import Real
import sys
from xml.etree import ElementTree as ET
from six import add_metaclass
import numpy as np
import openmc.checkvalue as cv
from openmc.mixin import EqualityMixin
if sys.version_info[0] >= 3:
basestring = str
_INTERPOLATION_SCHEMES = ['histogram', 'linear-linear', 'linear-log',
'log-linear', 'log-log']
@add_metaclass(ABCMeta)
class Univariate(EqualityMixin):
"""Probability distribution of a single random variable.
@ -23,9 +23,6 @@ class Univariate(EqualityMixin):
specific probability distribution.
"""
__metaclass__ = ABCMeta
def __init__(self):
pass

View file

@ -4,13 +4,12 @@ from xml.etree import ElementTree as ET
import sys
from math import sqrt
from six import add_metaclass, string_types
import numpy as np
from openmc.checkvalue import check_type, check_value, check_greater_than
from openmc.region import Region, Intersection
if sys.version_info[0] >= 3:
basestring = str
# A static variable for auto-generated Surface IDs
AUTO_SURFACE_ID = 10000
@ -134,14 +133,14 @@ class Surface(object):
@name.setter
def name(self, name):
if name is not None:
check_type('surface name', name, basestring)
check_type('surface name', name, string_types)
self._name = name
else:
self._name = ''
@boundary_type.setter
def boundary_type(self, boundary_type):
check_type('boundary type', boundary_type, basestring)
check_type('boundary type', boundary_type, string_types)
check_value('boundary type', boundary_type, _BC_TYPES)
self._boundary_type = boundary_type
@ -642,6 +641,7 @@ class ZPlane(Plane):
return point[2] - self.z0
@add_metaclass(ABCMeta)
class Cylinder(Surface):
"""A cylinder whose length is parallel to the x-, y-, or z-axis.
@ -677,9 +677,6 @@ class Cylinder(Surface):
Type of the surface
"""
__metaclass__ = ABCMeta
def __init__(self, surface_id=None, boundary_type='transmission',
R=1., name=''):
super(Cylinder, self).__init__(surface_id, boundary_type, name=name)
@ -1210,7 +1207,7 @@ class Sphere(Surface):
z = point[2] - self.z0
return x**2 + y**2 + z**2 - self.r**2
@add_metaclass(ABCMeta)
class Cone(Surface):
"""A conical surface parallel to the x-, y-, or z-axis.
@ -1257,9 +1254,6 @@ class Cone(Surface):
Type of the surface
"""
__metaclass__ = ABCMeta
def __init__(self, surface_id=None, boundary_type='transmission',
x0=0., y0=0., z0=0., R2=1., name=''):
super(Cone, self).__init__(surface_id, boundary_type, name=name)

View file

@ -11,15 +11,13 @@ import sys
import warnings
from xml.etree import ElementTree as ET
from six import string_types
import numpy as np
import openmc
import openmc.checkvalue as cv
from openmc.clean_xml import clean_xml_indentation
if sys.version_info[0] >= 3:
basestring = str
# "Static" variable for auto-generated Tally IDs
AUTO_TALLY_ID = 10000
@ -33,9 +31,9 @@ _PRODUCT_TYPES = ['tensor', 'entrywise']
# The following indicate acceptable types when setting Tally.scores,
# Tally.nuclides, and Tally.filters
_SCORE_CLASSES = (basestring, openmc.CrossScore, openmc.AggregateScore)
_NUCLIDE_CLASSES = (basestring, openmc.Nuclide, openmc.CrossNuclide,
openmc.AggregateNuclide)
_SCORE_CLASSES = string_types + (openmc.CrossScore, openmc.AggregateScore)
_NUCLIDE_CLASSES = string_types + (openmc.Nuclide, openmc.CrossNuclide,
openmc.AggregateNuclide)
_FILTER_CLASSES = (openmc.Filter, openmc.CrossFilter, openmc.AggregateFilter)
# Valid types of estimators
@ -446,7 +444,7 @@ class Tally(object):
@name.setter
def name(self, name):
if name is not None:
cv.check_type('tally name', name, basestring)
cv.check_type('tally name', name, string_types)
self._name = name
else:
self._name = ''
@ -499,7 +497,7 @@ class Tally(object):
raise ValueError(msg)
# If score is a string, strip whitespace
if isinstance(score, basestring):
if isinstance(score, string_types):
scores[i] = score.strip()
self._scores = cv.CheckedList(_SCORE_CLASSES, 'tally scores', scores)
@ -1381,7 +1379,7 @@ class Tally(object):
"""
cv.check_iterable_type('nuclides', nuclides, basestring)
cv.check_iterable_type('nuclides', nuclides, string_types)
# Determine the score indices from any of the requested scores
if nuclides:
@ -1416,7 +1414,7 @@ class Tally(object):
"""
for score in scores:
if not isinstance(score, (basestring, openmc.CrossScore)):
if not isinstance(score, string_types + (openmc.CrossScore,)):
msg = 'Unable to get score indices for score "{0}" in Tally ' \
'ID="{1}" since it is not a string or CrossScore'\
.format(score, self.id)
@ -1614,7 +1612,7 @@ class Tally(object):
column_name = 'score'
for score in self.scores:
if isinstance(score, (basestring, openmc.CrossScore)):
if isinstance(score, string_types + (openmc.CrossScore,)):
scores.append(str(score))
elif isinstance(score, openmc.AggregateScore):
scores.append(score.name)
@ -1737,13 +1735,13 @@ class Tally(object):
msg = 'The Tally ID="{0}" has no data to export'.format(self.id)
raise KeyError(msg)
if not isinstance(filename, basestring):
if not isinstance(filename, string_types):
msg = 'Unable to export the results for Tally ID="{0}" to ' \
'filename="{1}" since it is not a ' \
'string'.format(self.id, filename)
raise ValueError(msg)
elif not isinstance(directory, basestring):
elif not isinstance(directory, string_types):
msg = 'Unable to export the results for Tally ID="{0}" to ' \
'directory="{1}" since it is not a ' \
'string'.format(self.id, directory)
@ -2391,11 +2389,11 @@ class Tally(object):
raise ValueError(msg)
# Check that the scores are valid
if not isinstance(score1, (basestring, openmc.CrossScore)):
if not isinstance(score1, string_types + (openmc.CrossScore,)):
msg = 'Unable to swap score1 "{0}" in Tally ID="{1}" since it is ' \
'not a string or CrossScore'.format(score1, self.id)
raise ValueError(msg)
elif not isinstance(score2, (basestring, openmc.CrossScore)):
elif not isinstance(score2, string_types + (openmc.CrossScore,)):
msg = 'Unable to swap score2 "{0}" in Tally ID="{1}" since it is ' \
'not a string or CrossScore'.format(score2, self.id)
raise ValueError(msg)

View file

@ -4,10 +4,9 @@ import sys
import warnings
from collections import Iterable
import openmc.checkvalue as cv
from six import string_types
if sys.version_info[0] >= 3:
basestring = str
import openmc.checkvalue as cv
class Trigger(object):
@ -77,7 +76,7 @@ class Trigger(object):
@scores.setter
def scores(self, scores):
cv.check_type('trigger scores', scores, Iterable, basestring)
cv.check_type('trigger scores', scores, Iterable, string_types)
# Set scores making sure not to have duplicates
self._scores = []

View file

@ -3,13 +3,12 @@ from numbers import Integral
import random
import sys
from six import string_types
import numpy as np
import openmc
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
# A dictionary for storing IDs of cell elements that have already been written,
# used to optimize the writing process
@ -118,7 +117,7 @@ class Universe(object):
@name.setter
def name(self, name):
if name is not None:
cv.check_type('universe name', name, basestring)
cv.check_type('universe name', name, string_types)
self._name = name
else:
self._name = ''

View file

@ -108,7 +108,7 @@ elif args.xsdata is not None:
for line in xsdata:
words = line.split()
if len(words) >= 9:
path = os.path.join(os.path.dirname(args.xsdata, words[8]))
path = os.path.join(os.path.dirname(args.xsdata), words[8])
if path not in ace_libraries:
ace_libraries.append(path)

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
from __future__ import print_function
from argparse import ArgumentParser
import argparse
from collections import defaultdict
import glob
import os
@ -9,8 +9,21 @@ import os
import openmc.data
# Get path to MCNP data
parser = ArgumentParser()
description = """
Convert ENDF/B-VII.0 ACE data from the MCNP5/6 distribution into an HDF5 library
that can be used by OpenMC. This assumes that you have a directory containing
files named endf70a, endf70b, ..., endf70k, and endf70sab.
"""
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter,
argparse.RawDescriptionHelpFormatter):
pass
parser = argparse.ArgumentParser(
description=description,
formatter_class=CustomFormatter
)
parser.add_argument('-d', '--destination', default='mcnp_endfb70',
help='Directory to create new library in')
parser.add_argument('mcnpdata', help='Directory containing endf70[a-k] and endf70sab')

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
from __future__ import print_function
from argparse import ArgumentParser
import argparse
from collections import defaultdict
import glob
import os
@ -9,8 +9,21 @@ import os
import openmc.data
# Get path to MCNP data
parser = ArgumentParser()
description = """
Convert ENDF/B-VII.1 ACE data from the MCNP6 distribution into an HDF5 library
that can be used by OpenMC. This assumes that you have a directory containing
subdirectories 'endf71x' and 'ENDF71SaB'.
"""
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter,
argparse.RawDescriptionHelpFormatter):
pass
parser = argparse.ArgumentParser(
description=description,
formatter_class=CustomFormatter
)
parser.add_argument('-d', '--destination', default='mcnp_endfb71',
help='Directory to create new library in')
parser.add_argument('-f', '--fission_energy_release',

View file

@ -10,18 +10,17 @@ import glob
import argparse
from string import digits
from six.moves import input
from six.moves.urllib.request import urlopen
import openmc.data
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
if sys.version_info[0] < 3:
askuser = raw_input
else:
askuser = input
description = """
Download JEFF 3.2 ACE data from OECD/NEA and convert it to a multi-temperature
HDF5 library for use with OpenMC.
"""
download_warning = """
WARNING: This script will download approximately 9 GB of data. Extracting and
@ -32,14 +31,21 @@ space. Note that if you don't need all 11 temperatures, you can modify the
Are you sure you want to continue? ([y]/n)
"""
parser = argparse.ArgumentParser()
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter,
argparse.RawDescriptionHelpFormatter):
pass
parser = argparse.ArgumentParser(
description=description,
formatter_class=CustomFormatter
)
parser.add_argument('-b', '--batch', action='store_true',
help='supresses standard in')
parser.add_argument('-d', '--destination', default='jeff-3.2-hdf5',
help='Directory to create new library in')
args = parser.parse_args()
response = askuser(download_warning) if not args.batch else 'y'
response = input(download_warning) if not args.batch else 'y'
if response.lower().startswith('n'):
sys.exit()
@ -82,7 +88,7 @@ for f in files:
files_complete.append(f)
continue
else:
overwrite = askuser('Overwrite {}? ([y]/n) '.format(f))
overwrite = input('Overwrite {}? ([y]/n) '.format(f))
if overwrite.lower().startswith('n'):
continue

View file

@ -10,18 +10,27 @@ import glob
import hashlib
import argparse
parser = argparse.ArgumentParser()
from six.moves import input
from six.moves.urllib.request import urlopen
description = """
Download and extract windowed multipole data based on ENDF/B-VII.1.
"""
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter,
argparse.RawDescriptionHelpFormatter):
pass
parser = argparse.ArgumentParser(
description=description,
formatter_class=CustomFormatter
)
parser.add_argument('-b', '--batch', action='store_true',
help='supresses standard in')
args = parser.parse_args()
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
cwd = os.getcwd()
sys.path.insert(0, os.path.join(cwd, '..'))
baseUrl = 'https://github.com/smharper/windowed_multipole_library/blob/master/'
files = ['multipole_lib.tar.gz?raw=true']
@ -54,10 +63,7 @@ for f in files:
filesComplete.append(fname)
continue
else:
if sys.version_info[0] < 3:
overwrite = raw_input('Overwrite {0}? ([y]/n) '.format(fname))
else:
overwrite = input('Overwrite {0}? ([y]/n) '.format(fname))
overwrite = input('Overwrite {0}? ([y]/n) '.format(fname))
if overwrite.lower().startswith('n'):
continue
@ -110,10 +116,7 @@ os.rmdir('wmp/multipole_lib')
# Ask user to delete
if not args.batch:
if sys.version_info[0] < 3:
response = raw_input('Delete *.tar.gz files? ([y]/n) ')
else:
response = input('Delete *.tar.gz files? ([y]/n) ')
response = input('Delete *.tar.gz files? ([y]/n) ')
else:
response = 'y'

View file

@ -10,15 +10,31 @@ import glob
import hashlib
import argparse
parser = argparse.ArgumentParser()
from six.moves import input
from six.moves.urllib.request import urlopen
import openmc.data
description = """
Download ENDF/B-VII.1 ACE data from NNDC and convert it to an HDF5 library for
use with OpenMC.
"""
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter,
argparse.RawDescriptionHelpFormatter):
pass
parser = argparse.ArgumentParser(
description=description,
formatter_class=CustomFormatter
)
parser.add_argument('-b', '--batch', action='store_true',
help='supresses standard in')
args = parser.parse_args()
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
baseUrl = 'http://www.nndc.bnl.gov/endf/b7.1/aceFiles/'
files = ['ENDF-B-VII.1-neutron-293.6K.tar.gz',
@ -50,10 +66,7 @@ for f in files:
filesComplete.append(f)
continue
else:
if sys.version_info[0] < 3:
overwrite = raw_input('Overwrite {0}? ([y]/n) '.format(f))
else:
overwrite = input('Overwrite {0}? ([y]/n) '.format(f))
overwrite = input('Overwrite {0}? ([y]/n) '.format(f))
if overwrite.lower().startswith('n'):
continue
@ -65,7 +78,8 @@ for f in files:
if not chunk: break
fh.write(chunk)
downloaded += len(chunk)
status = '{0:10} [{1:3.2f}%]'.format(downloaded, downloaded * 100. / file_size)
status = '{0:10} [{1:3.2f}%]'.format(
downloaded, downloaded * 100. / file_size)
print(status + chr(8)*len(status), end='')
print('')
filesComplete.append(f)
@ -99,7 +113,7 @@ for f in files:
for filename in glob.glob('nndc/293.6K/ENDF-B-VII.1-neutron-293.6K/*'):
shutil.move(filename, 'nndc/293.6K/')
#===============================================================================
# ==============================================================================
# EDIT GRAPHITE ZAID (6012 to 6000)
print('Changing graphite ZAID from 6012 to 6000')
@ -115,10 +129,7 @@ with open(graphite, 'w') as fh:
# Ask user to delete
if not args.batch:
if sys.version_info[0] < 3:
response = raw_input('Delete *.tar.gz files? ([y]/n) ')
else:
response = input('Delete *.tar.gz files? ([y]/n) ')
response = input('Delete *.tar.gz files? ([y]/n) ')
else:
response = 'y'
@ -130,27 +141,16 @@ if not response or response.lower().startswith('y'):
os.remove(f)
# ==============================================================================
# PROMPT USER TO GENERATE HDF5 LIBRARY
# GENERATE HDF5 LIBRARY
# Ask user to convert
if not args.batch:
if sys.version_info[0] < 3:
response = raw_input('Generate HDF5 library? ([y]/n) ')
else:
response = input('Generate HDF5 library? ([y]/n) ')
else:
response = 'y'
# get a list of all ACE files
ace_files = sorted(glob.glob(os.path.join('nndc', '**', '*.ace*')))
# Convert files if requested
if not response or response.lower().startswith('y'):
# get a list of all ACE files
ace_files = sorted(glob.glob(os.path.join('nndc', '**', '*.ace*')))
# Get path to fission energy release data
data_dir = os.path.dirname(sys.modules['openmc.data'].__file__)
fer_file = os.path.join(data_dir, 'fission_Q_data_endfb71.h5')
# Ensure 'import openmc.data' works in the openmc-ace-to-xml script
cwd = os.getcwd()
env = os.environ.copy()
env['PYTHONPATH'] = os.path.join(cwd, '..')
subprocess.call(['../scripts/openmc-ace-to-hdf5', '-d', 'nndc_hdf5',
'--fission_energy_release', 'fission_Q_data_endfb71.h5']
+ ace_files, env=env)
pwd = os.path.dirname(os.path.realpath(__file__))
ace2hdf5 = os.path.join(pwd, 'openmc-ace-to-hdf5')
subprocess.call([ace2hdf5, '-d', 'nndc_hdf5', '--fission_energy_release',
fer_file] + ace_files)

View file

@ -5,6 +5,11 @@
import os
import sys
import six.moves.tkinter as tk
import six.moves.tkinter_filedialog as filedialog
import six.moves.tkinter_font as font
import six.moves.tkinter_messagebox as messagebox
import six.moves.tkinter_ttk as ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg
from matplotlib.figure import Figure
@ -13,19 +18,6 @@ import numpy as np
from openmc.statepoint import StatePoint
if sys.version_info[0] < 3:
import Tkinter as tk
import tkFileDialog as filedialog
import tkFont as font
import tkMessageBox as messagebox
import ttk as ttk
else:
import tkinter as tk
import tkinter.filedialog as filedialog
import tkinter.font as font
import tkinter.messagebox as messagebox
import tkinter.ttk as ttk
class MeshPlotter(tk.Frame):
def __init__(self, parent, filename):

229
scripts/openmc-update-mgxs Executable file
View file

@ -0,0 +1,229 @@
#!/usr/bin/env python
"""Update OpenMC's deprecated multi-group cross section XML files to the latest
HDF5-based format.
Usage information can be obtained by running 'openmc-update-mgxs --help':
usage: openmc-update-mgxs [-h] in out
Update mgxs.xml files to the latest format. This will remove 'outside'
attributes/elements from lattices and replace them with 'outer' attributes. For
'cell' elements, any 'surfaces' attributes/elements will be renamed
'region'. Note that this script will not delete the given files; it will append
'.original' to the given files and write new ones.
positional arguments:
in Input mgxs xml file
out Output mgxs hdf5 file
optional arguments:
-h, --help show this help message and exit
"""
from __future__ import print_function
import os
from shutil import move
import warnings
import xml.etree.ElementTree as ET
import argparse
import h5py
import numpy as np
import openmc.mgxs_library
description = """\
Update OpenMC's deprecated multi-group cross section XML files to the latest
HDF5-based format."""
def parse_args():
"""Read the input files from the commandline."""
# Create argument parser
parser = argparse.ArgumentParser(description=description,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('-i', '--input', type=argparse.FileType('r'),
help='input XML file')
parser.add_argument('-o', '--output', nargs='?', default='',
help='output file, in HDF5 format')
args = vars(parser.parse_args())
if args['output'] == '':
filename = args['input'].name
extension = filenameos.path.splitext()
if extension == '.xml':
filename = filename[:filename.rfind('.')] + '.h5'
args['output'] = filename
# Parse and return commandline arguments.
return args
def get_data(element, entry):
value = element.find(entry)
if value is not None:
value = value.text.strip()
else:
if entry in element.attrib:
value = element.attrib[entry].strip()
else:
value = None
return value
if __name__ == '__main__':
args = parse_args()
# Parse the XML data.
tree = ET.parse(args['input'])
root = tree.getroot()
# Get old metadata
temp = tree.find('group_structure').text.strip()
temp = np.array(temp.split())
group_structure = temp.astype(np.float)
energy_groups = openmc.mgxs.EnergyGroups(group_structure)
temp = tree.find('inverse_velocities')
if temp is not None:
temp = temp.text.strip()
temp = np.array(temp.split())
inverse_velocities = temp.astype(np.float)
else:
inverse_velocities = None
xsd = []
names = []
# Now move on to the cross section data itself
for xsdata_elem in root.iter('xsdata'):
name = get_data(xsdata_elem, 'name')
temperature = get_data(xsdata_elem, 'kT')
if temperature is not None:
temperature = \
float(temperature) / openmc.data.K_BOLTZMANN
else:
temperature = 294.
temperatures = [temperature]
awr = get_data(xsdata_elem, 'awr')
if awr is not None:
awr = float(awr)
representation = get_data(xsdata_elem, 'representation')
if representation is None:
representation = 'isotropic'
if representation == 'angle':
n_azi = int(get_data(xsdata_elem, 'num_azimuthal'))
n_pol = int(get_data(xsdata_elem, 'num_polar'))
scatter_format = get_data(xsdata_elem, 'scatt_type')
if scatter_format is None:
scatter_format = 'legendre'
order = int(get_data(xsdata_elem, 'order'))
tab_leg = get_data(xsdata_elem, 'tabular_legendre')
if tab_leg is not None:
warnings.Warning('The tabular_legendre option has moved to the '
'settings.xml file and must be added manually')
# Either add the data to a previously existing xsdata (if it is
# for the same 'name' but a different temperature), or create a
# new one.
try:
# It is in our list, so store that entry
i = names.index(name)
except ValueError:
# It is not in our list, so add it
i = -1
xsd.append(openmc.XSdata(name, energy_groups,
temperatures=temperatures,
representation=representation))
if awr is not None:
xsd[-1].atomic_weight_ratio = awr
if representation == 'angle':
xsd[-1].num_azimuthal = n_azi
xsd[-1].num_polar = n_pol
xsd[-1].scatter_format = scatter_format
xsd[-1].order = order
names.append(name)
if scatter_format == 'legendre':
order_dim = order + 1
else:
order_dim = order
if i != -1:
xsd[i].add_temperature(temperature)
temp = get_data(xsdata_elem, 'total')
if temp is not None:
temp = np.array(temp.split(), dtype=float)
total = temp.astype(np.float)
total.shape = xsd[i].vector_shape
xsd[i].set_total(total, temperature)
if inverse_velocities is not None:
xsd[i].set_inverse_velocities(inverse_velocities, temperature)
temp = get_data(xsdata_elem, 'absorption')
temp = np.array(temp.split())
absorption = temp.astype(np.float)
absorption.shape = xsd[i].vector_shape
xsd[i].set_absorption(absorption, temperature)
temp = get_data(xsdata_elem, 'scatter')
temp = np.array(temp.split())
scatter = temp.astype(np.float)
scatter.shape = xsd[i].pn_matrix_shape
xsd[i].set_scatter_matrix(scatter, temperature)
temp = get_data(xsdata_elem, 'multiplicity')
if temp is not None:
temp = np.array(temp.split())
multiplicity = temp.astype(np.float)
multiplicity.shape = xsd[i].matrix_shape
xsd[i].set_multiplicity_matrix(multiplicity, temperature)
temp = get_data(xsdata_elem, 'fission')
if temp is not None:
temp = np.array(temp.split())
fission = temp.astype(np.float)
fission.shape = xsd[i].vector_shape
xsd[i].set_fission(fission, temperature)
temp = get_data(xsdata_elem, 'kappa_fission')
if temp is not None:
temp = np.array(temp.split())
kappa_fission = temp.astype(np.float)
kappa_fission.shape = xsd[i].vector_shape
xsd[i].set_kappa_fission(kappa_fission, temperature)
temp = get_data(xsdata_elem, 'chi')
if temp is not None:
temp = np.array(temp.split())
chi = temp.astype(np.float)
chi.shape = xsd[i].vector_shape
xsd[i].set_chi(chi, temperature)
else:
chi = None
temp = get_data(xsdata_elem, 'nu_fission')
if temp is not None:
temp = np.array(temp.split())
nu_fission = temp.astype(np.float)
if chi is not None:
nu_fission.shape = xsd[i].vector_shape
else:
nu_fission.shape = xsd[i].matrix_shape
xsd[i].set_nu_fission(nu_fission, temperature)
# Build library as we go, but first we have enough to initialize it
lib = openmc.MGXSLibrary(energy_groups)
lib.add_xsdatas(xsd)
lib.export_to_hdf5(args['output'])

18
setup.py Normal file → Executable file
View file

@ -1,6 +1,7 @@
#!/usr/bin/env python
import glob
import numpy as np
try:
from setuptools import setup
have_setuptools = True
@ -8,6 +9,12 @@ except ImportError:
from distutils.core import setup
have_setuptools = False
try:
from Cython.Build import cythonize
have_cython = True
except ImportError:
have_cython = False
kwargs = {'name': 'openmc',
'version': '0.8.0',
'packages': ['openmc', 'openmc.data', 'openmc.mgxs', 'openmc.model',
@ -32,7 +39,7 @@ kwargs = {'name': 'openmc',
if have_setuptools:
kwargs.update({
# Required dependencies
'install_requires': ['numpy>=1.9', 'h5py', 'matplotlib'],
'install_requires': ['six', 'numpy>=1.9', 'h5py', 'matplotlib'],
# Optional dependencies
'extras_require': {
@ -44,8 +51,15 @@ if have_setuptools:
# Data files
'package_data': {
'openmc.data': ['mass.mas12']
'openmc.data': ['mass.mas12', 'fission_Q_data_endfb71.h5']
},
})
# If Cython is present, add resonance reconstruction capability
if have_cython:
kwargs.update({
'ext_modules': cythonize('openmc/data/reconstruct.pyx'),
'include_dirs': [np.get_include()]
})
setup(**kwargs)

View file

@ -1,6 +1,12 @@
module geometry_header
use constants, only: HALF, TWO, THREE, INFINITY
use algorithm, only: find
use constants, only: HALF, TWO, THREE, INFINITY, K_BOLTZMANN, &
MATERIAL_VOID, NONE
use dict_header, only: DictCharInt, DictIntInt
use material_header, only: Material
use stl_vector, only: VectorReal
use string, only: to_lower
implicit none
@ -319,4 +325,74 @@ contains
end if
end function get_local_hex
!===============================================================================
! GET_TEMPERATURES returns a list of temperatures that each nuclide/S(a,b) table
! appears at in the model. Later, this list is used to determine the actual
! temperatures to read (which may be different if interpolation is used)
!===============================================================================
subroutine get_temperatures(cells, materials, material_dict, nuclide_dict, &
n_nucs, nuc_temps, sab_dict, n_sabs, sab_temps)
type(Cell), allocatable, intent(in) :: cells(:)
type(Material), allocatable, intent(in) :: materials(:)
type(DictIntInt), intent(in) :: material_dict
type(DictCharInt), intent(in) :: nuclide_dict
integer, intent(in) :: n_nucs
type(VectorReal), allocatable, intent(out) :: nuc_temps(:)
type(DictCharInt), optional, intent(in) :: sab_dict
integer, optional, intent(in) :: n_sabs
type(VectorReal), optional, allocatable, intent(out) :: sab_temps(:)
integer :: i, j, k
integer :: i_nuclide ! index in nuclides array
integer :: i_sab ! index in S(a,b) array
integer :: i_material
real(8) :: temperature ! temperature in Kelvin
allocate(nuc_temps(n_nucs))
if (present(n_sabs) .and. present(sab_temps)) allocate(sab_temps(n_sabs))
do i = 1, size(cells)
do j = 1, size(cells(i) % material)
! Skip any non-material cells and void materials
if (cells(i) % material(j) == NONE .or. &
cells(i) % material(j) == MATERIAL_VOID) cycle
! Get temperature of cell (rounding to nearest integer)
if (size(cells(i) % sqrtkT) > 1) then
temperature = cells(i) % sqrtkT(j)**2 / K_BOLTZMANN
else
temperature = cells(i) % sqrtkT(1)**2 / K_BOLTZMANN
end if
i_material = material_dict % get_key(cells(i) % material(j))
associate (mat => materials(i_material))
NUC_NAMES_LOOP: do k = 1, size(mat % names)
! Get index in nuc_temps array
i_nuclide = nuclide_dict % get_key(to_lower(mat % names(k)))
! Add temperature if it hasn't already been added
if (find(nuc_temps(i_nuclide), temperature) == -1) then
call nuc_temps(i_nuclide) % push_back(temperature)
end if
end do NUC_NAMES_LOOP
if (present(sab_temps) .and. present(sab_dict) .and. &
mat % n_sab > 0) then
SAB_NAMES_LOOP: do k = 1, size(mat % sab_names)
! Get index in nuc_temps array
i_sab = sab_dict % get_key(to_lower(mat % sab_names(k)))
! Add temperature if it hasn't already been added
if (find(sab_temps(i_sab), temperature) == -1) then
call sab_temps(i_sab) % push_back(temperature)
end if
end do SAB_NAMES_LOOP
end if
end associate
end do
end do
end subroutine get_temperatures
end module geometry_header

View file

@ -123,12 +123,15 @@ module global
! Midpoint of the energy group structure
real(8), allocatable :: energy_bin_avg(:)
! Inverse velocities of the energy groups (provided or estimated)
real(8), allocatable :: inverse_velocities(:)
! Maximum Data Order
integer :: max_order
! Whether or not to convert Legendres to tabulars
logical :: legendre_to_tabular = .True.
! Number of points to use in the Legendre to tabular conversion
integer :: legendre_to_tabular_points = 33
! ============================================================================
! TALLY-RELATED VARIABLES
@ -307,6 +310,7 @@ module global
logical :: survival_biasing = .false.
real(8) :: weight_cutoff = 0.25_8
real(8) :: energy_cutoff = ZERO
real(8) :: weight_survive = ONE
! ============================================================================
@ -355,6 +359,9 @@ module global
! Write out initial source
logical :: write_initial_source = .false.
! Whether create fission neutrons or not. Only applied for MODE_FIXEDSOURCE
logical :: create_fission_neutrons = .true.
! ============================================================================
! CMFD VARIABLES
@ -472,12 +479,7 @@ contains
do i = 1, size(nuclides)
call nuclides(i) % clear()
end do
! WARNING: The following statement should work but doesn't under gfortran
! 4.6 because of a bug. Technically, commenting this out leaves a memory
! leak.
! deallocate(nuclides)
deallocate(nuclides)
end if
if (allocated(nuclides_0K)) then

View file

@ -74,6 +74,7 @@ module hdf5_interface
module procedure read_attribute_integer_2D
module procedure read_attribute_string
module procedure read_attribute_string_1D
module procedure read_attribute_logical
end interface read_attribute
interface write_attribute
@ -84,12 +85,14 @@ module hdf5_interface
public :: write_dataset
public :: read_dataset
public :: attribute_exists
public :: write_attribute
public :: read_attribute
public :: file_create
public :: file_open
public :: file_close
public :: create_group
public :: object_exists
public :: open_group
public :: close_group
public :: open_dataset
@ -98,6 +101,7 @@ module hdf5_interface
public :: write_attribute_string
public :: get_groups
public :: get_datasets
public :: get_name
contains
@ -213,12 +217,11 @@ contains
subroutine get_groups(object_id, names)
integer(HID_T), intent(in) :: object_id
character(len=255), allocatable, intent(out) :: names(:)
character(len=150), allocatable, intent(out) :: names(:)
integer :: n_members, i, group_count, type
integer :: hdf5_err
character(len=255) :: name
character(len=150) :: name
! Get number of members in this location
call h5gn_members_f(object_id, './', n_members, hdf5_err)
@ -245,17 +248,49 @@ contains
end subroutine get_groups
!===============================================================================
! CHECK_ATTRIBUTE Checks to see if an attribute exists in the object
!===============================================================================
function attribute_exists(object_id, name) result(exists)
integer(HID_T), intent(in) :: object_id
character(*), intent(in) :: name ! name of group
logical :: exists
integer :: hdf5_err ! HDF5 error code
! Check if attribute exists
call h5aexists_by_name_f(object_id, '.', trim(name), exists, hdf5_err)
end function attribute_exists
!===============================================================================
! CHECK_GROUP Checks to see if a group exists in the object
!===============================================================================
function object_exists(object_id, name) result(exists)
integer(HID_T), intent(in) :: object_id
character(*), intent(in) :: name ! name of group
logical :: exists
integer :: hdf5_err ! HDF5 error code
! Check if group exists
call h5ltpath_valid_f(object_id, trim(name), .true., exists, hdf5_err)
end function object_exists
!===============================================================================
! GET_DATASETS Gets a list of all the datasets in a given location.
!===============================================================================
subroutine get_datasets(object_id, names)
integer(HID_T), intent(in) :: object_id
character(len=255), allocatable, intent(out) :: names(:)
character(len=150), allocatable, intent(out) :: names(:)
integer :: n_members, i, dset_count, type
integer :: hdf5_err
character(len=255) :: name
character(len=150) :: name
! Get number of members in this location
@ -283,6 +318,27 @@ contains
end subroutine get_datasets
!===============================================================================
! GET_NAME Obtains the name of the current group in group_id
!===============================================================================
function get_name(group_id, name_len_) result(name)
integer(HID_T), intent(in) :: group_id
integer(SIZE_T), optional, intent(in) :: name_len_
character(len=150) :: name ! name of group
integer(SIZE_T) :: name_len, name_file_len
integer :: hdf5_err ! HDF5 error code
if (present(name_len_)) then
name_len = name_len_
else
name_len = 150
end if
call h5iget_name_f(group_id, name, name_len, name_file_len, hdf5_err)
end function get_name
!===============================================================================
! OPEN_GROUP opens an existing HDF5 group
!===============================================================================
@ -296,7 +352,7 @@ contains
integer :: hdf5_err ! HDF5 error code
! Check if group exists
call h5ltpath_valid_f(group_id, trim(name), .true., exists, hdf5_err)
exists = object_exists(group_id, name)
! open group if it exists
if (exists) then
@ -319,7 +375,7 @@ contains
logical :: exists ! does the group exist
! Check if group exists
call h5ltpath_valid_f(group_id, trim(name), .true., exists, hdf5_err)
exists = object_exists(group_id, name)
! create group
if (exists) then
@ -357,7 +413,7 @@ contains
integer :: hdf5_err ! HDF5 error code
! Check if group exists
call h5ltpath_valid_f(group_id, trim(name), .true., exists, hdf5_err)
exists = object_exists(group_id, name)
! open group if it exists
if (exists) then
@ -2486,6 +2542,29 @@ contains
call h5tclose_f(memtype, hdf5_err)
end subroutine read_attribute_string_1D_explicit
subroutine read_attribute_logical(buffer, obj_id, name)
logical, intent(inout), target :: buffer
integer(HID_T), intent(in) :: obj_id
character(*), intent(in) :: name
integer, target :: int_buffer
integer :: hdf5_err
integer(HID_T) :: attr_id
type(c_ptr) :: f_ptr
call h5aopen_f(obj_id, trim(name), attr_id, hdf5_err)
f_ptr = c_loc(int_buffer)
call h5aread_f(attr_id, H5T_NATIVE_INTEGER, f_ptr, hdf5_err)
call h5aclose_f(attr_id, hdf5_err)
! Convert to Fortran logical
if (int_buffer == 0) then
buffer = .false.
else
buffer = .true.
end if
end subroutine read_attribute_logical
subroutine get_shape(obj_id, dims)
integer(HID_T), intent(in) :: obj_id
integer(HSIZE_T), intent(out) :: dims(:)

View file

@ -113,12 +113,6 @@ contains
if (run_CE) then
! Construct log energy grid for cross-sections
call logarithmic_grid()
else
! Create material macroscopic data for MGXS
call time_read_xs%start()
call read_mgxs()
call create_macro_xs()
call time_read_xs%stop()
end if
! Allocate and setup tally stride, matching_bins, and tally maps

View file

@ -1,21 +1,20 @@
module input_xml
use hdf5
use algorithm, only: find
use cmfd_input, only: configure_cmfd
use constants
use dict_header, only: DictIntInt, ElemKeyValueCI
use dict_header, only: DictIntInt, DictCharInt, ElemKeyValueCI
use distribution_multivariate
use distribution_univariate
use endf, only: reaction_name
use energy_grid, only: grid_method, n_log_bins
use error, only: fatal_error, warning
use geometry_header, only: Cell, Lattice, RectLattice, HexLattice
use geometry_header, only: Cell, Lattice, RectLattice, HexLattice, &
get_temperatures
use global
use hdf5_interface
use list_header, only: ListChar, ListInt, ListReal
use mesh_header, only: RegularMesh
use mgxs_data, only: create_macro_xs, read_mgxs
use multipole, only: multipole_read
use output, only: write_message
use plot_header
@ -52,6 +51,17 @@ contains
call read_tallies_xml()
if (cmfd_run) call configure_cmfd()
if (.not. run_CE) then
! Create material macroscopic data for MGXS
call time_read_xs % start()
call read_mgxs()
call create_macro_xs()
call time_read_xs % stop()
end if
! Normalize atom/weight percents
if (run_mode /= MODE_PLOTTING) call normalize_ao()
end subroutine read_input_xml
!===============================================================================
@ -91,6 +101,7 @@ contains
type(Node), pointer :: node_trigger => null()
type(Node), pointer :: node_keff_trigger => null()
type(Node), pointer :: node_vol => null()
type(Node), pointer :: node_tab_leg => null()
type(NodeList), pointer :: node_scat_list => null()
type(NodeList), pointer :: node_source_list => null()
type(NodeList), pointer :: node_vol_list => null()
@ -659,8 +670,15 @@ contains
! Cutoffs
if (check_for_node(doc, "cutoff")) then
call get_node_ptr(doc, "cutoff", node_cutoff)
call get_node_value(node_cutoff, "weight", weight_cutoff)
call get_node_value(node_cutoff, "weight_avg", weight_survive)
if (check_for_node(node_cutoff, "weight")) then
call get_node_value(node_cutoff, "weight", weight_cutoff)
end if
if (check_for_node(node_cutoff, "weight_avg")) then
call get_node_value(node_cutoff, "weight_avg", weight_survive)
end if
if (check_for_node(node_cutoff, "energy")) then
call get_node_value(node_cutoff, "energy", energy_cutoff)
end if
end if
! Particle trace
@ -1102,6 +1120,44 @@ contains
end select
end if
! Check whether create fission sites
if (run_mode == MODE_FIXEDSOURCE) then
if (check_for_node(doc, "create_fission_neutrons")) then
call get_node_value(doc, "create_fission_neutrons", temp_str)
temp_str = to_lower(temp_str)
if (trim(temp_str) == 'true' .or. trim(temp_str) == '1') then
create_fission_neutrons = .true.
else if (trim(temp_str) == 'false' .or. trim(temp_str) == '0') then
create_fission_neutrons = .false.
end if
end if
end if
! Check for tabular_legendre options
if (check_for_node(doc, "tabular_legendre")) then
! Get pointer to tabular_legendre node
call get_node_ptr(doc, "tabular_legendre", node_tab_leg)
! Check for enable option
if (check_for_node(node_tab_leg, "enable")) then
call get_node_value(node_tab_leg, "enable", temp_str)
temp_str = to_lower(temp_str)
if (trim(temp_str) == 'false' .or. &
trim(temp_str) == '0') legendre_to_tabular = .false.
end if
! Check for the number of points
if (check_for_node(node_tab_leg, "num_points")) then
call get_node_value(node_tab_leg, "num_points", &
legendre_to_tabular_points)
if (legendre_to_tabular_points <= 1 .and. (.not. run_CE)) then
call fatal_error("The 'num_points' subelement/attribute of the &
&'tabular_legendre' element must contain a value greater than 1")
end if
end if
end if
! Close settings XML file
call close_xmldoc(doc)
@ -1366,11 +1422,7 @@ contains
! Read cell temperatures. If the temperature is not specified, set it to
! ERROR_REAL for now. During initialization we'll replace ERROR_REAL with
! the temperature from the material data.
if (.not. run_CE) then
! Cell temperatures are not used for MG mode.
allocate(c % sqrtkT(1))
c % sqrtkT(1) = ZERO
else if (check_for_node(node_cell, "temperature")) then
if (check_for_node(node_cell, "temperature")) then
n = get_arraysize_double(node_cell, "temperature")
if (n > 0) then
! Make sure this is a "normal" cell.
@ -2048,7 +2100,7 @@ contains
if (run_CE) then
call read_ce_cross_sections_xml(libraries)
else
call read_mg_cross_sections_xml(libraries)
call read_mg_cross_sections_header(libraries)
end if
! Creating dictionary that maps the name of the material to the entry
@ -2076,18 +2128,17 @@ contains
call assign_temperatures(material_temps)
! Determine desired temperatures for each nuclide and S(a,b) table
call get_temperatures(nuc_temps, sab_temps)
call get_temperatures(cells, materials, material_dict, nuclide_dict, &
n_nuclides_total, nuc_temps, sab_dict, &
n_sab_tables, sab_temps)
! Read continuous-energy cross sections
if (run_CE .and. run_mode /= MODE_PLOTTING) then
call time_read_xs%start()
call time_read_xs % start()
call read_ce_cross_sections(libraries, library_dict, nuc_temps, sab_temps)
call time_read_xs%stop()
call time_read_xs % stop()
end if
! Normalize atom/weight percents
if (run_mode /= MODE_PLOTTING) call normalize_ao()
! Clear dictionary
call library_dict % clear()
end subroutine read_materials
@ -3332,7 +3383,7 @@ contains
! Search through nuclides
pair_list => nuclide_dict % keys()
do while (associated(pair_list))
if (starts_with(pair_list % key, word)) then
if (trim(pair_list % key) == trim(word)) then
word = pair_list % key(1:150)
exit
end if
@ -4710,44 +4761,43 @@ contains
end subroutine read_ce_cross_sections_xml
subroutine read_mg_cross_sections_xml(libraries)
subroutine read_mg_cross_sections_header(libraries)
type(Library), allocatable, intent(out) :: libraries(:)
integer :: i ! loop index
integer :: n_libraries
logical :: file_exists ! does cross_sections.xml exist?
type(Node), pointer :: doc => null()
type(Node), pointer :: node_xsdata => null()
type(NodeList), pointer :: node_xsdata_list => null()
logical :: file_exists ! does mgxs.h5 exist?
integer(HID_T) :: file_id
real(8), allocatable :: rev_energy_bins(:)
character(len=MAX_WORD_LEN), allocatable :: names(:)
! Check if mgxs.xml exists
! Check if MGXS Library exists
inquire(FILE=path_cross_sections, EXIST=file_exists)
if (.not. file_exists) then
! Could not find mgxs.xml file
call fatal_error("Cross sections XML file '" &
! Could not find MGXS Library file
call fatal_error("Cross sections HDF5 file '" &
// trim(path_cross_sections) // "' does not exist!")
end if
call write_message("Reading cross sections XML file...", 5)
call write_message("Reading cross sections HDF5 file...", 5)
! Parse mgxs.xml file
call open_xmldoc(doc, path_cross_sections)
! Open file for reading
file_id = file_open(path_cross_sections, 'r', parallel=.true.)
if (check_for_node(doc, "groups")) then
if (attribute_exists(file_id, "groups")) then
! Get neutron group count
call get_node_value(doc, "groups", energy_groups)
call read_attribute(energy_groups, file_id, "groups")
else
call fatal_error("groups element must exist!")
call fatal_error("'groups' attribute must exist!")
end if
allocate(rev_energy_bins(energy_groups + 1))
allocate(energy_bins(energy_groups + 1))
if (check_for_node(doc, "group_structure")) then
if (attribute_exists(file_id, "group structure")) then
! Get neutron group structure
call get_node_array(doc, "group_structure", energy_bins)
call read_attribute(energy_bins, file_id, "group structure")
else
call fatal_error("group_structures element must exist!")
call fatal_error("'group structure' attribute must exist!")
end if
! First reverse the order of energy_groups
@ -4758,45 +4808,28 @@ contains
energy_bin_avg(i) = HALF * (energy_bins(i) + energy_bins(i + 1))
end do
allocate(inverse_velocities(energy_groups))
if (check_for_node(doc, "inverse_velocities")) then
! Get inverse velocities
call get_node_array(doc, "inverse_velocities", inverse_velocities)
else
! If not given, estimate them by using average energy in group which is
! assumed to be the midpoint
do i = 1, energy_groups
inverse_velocities(i) = ONE / &
(sqrt(TWO * energy_bin_avg(i) / (MASS_NEUTRON_MEV)) * &
C_LIGHT * 100.0_8)
end do
end if
! Get the datasets present in the library
call get_groups(file_id, names)
n_libraries = size(names)
! Get node list of all <xsdata>
call get_node_list(doc, "xsdata", node_xsdata_list)
n_libraries = get_list_size(node_xsdata_list)
! Allocate xs_listings array
! Allocate libraries array
if (n_libraries == 0) then
call fatal_error("At least one <xsdata> element must be present in &
&mgxs.xml file!")
call fatal_error("At least one MGXS data set must be present in &
&mgxs library file!")
else
allocate(libraries(n_libraries))
end if
do i = 1, n_libraries
! Get pointer to xsdata table XML node
call get_list_item(node_xsdata_list, i, node_xsdata)
! Get name of material
allocate(libraries(i) % materials(1))
call get_node_value(node_xsdata, "name", libraries(i) % materials(1))
libraries(i) % materials(1) = names(i)
end do
! Close cross sections XML file
call close_xmldoc(doc)
! Close MGXS HDF5 file
call file_close(file_id)
end subroutine read_mg_cross_sections_xml
end subroutine read_mg_cross_sections_header
!===============================================================================
! EXPAND_NATURAL_ELEMENT converts natural elements specified using an <element>
@ -5741,7 +5774,7 @@ contains
if (run_CE) then
awr = nuclides(mat % nuclide(j)) % awr
else
awr = ONE
awr = nuclides_MG(mat % nuclide(j)) % obj % awr
end if
! if given weight percent, convert all values so that they are divided
@ -5766,7 +5799,7 @@ contains
if (run_CE) then
awr = nuclides(mat % nuclide(j)) % awr
else
awr = ONE
awr = nuclides_MG(mat % nuclide(j)) % obj % awr
end if
x = mat % atom_density(j)
sum_percent = sum_percent + x*awr
@ -5910,7 +5943,7 @@ contains
file_id = file_open(libraries(i_library) % path, 'r')
group_id = open_group(file_id, name)
call nuclides(i_nuclide) % from_hdf5(group_id, nuc_temps(i_nuclide), &
temperature_method, temperature_tolerance)
temperature_method, temperature_tolerance, master)
call close_group(group_id)
call file_close(file_id)
@ -6041,67 +6074,6 @@ contains
end do
end subroutine assign_temperatures
!===============================================================================
! GET_TEMPERATURES returns a list of temperatures that each nuclide/S(a,b) table
! appears at in the model. Later, this list is used to determine the actual
! temperatures to read (which may be different if interpolation is used)
!===============================================================================
subroutine get_temperatures(nuc_temps, sab_temps)
type(VectorReal), allocatable, intent(out) :: nuc_temps(:)
type(VectorReal), allocatable, intent(out) :: sab_temps(:)
integer :: i, j, k
integer :: i_nuclide ! index in nuclides array
integer :: i_sab ! index in S(a,b) array
integer :: i_material
real(8) :: temperature ! temperature in Kelvin
allocate(nuc_temps(n_nuclides_total))
allocate(sab_temps(n_sab_tables))
do i = 1, size(cells)
do j = 1, size(cells(i) % material)
! Skip any non-material cells and void materials
if (cells(i) % material(j) == NONE .or. &
cells(i) % material(j) == MATERIAL_VOID) cycle
! Get temperature of cell (rounding to nearest integer)
if (size(cells(i) % sqrtkT) > 1) then
temperature = cells(i) % sqrtkT(j)**2 / K_BOLTZMANN
else
temperature = cells(i) % sqrtkT(1)**2 / K_BOLTZMANN
end if
i_material = material_dict % get_key(cells(i) % material(j))
associate (mat => materials(i_material))
NUC_NAMES_LOOP: do k = 1, size(mat % names)
! Get index in nuc_temps array
i_nuclide = nuclide_dict % get_key(to_lower(mat % names(k)))
! Add temperature if it hasn't already been added
if (find(nuc_temps(i_nuclide), temperature) == -1) then
call nuc_temps(i_nuclide) % push_back(temperature)
end if
end do NUC_NAMES_LOOP
if (mat % n_sab > 0) then
SAB_NAMES_LOOP: do k = 1, size(mat % sab_names)
! Get index in nuc_temps array
i_sab = sab_dict % get_key(to_lower(mat % sab_names(k)))
! Add temperature if it hasn't already been added
if (find(sab_temps(i_sab), temperature) == -1) then
call sab_temps(i_sab) % push_back(temperature)
end if
end do SAB_NAMES_LOOP
end if
end associate
end do
end do
end subroutine get_temperatures
!===============================================================================
! READ_0K_ELASTIC_SCATTERING
!===============================================================================
@ -6143,7 +6115,7 @@ contains
group_id = open_group(file_id, name)
method = TEMPERATURE_NEAREST
call resonant_nuc % from_hdf5(group_id, temperature, &
method, 1000.0_8)
method, 1000.0_8, master)
call close_group(group_id)
call file_close(file_id)

View file

@ -1,15 +1,17 @@
module mgxs_data
use constants
use constants
use algorithm, only: find
use error, only: fatal_error
use geometry_header, only: get_temperatures
use global
use hdf5_interface
use material_header, only: Material
use mgxs_header
use output, only: write_message
use set_header, only: SetChar
use stl_vector, only: VectorReal
use string, only: to_lower
use xml_interface
implicit none
contains
@ -20,52 +22,41 @@ contains
!===============================================================================
subroutine read_mgxs()
integer :: i ! index in materials array
integer :: j ! index over nuclides in material
integer :: i_xsdata ! index in <xsdata> list
integer :: i_nuclide ! index in nuclides
character(20) :: name ! name of isotope, e.g. 92235.03c
integer :: i_xsdata ! index in xsdata_dict
integer :: i_nuclide ! index in nuclides array
character(20) :: name ! name of library to load
integer :: representation ! Data representation
type(Material), pointer :: mat
type(SetChar) :: already_read
type(Node), pointer :: doc => null()
type(Node), pointer :: node_xsdata
type(NodeList), pointer :: node_xsdata_list => null()
logical :: file_exists
character(MAX_LINE_LEN) :: temp_str
type(Material), pointer :: mat
type(SetChar) :: already_read
integer(HID_T) :: file_id
integer(HID_T) :: xsdata_group
logical :: file_exists
logical :: get_kfiss, get_fiss
integer :: l
type(DictCharInt) :: xsdata_dict
type(VectorReal), allocatable :: temps(:)
! Check if cross_sections.xml exists
! Check if MGXS Library exists
inquire(FILE=path_cross_sections, EXIST=file_exists)
if (.not. file_exists) then
! Could not find cross_sections.xml file
call fatal_error("Cross sections XML file '" &
! Could not find MGXS Library file
call fatal_error("Cross sections HDF5 file '" &
&// trim(path_cross_sections) // "' does not exist!")
end if
call write_message("Loading Cross Section Data...", 5)
! Parse cross_sections.xml file
call open_xmldoc(doc, path_cross_sections)
! Get temperatures
call get_temperatures(cells, materials, material_dict, nuclide_dict, &
n_nuclides_total, temps)
! Get node list of all <xsdata>
call get_node_list(doc, "xsdata", node_xsdata_list)
! Open file for reading
file_id = file_open(path_cross_sections, 'r', parallel=.true.)
! Build dictionary mapping nuclide names to an index in the <xsdata> node
! list
do i = 1, get_list_size(node_xsdata_list)
! Get pointer to xsdata table XML node
call get_list_item(node_xsdata_list, i, node_xsdata)
! Get name and create pair (name, i)
call get_node_value(node_xsdata, "name", name)
call xsdata_dict % add_key(to_lower(name), i)
end do
! allocate arrays for ACE table storage and cross section cache
! allocate arrays for MGXS storage and cross section cache
allocate(nuclides_MG(n_nuclides_total))
!$omp parallel
allocate(micro_xs(n_nuclides_total))
@ -102,18 +93,22 @@ contains
i_xsdata = xsdata_dict % get_key(to_lower(name))
i_nuclide = mat % nuclide(j)
! Get pointer to xsdata table XML node
call get_list_item(node_xsdata_list, i_xsdata, node_xsdata)
call write_message("Loading " // trim(name) // " Data...", 5)
! Check to make sure cross section set exists in the library
if (object_exists(file_id, trim(name))) then
xsdata_group = open_group(file_id, trim(name))
else
call fatal_error("Data for '" // trim(name) // "' does not exist in "&
&// trim(path_cross_sections))
end if
! First find out the data representation
if (check_for_node(node_xsdata, "representation")) then
call get_node_value(node_xsdata, "representation", temp_str)
temp_str = trim(to_lower(temp_str))
if (temp_str == 'isotropic' .or. temp_str == 'iso') then
if (attribute_exists(xsdata_group, "representation")) then
call read_attribute(temp_str, xsdata_group, "representation")
if (trim(temp_str) == 'isotropic') then
representation = MGXS_ISOTROPIC
else if (temp_str == 'angle') then
else if (trim(temp_str) == 'angle') then
representation = MGXS_ANGLE
else
call fatal_error("Invalid Data Representation!")
@ -132,8 +127,10 @@ contains
end select
! Now read in the data specific to the type we just declared
call nuclides_MG(i_nuclide) % obj % init_file(node_xsdata, &
energy_groups, get_kfiss, get_fiss, max_order)
call nuclides_MG(i_nuclide) % obj % from_hdf5(xsdata_group, &
energy_groups, temps(i_nuclide), temperature_method, &
temperature_tolerance, get_kfiss, get_fiss, max_order, &
legendre_to_tabular, legendre_to_tabular_points)
! Add name to dictionary
call already_read % add(name)
@ -172,28 +169,74 @@ contains
subroutine create_macro_xs()
integer :: i_mat ! index in materials array
type(Material), pointer :: mat ! current material
integer :: scatt_type
type(VectorReal), allocatable :: kTs(:)
allocate(macro_xs(n_materials))
! Get temperatures to read for each material
call get_mat_kTs(kTs)
do i_mat = 1, n_materials
mat => materials(i_mat)
! Check to see how our nuclides are represented
! Force all to be the same type
! Therefore type(nuclides(mat % nuclide(1)) % obj) dictates type(macroxs)
! At the same time, we will find the scattering type, as that will dictate
! how we allocate the scatter object within macroxs
scatt_type = nuclides_MG(mat % nuclide(1)) % obj % scatt_type
select type(nuc => nuclides_MG(mat % nuclide(1)) % obj)
type is (MgxsIso)
allocate(MgxsIso :: macro_xs(i_mat) % obj)
type is (MgxsAngle)
allocate(MgxsAngle :: macro_xs(i_mat) % obj)
end select
call macro_xs(i_mat) % obj % combine(mat, nuclides_MG, energy_groups, &
max_order, scatt_type)
! Do not read materials which we do not actually use in the problem to
! save space
if (allocated(kTs(i_mat) % data)) then
call macro_xs(i_mat) % obj % combine(kTs(i_mat), mat, nuclides_MG, &
energy_groups, max_order, &
temperature_tolerance, &
temperature_method)
end if
end do
end subroutine create_macro_xs
!===============================================================================
! GET_MAT_kTs returns a list of temperatures (in MeV) that each
! material appears at in the model.
!===============================================================================
subroutine get_mat_kTs(kTs)
type(VectorReal), allocatable, intent(out) :: kTs(:)
integer :: i, j
integer :: i_material ! Index in materials array
real(8) :: kT ! temperature in MeV
allocate(kTs(size(materials)))
do i = 1, size(cells)
do j = 1, size(cells(i) % material)
! Skip any non-material cells and void materials
if (cells(i) % material(j) == NONE .or. &
cells(i) % material(j) == MATERIAL_VOID) cycle
! Get temperature of cell (rounding to nearest integer)
if (size(cells(i) % sqrtkT) > 1) then
kT = cells(i) % sqrtkT(j)**2
else
kT = cells(i) % sqrtkT(1)**2
end if
i_material = material_dict % get_key(cells(i) % material(j))
! Add temperature if it hasn't already been added
if (find(kTs(i_material), kT) == -1) then
call kTs(i_material) % push_back(kT)
end if
end do
end do
end subroutine get_mat_kTs
end module mgxs_data

File diff suppressed because it is too large Load diff

View file

@ -3,9 +3,7 @@ module nuclide_header
use, intrinsic :: ISO_FORTRAN_ENV
use, intrinsic :: ISO_C_BINDING
use hdf5, only: HID_T, HSIZE_T, SIZE_T, h5iget_name_f, h5gget_info_f, &
h5lget_name_by_idx_f, H5_INDEX_NAME_F, H5_ITER_INC_F
use h5lt, only: h5ltpath_valid_f
use hdf5, only: HID_T, HSIZE_T, SIZE_T
use algorithm, only: sort, find
use constants
@ -14,7 +12,8 @@ module nuclide_header
use endf_header, only: Function1D, Polynomial, Tabulated1D
use error, only: fatal_error, warning
use hdf5_interface, only: read_attribute, open_group, close_group, &
open_dataset, read_dataset, close_dataset, get_shape, get_datasets
open_dataset, read_dataset, close_dataset, get_shape, get_datasets, &
object_exists, get_name, get_groups
use list_header, only: ListInt
use math, only: evaluate_legendre
use multipole_header, only: MultipoleArray
@ -186,18 +185,16 @@ module nuclide_header
end subroutine nuclide_clear
subroutine nuclide_from_hdf5(this, group_id, temperature, method, tolerance)
class(Nuclide), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
subroutine nuclide_from_hdf5(this, group_id, temperature, method, tolerance, &
master)
class(Nuclide), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
type(VectorReal), intent(in) :: temperature ! list of desired temperatures
integer, intent(inout) :: method
real(8), intent(in) :: tolerance
integer, intent(inout) :: method
real(8), intent(in) :: tolerance
logical, intent(in) :: master ! if this is the master proc
integer :: i
integer :: storage_type
integer :: max_corder
integer :: n_links
integer :: hdf5_err
integer :: i_closest
integer :: n_temperature
integer(HID_T) :: urr_group, nu_group
@ -208,21 +205,21 @@ module nuclide_header
integer(HID_T) :: total_nu
integer(HID_T) :: fer_group ! fission_energy_release group
integer(HID_T) :: fer_dset
integer(SIZE_T) :: name_len, name_file_len
integer(SIZE_T) :: name_len
integer(HSIZE_T) :: j
integer(HSIZE_T) :: dims(1)
character(MAX_WORD_LEN) :: temp_str
character(MAX_FILE_LEN), allocatable :: dset_names(:)
character(MAX_WORD_LEN), allocatable :: dset_names(:)
character(MAX_WORD_LEN), allocatable :: grp_names(:)
real(8), allocatable :: temps_available(:) ! temperatures available
real(8) :: temp_desired
real(8) :: temp_actual
logical :: exists
type(VectorInt) :: MTs
type(VectorInt) :: temps_to_read
! Get name of nuclide from group
name_len = len(this % name)
call h5iget_name_f(group_id, this % name, name_len, name_file_len, hdf5_err)
this % name = get_name(group_id, name_len)
! Get rid of leading '/'
this % name = trim(this % name(2:))
@ -265,15 +262,17 @@ module nuclide_header
call temps_to_read % push_back(nint(temp_actual))
! Write warning for resonance scattering data if 0K is not available
if (abs(temp_actual - temp_desired) > 0 .and. temp_desired == 0) then
if (abs(temp_actual - temp_desired) > 0 .and. temp_desired == 0 &
.and. master) then
call warning(trim(this % name) // " does not contain 0K data &
&needed for resonance scattering options selected. Using &
&data at " // trim(to_str(nint(temp_actual))) // " K instead.")
&data at " // trim(to_str(temp_actual)) &
// " K instead.")
end if
end if
else
call fatal_error("Nuclear data library does not contain cross sections &
&for " // trim(this % name) // " at or near " // &
call fatal_error("Nuclear data library does not contain cross &
&sections for " // trim(this % name) // " at or near " // &
trim(to_str(nint(temp_desired))) // " K.")
end if
end do
@ -332,12 +331,10 @@ module nuclide_header
! Get MT values based on group names
rxs_group = open_group(group_id, 'reactions')
call h5gget_info_f(rxs_group, storage_type, n_links, max_corder, hdf5_err)
do j = 0, n_links - 1
call h5lget_name_by_idx_f(rxs_group, ".", H5_INDEX_NAME_F, H5_ITER_INC_F, &
j, temp_str, hdf5_err, name_len)
if (starts_with(temp_str, "reaction_")) then
call MTs % push_back(int(str_to_int(temp_str(10:12))))
call get_groups(rxs_group, grp_names)
do j = 1, size(grp_names)
if (starts_with(grp_names(j), "reaction_")) then
call MTs % push_back(int(str_to_int(grp_names(j)(10:12))))
end if
end do
@ -353,8 +350,7 @@ module nuclide_header
call close_group(rxs_group)
! Read unresolved resonance probability tables if present
call h5ltpath_valid_f(group_id, 'urr', .true., exists, hdf5_err)
if (exists) then
if (object_exists(group_id, 'urr')) then
this % urr_present = .true.
allocate(this % urr_data(n_temperature))
@ -395,8 +391,7 @@ module nuclide_header
end if
! Check for nu-total
call h5ltpath_valid_f(group_id, 'total_nu', .true., exists, hdf5_err)
if (exists) then
if (object_exists(group_id, 'total_nu')) then
nu_group = open_group(group_id, 'total_nu')
! Read total nu data
@ -415,9 +410,7 @@ module nuclide_header
end if
! Read fission energy release data if present
call h5ltpath_valid_f(group_id, 'fission_energy_release', .true., exists, &
hdf5_err)
if (exists) then
if (object_exists(group_id, 'fission_energy_release')) then
fer_group = open_group(group_id, 'fission_energy_release')
! Check to see if this is polynomial or tabulated data

View file

@ -873,8 +873,13 @@ contains
write(UNIT=unit_tally, FMT='(1X,2A,1X,A)') repeat(" ", indent), &
"Total Material"
else
write(UNIT=unit_tally, FMT='(1X,2A,1X,A)') repeat(" ", indent), &
trim(nuclides(i_nuclide) % name)
if (run_CE) then
write(UNIT=unit_tally, FMT='(1X,2A,1X,A)') repeat(" ", indent), &
trim(nuclides(i_nuclide) % name)
else
write(UNIT=unit_tally, FMT='(1X,2A,1X,A)') repeat(" ", indent), &
trim(nuclides_MG(i_nuclide) % obj % name)
end if
end if
indent = indent + 2

View file

@ -96,10 +96,11 @@ contains
! absorption (including fission)
if (nuc % fissionable) then
call sample_fission(i_nuclide, i_reaction)
if (run_mode == MODE_EIGENVALUE) then
call sample_fission(i_nuclide, i_reaction)
call create_fission_sites(p, i_nuclide, i_reaction, fission_bank, n_bank)
elseif (run_mode == MODE_FIXEDSOURCE) then
elseif (run_mode == MODE_FIXEDSOURCE .and. create_fission_neutrons) then
call sample_fission(i_nuclide, i_reaction)
call create_fission_sites(p, i_nuclide, i_reaction, &
p % secondary_bank, p % n_secondary)
end if
@ -126,6 +127,13 @@ contains
if (.not. p % alive) return
end if
! Kill neutron under certain energy
if (p % E < energy_cutoff) then
p % alive = .false.
p % wgt = ZERO
p % last_wgt = ZERO
end if
end subroutine sample_reaction
!===============================================================================

View file

@ -73,7 +73,7 @@ contains
if (mat % fissionable) then
if (run_mode == MODE_EIGENVALUE) then
call create_fission_sites(p, fission_bank, n_bank)
elseif (run_mode == MODE_FIXEDSOURCE) then
elseif (run_mode == MODE_FIXEDSOURCE .and. create_fission_neutrons) then
call create_fission_sites(p, p % secondary_bank, p % n_secondary)
end if
end if

View file

@ -5,7 +5,7 @@ module reaction_header
use constants, only: MAX_WORD_LEN
use hdf5_interface, only: read_attribute, open_group, close_group, &
open_dataset, read_dataset, close_dataset, get_shape
open_dataset, read_dataset, close_dataset, get_shape, get_groups
use product_header, only: ReactionProduct
use stl_vector, only: VectorInt
use string, only: to_str, starts_with
@ -42,17 +42,12 @@ contains
integer :: i
integer :: cm
integer :: n_product
integer :: storage_type
integer :: max_corder
integer :: n_links
integer :: hdf5_err
integer(HID_T) :: pgroup
integer(HID_T) :: xs, temp_group
integer(SIZE_T) :: name_len
integer(HSIZE_T) :: dims(1)
integer(HSIZE_T) :: j
character(MAX_WORD_LEN) :: name
character(MAX_WORD_LEN) :: temp_str ! temperature dataset name, e.g. '294K'
character(MAX_WORD_LEN), allocatable :: grp_names(:)
call read_attribute(this % Q_value, group_id, 'Q_value')
call read_attribute(this % MT, group_id, 'mt')
@ -74,12 +69,10 @@ contains
end do
! Determine number of products
call h5gget_info_f(group_id, storage_type, n_links, max_corder, hdf5_err)
n_product = 0
do j = 0, n_links - 1
call h5lget_name_by_idx_f(group_id, ".", H5_INDEX_NAME_F, H5_ITER_INC_F, &
j, name, hdf5_err, name_len)
if (starts_with(name, "product_")) n_product = n_product + 1
call get_groups(group_id, grp_names)
do j = 1, size(grp_names)
if (starts_with(grp_names(j), "product_")) n_product = n_product + 1
end do
! Read products

View file

@ -8,9 +8,9 @@ module sab_header
use distribution_univariate, only: Tabular
use error, only: warning, fatal_error
use hdf5, only: HID_T, HSIZE_T, SIZE_T
use h5lt, only: h5ltpath_valid_f, h5iget_name_f
use hdf5_interface, only: read_attribute, get_shape, open_group, close_group, &
open_dataset, read_dataset, close_dataset, get_datasets
open_dataset, read_dataset, close_dataset, get_datasets, object_exists, &
get_name
use secondary_correlated, only: CorrelatedAngleEnergy
use stl_vector, only: VectorInt, VectorReal
use string, only: to_str, str_to_int
@ -66,7 +66,7 @@ module sab_header
end type SabData
type SAlphaBeta
character(100) :: name ! name of table, e.g. lwtr.10t
character(150) :: name ! name of table, e.g. lwtr.10t
real(8) :: awr ! weight of nucleus in neutron masses
real(8), allocatable :: kTs(:) ! temperatures in MeV (k*T)
character(10), allocatable :: nuclides(:) ! List of valid nuclides
@ -92,8 +92,7 @@ contains
integer :: n_energy, n_energy_out, n_mu
integer :: i_closest
integer :: n_temperature
integer :: hdf5_err
integer(SIZE_T) :: name_len, name_file_len
integer(SIZE_T) :: name_len
integer(HID_T) :: T_group
integer(HID_T) :: elastic_group
integer(HID_T) :: inelastic_group
@ -103,11 +102,10 @@ contains
integer(HSIZE_T) :: dims3(3)
real(8), allocatable :: temp(:,:)
character(20) :: type
logical :: exists
type(CorrelatedAngleEnergy) :: correlated_dist
character(MAX_WORD_LEN) :: temp_str
character(MAX_FILE_LEN), allocatable :: dset_names(:)
character(MAX_WORD_LEN), allocatable :: dset_names(:)
real(8), allocatable :: temps_available(:) ! temperatures available
real(8) :: temp_desired
real(8) :: temp_actual
@ -115,7 +113,7 @@ contains
! Get name of table from group
name_len = len(this % name)
call h5iget_name_f(group_id, this % name, name_len, name_file_len, hdf5_err)
this % name = get_name(group_id, name_len)
! Get rid of leading '/'
this % name = trim(this % name(2:))
@ -207,8 +205,7 @@ contains
T_group = open_group(group_id, temp_str)
! Coherent elastic data
call h5ltpath_valid_f(T_group, 'elastic', .true., exists, hdf5_err)
if (exists) then
if (object_exists(T_group, 'elastic')) then
! Read cross section data
elastic_group = open_group(T_group, 'elastic')
dset_id = open_dataset(elastic_group, 'xs')
@ -250,8 +247,7 @@ contains
end if
! Inelastic data
call h5ltpath_valid_f(T_group, 'inelastic', .true., exists, hdf5_err)
if (exists) then
if (object_exists(T_group, 'inelastic')) then
! Read type of inelastic data
inelastic_group = open_group(T_group, 'inelastic')

View file

@ -41,18 +41,20 @@ module scattdata_header
real(8), allocatable :: scattxs(:) ! Isotropic Sigma_{s,g_{in}}
contains
procedure(scattdata_init_), deferred :: init ! Initializes ScattData
procedure(scattdata_init_), deferred :: init ! Initializes ScattData
procedure(scattdata_calc_f_), deferred :: calc_f ! Calculates f, given mu
procedure(scattdata_sample_), deferred :: sample ! sample the scatter event
procedure :: get_matrix => scattdata_get_matrix ! Rebuild scattering matrix
end type ScattData
abstract interface
subroutine scattdata_init_(this, mult, coeffs)
import ScattData
class(ScattData), intent(inout) :: this ! Object to work with
real(8), intent(in) :: mult(:, :) ! Scatter Prod'n Matrix
real(8), intent(in) :: coeffs(:, :, :) ! Coefficients to use
subroutine scattdata_init_(this, gmin, gmax, mult, coeffs)
import ScattData, Jagged1D, Jagged2D
class(ScattData), intent(inout) :: this ! Object to work with
integer, intent(in) :: gmin(:) ! Min Gout
integer, intent(in) :: gmax(:) ! Max Gout
type(Jagged1D), intent(in) :: mult(:) ! Scatter Prod'n Matrix
type(Jagged2D), intent(in) :: coeffs(:) ! Coefficients to use
end subroutine scattdata_init_
pure function scattdata_calc_f_(this, gin, gout, mu) result(f)
@ -79,9 +81,9 @@ module scattdata_header
! Maximal value for rejection sampling from rectangle
type(Jagged1D), allocatable :: max_val(:) ! (Gin % data(Gout))
contains
procedure :: init => scattdatalegendre_init
procedure :: calc_f => scattdatalegendre_calc_f
procedure :: sample => scattdatalegendre_sample
procedure :: init => scattdatalegendre_init
procedure :: calc_f => scattdatalegendre_calc_f
procedure :: sample => scattdatalegendre_sample
end type ScattDataLegendre
type, extends(ScattData) :: ScattDataHistogram
@ -90,10 +92,10 @@ module scattdata_header
! Histogram of f(mu) (dist has CDF)
type(Jagged2D), allocatable :: fmu(:) ! (Gin % data(Order/Nmu x Gout)
contains
procedure :: init => scattdatahistogram_init
procedure :: calc_f => scattdatahistogram_calc_f
procedure :: sample => scattdatahistogram_sample
procedure :: get_matrix => scattdatahistogram_get_matrix
procedure :: init => scattdatahistogram_init
procedure :: calc_f => scattdatahistogram_calc_f
procedure :: sample => scattdatahistogram_sample
procedure :: get_matrix => scattdatahistogram_get_matrix
end type ScattDataHistogram
type, extends(ScattData) :: ScattDataTabular
@ -102,10 +104,10 @@ module scattdata_header
! PDF of f(mu) (dist has CDF)
type(Jagged2D), allocatable :: fmu(:) ! (Gin % data(Order/Nmu x Gout)
contains
procedure :: init => scattdatatabular_init
procedure :: calc_f => scattdatatabular_calc_f
procedure :: sample => scattdatatabular_sample
procedure :: get_matrix => scattdatatabular_get_matrix
procedure :: init => scattdatatabular_init
procedure :: calc_f => scattdatatabular_calc_f
procedure :: sample => scattdatatabular_sample
procedure :: get_matrix => scattdatatabular_get_matrix
end type ScattDataTabular
!===============================================================================
@ -122,13 +124,15 @@ contains
! SCATTDATA*_INIT builds the scattdata object
!===============================================================================
subroutine scattdata_init(this, order, energy, mult)
class(ScattData), intent(inout) :: this ! Object to work on
integer, intent(in) :: order ! Data Order
real(8), intent(inout) :: energy(:, :) ! Energy Transfer Matrix
real(8), intent(in) :: mult(:, :) ! Scatter Prod'n Matrix
subroutine scattdata_init(this, order, gmin, gmax, energy, mult)
class(ScattData), intent(inout) :: this ! Object to work on
integer, intent(in) :: order ! Data Order
integer, intent(in) :: gmin(:) ! Min Gout
integer, intent(in) :: gmax(:) ! Max Gout
type(Jagged1D), intent(inout) :: energy(:) ! Energy Transfer Matrix
type(Jagged1D), intent(in) :: mult(:) ! Scatter Prod'n Matrix
integer :: groups, gmin, gmax, gin
integer :: groups, gin
real(8) :: norm
groups = size(energy, dim=1)
@ -138,83 +142,80 @@ contains
allocate(this % energy(groups))
allocate(this % mult(groups))
allocate(this % dist(groups))
! Use energy to find the gmin and gmax values
! Also set energy values when doing it
this % gmin = gmin
this % gmax = gmax
! Set the outgoing energy PDF values
do gin = 1, groups
! Make sure energy is normalized (i.e., CDF is 1)
norm = sum(energy(:, gin))
if (norm /= ZERO) energy(:, gin) = energy(:, gin) / norm
! Find gmin by checking the P0 moment
do gmin = 1, groups
if (energy(gmin, gin) > ZERO) exit
end do
! Find gmax by checking the P0 moment
do gmax = groups, 1, -1
if (energy(gmax, gin) > ZERO) exit
end do
! Treat the case of all zeros
if (gmin > gmax) then
gmin = gin
gmax = gin
! By not changing energy(gin) here we are leaving it as zero
end if
allocate(this % energy(gin) % data(gmin:gmax))
this % energy(gin) % data(gmin:gmax) = energy(gmin:gmax, gin)
allocate(this % mult(gin) % data(gmin:gmax))
this % mult(gin) % data(gmin:gmax) = mult(gmin:gmax, gin)
allocate(this % dist(gin) % data(order, gmin:gmax))
norm = sum(energy(gin) % data(:))
if (norm /= ZERO) energy(gin) % data(:) = energy(gin) % data(:) / norm
! Set the values
allocate(this % energy(gin) % data(gmin(gin):gmax(gin)))
this % energy(gin) % data(:) = energy(gin) % data(:)
allocate(this % mult(gin) % data(gmin(gin):gmax(gin)))
this % mult(gin) % data(gmin(gin):gmax(gin)) = &
mult(gin) % data(gmin(gin):gmax(gin))
allocate(this % dist(gin) % data(order, gmin(gin):gmax(gin)))
this % dist(gin) % data = ZERO
this % gmin(gin) = gmin
this % gmax(gin) = gmax
end do
end subroutine scattdata_init
subroutine scattdatalegendre_init(this, mult, coeffs)
class(ScattDataLegendre), intent(inout) :: this ! Object to work on
real(8), intent(in) :: mult(:, :) ! Scatter Prod'n Matrix
real(8), intent(in) :: coeffs(:, :, :) ! Coefficients to use
subroutine scattdatalegendre_init(this, gmin, gmax, mult, coeffs)
class(ScattDataLegendre), intent(inout) :: this ! Object to work on
integer, intent(in) :: gmin(:) ! Min Gout
integer, intent(in) :: gmax(:) ! Max Gout
type(Jagged1D), intent(in) :: mult(:) ! Scatter Prod'n Matrix
type(Jagged2D), intent(in) :: coeffs(:) ! Coefficients to use
real(8) :: dmu, mu, f, norm
integer :: imu, Nmu, gout, gin, groups, order
real(8), allocatable :: energy(:, :)
real(8), allocatable :: matrix(:, :, :)
type(Jagged1D), allocatable :: energy(:)
type(Jagged2D), allocatable :: matrix(:)
groups = size(coeffs, dim=3)
order = size(coeffs, dim=1)
groups = size(coeffs)
order = size(coeffs(1) % data, dim=1)
! make a copy of coeffs that we can use to extract data and normalize
allocate(matrix(order, groups, groups))
matrix (:, :, :)= coeffs
allocate(matrix(groups))
do gin = 1, groups
allocate(matrix(gin) % data(order, gmin(gin):gmax(gin)))
matrix(gin) % data = coeffs(gin) % data
end do
! Get scattxs value
allocate(this % scattxs(groups))
! Get this by summing the un-normalized P0 coefficient in matrix
! over all outgoing groups
this % scattxs(:) = sum(matrix(1, :, :), dim=1)
do gin = 1, groups
this % scattxs(gin) = sum(matrix(gin) % data(1, :), dim=1)
end do
allocate(energy(groups, groups))
energy(:, :) = ZERO
allocate(energy(groups))
! Build energy transfer probability matrix from data in matrix
! while also normalizing matrix itself (making CDF of f(mu=1)=1)
do gin = 1, groups
do gout = 1, groups
norm = matrix(1, gout, gin)
energy(gout, gin) = norm
allocate(energy(gin) % data(gmin(gin):gmax(gin)))
energy(gin) % data = ZERO
do gout = gmin(gin), gmax(gin)
norm = matrix(gin) % data(1, gout)
energy(gin) % data(gout) = norm
if (norm /= ZERO) then
matrix(:, gout, gin) = matrix(:, gout, gin) / norm
matrix(gin) % data(:, gout) = matrix(gin) % data(:, gout) / norm
end if
end do
end do
call scattdata_init(this, order, energy, mult)
call scattdata_init(this, order, gmin, gmax, energy, mult)
allocate(this % max_val(groups))
! Set dist values from matrix and initialize max_val
do gin = 1, groups
do gout = this % gmin(gin), this % gmax(gin)
this % dist(gin) % data(:, gout) = matrix(:, gout, gin)
do gout = gmin(gin), gmax(gin)
this % dist(gin) % data(:, gout) = matrix(gin) % data(:, gout)
end do
allocate(this % max_val(gin) % data(this % gmin(gin):this % gmax(gin)))
allocate(this % max_val(gin) % data(gmin(gin):gmax(gin)))
this % max_val(gin) % data(:) = ZERO
end do
@ -223,7 +224,7 @@ contains
Nmu = 1001
dmu = TWO / real(Nmu - 1, 8)
do gin = 1, groups
do gout = this % gmin(gin), this % gmax(gin)
do gout = gmin(gin), gmax(gin)
do imu = 1, Nmu
! Update mu. Do first and last seperate to avoid float errors
if (imu == 1) then
@ -246,44 +247,51 @@ contains
end do
end subroutine scattdatalegendre_init
subroutine scattdatahistogram_init(this, mult, coeffs)
class(ScattDataHistogram), intent(inout) :: this ! Object to work on
real(8), intent(in) :: mult(:, :) ! Scatter Prod'n Matrix
real(8), intent(in) :: coeffs(:, :, :) ! Coefficients to use
subroutine scattdatahistogram_init(this, gmin, gmax, mult, coeffs)
class(ScattDataHistogram), intent(inout) :: this ! Object to work on
integer, intent(in) :: gmin(:) ! Min Gout
integer, intent(in) :: gmax(:) ! Max Gout
type(Jagged1D), intent(in) :: mult(:) ! Scatter Prod'n Matrix
type(Jagged2D), intent(in) :: coeffs(:) ! Coefficients to use
integer :: imu, gin, gout, groups, order
real(8) :: norm
real(8), allocatable :: energy(:, :)
real(8), allocatable :: matrix(:, :, :)
type(Jagged1D), allocatable :: energy(:)
type(Jagged2D), allocatable :: matrix(:)
groups = size(coeffs, dim=3)
order = size(coeffs, dim=1)
groups = size(coeffs)
order = size(coeffs(1) % data, dim=1)
! make a copy of coeffs that we can use to extract data and normalize
allocate(matrix(order, groups, groups))
matrix(:, :, :) = coeffs
allocate(matrix(groups))
do gin = 1, groups
allocate(matrix(gin) % data(order, gmin(gin):gmax(gin)))
matrix(gin) % data = coeffs(gin) % data
end do
! Get scattxs value
allocate(this % scattxs(groups))
! Get this by summing the un-normalized P0 coefficient in matrix
! over all outgoing groups
this % scattxs(:) = sum(sum(matrix(:, :, :), dim=1), dim=1)
do gin = 1, groups
this % scattxs(gin) = sum(matrix(gin) % data(1, :), dim=1)
end do
allocate(energy(groups, groups))
energy(:, :) = ZERO
allocate(energy(groups))
! Build energy transfer probability matrix from data in matrix
! while also normalizing matrix itself (making CDF of f(mu=1)=1)
do gin = 1, groups
allocate(energy(gin) % data(gmin(gin):gmax(gin)))
do gout = 1, groups
norm = sum(matrix(:, gout, gin))
energy(gout, gin) = norm
norm = sum(matrix(gin) % data(:, gout))
energy(gin) % data(gout) = norm
if (norm /= ZERO) then
matrix(:, gout, gin) = matrix(:, gout, gin) / norm
matrix(gin) % data(:, gout) = matrix(gin) % data(:, gout) / norm
end if
end do
end do
call scattdata_init(this, order, energy, mult)
call scattdata_init(this, order, gmin, gmax, energy, mult)
allocate(this % mu(order))
this % dmu = TWO / real(order, 8)
@ -296,17 +304,16 @@ contains
! also saving the original histogram in fmu
allocate(this % fmu(groups))
do gin = 1, groups
allocate(this % fmu(gin) % data(order, &
this % gmin(gin):this % gmax(gin)))
do gout = this % gmin(gin), this % gmax(gin)
allocate(this % fmu(gin) % data(order, gmin(gin):gmax(gin)))
do gout = gmin(gin), gmax(gin)
! Store the histogram
this % fmu(gin) % data(:, gout) = matrix(:, gout, gin)
this % fmu(gin) % data(:, gout) = matrix(gin) % data(:, gout)
! Integrate the histogram
this % dist(gin) % data(1, gout) = &
this % dmu * matrix(1, gout, gin)
this % dmu * matrix(gin) % data(1, gout)
do imu = 2, order
this % dist(gin) % data(imu, gout) = &
this % dmu * matrix(imu, gout, gin) + &
this % dmu * matrix(gin) % data(imu, gout) + &
this % dist(gin) % data(imu - 1, gout)
end do
@ -323,22 +330,27 @@ contains
end subroutine scattdatahistogram_init
subroutine scattdatatabular_init(this, mult, coeffs)
class(ScattDataTabular), intent(inout) :: this ! Object to work on
real(8), intent(in) :: mult(:, :) ! Scatter Prod'n Matrix
real(8), intent(in) :: coeffs(:, :, :) ! Coefficients to use
subroutine scattdatatabular_init(this, gmin, gmax, mult, coeffs)
class(ScattDataTabular), intent(inout) :: this ! Object to work on
integer, intent(in) :: gmin(:) ! Min Gout
integer, intent(in) :: gmax(:) ! Max Gout
type(Jagged1D), intent(in) :: mult(:) ! Scatter Prod'n Matrix
type(Jagged2D), intent(in) :: coeffs(:) ! Coefficients to use
integer :: imu, gin, gout, groups, order
real(8) :: norm
real(8), allocatable :: energy(:, :)
real(8), allocatable :: matrix(:, :, :)
type(Jagged1D), allocatable :: energy(:)
type(Jagged2D), allocatable :: matrix(:)
groups = size(coeffs, dim=3)
order = size(coeffs, dim=1)
groups = size(coeffs)
order = size(coeffs(1) % data, dim=1)
! make a copy of coeffs that we can use to extract data and normalize
allocate(matrix(order, groups, groups))
matrix(:, :, :) = coeffs
allocate(matrix(groups))
do gin = 1, groups
allocate(matrix(gin) % data(order, gmin(gin):gmax(gin)))
matrix(gin) % data = coeffs(gin) % data
end do
! Build the angular distribution mu values
allocate(this % mu(order))
@ -356,39 +368,40 @@ contains
! over all outgoing groups
do gin = 1, groups
norm = ZERO
do gout = 1, groups
do gout = gmin(gin), gmax(gin)
do imu = 2, order
norm = norm + HALF * this % dmu * (matrix(imu - 1, gout, gin) + &
matrix(imu, gout, gin))
norm = norm + HALF * this % dmu * &
(matrix(gin) % data(imu - 1, gout) + &
matrix(gin) % data(imu, gout))
end do
end do
this % scattxs(gin) = norm
end do
allocate(energy(groups, groups))
energy(:, :) = ZERO
allocate(energy(groups))
! Build energy transfer probability matrix from data in matrix
do gin = 1, groups
do gout = 1, groups
allocate(energy(gin) % data(gmin(gin):gmax(gin)))
do gout = gmin(gin), gmax(gin)
norm = ZERO
do imu = 2, order
norm = norm + HALF * this % dmu * &
(matrix(imu - 1, gout, gin) + matrix(imu, gout, gin))
(matrix(gin) % data(imu - 1, gout) + &
matrix(gin) % data(imu, gout))
end do
energy(gout, gin) = norm
energy(gin) % data(gout) = norm
end do
end do
call scattdata_init(this, order, energy, mult)
call scattdata_init(this, order, gmin, gmax, energy, mult)
! Calculate f(mu) and integrate it so we can avoid rejection sampling
allocate(this % fmu(groups))
do gin = 1, groups
allocate(this % fmu(gin) % data(order, &
this % gmin(gin):this % gmax(gin)))
do gout = this % gmin(gin), this % gmax(gin)
allocate(this % fmu(gin) % data(order, gmin(gin):gmax(gin)))
do gout = gmin(gin), gmax(gin)
! Coeffs contain f(mu), put in f(mu) as that is where the
! PDF lives
this % fmu(gin) % data(:, gout) = matrix(:, gout, gin)
this % fmu(gin) % data(:, gout) = matrix(gin) % data(:, gout)
! Force positivity
do imu = 1, order
@ -640,10 +653,10 @@ contains
! using ScattData's information of fmu/dist, energy, and scattxs
!===============================================================================
pure function scattdata_get_matrix(this, req_order) result(matrix)
class(ScattData), intent(in) :: this ! Scattering Object to work with
integer, intent(in) :: req_order ! Requested order of matrix
real(8), allocatable :: matrix(:, :, :) ! Resultant matrix just built
subroutine scattdata_get_matrix(this, req_order, matrix)
class(ScattData), intent(in) :: this ! Scattering Object to work with
integer, intent(in) :: req_order ! Requested order of matrix
type(Jagged2D), allocatable, intent(inout) :: matrix(:) ! Resultant matrix just built
integer :: order, groups, gin, gout
@ -651,66 +664,189 @@ contains
! Set gin and gout for getting the order
order = min(req_order, size(this % dist(1) % data, dim=1))
allocate(matrix(order, groups, groups))
if (allocated(matrix)) deallocate(matrix)
allocate(matrix(groups))
! Initialize to 0; this way the zero entries in the dense matrix dont
! need to be explicitly set, requiring a significant increase in the
! lines of code.
matrix(:, :, :) = ZERO
do gin = 1, groups
allocate(matrix(gin) % data(order, groups))
do gout = this % gmin(gin), this % gmax(gin)
matrix(:, gout, gin) = this % scattxs(gin) * &
matrix(gin) % data(:, gout) = this % scattxs(gin) * &
this % energy(gin) % data(gout) * &
this % dist(gin) % data(1:order, gout)
end do
end do
end function scattdata_get_matrix
end subroutine scattdata_get_matrix
pure function scattdatahistogram_get_matrix(this, req_order) result(matrix)
class(ScattDataHistogram), intent(in) :: this ! Scattering Object to work with
integer, intent(in) :: req_order ! Requested order of matrix
real(8), allocatable :: matrix(:, :, :) ! Resultant matrix just built
subroutine scattdatahistogram_get_matrix(this, req_order, matrix)
class(ScattDataHistogram), intent(in) :: this ! Scattering Object to work with
integer, intent(in) :: req_order ! Requested order of matrix
type(Jagged2D), allocatable, intent(inout) :: matrix(:) ! Resultant matrix just built
integer :: order, groups, gin, gout
groups = size(this % energy)
order = min(req_order, size(this % dist(1) % data, dim=1))
allocate(matrix(order, groups, groups))
if (allocated(matrix)) deallocate(matrix)
allocate(matrix(groups))
! Initialize to 0; this way the zero entries in the dense matrix dont
! need to be explicitly set, requiring a significant increase in the
! lines of code.
matrix(:, :, :) = ZERO
do gin = 1, groups
allocate(matrix(gin) % data(order, groups))
do gout = this % gmin(gin), this % gmax(gin)
matrix(:, gout, gin) = this % scattxs(gin) * &
matrix(gin) % data(:, gout) = this % scattxs(gin) * &
this % energy(gin) % data(gout) * &
this % fmu(gin) % data(1:order, gout)
end do
end do
end function scattdatahistogram_get_matrix
end subroutine scattdatahistogram_get_matrix
pure function scattdatatabular_get_matrix(this, req_order) result(matrix)
class(ScattDataTabular), intent(in) :: this ! Scattering Object to work with
integer, intent(in) :: req_order ! Requested order of matrix
real(8), allocatable :: matrix(:, :, :) ! Resultant matrix just built
subroutine scattdatatabular_get_matrix(this, req_order, matrix)
class(ScattDataTabular), intent(in) :: this ! Scattering Object to work with
integer, intent(in) :: req_order ! Requested order of matrix
type(Jagged2D), allocatable, intent(inout) :: matrix(:) ! Resultant matrix just built
integer :: order, groups, gin, gout
groups = size(this % energy)
order = min(req_order, size(this % dist(1) % data, dim=1))
allocate(matrix(order, groups, groups))
if (allocated(matrix)) deallocate(matrix)
allocate(matrix(groups))
! Initialize to 0; this way the zero entries in the dense matrix dont
! need to be explicitly set, requiring a significant increase in the
! lines of code.
matrix(:, :, :) = ZERO
do gin = 1, groups
allocate(matrix(gin) % data(order, groups))
do gout = this % gmin(gin), this % gmax(gin)
matrix(:, gout, gin) = this % scattxs(gin) * &
matrix(gin) % data(:, gout) = this % scattxs(gin) * &
this % energy(gin) % data(gout) * &
this % fmu(gin) % data(1:order, gout)
end do
end do
end function scattdatatabular_get_matrix
end subroutine scattdatatabular_get_matrix
!===============================================================================
! JAGGED_FROM_DENSE_*D Creates a jagged array from a sparse dense matrix.
! The user can supply a key which indicates the values to remove, but the
! default is ZERO
!===============================================================================
subroutine jagged_from_dense_1D(dense, jagged, lo_bounds_, hi_bounds_, key_)
real(8), intent(in) :: dense(:, :)
type(Jagged1D), allocatable, intent(inout) :: jagged(:)
real(8), intent(in), optional :: key_
integer, intent(inout), allocatable, optional :: lo_bounds_(:)
integer, intent(inout), allocatable, optional :: hi_bounds_(:)
real(8) :: key
integer :: i, jmin, jmax
integer, allocatable :: lo_bounds(:), hi_bounds(:)
if (present(key_)) then
key = key_
else
key = ZERO
end if
allocate(lo_bounds(size(dense, dim=2)))
allocate(hi_bounds(size(dense, dim=2)))
if (allocated(jagged)) deallocate(jagged)
allocate(jagged(size(dense, dim=2)))
do i = 1, size(dense, dim=2)
! Find the min and max j values
do jmin = 1, size(dense, dim=1)
if (dense(jmin, i) /= key) exit
end do
do jmax = size(dense, dim=1), 1, -1
if (dense(jmax, i) /= key) exit
end do
! Treat the case of all values matching the key
if (jmin > jmax) then
jmin = i
jmax = i
end if
! Now store the jagged row
allocate(jagged(i) % data(jmin:jmax))
jagged(i) % data(jmin:jmax) = dense(jmin:jmax, i)
lo_bounds(i) = jmin
hi_bounds(i) = jmax
end do
if (present(lo_bounds_)) then
if (allocated(lo_bounds_)) deallocate(lo_bounds_)
allocate(lo_bounds_(size(dense, dim=2)))
lo_bounds_ = lo_bounds
end if
if (present(hi_bounds_)) then
if (allocated(hi_bounds_)) deallocate(hi_bounds_)
allocate(hi_bounds_(size(dense, dim=2)))
hi_bounds_ = hi_bounds
end if
end subroutine jagged_from_dense_1D
subroutine jagged_from_dense_2D(dense, jagged, lo_bounds_, hi_bounds_, key_)
real(8), intent(in) :: dense(:, :, :)
type(Jagged2D), allocatable, intent(inout) :: jagged(:)
real(8), intent(in), optional :: key_
integer, intent(inout), allocatable, optional :: lo_bounds_(:)
integer, intent(inout), allocatable, optional :: hi_bounds_(:)
real(8) :: key
integer :: i, jmin, jmax
integer, allocatable :: lo_bounds(:), hi_bounds(:)
if (present(key_)) then
key = key_
else
key = ZERO
end if
allocate(lo_bounds(size(dense, dim=3)))
allocate(hi_bounds(size(dense, dim=3)))
if (allocated(jagged)) deallocate(jagged)
allocate(jagged(size(dense, dim=3)))
do i = 1, size(dense, dim=3)
! Find the min and max j values
do jmin = 1, size(dense, dim=2)
if (any(dense(:, jmin, i) /= key)) exit
end do
do jmax = size(dense, dim=2), 1, -1
if (any(dense(:, jmax, i) /= key)) exit
end do
! Treat the case of all values matching the key
if (jmin > jmax) then
jmin = i
jmax = i
end if
! Now store the jagged row
allocate(jagged(i) % data(size(dense, dim=1), jmin:jmax))
jagged(i) % data(:, jmin:jmax) = dense(:, jmin:jmax, i)
lo_bounds(i) = jmin
hi_bounds(i) = jmax
end do
if (present(lo_bounds_)) then
if (allocated(lo_bounds_)) deallocate(lo_bounds_)
allocate(lo_bounds_(size(dense, dim=3)))
lo_bounds_ = lo_bounds
end if
if (present(hi_bounds_)) then
if (allocated(hi_bounds_)) deallocate(hi_bounds_)
allocate(hi_bounds_(size(dense, dim=3)))
hi_bounds_ = hi_bounds
end if
end subroutine jagged_from_dense_2D
end module scattdata_header

View file

@ -1,6 +1,5 @@
module secondary_uncorrelated
use h5lt, only: h5ltpath_valid_f
use hdf5, only: HID_T
use angle_distribution, only: AngleDistribution
@ -9,7 +8,8 @@ module secondary_uncorrelated
use energy_distribution, only: EnergyDistribution, LevelInelastic, &
ContinuousTabular, MaxwellEnergy, Evaporation, WattEnergy, DiscretePhoton
use error, only: warning
use hdf5_interface, only: read_attribute, open_group, close_group
use hdf5_interface, only: read_attribute, open_group, close_group, &
object_exists
use random_lcg, only: prn
!===============================================================================
@ -56,25 +56,19 @@ contains
class(UncorrelatedAngleEnergy), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
logical :: exists
integer :: hdf5_err
integer(HID_T) :: energy_group
integer(HID_T) :: angle_group
character(MAX_WORD_LEN) :: type
! Check if energy group is present
call h5ltpath_valid_f(group_id, 'angle', .true., exists, hdf5_err)
if (exists) then
! Check if angle group is present & read
if (object_exists(group_id, 'angle')) then
angle_group = open_group(group_id, 'angle')
call this%angle%from_hdf5(angle_group)
call close_group(angle_group)
end if
! Check if energy group is present
call h5ltpath_valid_f(group_id, 'energy', .true., exists, hdf5_err)
if (exists) then
! Check if energy group is present & read
if (object_exists(group_id, 'energy')) then
energy_group = open_group(group_id, 'energy')
call read_attribute(type, energy_group, 'type')
select case (type)

View file

@ -284,11 +284,20 @@ contains
allocate(str_array(tally % n_nuclide_bins))
NUCLIDE_LOOP: do j = 1, tally % n_nuclide_bins
if (tally % nuclide_bins(j) > 0) then
i_xs = index(nuclides(tally % nuclide_bins(j)) % name, '.')
if (i_xs > 0) then
str_array(j) = nuclides(tally % nuclide_bins(j)) % name(1 : i_xs-1)
if (run_CE) then
i_xs = index(nuclides(tally % nuclide_bins(j)) % name, '.')
if (i_xs > 0) then
str_array(j) = nuclides(tally % nuclide_bins(j)) % name(1 : i_xs-1)
else
str_array(j) = nuclides(tally % nuclide_bins(j)) % name
end if
else
str_array(j) = nuclides(tally % nuclide_bins(j)) % name
i_xs = index(nuclides_MG(tally % nuclide_bins(j)) % obj % name, '.')
if (i_xs > 0) then
str_array(j) = nuclides_MG(tally % nuclide_bins(j)) % obj % name(1 : i_xs-1)
else
str_array(j) = nuclides_MG(tally % nuclide_bins(j)) % obj % name
end if
end if
else
str_array(j) = 'total'

View file

@ -1158,6 +1158,8 @@ contains
! Do same for nucxs, point it to the microscopic nuclide data of interest
if (i_nuclide > 0) then
nucxs => nuclides_MG(i_nuclide) % obj
! And since we haven't calculated this temperature index yet, do so now
call nucxs % find_temperature(p % sqrtkT)
end if
i = 0
@ -1237,11 +1239,21 @@ contains
else
score = p % last_wgt
end if
score = score * inverse_velocities(p_g) / material_xs % total * flux
if (i_nuclide > 0) then
score = score * nucxs % get_xs('inv_vel', p_g, UVW=p_uvw) / &
matxs % get_xs('total', p_g, UVW=p_uvw) * flux
else
score = matxs % get_xs('inv_vel', p_g, UVW=p_uvw) * flux
end if
else
! For inverse velocity, we need no cross section
score = flux * inverse_velocities(p_g)
if (i_nuclide > 0) then
score = score * nucxs % get_xs('inv_vel', p_g, UVW=p_uvw) * &
atom_density * flux
else
score = flux * matxs % get_xs('inv_vel', p_g, UVW=p_uvw)
end if
end if
@ -1862,17 +1874,24 @@ contains
i_nuclide = t % nuclide_bins(k)
! Check to see if this nuclide was in the material of our collision.
do m = 1, mat % n_nuclides
if (mat % nuclide(m) == i_nuclide) then
atom_density = mat % atom_density(m)
exit
end if
end do
if (i_nuclide > 0) then
atom_density = -ONE
! Check to see if this nuclide was in the material of our collision
do m = 1, mat % n_nuclides
if (mat % nuclide(m) == i_nuclide) then
atom_density = mat % atom_density(m)
exit
end if
end do
else
atom_density = ZERO
end if
! Determine score for each bin
call score_general(p, t, (k-1)*t % n_score_bins, filter_index, &
i_nuclide, atom_density, filter_weight)
! If we found the nuclide, determine the score for each bin
if (atom_density >= ZERO) then
call score_general(p, t, (k-1)*t % n_score_bins, filter_index, &
i_nuclide, atom_density, filter_weight)
end if
end do NUCLIDE_LOOP

View file

@ -97,6 +97,9 @@ contains
! Since the MGXS can be angle dependent, this needs to be done
! After every collision for the MGXS mode
if (p % material /= MATERIAL_VOID) then
! Update the temperature index
call macro_xs(p % material) % obj % find_temperature(p % sqrtkT)
! Get the data
call macro_xs(p % material) % obj % calculate_xs(p % g, &
p % coord(p % n_coord) % uvw, material_xs)
else

BIN
tests/1d_mgxs.h5 Normal file

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,9 @@
import numpy as np
import openmc
from openmc.source import Source
from openmc.stats import Box
import numpy as np
class InputSet(object):
def __init__(self):
@ -27,37 +28,8 @@ class InputSet(object):
fuel.set_density('g/cm3', 10.062)
fuel.add_nuclide("U234", 4.9476e-6)
fuel.add_nuclide("U235", 4.8218e-4)
fuel.add_nuclide("U236", 9.0402e-5)
fuel.add_nuclide("U238", 2.1504e-2)
fuel.add_nuclide("Np237", 7.3733e-6)
fuel.add_nuclide("Pu238", 1.5148e-6)
fuel.add_nuclide("Pu239", 1.3955e-4)
fuel.add_nuclide("Pu240", 3.4405e-5)
fuel.add_nuclide("Pu241", 2.1439e-5)
fuel.add_nuclide("Pu242", 3.7422e-6)
fuel.add_nuclide("Am241", 4.5041e-7)
fuel.add_nuclide("Am242_m1", 9.2301e-9)
fuel.add_nuclide("Am243", 4.7878e-7)
fuel.add_nuclide("Cm242", 1.0485e-7)
fuel.add_nuclide("Cm243", 1.4268e-9)
fuel.add_nuclide("Cm244", 8.8756e-8)
fuel.add_nuclide("Cm245", 3.5285e-9)
fuel.add_nuclide("Mo95", 2.6497e-5)
fuel.add_nuclide("Tc99", 3.2772e-5)
fuel.add_nuclide("Ru101", 3.0742e-5)
fuel.add_nuclide("Ru103", 2.3505e-6)
fuel.add_nuclide("Ag109", 2.0009e-6)
fuel.add_nuclide("Xe135", 1.0801e-8)
fuel.add_nuclide("Cs133", 3.4612e-5)
fuel.add_nuclide("Nd143", 2.6078e-5)
fuel.add_nuclide("Nd145", 1.9898e-5)
fuel.add_nuclide("Sm147", 1.6128e-6)
fuel.add_nuclide("Sm149", 1.1627e-7)
fuel.add_nuclide("Sm150", 7.1727e-6)
fuel.add_nuclide("Sm151", 5.4947e-7)
fuel.add_nuclide("Sm152", 3.0221e-6)
fuel.add_nuclide("Eu153", 2.6209e-6)
fuel.add_nuclide("Gd155", 1.5369e-9)
fuel.add_nuclide("O16", 4.5737e-2)
clad = openmc.Material(name='Cladding', material_id=2)
@ -93,27 +65,10 @@ class InputSet(object):
rpv_steel.add_nuclide("Fe58", 0.00282159, 'wo')
rpv_steel.add_nuclide("Ni58", 0.0067198, 'wo')
rpv_steel.add_nuclide("Ni60", 0.0026776, 'wo')
rpv_steel.add_nuclide("Ni61", 0.0001183, 'wo')
rpv_steel.add_nuclide("Ni62", 0.0003835, 'wo')
rpv_steel.add_nuclide("Ni64", 0.0001008, 'wo')
rpv_steel.add_nuclide("Mn55", 0.01, 'wo')
rpv_steel.add_nuclide("Mo92", 0.000849, 'wo')
rpv_steel.add_nuclide("Mo94", 0.0005418, 'wo')
rpv_steel.add_nuclide("Mo95", 0.0009438, 'wo')
rpv_steel.add_nuclide("Mo96", 0.0010002, 'wo')
rpv_steel.add_nuclide("Mo97", 0.0005796, 'wo')
rpv_steel.add_nuclide("Mo98", 0.0014814, 'wo')
rpv_steel.add_nuclide("Mo100", 0.0006042, 'wo')
rpv_steel.add_nuclide("Si28", 0.00367464, 'wo')
rpv_steel.add_nuclide("Si29", 0.00019336, 'wo')
rpv_steel.add_nuclide("Si30", 0.000132, 'wo')
rpv_steel.add_nuclide("Cr50", 0.00010435, 'wo')
rpv_steel.add_nuclide("Cr52", 0.002092475, 'wo')
rpv_steel.add_nuclide("Cr53", 0.00024185, 'wo')
rpv_steel.add_nuclide("Cr54", 6.1325e-05, 'wo')
rpv_steel.add_nuclide("C0", 0.0025, 'wo')
rpv_steel.add_nuclide("Cu63", 0.0013696, 'wo')
rpv_steel.add_nuclide("Cu65", 0.0006304, 'wo')
lower_rad_ref = openmc.Material(name='Lower radial reflector',
material_id=6)
@ -127,18 +82,8 @@ class InputSet(object):
lower_rad_ref.add_nuclide("Fe57", 0.01362750048, 'wo')
lower_rad_ref.add_nuclide("Fe58", 0.001848545204, 'wo')
lower_rad_ref.add_nuclide("Ni58", 0.055298376566, 'wo')
lower_rad_ref.add_nuclide("Ni60", 0.022034425592, 'wo')
lower_rad_ref.add_nuclide("Ni61", 0.000973510811, 'wo')
lower_rad_ref.add_nuclide("Ni62", 0.003155886695, 'wo')
lower_rad_ref.add_nuclide("Ni64", 0.000829500336, 'wo')
lower_rad_ref.add_nuclide("Mn55", 0.0182870, 'wo')
lower_rad_ref.add_nuclide("Si28", 0.00839976771, 'wo')
lower_rad_ref.add_nuclide("Si29", 0.00044199679, 'wo')
lower_rad_ref.add_nuclide("Si30", 0.0003017355, 'wo')
lower_rad_ref.add_nuclide("Cr50", 0.007251360806, 'wo')
lower_rad_ref.add_nuclide("Cr52", 0.145407678031, 'wo')
lower_rad_ref.add_nuclide("Cr53", 0.016806340306, 'wo')
lower_rad_ref.add_nuclide("Cr54", 0.004261520857, 'wo')
lower_rad_ref.add_s_alpha_beta('c_H_in_H2O')
upper_rad_ref = openmc.Material(name='Upper radial reflector /'
@ -153,18 +98,8 @@ class InputSet(object):
upper_rad_ref.add_nuclide("Fe57", 0.01375486056, 'wo')
upper_rad_ref.add_nuclide("Fe58", 0.001865821363, 'wo')
upper_rad_ref.add_nuclide("Ni58", 0.055815129186, 'wo')
upper_rad_ref.add_nuclide("Ni60", 0.022240333032, 'wo')
upper_rad_ref.add_nuclide("Ni61", 0.000982608081, 'wo')
upper_rad_ref.add_nuclide("Ni62", 0.003185377845, 'wo')
upper_rad_ref.add_nuclide("Ni64", 0.000837251856, 'wo')
upper_rad_ref.add_nuclide("Mn55", 0.0184579, 'wo')
upper_rad_ref.add_nuclide("Si28", 0.00847831314, 'wo')
upper_rad_ref.add_nuclide("Si29", 0.00044612986, 'wo')
upper_rad_ref.add_nuclide("Si30", 0.000304557, 'wo')
upper_rad_ref.add_nuclide("Cr50", 0.00731912987, 'wo')
upper_rad_ref.add_nuclide("Cr52", 0.146766614995, 'wo')
upper_rad_ref.add_nuclide("Cr53", 0.01696340737, 'wo')
upper_rad_ref.add_nuclide("Cr54", 0.004301347765, 'wo')
upper_rad_ref.add_s_alpha_beta('c_H_in_H2O')
bot_plate = openmc.Material(name='Bottom plate region', material_id=8)
@ -178,18 +113,8 @@ class InputSet(object):
bot_plate.add_nuclide("Fe57", 0.014750478, 'wo')
bot_plate.add_nuclide("Fe58", 0.002000875025, 'wo')
bot_plate.add_nuclide("Ni58", 0.059855207342, 'wo')
bot_plate.add_nuclide("Ni60", 0.023850159704, 'wo')
bot_plate.add_nuclide("Ni61", 0.001053732407, 'wo')
bot_plate.add_nuclide("Ni62", 0.003415945715, 'wo')
bot_plate.add_nuclide("Ni64", 0.000897854832, 'wo')
bot_plate.add_nuclide("Mn55", 0.0197940, 'wo')
bot_plate.add_nuclide("Si28", 0.00909197802, 'wo')
bot_plate.add_nuclide("Si29", 0.00047842098, 'wo')
bot_plate.add_nuclide("Si30", 0.000326601, 'wo')
bot_plate.add_nuclide("Cr50", 0.007848910646, 'wo')
bot_plate.add_nuclide("Cr52", 0.157390026871, 'wo')
bot_plate.add_nuclide("Cr53", 0.018191270146, 'wo')
bot_plate.add_nuclide("Cr54", 0.004612692337, 'wo')
bot_plate.add_s_alpha_beta('c_H_in_H2O')
bot_nozzle = openmc.Material(name='Bottom nozzle region',
@ -204,18 +129,8 @@ class InputSet(object):
bot_nozzle.add_nuclide("Fe57", 0.01163454624, 'wo')
bot_nozzle.add_nuclide("Fe58", 0.001578204652, 'wo')
bot_nozzle.add_nuclide("Ni58", 0.047211231662, 'wo')
bot_nozzle.add_nuclide("Ni60", 0.018811987544, 'wo')
bot_nozzle.add_nuclide("Ni61", 0.000831139127, 'wo')
bot_nozzle.add_nuclide("Ni62", 0.002694352115, 'wo')
bot_nozzle.add_nuclide("Ni64", 0.000708189552, 'wo')
bot_nozzle.add_nuclide("Mn55", 0.0156126, 'wo')
bot_nozzle.add_nuclide("Si28", 0.007171335558, 'wo')
bot_nozzle.add_nuclide("Si29", 0.000377356542, 'wo')
bot_nozzle.add_nuclide("Si30", 0.0002576079, 'wo')
bot_nozzle.add_nuclide("Cr50", 0.006190885148, 'wo')
bot_nozzle.add_nuclide("Cr52", 0.124142524198, 'wo')
bot_nozzle.add_nuclide("Cr53", 0.014348496148, 'wo')
bot_nozzle.add_nuclide("Cr54", 0.003638294506, 'wo')
bot_nozzle.add_s_alpha_beta('c_H_in_H2O')
top_nozzle = openmc.Material(name='Top nozzle region', material_id=10)
@ -229,18 +144,8 @@ class InputSet(object):
top_nozzle.add_nuclide("Fe57", 0.0101152584, 'wo')
top_nozzle.add_nuclide("Fe58", 0.00137211607, 'wo')
top_nozzle.add_nuclide("Ni58", 0.04104621835, 'wo')
top_nozzle.add_nuclide("Ni60", 0.0163554502, 'wo')
top_nozzle.add_nuclide("Ni61", 0.000722605975, 'wo')
top_nozzle.add_nuclide("Ni62", 0.002342513875, 'wo')
top_nozzle.add_nuclide("Ni64", 0.0006157116, 'wo')
top_nozzle.add_nuclide("Mn55", 0.0135739, 'wo')
top_nozzle.add_nuclide("Si28", 0.006234853554, 'wo')
top_nozzle.add_nuclide("Si29", 0.000328078746, 'wo')
top_nozzle.add_nuclide("Si30", 0.0002239677, 'wo')
top_nozzle.add_nuclide("Cr50", 0.005382452306, 'wo')
top_nozzle.add_nuclide("Cr52", 0.107931450781, 'wo')
top_nozzle.add_nuclide("Cr53", 0.012474806806, 'wo')
top_nozzle.add_nuclide("Cr54", 0.003163190107, 'wo')
top_nozzle.add_s_alpha_beta('c_H_in_H2O')
top_fa = openmc.Material(name='Top of fuel assemblies', material_id=11)
@ -824,60 +729,59 @@ class AssemblyInputSet(object):
class MGInputSet(InputSet):
def build_default_materials_and_geometry(self):
def build_default_materials_and_geometry(self, reps=None, as_macro=True):
# Define materials needed for 1D/1G slab problem
uo2_data = openmc.Macroscopic('uo2_iso')
uo2 = openmc.Material(name='UO2', material_id=1)
uo2.set_density('macro', 1.0)
uo2.add_macroscopic(uo2_data)
mat_names = ['uo2', 'clad', 'lwtr']
mgxs_reps = ['ang', 'ang_mu', 'iso', 'iso_mu']
clad_data = openmc.Macroscopic('clad_ang_mu')
clad = openmc.Material(name='Clad', material_id=2)
clad.set_density('macro', 1.0)
clad.add_macroscopic(clad_data)
if reps is None:
reps = mgxs_reps
water_data = openmc.Macroscopic('lwtr_iso_mu')
water = openmc.Material(name='LWTR', material_id=3)
water.set_density('macro', 1.0)
water.add_macroscopic(water_data)
xs = []
mats = []
i = 0
for mat in mat_names:
for rep in reps:
i += 1
if as_macro:
xs.append(openmc.Macroscopic(mat + '_' + rep))
mats.append(openmc.Material(name=str(i)))
mats[-1].set_density('macro', 1.)
mats[-1].add_macroscopic(xs[-1])
else:
xs.append(openmc.Nuclide(mat + '_' + rep))
mats.append(openmc.Material(name=str(i)))
mats[-1].set_density('atom/b-cm', 1.)
mats[-1].add_nuclide(xs[-1].name, 1.0, 'ao')
# Define the materials file.
self.materials += (uo2, clad, water)
# Define the materials file
self.xs_data = xs
self.materials += mats
# Define surfaces.
# Assembly/Problem Boundary
left = openmc.XPlane(x0=0.0, surface_id=200,
boundary_type='reflective')
right = openmc.XPlane(x0=10.0, surface_id=201,
boundary_type='reflective')
bottom = openmc.YPlane(y0=0.0, surface_id=300,
boundary_type='reflective')
top = openmc.YPlane(y0=10.0, surface_id=301,
boundary_type='reflective')
left = openmc.XPlane(x0=0.0, boundary_type='reflective')
right = openmc.XPlane(x0=10.0, boundary_type='reflective')
bottom = openmc.YPlane(y0=0.0, boundary_type='reflective')
top = openmc.YPlane(y0=10.0, boundary_type='reflective')
# for each material add a plane
planes = [openmc.ZPlane(z0=0.0, boundary_type='reflective')]
dz = round(5. / float(len(mats)), 4)
for i in range(len(mats) - 1):
planes.append(openmc.ZPlane(z0=dz * float(i + 1)))
planes.append(openmc.ZPlane(z0=5.0, boundary_type='reflective'))
down = openmc.ZPlane(z0=0.0, surface_id=0,
boundary_type='reflective')
fuel_clad_intfc = openmc.ZPlane(z0=2.0, surface_id=1)
clad_lwtr_intfc = openmc.ZPlane(z0=2.4, surface_id=2)
up = openmc.ZPlane(z0=5.0, surface_id=3,
boundary_type='reflective')
# Define cells
c1 = openmc.Cell(cell_id=1)
c1.region = +left & -right & +bottom & -top & +down & -fuel_clad_intfc
c1.fill = uo2
c2 = openmc.Cell(cell_id=2)
c2.region = +left & -right & +bottom & -top & +fuel_clad_intfc & -clad_lwtr_intfc
c2.fill = clad
c3 = openmc.Cell(cell_id=3)
c3.region = +left & -right & +bottom & -top & +clad_lwtr_intfc & -up
c3.fill = water
# Define cells for each material
cells = []
xy = +left & -right & +bottom & -top
for i, mat in enumerate(mats):
cells.append(openmc.Cell())
cells[-1].region = xy & +planes[i] & -planes[i + 1]
cells[-1].fill = mat
# Define root universe.
root = openmc.Universe(universe_id=0, name='root universe')
root.add_cells((c1, c2, c3))
root.add_cells(cells)
# Assign root universe to geometry
self.geometry.root_universe = root
@ -887,9 +791,9 @@ class MGInputSet(InputSet):
self.settings.inactive = 5
self.settings.particles = 100
self.settings.source = Source(space=Box([0.0, 0.0, 0.0],
[10.0, 10.0, 2.0]))
[10.0, 10.0, 5.]))
self.settings.energy_mode = "multi-group"
self.settings.cross_sections = "../1d_mgxs.xml"
self.settings.cross_sections = "../1d_mgxs.h5"
def build_defualt_plots(self):
plot = openmc.Plot()

View file

@ -117,7 +117,7 @@ def cleanup(path):
for dirpath, dirnames, filenames in os.walk(path):
for fname in filenames:
for ext in ['.h5', '.ppm', '.voxel']:
if fname.endswith(ext):
if fname.endswith(ext) and fname != '1d_mgxs.h5':
os.remove(os.path.join(dirpath, fname))

View file

@ -1 +1 @@
dfb59bace10a91bb7ffc871d8ee87e91d94754bb8bb002ac6088f80fe0f480741c0489f74b753fc37158d0ff0f1368739ea60638b42083791311eefeac79168e
6bcc9cca24d42995bdff9bf9aca5e852c2dbca5cfb42a12ac637def9cf5cac227654182fc9cf9e17d07cf2e9af11fea832e3ae0eb7001cc09856f73d219664f9

View file

@ -1 +1 @@
bc8bef8121f9b6470e4fea817a4e48eabb1ecba1f42761a4cbd77d71181bf9e1612df4a3d6ddfbcd08a3086ac873e5f3c3e560bf96b2b7c959a2f7aad7e4e08d
a2848fdb0a12c99ce31f4ddee766e0cf33bd5c6feca927bcb4bceca6fbb094bb1309fb8548589a99fcb09a830911457b9175b460aa45773822f8112a34b3b5c1

View file

@ -0,0 +1 @@
c3581501d1486293c255390251d20584431a198fbb7c07dc2879dc95dc96e26786d3913ce0db8007f542468fd137ff3267824844c03b4687fdad81ea568c92d7

View file

@ -0,0 +1,3 @@
tally 1:
sum = 2.056839E+02
sum_sq = 4.244628E+03

View file

@ -0,0 +1,80 @@
#!/usr/bin/env python
import os
import sys
sys.path.insert(0, os.pardir)
from testing_harness import PyAPITestHarness
import openmc
class CreateFissionNeutronsTestHarness(PyAPITestHarness):
def _build_inputs(self):
# Material is composed of H-1 and U-235
mat = openmc.Material(material_id=1, name='mat')
mat.set_density('atom/b-cm', 0.069335)
mat.add_nuclide('H1', 40.0)
mat.add_nuclide('U235', 1.0)
materials_file = openmc.Materials([mat])
materials_file.export_to_xml()
# Cell is box with reflective boundary
x1 = openmc.XPlane(surface_id=1, x0=-1)
x2 = openmc.XPlane(surface_id=2, x0=1)
y1 = openmc.YPlane(surface_id=3, y0=-1)
y2 = openmc.YPlane(surface_id=4, y0=1)
z1 = openmc.ZPlane(surface_id=5, z0=-1)
z2 = openmc.ZPlane(surface_id=6, z0=1)
for surface in [x1, x2, y1, y2, z1, z2]:
surface.boundary_type = 'reflective'
box = openmc.Cell(cell_id=1, name='box')
box.region = +x1 & -x2 & +y1 & -y2 & +z1 & -z2
box.fill = mat
root = openmc.Universe(universe_id=0, name='root universe')
root.add_cell(box)
geometry = openmc.Geometry(root)
geometry.export_to_xml()
# Set the running parameters
settings_file = openmc.Settings()
settings_file.run_mode = 'fixed source'
settings_file.batches = 10
settings_file.particles = 100
settings_file.create_fission_neutrons = False
bounds = [-1, -1, -1, 1, 1, 1]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:])
watt_dist = openmc.stats.Watt()
settings_file.source = openmc.source.Source(space=uniform_dist,
energy=watt_dist)
settings_file.export_to_xml()
# Create tallies
tallies = openmc.Tallies()
tally = openmc.Tally(1)
tally.scores = ['flux']
tallies.append(tally)
tallies.export_to_xml()
def _get_results(self):
"""Digest info in the statepoint and return as a string."""
# Read the statepoint file.
sp = openmc.StatePoint(self._sp_name)
# Write out tally data.
outstr = ''
t = sp.get_tally()
outstr += 'tally {0}:\n'.format(t.id)
outstr += 'sum = {0:12.6E}\n'.format(t.sum[0, 0, 0])
outstr += 'sum_sq = {0:12.6E}\n'.format(t.sum_sq[0, 0, 0])
return outstr
def _cleanup(self):
super(CreateFissionNeutronsTestHarness, self)._cleanup()
f = os.path.join(os.getcwd(), 'tallies.xml')
if os.path.exists(f):
os.remove(f)
if __name__ == '__main__':
harness = CreateFissionNeutronsTestHarness('statepoint.10.h5', True)
harness.main()

View file

@ -0,0 +1 @@
4dc6a7b131f6757ecc9b06e93d425712f0813c546ed9eaa0aafa4990383b2f3a5b742a5a39ef5f27300d2c861b344e78de2d2c569b9439e7893dc06b03bf3b02

View file

@ -0,0 +1,3 @@
tally 1:
sum = 0.000000E+00
sum_sq = 0.000000E+00

View file

@ -0,0 +1,84 @@
#!/usr/bin/env python
import os
import sys
sys.path.insert(0, os.pardir)
from testing_harness import PyAPITestHarness
import openmc
class EnergyCutoffTestHarness(PyAPITestHarness):
def _build_inputs(self):
# Set energy cutoff
energy_cutoff = 4e-6
# Material is composed of H-1
mat = openmc.Material(material_id=1, name='mat')
mat.set_density('atom/b-cm', 0.069335)
mat.add_nuclide('H1', 40.0)
materials_file = openmc.Materials([mat])
materials_file.export_to_xml()
# Cell is box with reflective boundary
x1 = openmc.XPlane(surface_id=1, x0=-1)
x2 = openmc.XPlane(surface_id=2, x0=1)
y1 = openmc.YPlane(surface_id=3, y0=-1)
y2 = openmc.YPlane(surface_id=4, y0=1)
z1 = openmc.ZPlane(surface_id=5, z0=-1)
z2 = openmc.ZPlane(surface_id=6, z0=1)
for surface in [x1, x2, y1, y2, z1, z2]:
surface.boundary_type = 'reflective'
box = openmc.Cell(cell_id=1, name='box')
box.region = +x1 & -x2 & +y1 & -y2 & +z1 & -z2
box.fill = mat
root = openmc.Universe(universe_id=0, name='root universe')
root.add_cell(box)
geometry = openmc.Geometry(root)
geometry.export_to_xml()
# Set the running parameters
settings_file = openmc.Settings()
settings_file.run_mode = 'fixed source'
settings_file.batches = 10
settings_file.particles = 100
settings_file.cutoff = {'energy': energy_cutoff}
bounds = [-1, -1, -1, 1, 1, 1]
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:])
watt_dist = openmc.stats.Watt()
settings_file.source = openmc.source.Source(space=uniform_dist,
energy=watt_dist)
settings_file.export_to_xml()
# Tally flux under energy cutoff
tallies = openmc.Tallies()
tally = openmc.Tally(1)
tally.scores = ['flux']
energy_filter = openmc.filter.EnergyFilter((0.0, energy_cutoff))
tally.filters = [energy_filter]
tallies.append(tally)
tallies.export_to_xml()
def _get_results(self):
"""Digest info in the statepoint and return as a string."""
# Read the statepoint file.
sp = openmc.StatePoint(self._sp_name)
# Write out tally data.
outstr = ''
t = sp.get_tally()
outstr += 'tally {0}:\n'.format(t.id)
outstr += 'sum = {0:12.6E}\n'.format(t.sum[0, 0, 0])
outstr += 'sum_sq = {0:12.6E}\n'.format(t.sum_sq[0, 0, 0])
return outstr
def _cleanup(self):
super(EnergyCutoffTestHarness, self)._cleanup()
f = os.path.join(os.getcwd(), 'tallies.xml')
if os.path.exists(f):
os.remove(f)
if __name__ == '__main__':
harness = EnergyCutoffTestHarness('statepoint.10.h5', True)
harness.main()

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