mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 22:26:08 -04:00
Fixed merge conflicts with develop
This commit is contained in:
commit
4e6aca6028
331 changed files with 6506 additions and 222815 deletions
|
|
@ -1,10 +1,14 @@
|
|||
from collections import MappingView
|
||||
from collections import Iterable
|
||||
from copy import deepcopy
|
||||
from numbers import Real, Integral
|
||||
import warnings
|
||||
from xml.etree import ElementTree as ET
|
||||
import sys
|
||||
if sys.version_info[0] >= 3:
|
||||
basestring = str
|
||||
|
||||
import openmc
|
||||
from openmc.checkvalue import *
|
||||
from openmc.checkvalue import check_type, check_value, check_greater_than
|
||||
from openmc.clean_xml import *
|
||||
|
||||
|
||||
|
|
@ -14,6 +18,7 @@ MATERIAL_IDS = []
|
|||
# A static variable for auto-generated Material IDs
|
||||
AUTO_MATERIAL_ID = 10000
|
||||
|
||||
|
||||
def reset_auto_material_id():
|
||||
global AUTO_MATERIAL_ID, MATERIAL_IDS
|
||||
AUTO_MATERIAL_ID = 10000
|
||||
|
|
@ -23,20 +28,36 @@ def reset_auto_material_id():
|
|||
# Units for density supported by OpenMC
|
||||
DENSITY_UNITS = ['g/cm3', 'g/cc', 'kg/cm3', 'at/b-cm', 'at/cm3', 'sum']
|
||||
|
||||
# ENDF temperatures
|
||||
ENDF_TEMPS = np.array([300, 600, 700, 900, 1200, 1500])
|
||||
|
||||
# ENDF ZAIDs
|
||||
ENDF_ZAIDS = np.array(['70c', '71c', '72c', '73c', '74c'])
|
||||
|
||||
# Constant for density when not needed
|
||||
NO_DENSITY = 99999.
|
||||
|
||||
|
||||
class Material(object):
|
||||
"""A material composed of a collection of nuclides/elements that can be assigned
|
||||
to a region of space.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
material_id : int, optional
|
||||
Unique identifier for the material. If not specified, an identifier will
|
||||
automatically be assigned.
|
||||
name : str, optional
|
||||
Name of the material. If not specified, the name will be the empty
|
||||
string.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id : int
|
||||
Unique identifier for the material
|
||||
density : float
|
||||
Density of the material (units defined separately)
|
||||
density_units : str
|
||||
Units used for `density`. Can be one of 'g/cm3', 'g/cc', 'kg/cm3',
|
||||
'atom/b-cm', 'atom/cm3', or 'sum'.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, material_id=None, name=''):
|
||||
|
||||
# Initialize class attributes
|
||||
self.id = material_id
|
||||
self.name = name
|
||||
|
|
@ -62,95 +83,75 @@ class Material(object):
|
|||
# If specified, this file will be used instead of composition values
|
||||
self._distrib_otf_file = None
|
||||
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
|
||||
@property
|
||||
def density(self):
|
||||
return self._density
|
||||
|
||||
|
||||
@property
|
||||
def density_units(self):
|
||||
return self._density_units
|
||||
|
||||
|
||||
@property
|
||||
def convert_to_distrib_comps(self):
|
||||
return self._convert_to_distrib_comps
|
||||
|
||||
|
||||
@property
|
||||
def distrib_otf_file(self):
|
||||
return self._distrib_otf_file
|
||||
|
||||
|
||||
@id.setter
|
||||
def id(self, material_id):
|
||||
|
||||
global AUTO_MATERIAL_ID, MATERIAL_IDS
|
||||
|
||||
# If the Material already has an ID, remove it from global list
|
||||
if hasattr(self, '_id') and not self._id is None:
|
||||
if hasattr(self, '_id') and self._id is not None:
|
||||
MATERIAL_IDS.remove(self._id)
|
||||
|
||||
if material_id is None:
|
||||
self._id = AUTO_MATERIAL_ID
|
||||
MATERIAL_IDS.append(AUTO_MATERIAL_ID)
|
||||
AUTO_MATERIAL_ID += 1
|
||||
|
||||
# Check that the ID is an integer and wasn't already used
|
||||
elif not is_integer(material_id):
|
||||
msg = 'Unable to set a non-integer Material ' \
|
||||
'ID {0}'.format(material_id)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif material_id in MATERIAL_IDS:
|
||||
msg = 'Unable to set Material ID to {0} since a Material with ' \
|
||||
'this ID was already initialized'.format(material_id)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif material_id < 0:
|
||||
msg = 'Unable to set Material ID to {0} since it must be a ' \
|
||||
'non-negative integer'.format(material_id)
|
||||
raise ValueError(msg)
|
||||
|
||||
else:
|
||||
check_type('material ID', material_id, Integral)
|
||||
if material_id in MATERIAL_IDS:
|
||||
msg = 'Unable to set Material ID to {0} since a Material with ' \
|
||||
'this ID was already initialized'.format(material_id)
|
||||
raise ValueError(msg)
|
||||
check_greater_than('material ID', material_id, 0)
|
||||
|
||||
self._id = material_id
|
||||
MATERIAL_IDS.append(material_id)
|
||||
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
|
||||
if not is_string(name):
|
||||
msg = 'Unable to set name for Material ID={0} with a non-string ' \
|
||||
'value {1}'.format(self._id, name)
|
||||
raise ValueError(msg)
|
||||
|
||||
else:
|
||||
self._name = name
|
||||
|
||||
check_type('name for Material ID={0}'.format(self._id),
|
||||
name, basestring)
|
||||
self._name = name
|
||||
|
||||
def set_density(self, units, density=NO_DENSITY):
|
||||
"""Set the density of the material
|
||||
|
||||
if not is_float(density):
|
||||
msg = 'Unable to set the density for Material ID={0} to a ' \
|
||||
'non-floating point value {1}'.format(self._id, density)
|
||||
raise ValueError(msg)
|
||||
Parameters
|
||||
----------
|
||||
units : str
|
||||
Physical units of density
|
||||
density : float, optional
|
||||
Value of the density. Must be specified unless units is given as
|
||||
'sum'.
|
||||
|
||||
elif not units in DENSITY_UNITS:
|
||||
msg = 'Unable to set the density for Material ID={0} with ' \
|
||||
'units {1}'.format(self._id, units)
|
||||
raise ValueError(msg)
|
||||
"""
|
||||
|
||||
check_type('the density for Material ID={0}'.format(self._id),
|
||||
density, Real)
|
||||
check_value('density units', units, DENSITY_UNITS)
|
||||
|
||||
if density == NO_DENSITY and units is not 'sum':
|
||||
msg = 'Unable to set the density Material ID={0} ' \
|
||||
|
|
@ -161,45 +162,52 @@ class Material(object):
|
|||
self._density = density
|
||||
self._density_units = units
|
||||
|
||||
|
||||
@distrib_otf_file.setter
|
||||
def distrib_otf_file(self, filename):
|
||||
|
||||
# TODO: remove this when distributed materials are merged
|
||||
warnings.warn('This feature is not yet implemented in a release ' \
|
||||
warnings.warn('This feature is not yet implemented in a release '
|
||||
'version of openmc')
|
||||
|
||||
if not is_string(filename) and not filename is None:
|
||||
if not isinstance(filename, basestring) 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)
|
||||
|
||||
self._distrib_otf_file = filename
|
||||
|
||||
|
||||
@convert_to_distrib_comps.setter
|
||||
def convert_to_distrib_comps(self):
|
||||
|
||||
# TODO: remove this when distributed materials are merged
|
||||
warnings.warn('This feature is not yet implemented in a release ' \
|
||||
warnings.warn('This feature is not yet implemented in a release '
|
||||
'version of openmc')
|
||||
|
||||
self._convert_to_distrib_comps = True
|
||||
|
||||
|
||||
def add_nuclide(self, nuclide, percent, percent_type='ao'):
|
||||
"""Add a nuclide to the material
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclide : str or openmc.nuclide.Nuclide
|
||||
Nuclide to add
|
||||
percent : float
|
||||
Atom or weight percent
|
||||
percent_type : str
|
||||
'ao' for atom percent and 'wo' for weight percent
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(nuclide, (openmc.Nuclide, str)):
|
||||
msg = 'Unable to add a Nuclide to Material ID={0} with a ' \
|
||||
'non-Nuclide value {1}'.format(self._id, nuclide)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif not is_float(percent):
|
||||
elif not isinstance(percent, Real):
|
||||
msg = 'Unable to add a Nuclide to Material ID={0} with a ' \
|
||||
'non-floating point value {1}'.format(self._id, percent)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif not percent_type in ['ao', 'wo', 'at/g-cm']:
|
||||
elif percent_type not in ['ao', 'wo', 'at/g-cm']:
|
||||
msg = 'Unable to add a Nuclide to Material ID={0} with a ' \
|
||||
'percent type {1}'.format(self._id, percent_type)
|
||||
raise ValueError(msg)
|
||||
|
|
@ -213,8 +221,15 @@ class Material(object):
|
|||
|
||||
self._nuclides[nuclide._name] = (nuclide, percent, percent_type)
|
||||
|
||||
|
||||
def remove_nuclide(self, nuclide):
|
||||
"""Remove a nuclide from the material
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuclide : openmc.nuclide.Nuclide
|
||||
Nuclide to remove
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(nuclide, openmc.Nuclide):
|
||||
msg = 'Unable to remove a Nuclide {0} in Material ID={1} ' \
|
||||
|
|
@ -225,20 +240,31 @@ class Material(object):
|
|||
if nuclide._name in self._nuclides:
|
||||
del self._nuclides[nuclide._name]
|
||||
|
||||
|
||||
def add_element(self, element, percent, percent_type='ao'):
|
||||
"""Add a natural element to the material
|
||||
|
||||
Parameters
|
||||
----------
|
||||
element : openmc.element.Element
|
||||
Element to add
|
||||
percent : float
|
||||
Atom or weight percent
|
||||
percent_type : str
|
||||
'ao' for atom percent and 'wo' for weight percent
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(element, openmc.Element):
|
||||
msg = 'Unable to add an Element to Material ID={0} with a ' \
|
||||
'non-Element value {1}'.format(self._id, element)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not is_float(percent):
|
||||
if not isinstance(percent, Real):
|
||||
msg = 'Unable to add an Element to Material ID={0} with a ' \
|
||||
'non-floating point value {1}'.format(self._id, percent)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not percent_type in ['ao', 'wo']:
|
||||
if percent_type not in ['ao', 'wo']:
|
||||
msg = 'Unable to add an Element to Material ID={0} with a ' \
|
||||
'percent type {1}'.format(self._id, percent_type)
|
||||
raise ValueError(msg)
|
||||
|
|
@ -248,36 +274,58 @@ class Material(object):
|
|||
|
||||
self._elements[element._name] = (element, percent, percent_type)
|
||||
|
||||
|
||||
def remove_element(self, element):
|
||||
"""Remove a natural element from the material
|
||||
|
||||
Parameters
|
||||
----------
|
||||
element : openmc.element.Element
|
||||
Element to remove
|
||||
|
||||
"""
|
||||
|
||||
# If the Material contains the Element, delete it
|
||||
if element._name in self._elements:
|
||||
del self._elements[element._name]
|
||||
|
||||
|
||||
def add_s_alpha_beta(self, name, xs):
|
||||
r"""Add an :math:`S(\alpha,\beta)` table to the material
|
||||
|
||||
if not is_string(name):
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of the :math:`S(\alpha,\beta)` table
|
||||
xs : str
|
||||
Cross section identifier, e.g. '71t'
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(name, basestring):
|
||||
msg = 'Unable to add an S(a,b) table to Material ID={0} with a ' \
|
||||
'non-string table name {1}'.format(self._id, name)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not is_string(xs):
|
||||
if not isinstance(xs, basestring):
|
||||
msg = 'Unable to add an S(a,b) table to Material ID={0} with a ' \
|
||||
'non-string cross-section identifier {1}'.format(self._id, xs)
|
||||
raise ValueError(msg)
|
||||
|
||||
self._sab.append((name, xs))
|
||||
|
||||
|
||||
def make_isotropic_in_lab(self):
|
||||
|
||||
for nuclide_name in self._nuclides:
|
||||
self._nuclides[nuclide_name][0].make_isotropic_in_lab()
|
||||
|
||||
|
||||
def get_all_nuclides(self):
|
||||
"""Returns all nuclides in the material
|
||||
|
||||
Returns
|
||||
-------
|
||||
nuclides : dict
|
||||
Dictionary whose keys are nuclide names and values are 2-tuples of
|
||||
(nuclide, density)
|
||||
|
||||
"""
|
||||
|
||||
nuclides = {}
|
||||
|
||||
|
|
@ -288,9 +336,7 @@ class Material(object):
|
|||
|
||||
return nuclides
|
||||
|
||||
|
||||
def _repr__(self):
|
||||
|
||||
string = 'Material\n'
|
||||
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
|
||||
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
|
||||
|
|
@ -322,9 +368,7 @@ class Material(object):
|
|||
|
||||
return string
|
||||
|
||||
|
||||
def get_nuclide_xml(self, nuclide, distrib=False):
|
||||
|
||||
def _get_nuclide_xml(self, nuclide, distrib=False):
|
||||
xml_element = ET.Element("nuclide")
|
||||
xml_element.set("name", nuclide[0]._name)
|
||||
|
||||
|
|
@ -334,7 +378,7 @@ class Material(object):
|
|||
else:
|
||||
xml_element.set("wo", str(nuclide[1]))
|
||||
|
||||
if not nuclide[0]._xs is None:
|
||||
if nuclide[0]._xs is not None:
|
||||
xml_element.set("xs", nuclide[0]._xs)
|
||||
|
||||
if not nuclide[0]._scattering is None:
|
||||
|
|
@ -342,9 +386,7 @@ class Material(object):
|
|||
|
||||
return xml_element
|
||||
|
||||
|
||||
def get_element_xml(self, element, distrib=False):
|
||||
|
||||
def _get_element_xml(self, element, distrib=False):
|
||||
xml_element = ET.Element("element")
|
||||
xml_element.set("name", str(element[0]._name))
|
||||
|
||||
|
|
@ -356,28 +398,31 @@ class Material(object):
|
|||
|
||||
return xml_element
|
||||
|
||||
|
||||
def get_nuclides_xml(self, nuclides, distrib=False):
|
||||
|
||||
def _get_nuclides_xml(self, nuclides, distrib=False):
|
||||
xml_elements = []
|
||||
|
||||
for nuclide in nuclides.values():
|
||||
xml_elements.append(self.get_nuclide_xml(nuclide, distrib))
|
||||
xml_elements.append(self._get_nuclide_xml(nuclide, distrib))
|
||||
|
||||
return xml_elements
|
||||
|
||||
|
||||
def get_elements_xml(self, elements, distrib=False):
|
||||
|
||||
def _get_elements_xml(self, elements, distrib=False):
|
||||
xml_elements = []
|
||||
|
||||
for element in elements.values():
|
||||
xml_elements.append(self.get_element_xml(element, distrib))
|
||||
xml_elements.append(self._get_element_xml(element, distrib))
|
||||
|
||||
return xml_elements
|
||||
|
||||
|
||||
def get_material_xml(self):
|
||||
"""Return XML representation of the material
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing material data
|
||||
|
||||
"""
|
||||
|
||||
# Create Material XML element
|
||||
element = ET.Element("material")
|
||||
|
|
@ -393,19 +438,17 @@ class Material(object):
|
|||
subelement.set("units", self._density_units)
|
||||
|
||||
if not self._convert_to_distrib_comps:
|
||||
|
||||
# Create nuclide XML subelements
|
||||
subelements = self.get_nuclides_xml(self._nuclides)
|
||||
subelements = self._get_nuclides_xml(self._nuclides)
|
||||
for subelement in subelements:
|
||||
element.append(subelement)
|
||||
|
||||
# Create element XML subelements
|
||||
subelements = self.get_elements_xml(self._elements)
|
||||
subelements = self._get_elements_xml(self._elements)
|
||||
for subelement in subelements:
|
||||
element.append(subelement)
|
||||
|
||||
else:
|
||||
|
||||
subelement = ET.SubElement(element, "compositions")
|
||||
|
||||
comps = []
|
||||
|
|
@ -418,29 +461,24 @@ class Material(object):
|
|||
raise ValueError(msg)
|
||||
comps.append(per)
|
||||
|
||||
|
||||
if self._distrib_otf_file is None:
|
||||
|
||||
# Create values and units subelements
|
||||
subsubelement = ET.SubElement(subelement, "values")
|
||||
subsubelement.text = ' '.join([str(c) for c in comps])
|
||||
subsubelement = ET.SubElement(subelement, "units")
|
||||
subsubelement.text = dist_per_type
|
||||
|
||||
else:
|
||||
|
||||
# Specify the materials file
|
||||
subsubelement = ET.SubElement(subelement, "otf_file_path")
|
||||
subsubelement.text = self._distrib_otf_file
|
||||
|
||||
|
||||
# Create nuclide XML subelements
|
||||
subelements = self.get_nuclides_xml(self._nuclides, distrib=True)
|
||||
for subelement_nuc in subelements:
|
||||
subelement.append(subelement_nuc)
|
||||
|
||||
# Create element XML subelements
|
||||
subelements = self.get_elements_xml(self._elements, distrib=True)
|
||||
subelements = self._get_elements_xml(self._elements, distrib=True)
|
||||
for subelement_ele in subelements:
|
||||
subelement.append(subelement_ele)
|
||||
|
||||
|
|
@ -454,31 +492,41 @@ class Material(object):
|
|||
|
||||
|
||||
class MaterialsFile(object):
|
||||
"""Materials file used for an OpenMC simulation. Corresponds directly to the
|
||||
materials.xml input file.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
default_xs : str
|
||||
The default cross section identifier applied to a nuclide when none is
|
||||
specified
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# Initialize MaterialsFile class attributes
|
||||
self._materials = []
|
||||
self._default_xs = None
|
||||
self._materials_file = ET.Element("materials")
|
||||
|
||||
|
||||
@property
|
||||
def default_xs(self):
|
||||
return self._default_xs
|
||||
|
||||
|
||||
@default_xs.setter
|
||||
def default_xs(self, xs):
|
||||
|
||||
if not is_string(xs):
|
||||
msg = 'Unable to set default xs to a non-string value'.format(xs)
|
||||
raise ValueError(msg)
|
||||
|
||||
check_type('default xs', xs, basestring)
|
||||
self._default_xs = xs
|
||||
|
||||
|
||||
def add_material(self, material):
|
||||
"""Add a material to the file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
material : Material
|
||||
Material to add
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(material, Material):
|
||||
msg = 'Unable to add a non-Material {0} to the ' \
|
||||
|
|
@ -487,19 +535,33 @@ class MaterialsFile(object):
|
|||
|
||||
self._materials.append(material)
|
||||
|
||||
|
||||
def add_materials(self, materials):
|
||||
"""Add multiple materials to the file.
|
||||
|
||||
if not isinstance(materials, (tuple, list, MappingView)):
|
||||
Parameters
|
||||
----------
|
||||
materials : tuple or list of Material
|
||||
Materials to add
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(materials, Iterable):
|
||||
msg = 'Unable to create OpenMC materials.xml file from {0} which ' \
|
||||
'is not a Python tuple/list'.format(materials)
|
||||
'is not iterable'.format(materials)
|
||||
raise ValueError(msg)
|
||||
|
||||
for material in materials:
|
||||
self.add_material(material)
|
||||
|
||||
def remove_material(self, material):
|
||||
"""Remove a material from the file
|
||||
|
||||
def remove_materials(self, material):
|
||||
Parameters
|
||||
----------
|
||||
material : Material
|
||||
Material to remove
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(material, Material):
|
||||
msg = 'Unable to remove a non-Material {0} from the ' \
|
||||
|
|
@ -509,26 +571,25 @@ class MaterialsFile(object):
|
|||
self._materials.remove(material)
|
||||
|
||||
def make_isotropic_in_lab(self):
|
||||
|
||||
for material in self._materials:
|
||||
materials.make_isotropic_in_lab()
|
||||
|
||||
|
||||
def create_material_subelements(self):
|
||||
|
||||
def _create_material_subelements(self):
|
||||
subelement = ET.SubElement(self._materials_file, "default_xs")
|
||||
|
||||
if not self._default_xs is None:
|
||||
if self._default_xs is not None:
|
||||
subelement.text = self._default_xs
|
||||
|
||||
for material in self._materials:
|
||||
xml_element = material.get_material_xml()
|
||||
self._materials_file.append(xml_element)
|
||||
|
||||
|
||||
def export_to_xml(self):
|
||||
"""Create a materials.xml file that can be used for a simulation.
|
||||
|
||||
self.create_material_subelements()
|
||||
"""
|
||||
|
||||
self._create_material_subelements()
|
||||
|
||||
# Clean the indentation in the file to be user-readable
|
||||
sort_xml_elements(self._materials_file)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue