Adding docstrings and updated the element, executor, filter, geometry, material,

mesh, and nuclide modules.
This commit is contained in:
Paul Romano 2015-06-01 22:22:53 +07:00
parent f41d7d9b45
commit 7ed1dd17e6
14 changed files with 401 additions and 275 deletions

View file

@ -6,4 +6,3 @@ Element
.. automodule:: openmc.element
:members:
:undoc-members:

View file

@ -6,4 +6,3 @@ Executor
.. automodule:: openmc.executor
:members:
:undoc-members:

View file

@ -6,4 +6,3 @@ Filter
.. automodule:: openmc.filter
:members:
:undoc-members:

View file

@ -6,4 +6,3 @@ Geometry
.. automodule:: openmc.geometry
:members:
:undoc-members:

View file

@ -6,4 +6,3 @@ Materials
.. automodule:: openmc.material
:members:
:undoc-members:

View file

@ -6,4 +6,3 @@ Mesh
.. automodule:: openmc.mesh
:members:
:undoc-members:

View file

@ -6,4 +6,3 @@ Nuclide
.. automodule:: openmc.nuclide
:members:
:undoc-members:

View file

@ -1,58 +1,63 @@
from openmc.checkvalue import *
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
abundances.
Parameters
----------
name : str
Chemical symbol of the element, e.g. Pu
xs : str
Cross section identifier, e.g. 71c
Attributes
----------
name : str
Chemical symbol of the element, e.g. Pu
xs : str
Cross section identifier, e.g. 71c
"""
def __init__(self, name='', xs=None):
# Initialize class attributes
self._name = ''
self._xs = None
# Set the Material class attributes
# Set class attributes
self.name = name
if not xs is None:
self.xs = xs
def __eq__(self, element2):
# Check type
if not isinstance(element2, Element):
return False
# Check name
# Check name and xs
if self._name != element2._name:
return False
# Check xs
elif self._xs != element2._xs:
return False
else:
return True
def __hash__(self):
hashable = []
hashable.append(self._name)
hashable.append(self._xs)
return hash(tuple(hashable))
return hash((self._name, self._xs))
@property
def xs(self):
return self._xs
@property
def name(self):
return self._name
@xs.setter
def xs(self, xs):
if not is_string(xs):
msg = 'Unable to set cross-section identifier xs for Element ' \
'with a non-string value {0}'.format(xs)
@ -60,10 +65,8 @@ class Element(object):
self._xs = xs
@name.setter
def name(self, name):
if not is_string(name):
msg = 'Unable to set name for Element with a non-string ' \
'value {0}'.format(name)
@ -71,9 +74,7 @@ class Element(object):
self._name = name
def __repr__(self):
string = 'Element - {0}\n'.format(self._name)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
return string

View file

@ -1,3 +1,4 @@
from __future__ import print_function
import subprocess
import os
@ -5,37 +6,39 @@ from openmc.checkvalue import *
class Executor(object):
"""Control execution of OpenMC
Attributes
----------
working_directory : str
Path to working directory to run in
"""
def __init__(self):
self._working_directory = '.'
def _run_openmc(self, command, output):
# Launch a subprocess to run OpenMC
p = subprocess.Popen(command, shell=True,
p = subprocess.Popen(command, shell=True,
cwd=self._working_directory,
stdout=subprocess.PIPE)
# Capture and re-print OpenMC output in real-time
while (True and output):
line = p.stdout.readline()
print(line),
print(line, end='')
# If OpenMC is finished, break loop
if line == '' and p.poll() != None:
break
@property
def working_directory(self):
return self._working_directory
@working_directory.setter
def working_directory(self, working_directory):
if not is_string(working_directory):
msg = 'Unable to set Executor\'s working directory to {0} ' \
'since it is not a string'.format(working_directory)
@ -48,14 +51,34 @@ class Executor(object):
self._working_directory = working_directory
def plot_geometry(self, output=True):
self._run_openmc('openmc -p', output)
"""Run OpenMC in plotting mode"""
self._run_openmc('openmc -p', output)
def run_simulation(self, particles=None, threads=None,
geometry_debug=False, restart_file=None,
tracks=False, mpi_procs=1, output=True):
"""Run an OpenMC simulation.
Parameters
----------
particles : int
Number of particles to simulate per generation
threads : int
Number of OpenMP threads
geometry_debug : bool
Turn on geometry debugging during simulation
restart_file : str
Path to restart file to use
tracks : bool
Write tracks for all particles
mpi_procs : int
Number of MPI processes
output : bool
Capture OpenMC output from standard out
"""
post_args = ' '
pre_args = ''

View file

@ -6,10 +6,30 @@ from openmc.constants import *
class Filter(object):
"""A filter used to constrain a tally to a specific criterion, e.g. only tally
events when the particle is in a certain cell and energy range.
Parameters
----------
type : str
The type of the tally filter. Acceptable values are "universe",
"material", "cell", "cellborn", "surface", "mesh", "energy",
"energyout", and "distribcell".
bins : int or list of int or list of float or ndarray
The bins for the filter. This takes on different meaning for different
filters.
Attributes
----------
type : str
The type of the tally filter.
bins : int or list of int or list of float or ndarray
The bins for the filter
"""
# Initialize Filter class attributes
def __init__(self, type=None, bins=None):
self.type = type
self._num_bins = 0
self.bins = bins
@ -17,9 +37,7 @@ class Filter(object):
self._offset = -1
self._stride = None
def __eq__(self, filter2):
# Check type
if self._type != filter2._type:
return False
@ -37,19 +55,14 @@ class Filter(object):
def __hash__(self):
hashable = []
hashable.append(self._type)
hashable.append(self._bins)
return hash(tuple(hashable))
return hash((self._type, self._bins))
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._type = self._type
clone._bins = copy.deepcopy(self._bins, memo)
@ -66,43 +79,34 @@ class Filter(object):
else:
return existing
@property
def type(self):
return self._type
@property
def bins(self):
return self._bins
@property
def num_bins(self):
return self._num_bins
@property
def mesh(self):
return self._mesh
@property
def offset(self):
return self._offset
@property
def stride(self):
return self._stride
@type.setter
def type(self, type):
if type is None:
self._type = type
elif not type in FILTER_TYPES.values():
msg = 'Unable to set Filter type to "{0}" since it is not one ' \
'of the supported types'.format(type)
@ -110,13 +114,10 @@ class Filter(object):
self._type = type
@bins.setter
def bins(self, bins):
if bins is None:
self.num_bins = 0
elif self._type is None:
msg = 'Unable to set bins for Filter to "{0}" since ' \
'the Filter type has not yet been set'.format(bins)
@ -132,30 +133,23 @@ class Filter(object):
if self._type in ['cell', 'cellborn', 'surface', 'material',
'universe', 'distribcell']:
for edge in bins:
if not is_integer(edge):
msg = 'Unable to add bin "{0}" to a {1} Filter since ' \
'it is a non-integer'.format(edge, self._type)
raise ValueError(msg)
elif edge < 0:
msg = 'Unable to add bin "{0}" to a {1} Filter since ' \
'it is a negative integer'.format(edge, self._type)
raise ValueError(msg)
elif self._type in ['energy', 'energyout']:
for edge in bins:
if not is_integer(edge) and not is_float(edge):
msg = 'Unable to add bin edge "{0}" to a {1} Filter ' \
'since it is a non-integer or floating point ' \
'value'.format(edge, self._type)
raise ValueError(msg)
elif edge < 0.:
msg = 'Unable to add bin edge "{0}" to a {1} Filter ' \
'since it is a negative value'.format(edge, self._type)
@ -163,27 +157,22 @@ class Filter(object):
# Check that bin edges are monotonically increasing
for index in range(len(bins)):
if index > 0 and bins[index] < bins[index-1]:
msg = 'Unable to add bin edges "{0}" to a {1} Filter ' \
'since they are not monotonically ' \
'increasing'.format(bins, self._type)
raise ValueError(msg)
# mesh filters
elif self._type == 'mesh':
if not len(bins) == 1:
msg = 'Unable to add bins "{0}" to a mesh Filter since ' \
'only a single mesh can be used per tally'.format(bins)
raise ValueError(msg)
elif not is_integer(bins[0]):
msg = 'Unable to add bin "{0}" to mesh Filter since it ' \
'is a non-integer'.format(bins[0])
raise ValueError(msg)
elif bins[0] < 0:
msg = 'Unable to add bin "{0}" to mesh Filter since it ' \
'is a negative integer'.format(bins[0])
@ -192,11 +181,9 @@ class Filter(object):
# If all error checks passed, add bin edges
self._bins = bins
# FIXME
@num_bins.setter
def num_bins(self, num_bins):
if not is_integer(num_bins) or num_bins < 0:
msg = 'Unable to set the number of bins "{0}" for a {1} Filter ' \
'since it is not a positive ' \
@ -205,10 +192,8 @@ class Filter(object):
self._num_bins = num_bins
@mesh.setter
def mesh(self, mesh):
if not isinstance(mesh, Mesh):
msg = 'Unable to set Mesh to "{0}" for Filter since it is not a ' \
'Mesh object'.format(mesh)
@ -218,10 +203,8 @@ class Filter(object):
self.type = 'mesh'
self.bins = self._mesh._id
@offset.setter
def offset(self, offset):
if not is_integer(offset):
msg = 'Unable to set offset "{0}" for a {1} Filter since it is a ' \
'non-integer value'.format(offset, self._type)
@ -229,10 +212,8 @@ class Filter(object):
self._offset = offset
@stride.setter
def stride(self, stride):
if not is_integer(stride):
msg = 'Unable to set stride "{0}" for a {1} Filter since it is a ' \
'non-integer value'.format(stride, self._type)
@ -247,6 +228,19 @@ class Filter(object):
def can_merge(self, filter):
"""Determine if filter can be merged with another.
Parameters
----------
filter : Filter
Filter to compare with
Returns
-------
bool
Whether the filter can be merged
"""
if not isinstance(filter, Filter):
return False
@ -270,8 +264,20 @@ class Filter(object):
else:
return True
def merge(self, filter):
"""Merge this filter with another.
Parameters
----------
filter : Filter
Filter to merge with
Returns
-------
merged_filter : Filter
Filter resulting from the merge
"""
if not self.can_merge(filter):
msg = 'Unable to merge {0} with {1} filters'.format(self._type, filter._type)
@ -287,33 +293,32 @@ class Filter(object):
return merged_filter
def get_bin_index(self, filter_bin):
"""Returns the index in the Filter for some bin.
Parameters
----------
filter_bin : int, tuple
The bin is the integer ID for 'material', 'surface', 'cell',
'cellborn', and 'universe' Filters. The bin is an integer for
the cell instance ID for 'distribcell' Filters. The bin is
a 2-tuple of floats for 'energy' and 'energyout' filters
corresponding to the energy boundaries of the bin of interest.
The bin is a (x,y,z) 3-tuple for 'mesh' filters corresponding to
the mesh cell of interest.
filter_bin : int or tuple
The bin is the integer ID for 'material', 'surface', 'cell',
'cellborn', and 'universe' Filters. The bin is an integer for the
cell instance ID for 'distribcell' Filters. The bin is a 2-tuple of
floats for 'energy' and 'energyout' filters corresponding to the
energy boundaries of the bin of interest. The bin is a (x,y,z)
3-tuple for 'mesh' filters corresponding to the mesh cell of
interest.
Returns
-------
filter_index : int
The index in the Tally data array for this filter bin.
"""
"""
# FIXME: This does not work for distribcells!!!
try:
# Filter bins for a mesh are an (x,y,z) tuple
if self.type == 'mesh':
# Convert (x,y,z) to a single bin -- this is similar to
# subroutine mesh_indices_to_bin in openmc/src/mesh.F90.
if (len(self.mesh.dimension) == 3):
@ -350,9 +355,7 @@ class Filter(object):
return filter_index
def __repr__(self):
string = 'Filter\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type)
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', self._bins)

View file

@ -12,19 +12,24 @@ def reset_auto_ids():
class Geometry(object):
"""Geometry representing a collection of surfaces, cells, and universes.
Attributes
----------
root_universe : openmc.universe.Universe
Root universe which contains all others
"""
def __init__(self):
# Initialize Geometry class attributes
self._root_universe = None
self._offsets = {}
@property
def root_universe(self):
return self._root_universe
@root_universe.setter
def root_universe(self, root_universe):
@ -34,7 +39,7 @@ class Geometry(object):
raise ValueError(msg)
elif root_universe._id != 0:
msg = 'Unable to add root Universe {0} to Geometry since ' \
msg = 'Unable to add root Universe {0} to Geometry since ' \
'it has ID={1} instead of ' \
'ID=0'.format(root_universe, root_universe._id)
raise ValueError(msg)
@ -43,22 +48,25 @@ class Geometry(object):
def get_offset(self, path, filter_offset):
"""
Returns the corresponding location in the results array for a given
"""Returns the corresponding location in the results array for a given
path and filter number.
Parameters
----------
path : list
A list of IDs that form the path to the target. It should begin
with 0 for the base universe, and should cover every universe,
cell, and lattice passed through. For the case of the lattice,
a tuple should be provided to indicate which coordinates in the
lattice should be entered. This should be in the
form: (lat_id, i_x, i_y, i_z)
A list of IDs that form the path to the target. It should begin with
0 for the base universe, and should cover every universe, cell, and
lattice passed through. For the case of the lattice, a tuple should
be provided to indicate which coordinates in the lattice should be
entered. This should be in the form: (lat_id, i_x, i_y, i_z)
filter_offset : int
An integer that specifies which offset map the filter is using
An integer that specifies which offset map the filter is using
Returns
-------
offset : int
Location in the results array for the path and filter
"""
@ -74,16 +82,39 @@ class Geometry(object):
# Return the final offset
return offset
def get_all_cells(self):
"""Return all cells defined
Returns
-------
list of openmc.universe.Cell
Cells in the geometry
"""
return self._root_universe.get_all_cells()
def get_all_universes(self):
"""Return all universes defined
Returns
-------
list of openmc.universe.Universe
Universes in the geometry
"""
return self._root_universe.get_all_universes()
def get_all_nuclides(self):
"""Return all nuclides assigned to a material in the geometry
Returns
-------
list of openmc.nuclide.Nuclide
Nuclides in the geometry
"""
nuclides = {}
materials = self.get_all_materials()
@ -93,8 +124,15 @@ class Geometry(object):
return nuclides
def get_all_materials(self):
"""Return all materials assigned to a cell
Returns
-------
list of openmc.material.Material
Materials in the geometry
"""
material_cells = self.get_all_material_cells()
materials = set()
@ -104,9 +142,7 @@ class Geometry(object):
return list(materials)
def get_all_material_cells(self):
all_cells = self.get_all_cells()
material_cells = set()
@ -116,16 +152,21 @@ class Geometry(object):
return list(material_cells)
def get_all_material_universes(self):
"""Return all universes composed of at least one non-fill cell
Returns
-------
list of openmc.universe.Universe
Universes with non-fill cells
"""
all_universes = self.get_all_universes()
material_universes = set()
for universe_id, universe in all_universes.items():
cells = universe._cells
for cell_id, cell in cells.items():
if cell._type == 'normal':
material_universes.add(universe)
@ -133,24 +174,28 @@ class Geometry(object):
return list(material_universes)
class GeometryFile(object):
"""Geometry file used for an OpenMC simulation. Corresponds directly to the
geometry.xml input file.
Attributes
----------
geometry : Geometry
The geometry to be used
"""
def __init__(self):
# Initialize GeometryFile class attributes
self._geometry = None
self._geometry_file = ET.Element("geometry")
@property
def geometry(self):
return self._geometry
@geometry.setter
def geometry(self, geometry):
if not isinstance(geometry, Geometry):
msg = 'Unable to set the Geometry to {0} for the GeometryFile ' \
'since it is not a Geometry object'.format(geometry)
@ -158,8 +203,10 @@ class GeometryFile(object):
self._geometry = geometry
def export_to_xml(self):
"""Create a geometry.xml file that can be used for a simulation.
"""
root_universe = self._geometry._root_universe
root_universe.create_xml_subelement(self._geometry_file)

View file

@ -23,20 +23,34 @@ 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
Unique identifier for the material
name : str
Name of the material
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,40 +76,32 @@ 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
@ -127,20 +133,26 @@ class Material(object):
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
def set_density(self, units, density=NO_DENSITY):
"""Set the density of the material
Parameters
----------
units : str
Physical units of density
density : float, optional
Value of the density
"""
if not is_float(density):
msg = 'Unable to set the density for Material ID={0} to a ' \
@ -161,10 +173,8 @@ 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 ' \
'version of openmc')
@ -176,10 +186,8 @@ class Material(object):
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 ' \
'version of openmc')
@ -188,6 +196,18 @@ class Material(object):
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 ' \
@ -215,6 +235,14 @@ class Material(object):
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,8 +253,19 @@ 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 ' \
@ -250,6 +289,14 @@ class Material(object):
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:
@ -257,6 +304,16 @@ class Material(object):
def add_s_alpha_beta(self, name, xs):
r"""Add an :math:`S(\alpha,\beta)` table to the material
Parameters
----------
name : str
Name of the :math:`S(\alpha,\beta)` table
xs : str
Cross section identifier, e.g. '71t'
"""
if not is_string(name):
msg = 'Unable to add an S(a,b) table to Material ID={0} with a ' \
@ -272,6 +329,15 @@ class Material(object):
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 = {}
@ -282,9 +348,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)
@ -316,9 +380,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)
@ -333,9 +395,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))
@ -347,28 +407,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")
@ -384,19 +447,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 = []
@ -409,29 +470,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)
@ -445,31 +501,44 @@ 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)
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 ' \
@ -480,6 +549,14 @@ class MaterialsFile(object):
def add_materials(self, materials):
"""Add multiple materials to the file.
Parameters
----------
materials : tuple or list of Material
Materials to add
"""
if not isinstance(materials, (tuple, list, MappingView)):
msg = 'Unable to create OpenMC materials.xml file from {0} which ' \
@ -489,8 +566,15 @@ class MaterialsFile(object):
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 ' \
@ -499,9 +583,7 @@ class MaterialsFile(object):
self._materials.remove(material)
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:
@ -511,10 +593,12 @@ class MaterialsFile(object):
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)

View file

@ -14,9 +14,37 @@ def reset_auto_mesh_id():
class Mesh(object):
"""A structured Cartesian mesh in two or three dimensions
Parameters
----------
mesh_id : int
Unique identifier for the mesh
name : str
Name of the mesh
Attributes
----------
id : int
Unique identifier for the mesh
name : str
Name of the mesh
type : str
Type of the mesh
dimension : tuple or list or ndarray
The number of mesh cells in each direction.
lower_left : tuple or list or ndarray
The lower-left corner of the structured mesh. If only two coordinate are
given, it is assumed that the mesh is an x-y mesh.
upper_right : tuple or list or ndarray
The upper-right corner of the structrued mesh. If only two coordinate
are given, it is assumed that the mesh is an x-y mesh.
width : tuple or list or ndarray
The width of mesh cells in each direction.
"""
def __init__(self, mesh_id=None, name=''):
# Initialize Mesh class attributes
self.id = mesh_id
self.name = name
@ -26,9 +54,7 @@ class Mesh(object):
self._upper_right = None
self._width = None
def __eq__(self, mesh2):
# Check type
if self._type != mesh2._type:
return False
@ -49,14 +75,11 @@ class Mesh(object):
else:
return True
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._id = self._id
clone._name = self._name
@ -74,50 +97,40 @@ class Mesh(object):
else:
return existing
@property
def id(self):
return self._id
@property
def name(self):
return self._name
@property
def type(self):
return self._type
@property
def dimension(self):
return self._dimension
@property
def lower_left(self):
return self._lower_left
@property
def upper_right(self):
return self._upper_right
@property
def width(self):
return self._width
@property
def num_mesh_cells(self):
return np.prod(self._dimension)
@id.setter
def id(self, mesh_id):
if mesh_id is None:
global AUTO_MESH_ID
self._id = AUTO_MESH_ID
@ -136,27 +149,21 @@ class Mesh(object):
else:
self._id = mesh_id
@name.setter
def name(self, name):
if not is_string(name):
msg = 'Unable to set name for Mesh ID={0} with a non-string ' \
'value {1}'.format(self._id, name)
raise ValueError(msg)
else:
self._name = name
@type.setter
def type(self, type):
if not is_string(type):
msg = 'Unable to set Mesh ID={0} for type {1} which is not ' \
'a string'.format(self._id, type)
raise ValueError(msg)
elif not type in ['rectangular', 'hexagonal']:
msg = 'Unable to set Mesh ID={0} for type {1} which since ' \
'only rectangular and hexagonal meshes are ' \
@ -165,23 +172,19 @@ class Mesh(object):
self._type = type
@dimension.setter
def dimension(self, dimension):
if not isinstance(dimension, (tuple, list, np.ndarray)):
msg = 'Unable to set Mesh ID={0} with dimension {1} which is ' \
'not a Python list, tuple or NumPy ' \
'array'.format(self._id, dimension)
raise ValueError(msg)
elif len(dimension) != 2 and len(dimension) != 3:
msg = 'Unable to set Mesh ID={0} with dimension {1} since it ' \
'must include 2 or 3 dimensions'.format(self._id, dimension)
raise ValueError(msg)
for dim in dimension:
if not is_integer(dim):
msg = 'Unable to set Mesh ID={0} with dimension {1} which ' \
'is a non-integer'.format(self._id, dim)
@ -189,10 +192,8 @@ class Mesh(object):
self._dimension = dimension
@lower_left.setter
def lower_left(self, lower_left):
if not isinstance(lower_left, (tuple, list, np.ndarray)):
msg = 'Unable to set Mesh ID={0} with lower_left {1} which is ' \
'not a Python list, tuple or NumPy ' \
@ -205,7 +206,6 @@ class Mesh(object):
raise ValueError(msg)
for coord in lower_left:
if not is_integer(coord) and not is_float(coord):
msg = 'Unable to set Mesh ID={0} with lower_left {1} which ' \
'is neither neither an integer nor a floating point ' \
@ -214,10 +214,8 @@ class Mesh(object):
self._lower_left = lower_left
@upper_right.setter
def upper_right(self, upper_right):
if not isinstance(upper_right, (tuple, list, np.ndarray)):
msg = 'Unable to set Mesh ID={0} with upper_right {1} which ' \
'is not a Python list, tuple or NumPy ' \
@ -230,7 +228,6 @@ class Mesh(object):
raise ValueError(msg)
for coord in upper_right:
if not is_integer(coord) and not is_float(coord):
msg = 'Unable to set Mesh ID={0} with upper_right {1} which ' \
'is neither an integer nor a floating point ' \
@ -239,12 +236,9 @@ class Mesh(object):
self._upper_right = upper_right
@width.setter
def width(self, width):
if not width is None:
if not isinstance(width, (tuple, list, np.ndarray)):
msg = 'Unable to set Mesh ID={0} with width {1} which ' \
'is not a Python list, tuple or NumPy ' \
@ -257,7 +251,6 @@ class Mesh(object):
raise ValueError(msg)
for dim in width:
if not is_integer(dim) and not is_float(dim):
msg = 'Unable to set Mesh ID={0} with width {1} which is ' \
'neither an integer nor a floating point ' \
@ -266,9 +259,7 @@ class Mesh(object):
self._width = width
def __repr__(self):
string = 'Mesh\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
@ -279,53 +270,32 @@ class Mesh(object):
string += '{0: <16}{1}{2}\n'.format('\tPixels', '=\t', self._width)
return string
def get_mesh_xml(self):
"""Return XML representation of the mesh
Returns
-------
element : xml.etree.ElementTree.Element
XML element containing mesh data
"""
element = ET.Element("mesh")
element.set("id", str(self._id))
element.set("type", self._type)
if len(self._dimension) == 2:
subelement = ET.SubElement(element, "dimension")
subelement.text = '{0} {1}'.format(self._dimension[0],
self._dimension[1])
else:
subelement = ET.SubElement(element, "dimension")
subelement.text = '{0} {1} {2}'.format(self._dimension[0],
self._dimension[1],
self._dimension[2])
subelement = ET.SubElement(element, "dimension")
subelement.text = ' '.join(map(str, self._dimension))
if len(self._lower_left) == 2:
subelement = ET.SubElement(element, "lower_left")
subelement.text = '{0} {1}'.format(self._lower_left[0],
self._lower_left[1])
else:
subelement = ET.SubElement(element, "lower_left")
subelement.text = '{0} {1} {2}'.format(self._lower_left[0],
self._lower_left[1],
self._lower_left[2])
subelement = ET.SubElement(element, "lower_left")
subelement.text = ' '.join(map(str, self._lower_left))
if not self._upper_right is None:
if len(self._upper_right) == 2:
subelement = ET.SubElement(element, "upper_right")
subelement.text = '{0} {1}'.format(self._upper_right[0],
self._upper_right[1])
else:
subelement = ET.SubElement(element, "upper_right")
subelement.text = '{0} {1} {2}'.format(self._upper_right[0],
self._upper_right[1],
self._upper_right[2])
subelement = ET.SubElement(element, "upper_right")
subelement.text = ' '.join(map(str, self._upper_right))
if not self._width is None:
if len(self._width) == 2:
subelement = ET.SubElement(element, "width")
subelement.text = '{0} {1}'.format(self._width[0],
self._width[1])
else:
subelement = ET.SubElement(element, "width")
subelement.text = '{0} {1} {2}'.format(self._width[0],
self._width[1],
self._width[2])
subelement = ET.SubElement(element, "width")
subelement.text = ' '.join(map(str, self._width))
return element
return element

View file

@ -2,9 +2,28 @@ from openmc.checkvalue import *
class Nuclide(object):
"""A nuclide that can be used in a material.
Parameters
----------
name : str
Name of the nuclide, e.g. U-235
xs : str
Cross section identifier, e.g. 71c
Attributes
----------
name : str
Name of the nuclide, e.g. U-235
xs : str
Cross section identifier, e.g. 71c
zaid : int
1000*(atomic number) + mass number. As an example, the zaid of U-235
would be 92235.
"""
def __init__(self, name='', xs=None):
# Initialize class attributes
self._name = ''
self._xs = None
@ -16,9 +35,7 @@ class Nuclide(object):
if not xs is None:
self.xs = xs
def __eq__(self, nuclide2):
# Check type
if not isinstance(nuclide2, Nuclide):
return False
@ -34,29 +51,23 @@ class Nuclide(object):
else:
return True
def __hash__(self):
return hash((self._name, self._xs))
@property
def name(self):
return self._name
@property
def xs(self):
return self._xs
@property
def zaid(self):
return self._zaid
@name.setter
def name(self, name):
if not is_string(name):
msg = 'Unable to set name for Nuclide with a non-string ' \
'value {0}'.format(name)
@ -64,10 +75,8 @@ class Nuclide(object):
self._name = name
@xs.setter
def xs(self, xs):
if not is_string(xs):
msg = 'Unable to set cross-section identifier xs for Nuclide ' \
'with a non-string value {0}'.format(xs)
@ -75,10 +84,8 @@ class Nuclide(object):
self._xs = xs
@zaid.setter
def zaid(self, zaid):
if not is_integer(zaid):
msg = 'Unable to set zaid for Nuclide ' \
'with a non-integer {0}'.format(zaid)
@ -86,11 +93,9 @@ class Nuclide(object):
self._zaid = zaid
def __repr__(self):
string = 'Nuclide - {0}\n'.format(self._name)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
if self._zaid is not None:
string += '{0: <16}{1}{2}\n'.format('\tZAID', '=\t', self._zaid)
return string
return string