added enrichment and wo element support in Python API and added new tests

This commit is contained in:
Sam Shaner 2016-10-16 22:09:25 -04:00
parent d579440b02
commit 663636ce2e
12 changed files with 278 additions and 46 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1 @@
7db97c5303ca1f0beaba1b610c0d57cede072af2200b7494515caeca7f0ca43b1e1dbda1968f4f052af6c632cbba8c92203cd53670c19350c55d2339d28b2fb2

View file

@ -0,0 +1,5 @@
k-combined:
8.863826E-01 5.114399E-02
tally 1:
7.906207E+01
1.251720E+03

View 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()

View file

@ -0,0 +1 @@
9284e022d343431db161faee9290658addde59427a4e42adbdfd5069cd6bd54929bedf87ccb2fb369c91ca52c2c572e8825c4997b662ca2ac34005bf4c58ba83

View file

@ -0,0 +1,5 @@
k-combined:
1.457508E+00 5.577598E-02
tally 1:
6.199709E+01
7.692381E+02

View 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()

View file

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