mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 22:26:08 -04:00
added enrichment and wo element support in Python API and added new tests
This commit is contained in:
parent
d579440b02
commit
663636ce2e
12 changed files with 278 additions and 46 deletions
|
|
@ -1415,9 +1415,9 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
Specifies that a natural element is present in the material. The natural
|
||||
element is split up into individual isotopes based on `IUPAC Isotopic
|
||||
Compositions of the Elements 2009`_. This element has
|
||||
attributes/sub-elements called ``name``, and ``ao``. The ``name``
|
||||
attribute is the atomic symbol of the element. Finally, the ``ao``
|
||||
attribute specifies the atom percent of the element within the material,
|
||||
attributes/sub-elements called ``name``, and ``ao`` or ``wo``. The ``name``
|
||||
attribute is the atomic symbol of the element. Finally, the ``ao`` and ``wo``
|
||||
attributes specify the atom or weight percent of the element within the material,
|
||||
respectively. One example would be as follows:
|
||||
|
||||
.. code-block:: xml
|
||||
|
|
@ -1447,6 +1447,14 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
.. note:: The ``scattering`` attribute/sub-element is not used in the
|
||||
multi-group :ref:`energy_mode`.
|
||||
|
||||
An optional attribute/sub-element for uranium is ``enrichment``. This
|
||||
attribute lets the user set the weight-percent enrichment of U-235 in
|
||||
uranium. The weight-percent of U-234 is computed as 0.008 times the weight
|
||||
percent of U-235 with U-238 comprising the balance. Valid values for
|
||||
enrichment range between 0 and 1/1.008.
|
||||
|
||||
*Default*: None
|
||||
|
||||
:sab:
|
||||
Associates an S(a,b) table with the material. This element has one
|
||||
attribute/sub-element called ``name``. The ``name`` attribute
|
||||
|
|
|
|||
|
|
@ -45,18 +45,14 @@ sn119 = openmc.Nuclide('Sn119')
|
|||
sn120 = openmc.Nuclide('Sn120')
|
||||
sn122 = openmc.Nuclide('Sn122')
|
||||
sn124 = openmc.Nuclide('Sn124')
|
||||
u234 = openmc.Nuclide('U234')
|
||||
u235 = openmc.Nuclide('U235')
|
||||
u238 = openmc.Nuclide('U238')
|
||||
u = openmc.Element('U')
|
||||
o = openmc.Element('O')
|
||||
|
||||
# Instantiate some Materials and register the appropriate Nuclides
|
||||
uo2 = openmc.Material(material_id=1, name='UO2 fuel at 2.4% wt enrichment')
|
||||
uo2.set_density('g/cm3', 10.29769)
|
||||
uo2.add_nuclide(u234, 4.4843e-6)
|
||||
uo2.add_nuclide(u235, 5.5815e-4)
|
||||
uo2.add_nuclide(u238, 2.2408e-2)
|
||||
uo2.add_nuclide(o16, 4.5829e-2)
|
||||
uo2.add_nuclide(o17, 1.1164e-4)
|
||||
uo2.add_element(u, 1., enrichment=0.05)
|
||||
uo2.add_element(o, 2.)
|
||||
|
||||
helium = openmc.Material(material_id=2, name='Helium for gap')
|
||||
helium.set_density('g/cm3', 0.001598)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ if sys.version_info[0] >= 3:
|
|||
|
||||
|
||||
class Element(object):
|
||||
"""A natural element used in a material via <element>. Internally, OpenMC will
|
||||
expand the natural element into isotopes based on the known natural
|
||||
"""A natural element used in a material via <element>. Internally, OpenMC
|
||||
will expand the natural element into isotopes based on the known natural
|
||||
abundances.
|
||||
|
||||
Parameters
|
||||
|
|
|
|||
|
|
@ -58,9 +58,9 @@ class Material(object):
|
|||
'atom/b-cm', 'atom/cm3', 'sum', or 'macro'. The 'macro' unit only
|
||||
applies in the case of a multi-group calculation.
|
||||
elements : list of tuple
|
||||
List in which each item is a 3-tuple consisting of an
|
||||
:class:`openmc.Element` instance, the percent density, and the percent
|
||||
type ('ao' or 'wo').
|
||||
List in which each item is a 3-tuple or 4-tuple consisting of an
|
||||
:class:`openmc.Element` instance, the percent density, the percent
|
||||
type ('ao' or 'wo'), and an optional weight-percent enrichment.
|
||||
nuclides : list of tuple
|
||||
List in which each item is a 3-tuple consisting of an
|
||||
:class:`openmc.Nuclide` instance, the percent density, and the percent
|
||||
|
|
@ -83,7 +83,7 @@ class Material(object):
|
|||
# (only one is allowed, hence this is different than _nuclides, etc)
|
||||
self._macroscopic = None
|
||||
|
||||
# A list of tuples (element, percent, percent type)
|
||||
# A list of tuples (element, percent, percent type, enrichment (optional))
|
||||
self._elements = []
|
||||
|
||||
# If specified, a list of table names
|
||||
|
|
@ -149,9 +149,13 @@ class Material(object):
|
|||
|
||||
string += '{0: <16}\n'.format('\tElements')
|
||||
|
||||
for element, percent, percent_type in self._elements:
|
||||
string += '{0: <16}'.format('\t{0.name}'.format(element))
|
||||
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
|
||||
for element in self._elements:
|
||||
string += '{0: <16}'.format('\t{0.name}'.format(element[0]))
|
||||
if len(element) == 3:
|
||||
string += '=\t{0: <12} [{1}]\n'.format(element[1], element[2])
|
||||
else:
|
||||
string += '=\t{0: <12} [{1}] {2:4.2f} % enrichment\n'\
|
||||
.format(element[1], element[2], element[3]*100.)
|
||||
|
||||
return string
|
||||
|
||||
|
|
@ -402,7 +406,8 @@ class Material(object):
|
|||
if macroscopic.name == self._macroscopic.name:
|
||||
self._macroscopic = None
|
||||
|
||||
def add_element(self, element, percent, percent_type='ao', expand=False):
|
||||
def add_element(self, element, percent, percent_type='ao', enrichment=None,
|
||||
expand=False):
|
||||
"""Add a natural element to the material
|
||||
|
||||
Parameters
|
||||
|
|
@ -414,6 +419,8 @@ class Material(object):
|
|||
percent_type : {'ao', 'wo'}, optional
|
||||
'ao' for atom percent and 'wo' for weight percent. Defaults to atom
|
||||
percent.
|
||||
enrichment : float, optional
|
||||
Optional weight percent enrichment for uranium. Defaults to None.
|
||||
expand : bool, optional
|
||||
Whether to expand the natural element into its naturally-occurring
|
||||
isotopes. Defaults to False.
|
||||
|
|
@ -440,6 +447,19 @@ class Material(object):
|
|||
'percent type "{1}"'.format(self._id, percent_type)
|
||||
raise ValueError(msg)
|
||||
|
||||
if enrichment is not None:
|
||||
if not isinstance(enrichment, Real):
|
||||
msg = 'Unable to add an Element to Material ID="{0}" with a ' \
|
||||
'non-floating point enrichment value "{1}"'\
|
||||
.format(self._id, enrichment)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif element != 'U':
|
||||
msg = 'Unable to use enrichment for element {0} which is not ' \
|
||||
'uranium for Material ID="{1}"'.format(enrichment,
|
||||
self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
# Copy this Element to separate it from same Element in other Materials
|
||||
if isinstance(element, openmc.Element):
|
||||
element = deepcopy(element)
|
||||
|
|
@ -450,10 +470,16 @@ class Material(object):
|
|||
if percent_type == 'wo':
|
||||
raise NotImplementedError('Expanding natural element based on '
|
||||
'weight percent is not yet supported.')
|
||||
|
||||
for isotope, abundance in element.expand():
|
||||
self._nuclides.append((isotope, percent*abundance, percent_type))
|
||||
|
||||
else:
|
||||
self._elements.append((element, percent, percent_type))
|
||||
if enrichment is not None:
|
||||
self._elements.append((element, percent, percent_type,
|
||||
enrichment))
|
||||
else:
|
||||
self._elements.append((element, percent, percent_type))
|
||||
|
||||
def remove_element(self, element):
|
||||
"""Remove a natural element from the material
|
||||
|
|
@ -525,9 +551,9 @@ class Material(object):
|
|||
for nuclide, density, density_type in self._nuclides:
|
||||
nuclides.append(nuclide.name)
|
||||
|
||||
for element, density, density_type in self._elements:
|
||||
for element in self._elements:
|
||||
# Expand natural element into isotopes
|
||||
for isotope, abundance in element.expand():
|
||||
for isotope, abundance in element[0].expand():
|
||||
nuclides.append(isotope.name)
|
||||
|
||||
return nuclides
|
||||
|
|
@ -548,10 +574,10 @@ class Material(object):
|
|||
for nuclide, density, density_type in self._nuclides:
|
||||
nuclides[nuclide.name] = (nuclide, density)
|
||||
|
||||
for element, density, density_type in self._elements:
|
||||
for element in self._elements:
|
||||
# Expand natural element into isotopes
|
||||
for isotope, abundance in element.expand():
|
||||
nuclides[isotope.name] = (isotope, density*abundance)
|
||||
for isotope, abundance in element[0].expand():
|
||||
nuclides[isotope.name] = (isotope, element[1]*abundance)
|
||||
|
||||
return nuclides
|
||||
|
||||
|
|
@ -586,6 +612,9 @@ class Material(object):
|
|||
else:
|
||||
xml_element.set("wo", str(element[1]))
|
||||
|
||||
if len(element) == 4:
|
||||
xml_element.set("enrichment", str(element[3]))
|
||||
|
||||
if not element[0].scattering is None:
|
||||
xml_element.set("scattering", element[0].scattering)
|
||||
|
||||
|
|
@ -657,12 +686,12 @@ class Material(object):
|
|||
comps = []
|
||||
allnucs = self._nuclides + self._elements
|
||||
dist_per_type = allnucs[0][2]
|
||||
for nuc, per, typ in allnucs:
|
||||
if not typ == dist_per_type:
|
||||
for nuc in allnucs:
|
||||
if not nuc[2] == dist_per_type:
|
||||
msg = 'All nuclides and elements in a distributed ' \
|
||||
'material must have the same type, either ao or wo'
|
||||
raise ValueError(msg)
|
||||
comps.append(per)
|
||||
comps.append(nuc[1])
|
||||
|
||||
if self._distrib_otf_file is None:
|
||||
# Create values and units subelements
|
||||
|
|
|
|||
|
|
@ -4573,10 +4573,9 @@ contains
|
|||
end subroutine read_mg_cross_sections_xml
|
||||
|
||||
!===============================================================================
|
||||
! EXPAND_NATURAL_ELEMENT converts natural elements specified using an <element>
|
||||
! tag within a material into individual isotopes based on IUPAC Isotopic
|
||||
! Compositions of the Elements 2009 (doi:10.1351/PAC-REP-10-06-02). In some
|
||||
! cases, modifications have been made to work with ENDF/B-VII.1 where
|
||||
! EXPAND_NATURAL_ELEMENT_NAMES converts natural elements specified using an
|
||||
! <element> tag within a material and adds the names to the input names array.
|
||||
! In some cases, modifications have been made to work with ENDF/B-VII.1 where
|
||||
! evaluations of particular isotopes don't exist.
|
||||
!===============================================================================
|
||||
|
||||
|
|
@ -5101,21 +5100,28 @@ contains
|
|||
|
||||
end subroutine expand_natural_element_names
|
||||
|
||||
!===============================================================================
|
||||
! EXPAND_NATURAL_ELEMENT_DENSITIES converts natural elements specified using an
|
||||
! <element> tag within a material into individual isotopes based on IUPAC
|
||||
! Isotopic Compositions of the Elements 2009 (doi:10.1351/PAC-REP-10-06-02). In
|
||||
! some cases, modifications have been made to work with ENDF/B-VII.1 where
|
||||
! evaluations of particular isotopes don't exist.
|
||||
!===============================================================================
|
||||
|
||||
subroutine expand_natural_element_densities(name, expand_by, density, &
|
||||
enrichment, densities)
|
||||
character(*), intent(in) :: name
|
||||
character(*), intent(in) :: expand_by
|
||||
real(8), intent(in) :: density
|
||||
real(8), intent(in) :: enrichment
|
||||
type(VectorReal), intent(inout) :: densities
|
||||
character(*), intent(in) :: name ! element name
|
||||
character(*), intent(in) :: expand_by ! "ao" or "wo"
|
||||
real(8), intent(in) :: density ! value for "ao" or "wo"
|
||||
real(8), intent(in) :: enrichment ! enrichment in weight %
|
||||
type(VectorReal), intent(inout) :: densities ! isotope densities vector
|
||||
|
||||
integer :: i
|
||||
integer :: n_isotopes
|
||||
character(2) :: element_name
|
||||
real(8) :: element_awr
|
||||
real(8), allocatable :: awr(:)
|
||||
real(8), allocatable :: mf(:)
|
||||
integer :: i ! iterator
|
||||
integer :: n_isotopes ! number of isotopes in the element
|
||||
character(2) :: element_name ! element atomic symbol
|
||||
real(8) :: element_awr ! element atomic weight ratio
|
||||
real(8), allocatable :: awr(:) ! isotope atomic weight ratios
|
||||
real(8), allocatable :: mf(:) ! isotope mole fractions
|
||||
|
||||
element_name = name(1:2)
|
||||
|
||||
|
|
@ -7247,7 +7253,7 @@ contains
|
|||
awr(3) = nuclides(nuclide_dict % get_key('u238')) % awr
|
||||
end if
|
||||
|
||||
! Modify mole fractions in enrichment provided
|
||||
! Modify mole fractions if enrichment provided
|
||||
if (enrichment /= -ONE) then
|
||||
|
||||
! Calculate the mass fractions of isotopes
|
||||
|
|
|
|||
1
tests/test_element_wo/inputs_true.dat
Normal file
1
tests/test_element_wo/inputs_true.dat
Normal file
|
|
@ -0,0 +1 @@
|
|||
7db97c5303ca1f0beaba1b610c0d57cede072af2200b7494515caeca7f0ca43b1e1dbda1968f4f052af6c632cbba8c92203cd53670c19350c55d2339d28b2fb2
|
||||
5
tests/test_element_wo/results_true.dat
Normal file
5
tests/test_element_wo/results_true.dat
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
k-combined:
|
||||
8.863826E-01 5.114399E-02
|
||||
tally 1:
|
||||
7.906207E+01
|
||||
1.251720E+03
|
||||
90
tests/test_element_wo/test_element_wo.py
Normal file
90
tests/test_element_wo/test_element_wo.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import hashlib
|
||||
sys.path.insert(0, os.pardir)
|
||||
from testing_harness import PyAPITestHarness
|
||||
from input_set import PinCellInputSet
|
||||
import openmc
|
||||
import openmc.mgxs
|
||||
|
||||
|
||||
class ElementWOTestHarness(PyAPITestHarness):
|
||||
def _build_inputs(self):
|
||||
|
||||
# Set the input set to use the pincell model
|
||||
self._input_set = PinCellInputSet()
|
||||
|
||||
# Define materials.
|
||||
fuel = openmc.Material(name='Fuel')
|
||||
fuel.set_density('g/cm3', 10.29769)
|
||||
fuel.add_element("U", 0.88, 'wo')
|
||||
fuel.add_element("O", 0.12, 'wo')
|
||||
|
||||
clad = openmc.Material(name='Cladding')
|
||||
clad.set_density('g/cm3', 6.55)
|
||||
clad.add_element("Zr", 1.0, 'wo')
|
||||
|
||||
hot_water = openmc.Material(name='Hot borated water')
|
||||
hot_water.set_density('g/cm3', 0.740582)
|
||||
hot_water.add_element("H", 2./18., 'wo')
|
||||
hot_water.add_element("O", 16./18., 'wo')
|
||||
hot_water.add_s_alpha_beta('c_H_in_H2O')
|
||||
|
||||
# Define the materials file.
|
||||
self._input_set.materials += (fuel, clad, hot_water)
|
||||
|
||||
# Instantiate ZCylinder surfaces
|
||||
fuel_or = openmc.ZCylinder(x0=0, y0=0, R=0.39218, name='Fuel OR')
|
||||
clad_or = openmc.ZCylinder(x0=0, y0=0, R=0.45720, name='Clad OR')
|
||||
left = openmc.XPlane(x0=-0.63, name='left')
|
||||
right = openmc.XPlane(x0=0.63, name='right')
|
||||
bottom = openmc.YPlane(y0=-0.63, name='bottom')
|
||||
top = openmc.YPlane(y0=0.63, name='top')
|
||||
|
||||
left.boundary_type = 'reflective'
|
||||
right.boundary_type = 'reflective'
|
||||
top.boundary_type = 'reflective'
|
||||
bottom.boundary_type = 'reflective'
|
||||
|
||||
# Instantiate Cells
|
||||
fuel_pin = openmc.Cell(name='cell 1')
|
||||
cladding = openmc.Cell(name='cell 3')
|
||||
water = openmc.Cell(name='cell 2')
|
||||
|
||||
# Use surface half-spaces to define regions
|
||||
fuel_pin.region = -fuel_or
|
||||
cladding.region = +fuel_or & -clad_or
|
||||
water.region = +clad_or & +left & -right & +bottom & -top
|
||||
|
||||
# Register Materials with Cells
|
||||
fuel_pin.fill = fuel
|
||||
cladding.fill = clad
|
||||
water.fill = hot_water
|
||||
|
||||
# Instantiate Universe
|
||||
root = openmc.Universe(universe_id=0, name='root universe')
|
||||
|
||||
# Register Cells with Universe
|
||||
root.add_cells([fuel_pin, cladding, water])
|
||||
|
||||
# Instantiate a Geometry, register the root Universe, and export to XML
|
||||
self._input_set.geometry.root_universe = root
|
||||
|
||||
|
||||
mat_filter = openmc.MaterialFilter((fuel.id,))
|
||||
flux_tally = openmc.Tally()
|
||||
flux_tally.filters = [mat_filter]
|
||||
flux_tally.scores = ['flux']
|
||||
|
||||
self._input_set.tallies = openmc.Tallies()
|
||||
self._input_set.tallies += [flux_tally]
|
||||
self._input_set.build_default_settings()
|
||||
self._input_set.export()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
harness = ElementWOTestHarness('statepoint.10.*', True)
|
||||
harness.main()
|
||||
1
tests/test_enrichment/inputs_true.dat
Normal file
1
tests/test_enrichment/inputs_true.dat
Normal file
|
|
@ -0,0 +1 @@
|
|||
9284e022d343431db161faee9290658addde59427a4e42adbdfd5069cd6bd54929bedf87ccb2fb369c91ca52c2c572e8825c4997b662ca2ac34005bf4c58ba83
|
||||
5
tests/test_enrichment/results_true.dat
Normal file
5
tests/test_enrichment/results_true.dat
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
k-combined:
|
||||
1.457508E+00 5.577598E-02
|
||||
tally 1:
|
||||
6.199709E+01
|
||||
7.692381E+02
|
||||
90
tests/test_enrichment/test_enrichment.py
Normal file
90
tests/test_enrichment/test_enrichment.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import hashlib
|
||||
sys.path.insert(0, os.pardir)
|
||||
from testing_harness import PyAPITestHarness
|
||||
from input_set import PinCellInputSet
|
||||
import openmc
|
||||
import openmc.mgxs
|
||||
|
||||
|
||||
class EnrichmentTestHarness(PyAPITestHarness):
|
||||
def _build_inputs(self):
|
||||
|
||||
# Set the input set to use the pincell model
|
||||
self._input_set = PinCellInputSet()
|
||||
|
||||
# Define materials.
|
||||
fuel = openmc.Material(name='Fuel')
|
||||
fuel.set_density('g/cm3', 10.29769)
|
||||
fuel.add_element("U", 0.88, 'wo', enrichment=0.05)
|
||||
fuel.add_element("O", 0.12, 'wo')
|
||||
|
||||
clad = openmc.Material(name='Cladding')
|
||||
clad.set_density('g/cm3', 6.55)
|
||||
clad.add_element("Zr", 1.0, 'wo')
|
||||
|
||||
hot_water = openmc.Material(name='Hot borated water')
|
||||
hot_water.set_density('g/cm3', 0.740582)
|
||||
hot_water.add_element("H", 2./18., 'wo')
|
||||
hot_water.add_element("O", 16./18., 'wo')
|
||||
hot_water.add_s_alpha_beta('c_H_in_H2O')
|
||||
|
||||
# Define the materials file.
|
||||
self._input_set.materials += (fuel, clad, hot_water)
|
||||
|
||||
# Instantiate ZCylinder surfaces
|
||||
fuel_or = openmc.ZCylinder(x0=0, y0=0, R=0.39218, name='Fuel OR')
|
||||
clad_or = openmc.ZCylinder(x0=0, y0=0, R=0.45720, name='Clad OR')
|
||||
left = openmc.XPlane(x0=-0.63, name='left')
|
||||
right = openmc.XPlane(x0=0.63, name='right')
|
||||
bottom = openmc.YPlane(y0=-0.63, name='bottom')
|
||||
top = openmc.YPlane(y0=0.63, name='top')
|
||||
|
||||
left.boundary_type = 'reflective'
|
||||
right.boundary_type = 'reflective'
|
||||
top.boundary_type = 'reflective'
|
||||
bottom.boundary_type = 'reflective'
|
||||
|
||||
# Instantiate Cells
|
||||
fuel_pin = openmc.Cell(name='cell 1')
|
||||
cladding = openmc.Cell(name='cell 3')
|
||||
water = openmc.Cell(name='cell 2')
|
||||
|
||||
# Use surface half-spaces to define regions
|
||||
fuel_pin.region = -fuel_or
|
||||
cladding.region = +fuel_or & -clad_or
|
||||
water.region = +clad_or & +left & -right & +bottom & -top
|
||||
|
||||
# Register Materials with Cells
|
||||
fuel_pin.fill = fuel
|
||||
cladding.fill = clad
|
||||
water.fill = hot_water
|
||||
|
||||
# Instantiate Universe
|
||||
root = openmc.Universe(universe_id=0, name='root universe')
|
||||
|
||||
# Register Cells with Universe
|
||||
root.add_cells([fuel_pin, cladding, water])
|
||||
|
||||
# Instantiate a Geometry, register the root Universe, and export to XML
|
||||
self._input_set.geometry.root_universe = root
|
||||
|
||||
|
||||
mat_filter = openmc.MaterialFilter((fuel.id,))
|
||||
flux_tally = openmc.Tally()
|
||||
flux_tally.filters = [mat_filter]
|
||||
flux_tally.scores = ['flux']
|
||||
|
||||
self._input_set.tallies = openmc.Tallies()
|
||||
self._input_set.tallies += [flux_tally]
|
||||
self._input_set.build_default_settings()
|
||||
self._input_set.export()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
harness = EnrichmentTestHarness('statepoint.10.*', True)
|
||||
harness.main()
|
||||
|
|
@ -339,6 +339,7 @@ class PyAPITestHarness(TestHarness):
|
|||
output = [os.path.join(os.getcwd(), 'materials.xml')]
|
||||
output.append(os.path.join(os.getcwd(), 'geometry.xml'))
|
||||
output.append(os.path.join(os.getcwd(), 'settings.xml'))
|
||||
output.append(os.path.join(os.getcwd(), 'tallies.xml'))
|
||||
output.append(os.path.join(os.getcwd(), 'inputs_test.dat'))
|
||||
output.append(os.path.join(os.getcwd(), 'summary.h5'))
|
||||
for f in output:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue