From c8e176a9f75763e4e4e10d79c82cef482eebe71f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 2 Jun 2015 19:22:33 +0700 Subject: [PATCH] Continue adding docstrings and updating modules. --- docs/source/pythonapi/opencg_compatible.rst | 1 - docs/source/pythonapi/particle_restart.rst | 1 - docs/source/pythonapi/plots.rst | 1 - docs/source/pythonapi/settings.rst | 1 - docs/source/pythonapi/statepoint.rst | 1 - docs/source/pythonapi/summary.rst | 1 - docs/source/pythonapi/surface.rst | 1 - docs/source/pythonapi/tallies.rst | 1 - docs/source/pythonapi/trigger.rst | 1 - docs/source/pythonapi/universe.rst | 1 - docs/source/usersguide/input.rst | 12 +- openmc/opencg_compatible.py | 207 +++++- openmc/particle_restart.py | 39 +- openmc/plots.py | 172 ++--- openmc/settings.py | 759 +++++++++----------- openmc/statepoint.py | 148 ++-- openmc/summary.py | 48 +- 17 files changed, 760 insertions(+), 635 deletions(-) diff --git a/docs/source/pythonapi/opencg_compatible.rst b/docs/source/pythonapi/opencg_compatible.rst index 86cb03433..c807e19cc 100644 --- a/docs/source/pythonapi/opencg_compatible.rst +++ b/docs/source/pythonapi/opencg_compatible.rst @@ -6,4 +6,3 @@ OpenCG Compatibility .. automodule:: openmc.opencg_compatible :members: - :undoc-members: diff --git a/docs/source/pythonapi/particle_restart.rst b/docs/source/pythonapi/particle_restart.rst index 2965319b0..66ed89988 100644 --- a/docs/source/pythonapi/particle_restart.rst +++ b/docs/source/pythonapi/particle_restart.rst @@ -6,4 +6,3 @@ Particle Restart .. automodule:: openmc.particle_restart :members: - :undoc-members: diff --git a/docs/source/pythonapi/plots.rst b/docs/source/pythonapi/plots.rst index 4ff557178..8ad5348be 100644 --- a/docs/source/pythonapi/plots.rst +++ b/docs/source/pythonapi/plots.rst @@ -6,4 +6,3 @@ Plots .. automodule:: openmc.plots :members: - :undoc-members: diff --git a/docs/source/pythonapi/settings.rst b/docs/source/pythonapi/settings.rst index 4457e1c3d..3a3915ff5 100644 --- a/docs/source/pythonapi/settings.rst +++ b/docs/source/pythonapi/settings.rst @@ -6,4 +6,3 @@ Settings .. automodule:: openmc.settings :members: - :undoc-members: diff --git a/docs/source/pythonapi/statepoint.rst b/docs/source/pythonapi/statepoint.rst index b97328019..737fc03fc 100644 --- a/docs/source/pythonapi/statepoint.rst +++ b/docs/source/pythonapi/statepoint.rst @@ -6,4 +6,3 @@ Statepoint .. automodule:: openmc.statepoint :members: - :undoc-members: diff --git a/docs/source/pythonapi/summary.rst b/docs/source/pythonapi/summary.rst index 149db7844..9a791127b 100644 --- a/docs/source/pythonapi/summary.rst +++ b/docs/source/pythonapi/summary.rst @@ -6,4 +6,3 @@ Summary .. automodule:: openmc.summary :members: - :undoc-members: diff --git a/docs/source/pythonapi/surface.rst b/docs/source/pythonapi/surface.rst index 6f438b0da..cc31f5b3e 100644 --- a/docs/source/pythonapi/surface.rst +++ b/docs/source/pythonapi/surface.rst @@ -6,4 +6,3 @@ Surface .. automodule:: openmc.surface :members: - :undoc-members: diff --git a/docs/source/pythonapi/tallies.rst b/docs/source/pythonapi/tallies.rst index 29fb2e978..2f24edf3a 100644 --- a/docs/source/pythonapi/tallies.rst +++ b/docs/source/pythonapi/tallies.rst @@ -6,4 +6,3 @@ Tallies .. automodule:: openmc.tallies :members: - :undoc-members: diff --git a/docs/source/pythonapi/trigger.rst b/docs/source/pythonapi/trigger.rst index 17d211ab5..82567c2cf 100644 --- a/docs/source/pythonapi/trigger.rst +++ b/docs/source/pythonapi/trigger.rst @@ -6,4 +6,3 @@ Trigger .. automodule:: openmc.trigger :members: - :undoc-members: diff --git a/docs/source/pythonapi/universe.rst b/docs/source/pythonapi/universe.rst index 16eb1c14e..fd4a3c1e2 100644 --- a/docs/source/pythonapi/universe.rst +++ b/docs/source/pythonapi/universe.rst @@ -6,4 +6,3 @@ Universe .. automodule:: openmc.universe :members: - :undoc-members: diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index 5b6bee8c6..3c996178c 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -481,7 +481,7 @@ attributes/sub-elements: :parameters: For an "isotropic" angular distribution, ``parameters`` should not be - specified + specified. For a "monodirectional" angular distribution, ``parameters`` should be given as three real numbers which specify the angular cosines with respect @@ -605,8 +605,6 @@ survival biasing, otherwise known as implicit capture or absorption. *Default*: false -.. _trace: - ```` Element --------------------- @@ -615,6 +613,8 @@ a simulation. It has no attributes and accepts a positive integer value. *Default*: None (Determined by environment variable :envvar:`OMP_NUM_THREADS`) +.. _trace: + ```` Element ------------------- @@ -631,9 +631,9 @@ integers: the batch number, generation number, and particle number. The ```` element specifies particles for which OpenMC will output binary files describing particle position at every step of its transport. This element -should be followed by triplets of integers. Each triplet describes one particle -. The integers in each triplet specify the batch number, generation number, and -particle number, respectively. +should be followed by triplets of integers. Each triplet describes one +particle. The integers in each triplet specify the batch number, generation +number, and particle number, respectively. *Default*: None diff --git a/openmc/opencg_compatible.py b/openmc/opencg_compatible.py index c0e6d595d..b68b1032a 100644 --- a/openmc/opencg_compatible.py +++ b/openmc/opencg_compatible.py @@ -62,8 +62,20 @@ OPENMC_LATTICES = {} OPENCG_LATTICES = {} - def get_opencg_material(openmc_material): + """Return an OpenCG material corresponding to an OpenMC material. + + Parameters + ---------- + openmc_material : openmc.material.Material + OpenMC material + + Returns + ------- + opencg_material : opencg.Material + Equivalent OpenCG material + + """ if not isinstance(openmc_material, openmc.Material): msg = 'Unable to create an OpenCG Material from {0} ' \ @@ -91,6 +103,19 @@ def get_opencg_material(openmc_material): def get_openmc_material(opencg_material): + """Return an OpenMC material corresponding to an OpenCG material. + + Parameters + ---------- + opencg_material : opencg.Material + OpenCG material + + Returns + ------- + openmc_material : openmc.material.Material + Equivalent OpenMC material + + """ if not isinstance(opencg_material, opencg.Material): msg = 'Unable to create an OpenMC Material from {0} ' \ @@ -118,6 +143,19 @@ def get_openmc_material(opencg_material): def is_opencg_surface_compatible(opencg_surface): + """Determine whether OpenCG surface is compatible with OpenMC geometry + + Parameters + ---------- + opencg_surface : opencg.Surface + OpenCG surface + + Returns + ------- + bool + Whether OpenCG surface is compatible with OpenMC + + """ if not isinstance(opencg_surface, opencg.Surface): msg = 'Unable to check if OpenCG Surface is compatible' \ @@ -132,6 +170,19 @@ def is_opencg_surface_compatible(opencg_surface): def get_opencg_surface(openmc_surface): + """Return an OpenCG surface corresponding to an OpenMC surface. + + Parameters + ---------- + openmc_surface : openmc.surface.Surface + OpenMC surface + + Returns + ------- + opencg_surface : opencg.Surface + Equivalent OpenCG surface + + """ if not isinstance(openmc_surface, openmc.Surface): msg = 'Unable to create an OpenCG Surface from {0} ' \ @@ -205,6 +256,19 @@ def get_opencg_surface(openmc_surface): def get_openmc_surface(opencg_surface): + """Return an OpenMC surface corresponding to an OpenCG surface. + + Parameters + ---------- + opencg_surface : opencg.Surface + OpenCG surface + + Returns + ------- + openmc_surface : openmc.surface.Surface + Equivalent OpenMC surface + + """ if not isinstance(opencg_surface, opencg.Surface): msg = 'Unable to create an OpenMC Surface from {0} which ' \ @@ -280,6 +344,21 @@ def get_openmc_surface(opencg_surface): def get_compatible_opencg_surfaces(opencg_surface): + """Generate OpenCG surfaces that are compatible with OpenMC equivalent to an + OpenCG surface that is not compatible. + + Parameters + ---------- + opencg_surface : opencg.Surface + OpenCG surface that is incompatible with OpenMC + + Returns + ------- + surfaces : list of opencg.Surface + Collection of surfaces equivalent to the original one but compatible + with OpenMC + + """ if not isinstance(opencg_surface, opencg.Surface): msg = 'Unable to create an OpenMC Surface from {0} which ' \ @@ -349,6 +428,19 @@ def get_compatible_opencg_surfaces(opencg_surface): def get_opencg_cell(openmc_cell): + """Return an OpenCG cell corresponding to an OpenMC cell. + + Parameters + ---------- + openmc_cell : openmc.universe.Cell + OpenMC cell + + Returns + ------- + opencg_cell : opencg.Cell + Equivalent OpenCG cell + + """ if not isinstance(openmc_cell, openmc.Cell): msg = 'Unable to create an OpenCG Cell from {0} which ' \ @@ -398,7 +490,26 @@ def get_opencg_cell(openmc_cell): def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace): + """Generate OpenCG cells that are compatible with OpenMC equivalent to an OpenCG + cell that is not compatible. + Parameters + ---------- + opencg_cell : opencg.Cell + OpenCG cell + opencg_surface : opencg.Surface + OpenCG surface that causes the incompatibility, e.g. an x-squareprism + surface + halfspace : {-1, 1} + Which halfspace defined by the surface is contained in the cell + + Returns + ------- + compatible_cells : list of opencg.Cell + Collection of cells equivalent to the original one but compatible with + OpenMC + + """ if not isinstance(opencg_cell, opencg.Cell): msg = 'Unable to create compatible OpenMC Cell from {0} which ' \ 'is not an OpenCG Cell'.format(opencg_cell) @@ -418,8 +529,8 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace): compatible_cells = [] # SquarePrism Surfaces - if opencg_surface._type in ['x-squareprism', - 'y-squareprism', 'z-squareprism']: + if opencg_surface._type in ['x-squareprism', 'y-squareprism', + 'z-squareprism']: # Get the compatible Surfaces (XPlanes and YPlanes) compatible_surfaces = get_compatible_opencg_surfaces(opencg_surface) @@ -436,13 +547,11 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace): # If Cell is outside SquarePrism, add "outside" of Surface halfspaces else: - # Create 8 Cell clones to represent each of the disjoint planar # Surface halfspace intersections num_clones = 8 for clone_id in range(num_clones): - # Create a cloned OpenCG Cell with Surfaces compatible with OpenMC clone = opencg_cell.clone() compatible_cells.append(clone) @@ -500,6 +609,14 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace): def make_opencg_cells_compatible(opencg_universe): + """Make all cells in an OpenCG universe compatible with OpenMC. + + Parameters + ---------- + opencg_universe : opencg.Universe + Universe to check + + """ if not isinstance(opencg_universe, opencg.Universe): msg = 'Unable to make compatible OpenCG Cells for {0} which ' \ @@ -545,8 +662,20 @@ def make_opencg_cells_compatible(opencg_universe): return - def get_openmc_cell(opencg_cell): + """Return an OpenMC cell corresponding to an OpenCG cell. + + Parameters + ---------- + opencg_cell : opencg.Cell + OpenCG cell + + Returns + ------- + openmc_cell : openmc.universe.Cell + Equivalent OpenMC cell + + """ if not isinstance(opencg_cell, opencg.Cell): msg = 'Unable to create an OpenMC Cell from {0} which ' \ @@ -597,8 +726,20 @@ def get_openmc_cell(opencg_cell): return openmc_cell - def get_opencg_universe(openmc_universe): + """Return an OpenCG universe corresponding to an OpenMC universe. + + Parameters + ---------- + openmc_universe : openmc.universe.Universe + OpenMC universe + + Returns + ------- + opencg_universe : opencg.Universe + Equivalent OpenCG universe + + """ if not isinstance(openmc_universe, openmc.Universe): msg = 'Unable to create an OpenCG Universe from {0} which ' \ @@ -633,6 +774,19 @@ def get_opencg_universe(openmc_universe): def get_openmc_universe(opencg_universe): + """Return an OpenMC universe corresponding to an OpenCG universe. + + Parameters + ---------- + opencg_universe : opencg.Universe + OpenCG universe + + Returns + ------- + openmc_universe : openmc.universe.Universe + Equivalent OpenMC universe + + """ if not isinstance(opencg_universe, opencg.Universe): msg = 'Unable to create an OpenMC Universe from {0} which ' \ @@ -670,6 +824,19 @@ def get_openmc_universe(opencg_universe): def get_opencg_lattice(openmc_lattice): + """Return an OpenCG lattice corresponding to an OpenMC lattice. + + Parameters + ---------- + openmc_lattice : openmc.universe.Lattice + OpenMC lattice + + Returns + ------- + opencg_lattice : opencg.Lattice + Equivalent OpenCG lattice + + """ if not isinstance(openmc_lattice, openmc.Lattice): msg = 'Unable to create an OpenCG Lattice from {0} which ' \ @@ -737,6 +904,19 @@ def get_opencg_lattice(openmc_lattice): def get_openmc_lattice(opencg_lattice): + """Return an OpenMC lattice corresponding to an OpenCG lattice. + + Parameters + ---------- + opencg_lattice : opencg.Lattice + OpenCG lattice + + Returns + ------- + openmc_lattice : openmc.universe.Lattice + Equivalent OpenMC lattice + + """ if not isinstance(opencg_lattice, opencg.Lattice): msg = 'Unable to create an OpenMC Lattice from {0} which ' \ @@ -822,6 +1002,19 @@ def get_opencg_geometry(openmc_geometry): def get_openmc_geometry(opencg_geometry): + """Return an OpenMC geometry corresponding to an OpenCG geometry. + + Parameters + ---------- + opencg_geometry : opencg.Geometry + OpenCG geometry + + Returns + ------- + openmc_geometry : openmc.universe.Geometry + Equivalent OpenMC geometry + + """ if not isinstance(opencg_geometry, opencg.Geometry): msg = 'Unable to get OpenMC geometry from {0} which is ' \ diff --git a/openmc/particle_restart.py b/openmc/particle_restart.py index b4a718eb5..d664de1ae 100644 --- a/openmc/particle_restart.py +++ b/openmc/particle_restart.py @@ -1,9 +1,44 @@ -#!/usr/bin/env python - import struct class Particle(object): + """Information used to restart a specific particle that caused a simulation to + fail. + + Parameters + ---------- + filename : str + Path to the particle restart file + + Attributes + ---------- + filetype : int + Integer indicating the file type + revision : int + Revision of the particle restart format + current_batch : int + The batch containing the particle + gen_per_batch : int + Number of generations per batch + current_gen : int + The generation containing the particle + n_particles : int + Number of particles per generation + run_mode : int + Type of simulation (criticality or fixed source) + id : long + Identifier of the particle + weight : float + Weight of the particle + energy : float + Energy of the particle in MeV + xyz : list of float + Position of the particle + uvw : list of float + Directional cosines of the particle + + """ + def __init__(self, filename): if filename.endswith('.h5'): import h5py diff --git a/openmc/plots.py b/openmc/plots.py index 1468b11b0..39c5e0c3f 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -18,9 +18,50 @@ BASES = ['xy', 'xz', 'yz'] class Plot(object): + """Definition of a finite region of space to be plotted, either as a slice plot + in two dimensions or as a voxel plot in three dimensions. + + Parameters + ---------- + plot_id : int + Unique identifier for the plot + name : str + Name of the plot + + Attributes + ---------- + id : int + Unique identifier + name : str + Name of the plot + width : tuple or list or ndarray + Width of the plot in each basis direction + pixels : tuple or list or ndarray + Number of pixels to use in each basis direction + origin : tuple or list of ndarray + Origin (center) of the plot + filename : + Path to write the plot to + color : {'cell', 'mat'} + Indicate whether the plot should be colored by cell or by material + type : {'slice', 'voxel'} + The type of the plot + basis : {'xy', 'xz', 'yz'} + The basis directions for the plot + background : tuple or list of ndarray + Color of the background defined by RGB + mask_components : tuple or list or ndarray + Unique id numbers of the cells or materials to plot + mask_background : tuple or list or ndarray + Color to apply to all cells/materials not listed in mask_components + defined by RGB + col_spec : dict + Dictionary indicating that certain cells/materials (keys) should be + colored with a specific RGB (values) + + """ def __init__(self, plot_id=None, name=''): - # Initialize Plot class attributes self.id = plot_id self.name = name @@ -36,75 +77,60 @@ class Plot(object): self._mask_background = None self._col_spec = None - @property def id(self): return self._id - @property def name(self): return self._name - @property def width(self): return self._width - @property def pixels(self): return self._pixels - @property def origin(self): return self._origin - @property def filename(self): return self._filename - @property def color(self): return self._color - @property def type(self): return self._type - @property def basis(self): return self._basis - @property def background(self): return self._background - @property def mask_componenets(self): return self._mask_components - @property def mask_background(self): return self._mask_background - @property def col_spec(self): return self._col_spec - @id.setter def id(self, plot_id): - if plot_id is None: global AUTO_PLOT_ID self._id = AUTO_PLOT_ID @@ -123,7 +149,6 @@ class Plot(object): else: self._id = plot_id - @name.setter def name(self, name): @@ -135,10 +160,8 @@ class Plot(object): else: self._name = name - @width.setter def width(self, width): - if not isinstance(width, (tuple, list, np.ndarray)): msg = 'Unable to create Plot ID={0} with width {1} which is not ' \ 'a Python tuple/list or NumPy array'.format(self._id, width) @@ -158,10 +181,8 @@ class Plot(object): self._width = width - @origin.setter def origin(self, origin): - if not isinstance(origin, (tuple, list, np.ndarray)): msg = 'Unable to create Plot ID={0} with origin {1} which is not ' \ 'a Python tuple/list or NumPy array'.format(self._id, origin) @@ -172,7 +193,6 @@ class Plot(object): 'a 3D coordinate must be input'.format(self._id, origin) raise ValueError(msg) - for dim in origin: if not is_integer(dim) and not is_float(dim): msg = 'Unable to create Plot ID={0} with origin {1} since ' \ @@ -182,10 +202,8 @@ class Plot(object): self._origin = origin - @pixels.setter def pixels(self, pixels): - if not isinstance(pixels, (tuple, list, np.ndarray)): msg = 'Unable to create Plot ID={0} with pixels {1} which is not ' \ 'a Python tuple/list or NumPy array'.format(self._id, pixels) @@ -197,7 +215,6 @@ class Plot(object): raise ValueError(msg) for dim in pixels: - if not is_integer(dim): msg = 'Unable to create Plot ID={0} with pixel value {1} ' \ 'which is not an integer'.format(self._id, dim) @@ -210,10 +227,8 @@ class Plot(object): self._pixels = pixels - @filename.setter def filename(self, filename): - if not is_string(filename): msg = 'Unable to create Plot ID={0} with filename {1} which is ' \ 'not a string'.format(self._id, filename) @@ -221,10 +236,8 @@ class Plot(object): self._filename = filename - @color.setter def color(self, color): - if not is_string(color): msg = 'Unable to create Plot ID={0} with color {1} which is not ' \ 'a string'.format(self._id, color) @@ -237,10 +250,8 @@ class Plot(object): self._color = color - @type.setter def type(self, type): - if not is_string(type): msg = 'Unable to create Plot ID={0} with type {1} which is not ' \ 'a string'.format(self._id, type) @@ -253,10 +264,8 @@ class Plot(object): self._type = type - @basis.setter def basis(self, basis): - if not is_string(basis): msg = 'Unable to create Plot ID={0} with basis {1} which is not ' \ 'a string'.format(self._id, basis) @@ -269,10 +278,8 @@ class Plot(object): self._basis = basis - @background.setter def background(self, background): - if not isinstance(background, (tuple, list, np.ndarray)): msg = 'Unable to create Plot ID={0} with background {1} ' \ 'which is not a Python tuple/list or NumPy ' \ @@ -286,7 +293,6 @@ class Plot(object): raise ValueError(msg) for rgb in background: - if not is_integer(rgb): msg = 'Unable to create Plot ID={0} with background RGB ' \ 'value {1} which is not an integer'.format(self._id, rgb) @@ -299,10 +305,8 @@ class Plot(object): self._background = background - @col_spec.setter def col_spec(self, col_spec): - if not isinstance(col_spec, dict): msg= 'Unable to create Plot ID={0} with col_spec parameter {1} ' \ 'which is not a Python dictionary of IDs to ' \ @@ -310,7 +314,6 @@ class Plot(object): raise ValueError(msg) for key in col_spec: - if not is_integer(key): msg = 'Unable to create Plot ID={0} with col_spec ID {1} ' \ 'which is not an integer'.format(self._id, key) @@ -335,10 +338,8 @@ class Plot(object): self._col_spec = col_spec - @mask_componenets.setter def mask_components(self, mask_components): - if not isinstance(mask_components, (list, tuple, np.ndarray)): msg = 'Unable to create Plot ID={0} with mask components {1} ' \ 'which is not a Python tuple/list or NumPy ' \ @@ -358,10 +359,8 @@ class Plot(object): self._mask_components = mask_components - @mask_background.setter def mask_background(self, mask_background): - if not isinstance(mask_background, (list, tuple, np.ndarray)): msg = 'Unable to create Plot ID={0} with mask background {1} ' \ 'which is not a Python tuple/list or NumPy ' \ @@ -389,9 +388,7 @@ class Plot(object): self._mask_background = mask_background - def __repr__(self): - string = 'Plot\n' string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) @@ -409,8 +406,15 @@ class Plot(object): string += '{0: <16}{1}{2}\n'.format('\tCol Spec', '=\t', self._col_spec) return string - def get_plot_xml(self): + """Return XML representation of the plot + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing plot data + + """ element = ET.Element("plot") element.set("id", str(self._id)) @@ -422,64 +426,55 @@ class Plot(object): element.set("basis", self._basis) subelement = ET.SubElement(element, "origin") - text = '' - for coord in self._origin: - text += str(coord) + ' ' - subelement.text = text.rstrip(' ') + subelement.text = ' '.join(map(str, self._origin)) subelement = ET.SubElement(element, "width") - text = '' - for dim in self._width: - text += str(dim) + ' ' - subelement.text = text.rstrip(' ') + subelement.text = ' '.join(map(str, self._width)) subelement = ET.SubElement(element, "pixels") - text = '' - for dim in self._pixels: - text += str(dim) + ' ' - subelement.text = text.rstrip(' ') + subelement.text = ' '.join(map(str, self._pixels)) if not self._mask_background is None: subelement = ET.SubElement(element, "background") - text = '' - for rgb in self._background: - text += str(rgb) + ' ' - subelement.text = text.rstrip(' ') + subelement.text = ' '.join(map(str, self._background)) if not self._col_spec is None: - for key in self._col_spec: subelement = ET.SubElement(element, "col_spec") - subelement.set("id", '{0}'.format(key)) - value = self._col_spec[key] - subelement.set("rgb",'{0} {1} {2}'.format(value[0], - value[1], value[2])) + subelement.set("id", str(key)) + subelement.set("rgb", ' '.join(map( + str, self._col_spec[key]))) if not self._mask_components is None: subelement = ET.SubElement(element, "mask") - - text = '' - for id in self._mask_components: - text += str(id) + ' ' - subelement.set("components", text.rstrip(' ')) - - rgb = self._mask_background - subelement.set("background", '{0} {1} {2}'.format(rgb[0], - rgb[1], rgb[2])) + subelement.set("components", ' '.join(map( + str, self._mask_components))) + subelement.set("background", ' '.join(map( + str, self._mask_background))) return element class PlotsFile(object): + """Plots file used for an OpenMC simulation. Corresponds directly to the + plots.xml input file. + + """ def __init__(self): - # Initialize PlotsFile class attributes self._plots = [] self._plots_file = ET.Element("plots") - def add_plot(self, plot): + """Add a plot to the file. + + Parameters + ---------- + plot : Plot + Plot to add + + """ if not isinstance(plot, Plot): msg = 'Unable to add a non-Plot {0} to the PlotsFile'.format(plot) @@ -487,15 +482,20 @@ class PlotsFile(object): self._plots.append(plot) - def remove_plot(self, plot): + """Remove a plot from the file. + + Parameters + ---------- + plot : Plot + Plot to remove + + """ + self._plots.remove(plot) - - def create_plot_subelements(self): - + def _create_plot_subelements(self): for plot in self._plots: - xml_element = plot.get_plot_xml() if len(plot._name) > 0: @@ -503,10 +503,12 @@ class PlotsFile(object): self._plots_file.append(xml_element) - def export_to_xml(self): + """Create a plots.xml file that can be used by OpenMC. - self.create_plot_subelements() + """ + + self._create_plot_subelements() # Clean the indentation in the file to be user-readable clean_xml_indentation(self._plots_file) diff --git a/openmc/settings.py b/openmc/settings.py index 632565946..7b4139767 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -9,9 +9,113 @@ from openmc.clean_xml import * class SettingsFile(object): + """Settings file used for an OpenMC simulation. Corresponds directly to the + settings.xml input file. + + Attributes + ---------- + batches : int + Number of batches to simulate + generations_per_batch : int + Number of generations per batch + inactive : int + Number of inactive batches + particles : int + Number of particles per generation + keff_trigger : dict + Dictionary defining a trigger on eigenvalue. The dictionary must have + two keys, 'type' and 'threshold'. Acceptable values corresponding to + type are 'variance', 'std_dev', and 'rel_err'. The threshold value + should be a float indicating the variance, standard deviation, or + relative error used. + source_file : str + Path to a source file + output : dict + Dictionary indicating what files to output. Valid keys are 'summary', + 'cross_sections', 'tallies', and 'distribmats'. Values corresponding to + each key should be given as a boolean value. + output_path : str + Path to write output to + verbosity : int + Verbosity during simulation between 1 and 10 + statepoint_batches : tuple or list or ndarray + List of batches at which to write statepoint files + statepoint_interval : int + Number of batches after which a new statepoint file should be written + sourcepoint_batches : tuple or list or ndarray + List of batches at which to write source files + sourcepoint_interval : int + Number of batches after which a new source file should be written + sourcepoint_separate : bool + Indicate whether the souce should be written as part of the statepoint + file or on its own + sourcepoint_write : bool + Indicate whether the source should be written at all + sourcepoint_overwrite : bool + Indicate whether to + confidence_intervals : bool + If True, uncertainties on tally results will be reported as the + half-width of the 95% two-sided confidence interval. If False, + uncertainties on tally results will be reported as the sample standard + deviation. + cross_sections : str + Indicates the path to an XML cross section listing file (usually named + cross_sections.xml). If it is not set, the :envvar:`CROSS_SECTIONS` + environment variable will be used to find the path to the XML cross + section listing. + energy_grid : str + Set the method used to search energy grids. Acceptable values are + 'nuclide', 'logarithm', and 'material-union'. + ptables : bool + Determine whether probability tables are used. + run_cmfd : bool + Indicate if coarse mesh finite difference acceleration is to be used + seed : int + Seed for the linear congruential pseudorandom number generator + survival_biasing : bool + Indicate whether survival biasing is to be used + weight : float + Weight cutoff below which particle undergo Russian roulette + weight_avg : float + Weight assigned to particles that are not killed after Russian roulette + entropy_dimension : tuple or list + Number of Shannon entropy mesh cells in the x, y, and z directions, + respectively + entropy_lower_left : tuple or list + Coordinates of the lower-left point of the Shannon entropy mesh + entropy_upper_right : tuple or list + Coordinates of the upper-right point of the Shannon entropy mesh + trigger_active : bool + Indicate whether tally triggers are used + trigger_max_batches : int + Maximum number of batches simulated. If this is set, the number of + batches specified via ``batches`` is interpreted as the minimum number + of batches + trigger_batch_interval : int + Number of batches in between convergence checks + no_reduce : bool + Indicate that all user-defined and global tallies should not be reduced + across processes in a parallel calculation. + threads : int + Number of OpenMP threads + trace : tuple or list + Show detailed information about a single particle, indicated by three + integers: the batch number, generation number, and particle number + track : tuple or list + Specify particles for which track files should be written. Each particle + is identified by a triplet with the batch number, generation number, and + particle number. + ufs_dimension : tuple or list + Number of uniform fission site (UFS) mesh cells in the x, y, and z + directions, respectively + ufs_lower_left : tuple or list + Coordinates of the lower-left point of the UFS mesh + ufs_upper_right : tuple or list + Coordinates of the upper-right point of the UFS mesh + + """ def __init__(self): - # Eigenvalue subelement self._batches = None self._generations_per_batch = None @@ -89,147 +193,118 @@ class SettingsFile(object): self._eigenvalue_subelement = None self._source_element = None - @property def batches(self): return self._batches - @property def generations_per_batch(self): return self._generations_per_batch - @property def inactive(self): return self._inactive - @property def particles(self): return self._particles - @property def keff_trigger(self): return self._keff_trigger - @property def source_file(self): return self._source_file - @property def source_space_type(self): return self._source_space_type - @property def source_space_params(self): return self._source_space_params - @property def source_angle_type(self): return self._source_angle_type - @property def source_angle_params(self): return self._source_angle_params - @property def source_energy_type(self): return self._source_energy_type - @property def source_energy_params(self): return self._source_energy_params - @property def confidence_intervals(self): return self._confidence_intervals - @property def cross_sections(self): return self._cross_sections - @property def energy_grid(self): return self._energy_grid - @property def ptables(self): return self._ptables - @property def run_cmfd(self): return self._run_cmfd - @property def seed(self): return self._seed - @property def survival_biasing(self): return self._survival_biasing - @property def entropy_dimension(self): return self._entropy_dimension - @property def entropy_lower_left(self): return self._entropy_lower_left - @property def entropy_upper_right(self): return self._entropy_upper_right - @property def trigger_active(self): return self._trigger_active - @property def trigger_max_batches(self): return self._trigger_max_batches - @property def trigger_batch_interval(self): return self._batch_interval - @property def output(self): return self._output - @property def output_path(self): return self._output_path - @property def statepoint_batches(self): return self._statepoint_batches - @property def statepoint_interval(self): return self._statepoint_interval @@ -238,110 +313,88 @@ class SettingsFile(object): def sourcepoint_batches(self): return self._sourcepoint_interval - @property def sourcepoint_interval(self): return self._sourcepoint_interval - @property def sourcepoint_separate(self): return self._sourcepoint_separate - @property def sourcepoint_write(self): return self._sourcepoint_write - @property def sourcepoint_overwrite(self): return self._sourcepoint_overwrite - @property def threads(self): return self._threads - @property def no_reduce(self): return self._no_reduce - @property def verbosity(self): return self._verbosity - @property def trace(self): return self._trace - @property def track(self): return self._track - @property def weight(self): return self._weight - @property def weight_avg(self): return self._weight_avg - @property def ufs_dimension(self): return self._ufs_dimension - @property def ufs_lower_left(self): return self._ufs_lower_left - @property def ufs_upper_right(self): return self._ufs_upper_right - @property def dd_mesh_dimension(self): return self._dd_mesh_dimension - @property def dd_mesh_lower_left(self): return self._dd_mesh_lower_left - @property def dd_mesh_upper_right(self): return self._dd_mesh_upper_right - @property def dd_nodemap(self): return self._dd_nodemap - @property def dd_allow_leakage(self): return self._dd_allow_leakage - @property def dd_count_interactions(self): return self._dd_count_interactions - @batches.setter def batches(self, batches): - if not is_integer(batches): msg = 'Unable to set batches to a non-integer ' \ 'value {0}'.format(batches) @@ -354,10 +407,8 @@ class SettingsFile(object): self._batches = batches - @generations_per_batch.setter def generations_per_batch(self, generations_per_batch): - if not is_integer(generations_per_batch): msg = 'Unable to set generations per batch to a non-integer ' \ 'value {0}'.format(generations_per_batch) @@ -370,10 +421,8 @@ class SettingsFile(object): self._generations_per_batch = generations_per_batch - @inactive.setter def inactive(self, inactive): - if not is_integer(inactive): msg = 'Unable to set inactive batches to a non-integer ' \ 'value {0}'.format(inactive) @@ -386,10 +435,8 @@ class SettingsFile(object): self._inactive = inactive - @particles.setter def particles(self, particles): - if not is_integer(particles): msg = 'Unable to set particles to a non-integer ' \ 'value {0}'.format(particles) @@ -402,10 +449,8 @@ class SettingsFile(object): self._particles = particles - @keff_trigger.setter def keff_trigger(self, keff_trigger): - if not isinstance(keff_trigger, dict): msg = 'Unable to set a trigger on keff from {0} which ' \ 'is not a Python dictionary'.format(keff_trigger) @@ -433,10 +478,8 @@ class SettingsFile(object): self._keff_trigger = keff_trigger - @source_file.setter def source_file(self, source_file): - if not is_string(source_file): msg = 'Unable to set source file to a non-string ' \ 'value {0}'.format(source_file) @@ -444,8 +487,35 @@ class SettingsFile(object): self._source_file = source_file - def set_source_space(self, stype, params): + """Defined the spatial bounds of the external/starting source. + + Parameters + ---------- + stype : str + The type of spatial distribution. Valid options are "box" and + "point". A "box" spatial distribution has coordinates sampled + uniformly in a parallelepiped. A "point" spatial distribution has + coordinates specified by a triplet. + params : tuple or list or ndarray + For a "box" spatial distribution, ``params`` should be given as six + real numbers, the first three of which specify the lower-left corner + of a parallelepiped and the last three of which specify the + upper-right corner. Source sites are sampled uniformly through that + parallelepiped. + + To filter a "box" spatial distribution by fissionable material, + specify "fission" tag instead of "box". The ``params`` should be + given as six real numbers, the first three of which specify the + lower-left corner of a parallelepiped and the last three of which + specify the upper-right corner. Source sites are sampled uniformly + through that parallelepiped. + + For a "point" spatial distribution, ``params`` should be given as + three real numbers which specify the (x,y,z) location of an + isotropic point source + + """ if not is_string(stype): msg = 'Unable to set source space type to a non-string ' \ @@ -462,13 +532,17 @@ class SettingsFile(object): 'not a Python tuple, list or NumPy array'.format(params) raise ValueError(msg) - elif len(params) != 6: - msg = 'Unable to set source space parameters to {0} since it ' \ - 'does not contain 6 values'.format(params) + elif stype == 'box' and len(params) != 6: + msg = 'Unable to set source space parameters for a box to {0} ' \ + 'since it does not contain 6 values'.format(params) + raise ValueError(msg) + + elif stype == 'point' and len(params) != 3: + msg = 'Unable to set source space parameters for a point to {0} ' \ + 'since it does not contain 3 values'.format(params) raise ValueError(msg) for param in params: - if not is_integer(param) and not is_float(param): msg = 'Unable to set source space parameters to {0} since it ' \ 'is not an integer or floating point value'.format(param) @@ -477,8 +551,26 @@ class SettingsFile(object): self._source_space_type = stype self._source_space_params = params - def set_source_angle(self, stype, params=[]): + """Defined the angular distribution of the external/starting source. + + Parameters + ---------- + stype : str + The type of angular distribution. Valid options are "isotropic" and + "monodirectional". The angle of the particle emitted from a source + site is isotropic if the "isotropic" option is given. The angle of + the particle emitted from a source site is the direction specified + in ``params`` if the "monodirectional" option is given. + params : tuple or list or ndarray + For an "isotropic" angular distribution, ``params`` should not + be specified. + + For a "monodirectional" angular distribution, ``params`` should + be given as three floats which specify the angular cosines + with respect to each axis. + + """ if not is_string(stype): msg = 'Unable to set source angle type to a non-string ' \ @@ -507,7 +599,6 @@ class SettingsFile(object): raise ValueError(msg) for param in params: - if not is_integer(param) and not is_float(param): msg = 'Unable to set source angle parameters to {0} since it ' \ 'is not an integer or floating point value'.format(param) @@ -516,8 +607,31 @@ class SettingsFile(object): self._source_angle_type = stype self._source_angle_params = params - def set_source_energy(self, stype, params=[]): + """Defined the energy distribution of the external/starting source. + + Parameters + ---------- + stype : str + The type of energy distribution. Valid options are "monoenergetic", + "watt", and "maxwell". The "monoenergetic" option produces source + sites at a single energy. The "watt" option produces source sites + whose energy is sampled from a Watt fission spectrum. The "maxwell" + option produce source sites whose energy is sampled from a Maxwell + fission spectrum + params : tuple or list or ndarray + For a "monoenergetic" energy distribution, ``params`` should be + given as the energy in MeV of the source sites. + + For a "watt" energy distribution, ``params`` should be given as two + real numbers :math:`a` and :math:`b` that parameterize the + distribution :math:`p(E) dE = c e^{-E/a} \sinh \sqrt{b \, E} dE`. + + For a "maxwell" energy distribution, ``params`` should be given as + one real number :math:`a` that parameterizes the distribution + :math:`p(E) dE = c E e^{-E/a} dE`. + + """ if not is_string(stype): msg = 'Unable to set source energy type to a non-string ' \ @@ -530,31 +644,30 @@ class SettingsFile(object): raise ValueError(msg) elif not isinstance(params, (tuple, list, np.ndarray)): - msg = 'Unable to set source energy parameters to {0} since it ' \ + msg = 'Unable to set source energy params to {0} since it ' \ 'is not a Python list/tuple or NumPy array'.format(params) raise ValueError(msg) elif stype == 'monoenergetic' and not len(params) != 1: - msg = 'Unable to set source energy parameters to {0} ' \ + msg = 'Unable to set source energy params to {0} ' \ 'since 1 paramater is required for monenergetic ' \ 'sources'.format(params) raise ValueError(msg) elif stype == 'watt' and len(params) != 2: - msg = 'Unable to set source energy parameters to {0} ' \ - 'since 2 parameters are required for monoenergetic ' \ + msg = 'Unable to set source energy params to {0} ' \ + 'since 2 params are required for monoenergetic ' \ 'sources'.format(params) raise ValueError(msg) elif stype == 'maxwell' and len(params) != 2: - msg = 'Unable to set source energy parameters to {0} since 1 ' \ + msg = 'Unable to set source energy params to {0} since 1 ' \ 'parameter is required for maxwell sources'.format(params) raise ValueError(msg) for param in params: - if not is_integer(param) and not is_float(param): - msg = 'Unable to set source energy parameters to {0} ' \ + msg = 'Unable to set source energy params to {0} ' \ 'since it is not an integer or floating point ' \ 'value'.format(param) raise ValueError(msg) @@ -562,18 +675,14 @@ class SettingsFile(object): self._source_energy_type = stype self._source_energy_params = params - @output.setter def output(self, output): - if not isinstance(output, dict): msg = 'Unable to set output to {0} which is not a Python ' \ 'dictionary of string keys and boolean values'.format(output) raise ValueError(msg) - for element in output: - keys = ['summary', 'cross_sections', 'tallies', 'distribmats'] if not element in keys: msg = 'Unable to set output to {0} which is unsupported by ' \ @@ -587,10 +696,8 @@ class SettingsFile(object): self._output = output - @output_path.setter def output_path(self, output_path): - if not is_string(output_path): msg = 'Unable to set output path to non-string ' \ 'value {0}'.format(output_path) @@ -598,10 +705,8 @@ class SettingsFile(object): self._output_path = output_path - @verbosity.setter def verbosity(self, verbosity): - if not is_integer(verbosity): msg = 'Unable to set verbosity to non-integer ' \ 'value {0}'.format(verbosity) @@ -614,17 +719,14 @@ class SettingsFile(object): self._verbosity = verbosity - @statepoint_batches.setter def statepoint_batches(self, batches): - if not isinstance(batches, (tuple, list, np.ndarray)): msg = 'Unable to set statepoint batches to {0} which is not a ' \ 'Python tuple/list or NumPy array'.format(batches) raise ValueError(msg) for batch in batches: - if not is_integer(batch): msg = 'Unable to set statepoint batches with non-integer ' \ 'value {0}'.format(batch) @@ -637,10 +739,8 @@ class SettingsFile(object): self._statepoint_batches = batches - @statepoint_interval.setter def statepoint_interval(self, interval): - if not is_integer(interval): msg = 'Unable to set statepoint interval to non-integer ' \ 'value {0}'.format(interval) @@ -648,17 +748,14 @@ class SettingsFile(object): self._statepoint_interval = interval - @sourcepoint_batches.setter def sourcepoint_batches(self, batches): - if not isinstance(batches, (tuple, list, np.ndarray)): msg = 'Unable to set sourcepoint batches to {0} which is ' \ 'not a Python tuple/list or NumPy array'.format(batches) raise ValueError(msg) for batch in batches: - if not is_integer(batch): msg = 'Unable to set sourcepoint batches with non-integer ' \ 'value {0}'.format(batch) @@ -671,10 +768,8 @@ class SettingsFile(object): self._sourcepoint_batches = batches - @sourcepoint_interval.setter def sourcepoint_interval(self, interval): - if not is_integer(interval): msg = 'Unable to set sourcepoint interval to non-integer ' \ 'value {0}'.format(interval) @@ -682,10 +777,8 @@ class SettingsFile(object): self._sourcepoint_interval = interval - @sourcepoint_separate.setter def sourcepoint_separate(self, source_separate): - if not isinstance(source_separate, (bool, np.bool)): msg = 'Unable to set sourcepoint separate to non-boolean ' \ 'value {0}'.format(source_separate) @@ -693,10 +786,8 @@ class SettingsFile(object): self._sourcepoint_separate = source_separate - @sourcepoint_write.setter def sourcepoint_write(self, source_write): - if not isinstance(source_write, (bool, np.bool)): msg = 'Unable to set sourcepoint write to non-boolean ' \ 'value {0}'.format(source_write) @@ -704,10 +795,8 @@ class SettingsFile(object): self._sourcepoint_write = source_write - @sourcepoint_overwrite.setter def sourcepoint_overwrite(self, source_overwrite): - if not isinstance(source_overwrite, (bool, np.bool)): msg = 'Unable to set sourcepoint overwrite to non-boolean ' \ 'value {0}'.format(source_overwrite) @@ -715,10 +804,8 @@ class SettingsFile(object): self._sourcepoint_overwrite = source_overwrite - @confidence_intervals.setter def confidence_intervals(self, confidence_intervals): - if not isinstance(confidence_intervals, (bool, np.bool)): msg = 'Unable to set confidence interval to non-boolean ' \ 'value {0}'.format(confidence_intervals) @@ -726,10 +813,8 @@ class SettingsFile(object): self._confidence_intervals = confidence_intervals - @cross_sections.setter def cross_sections(self, cross_sections): - if not is_string(cross_sections): msg = 'Unable to set cross sections to non-string ' \ 'value {0}'.format(cross_sections) @@ -737,10 +822,8 @@ class SettingsFile(object): self._cross_sections = cross_sections - @energy_grid.setter def energy_grid(self, energy_grid): - if not energy_grid in ['nuclide', 'logarithm', 'material-union']: msg = 'Unable to set energy grid to {0} which is neither ' \ 'nuclide, logarithm, nor material-union'.format(energy_grid) @@ -748,10 +831,8 @@ class SettingsFile(object): self._energy_grid = energy_grid - @ptables.setter def ptables(self, ptables): - if not isinstance(ptables, (bool, np.bool)): msg = 'Unable to set ptables to non-boolean ' \ 'value {0}'.format(ptables) @@ -759,10 +840,8 @@ class SettingsFile(object): self._ptables = ptables - @run_cmfd.setter def run_cmfd(self, run_cmfd): - if not isinstance(run_cmfd, (bool, np.bool)): msg = 'Unable to set run_cmfd to non-boolean ' \ 'value {0}'.format(run_cmfd) @@ -770,10 +849,8 @@ class SettingsFile(object): self._run_cmfd = run_cmfd - @seed.setter def seed(self, seed): - if not is_integer(seed): msg = 'Unable to set seed to non-integer value {0}'.format(seed) raise ValueError(msg) @@ -784,10 +861,8 @@ class SettingsFile(object): self._seed = seed - @survival_biasing.setter def survival_biasing(self, survival_biasing): - if not isinstance(survival_biasing, (bool, np.bool)): msg = 'Unable to set survival biasing to non-boolean ' \ 'value {0}'.format(survival_biasing) @@ -795,49 +870,46 @@ class SettingsFile(object): self._survival_biasing = survival_biasing - @weight.setter - def weight(self, weight, weight_avg): - + def weight(self, weight): if not is_float(weight): msg = 'Unable to set weight cutoff to non-floating point ' \ 'value {0}'.format(weight) raise ValueError(msg) - elif not is_float(weight_avg): - msg = 'Unable to set weight avg. to non-floating point ' \ - 'value {0}'.format(weight_avg) - raise ValueError(msg) - elif weight < 0.0: msg = 'Unable to set weight cutoff to negative ' \ 'value {0}'.format(weight) raise ValueError(msg) + self._weight = weight + + @weight_avg.setter + def weight_avg(self, weight_avg): + if not is_float(weight_avg): + msg = 'Unable to set weight avg. to non-floating point ' \ + 'value {0}'.format(weight_avg) + raise ValueError(msg) elif weight_avg < 0.0: msg = 'Unable to set weight avg. to negative ' \ 'value {0}'.format(weight_avg) raise ValueError(msg) - self._weight = weight self._weight_avg = weight_avg - @entropy_dimension.setter def entropy_dimension(self, dimension): - if not isinstance(dimension, (tuple, list)): msg = 'Unable to set entropy mesh dimension to {0} which is ' \ 'not a Python tuple or list'.format(dimension) raise ValueError(msg) - elif len(dimension) < 3 or len(dimension) > 3: + elif len(dimension) == 3: msg = 'Unable to set entropy mesh dimension to {0} which is ' \ 'not a set of 3 integer dimensions'.format(dimension) raise ValueError(msg) for dim in dimension: - if not is_integer(dim) and not is_float(dim): msg = 'Unable to set entropy mesh dimension to a ' \ 'non-integer or floating point value {0}'.format(dim) @@ -845,10 +917,8 @@ class SettingsFile(object): self._entropy_dimension = dimension - @entropy_lower_left.setter def entropy_lower_left(self, lower_left): - if not isinstance(lower_left, (tuple, list)): msg = 'Unable to set entropy mesh lower left corner to {0} which ' \ 'is not a Python tuple or list'.format(lower_left) @@ -860,19 +930,15 @@ class SettingsFile(object): raise ValueError(msg) for coord in lower_left: - if not is_integer(coord) and not is_float(coord): - msg = 'Unable to set entropy mesh lower left corner to a ' \ 'non-integer or floating point value {0}'.format(coord) raise ValueError(msg) self._entropy_lower_left = lower_left - @entropy_upper_right.setter def entropy_upper_right(self, upper_right): - if not isinstance(upper_right, (tuple, list)): msg = 'Unable to set entropy mesh upper right corner to {0} ' \ 'which is not a Python tuple or list'.format(upper_right) @@ -884,20 +950,15 @@ class SettingsFile(object): raise ValueError(msg) for coord in upper_right: - if not is_integer(coord) and not is_float(coord): - msg = 'Unable to set entropy mesh upper right corner to a ' \ 'non-integer or floating point value {0}'.format(coord) raise ValueError(msg) self._entropy_upper_right = upper_right - - @trigger_active.setter def trigger_active(self, trigger_active): - if not isinstance(trigger_active, bool): msg = 'Unable to set trigger active to a ' \ 'non-boolean value {0}'.format(trigger_active) @@ -905,10 +966,8 @@ class SettingsFile(object): self._trigger_active = trigger_active - @trigger_max_batches.setter def trigger_max_batches(self, trigger_max_batches): - if not is_integer(trigger_max_batches): msg = 'Unable to set trigger max batches to a non-integer ' \ 'value {0}'.format(trigger_max_batches) @@ -921,10 +980,8 @@ class SettingsFile(object): self._trigger_max_batches = trigger_max_batches - @trigger_batch_interval.setter def trigger_batch_interval(self, trigger_batch_interval): - if not is_integer(trigger_batch_interval): msg = 'Unable to set trigger batch interval to a non-integer ' \ 'value {0}'.format(trigger_batch_interval) @@ -937,10 +994,8 @@ class SettingsFile(object): self._trigger_batch_interval = trigger_batch_interval - @no_reduce.setter def no_reduce(self, no_reduce): - if not isinstance(no_reduce, (bool, np.bool)): msg = 'Unable to set the no_reduce to a non-boolean ' \ 'value {0}'.format(no_reduce) @@ -948,10 +1003,8 @@ class SettingsFile(object): self._no_reduce = no_reduce - @threads.setter def threads(self, threads): - if not is_integer(threads): msg = 'Unable to set the threads to a non-integer ' \ 'value {0}'.format(threads) @@ -964,10 +1017,8 @@ class SettingsFile(object): self._threads = threads - @trace.setter def trace(self, trace): - if not isinstance(trace, (list, tuple)): msg = 'Unable to set the trace to {0} which is not a Python ' \ 'tuple or list'.format(trace) @@ -995,10 +1046,8 @@ class SettingsFile(object): self._trace = trace - @track.setter def track(self, track): - if not isinstance(track, (list, tuple)): msg = 'Unable to set the track to {0} which is not a Python ' \ 'tuple or list'.format(track) @@ -1009,7 +1058,6 @@ class SettingsFile(object): '3 elements - batch, generation, and particle'.format(track) raise ValueError(msg) - elif track[0] < 1: msg = 'Unable to set the track batch to {0} since it must be ' \ 'greater than or equal to 1'.format(track[0]) @@ -1027,80 +1075,78 @@ class SettingsFile(object): self._track = track - @ufs_dimension.setter def ufs_dimension(self, dimension): - - if not is_integer(dimension) and not is_float(dimension): - msg = 'Unable to set UFS dimension to non-integer or ' \ - 'non-floating point value {0}'.format(dimension) + if not isinstance(dimension, (tuple, list)): + msg = 'Unable to set UFS mesh dimension to {0} which is ' \ + 'not a Python tuple or list'.format(dimension) raise ValueError(msg) - elif dimension < 1: - msg = 'Unable to set UFS dimension to value {0} which is ' \ - 'less than one'.format(dimension) + elif len(dimension) == 3: + msg = 'Unable to set UFS mesh dimension to {0} which is ' \ + 'not a set of 3 integer dimensions'.format(dimension) raise ValueError(msg) + for dim in dimension: + if not is_integer(dim): + msg = 'Unable to set entropy mesh dimension to a ' \ + 'non-integer {0}'.format(dim) + raise ValueError(msg) + elif dim < 1: + msg = 'Unable to set UFS dimension to value {0} which is ' \ + 'less than one'.format(dimension) + raise ValueError(msg) + self._ufs_dimension = dimension - @ufs_lower_left.setter def ufs_lower_left(self, lower_left): - if not isinstance(lower_left, (tuple, list, np.ndarray)): msg = 'Unable to set UFS mesh lower left corner to {0} which is ' \ 'not a Python tuple or list'.format(lower_left) raise ValueError(msg) - elif len(lower_left) < 3 or len(lower_left) > 3: + elif len(lower_left) == 3: msg = 'Unable to set UFS mesh lower left corner to {0} which ' \ 'is not a 3D point'.format(lower_left) raise ValueError(msg) self._ufs_lower_left = lower_left - @ufs_upper_right.setter def ufs_upper_right(self, upper_right): - - if not isinstance(upper_right, tuple) and \ - not isinstance(upper_right, list): + if not isinstance(upper_right, (tuple, list)): msg = 'Unable to set UFs mesh upper right corner to {0} which is ' \ 'not a Python tuple or list'.format(upper_right) raise ValueError(msg) - if len(upper_right) < 3 or len(upper_right) > 3: + if len(upper_right) == 3: msg = 'Unable to set UFS mesh upper right corner to {0} which ' \ 'is not a 3D point'.format(upper_right) raise ValueError(msg) self._ufs_upper_right = upper_right - @dd_mesh_dimension.setter def dd_mesh_dimension(self, dimension): - # TODO: remove this when domain decomposition is merged warnings.warn('This feature is not yet implemented in a release ' \ 'version of openmc') - if not isinstance(dimension, tuple) and \ - not isinstance(dimension, list): + if not isinstance(dimension, (tuple, list)): msg = 'Unable to set DD mesh upper right corner to {0} which is ' \ 'not a Python tuple or list'.format(dimension) raise ValueError(msg) - if len(dimension) < 3 or len(dimension) > 3: + if len(dimension) == 3: msg = 'Unable to set DD mesh upper right corner to {0} which ' \ 'is not a 3D point'.format(dimension) raise ValueError(msg) self._dd_mesh_dimension = dimension - @dd_mesh_lower_left.setter def dd_mesh_lower_left(self, lower_left): - # TODO: remove this when domain decomposition is merged warnings.warn('This feature is not yet implemented in a release ' \ 'version of openmc') @@ -1117,10 +1163,8 @@ class SettingsFile(object): self._dd_mesh_lower_left = lower_left - @dd_mesh_upper_right.setter def dd_mesh_upper_right(self, upper_right): - # TODO: remove this when domain decomposition is merged warnings.warn('This feature is not yet implemented in a release ' \ 'version of openmc') @@ -1138,10 +1182,8 @@ class SettingsFile(object): self._dd_mesh_upper_right = upper_right - @dd_nodemap.setter def dd_nodemap(self, nodemap): - # TODO: remove this when domain decomposition is merged warnings.warn('This feature is not yet implemented in a release ' \ 'version of openmc') @@ -1168,7 +1210,6 @@ class SettingsFile(object): self._dd_nodemap = nodemap - @dd_allow_leakage.setter def dd_allow_leakage(self, allow): @@ -1183,7 +1224,6 @@ class SettingsFile(object): self._dd_allow_leakage = allow - @dd_count_interactions.setter def dd_count_interactions(self, interactions): @@ -1198,69 +1238,52 @@ class SettingsFile(object): self._dd_count_interactions = interactions + def _create_eigenvalue_subelement(self): + self._create_particles_subelement() + self._create_batches_subelement() + self._create_inactive_subelement() + self._create_generations_per_batch_subelement() + self._create_keff_trigger_subelement() - def create_eigenvalue_subelement(self): - - self.create_particles_subelement() - self.create_batches_subelement() - self.create_inactive_subelement() - self.create_generations_per_batch_subelement() - self.create_keff_trigger_subelement() - - - def create_batches_subelement(self): - + def _create_batches_subelement(self): if not self._batches is None: - if self._eigenvalue_subelement is None: self._eigenvalue_subelement = ET.SubElement(self._settings_file, "eigenvalue") element = ET.SubElement(self._eigenvalue_subelement, "batches") - element.text = '{0}'.format(self._batches) - - - def create_generations_per_batch_subelement(self): + element.text = str(self._batches) + def _create_generations_per_batch_subelement(self): if not self._generations_per_batch is None: - if self._eigenvalue_subelement is None: self._eigenvalue_subelement = ET.SubElement(self._settings_file, "eigenvalue") element = ET.SubElement(self._eigenvalue_subelement, "generations_per_batch") - element.text = '{0}'.format(self._generations_per_batch) - - - def create_inactive_subelement(self): + element.text = str(self._generations_per_batch) + def _create_inactive_subelement(self): if not self._inactive is None: - if self._eigenvalue_subelement is None: self._eigenvalue_subelement = ET.SubElement(self._settings_file, "eigenvalue") element = ET.SubElement(self._eigenvalue_subelement, "inactive") - element.text = '{0}'.format(self._inactive) - - - def create_particles_subelement(self): + element.text = str(self._inactive) + def _create_particles_subelement(self): if not self._particles is None: - if self._eigenvalue_subelement is None: self._eigenvalue_subelement = ET.SubElement(self._settings_file, "eigenvalue") element = ET.SubElement(self._eigenvalue_subelement, "particles") - element.text = '{0}'.format(self._particles) - - - def create_keff_trigger_subelement(self): + element.text = str(self._particles) + def _create_keff_trigger_subelement(self): if not self._keff_trigger is None: - if self._eigenvalue_subelement is None: self._eigenvalue_subelement = ET.SubElement(self._settings_file, "eigenvalue") @@ -1271,19 +1294,13 @@ class SettingsFile(object): subelement = ET.SubElement(element, key) subelement.text = str(self._keff_trigger[key]).lower() + def _create_source_subelement(self): + self._create_source_space_subelement() + self._create_source_energy_subelement() + self._create_source_angle_subelement() - def create_source_subelement(self): - - self.create_source_space_subelement() - self.create_source_energy_subelement() - self.create_source_angle_subelement() - - - def create_source_space_subelement(self): - - + def _create_source_space_subelement(self): if not self._source_space_params is None: - if self._source_subelement is None: self._source_subelement = ET.SubElement(self._settings_file, "source") @@ -1292,17 +1309,10 @@ class SettingsFile(object): element.set("type", self._source_space_type) subelement = ET.SubElement(element, "parameters") + subelement.text = ' '.join(map(str, self._source_space_params)) - text = '' - for param in self._source_space_params: - text += '{0} '.format(param) - subelement.text = text.rstrip(' ') - - - def create_source_angle_subelement(self): - + def _create_source_angle_subelement(self): if not self._source_angle_params is None: - if self._source_subelement is None: self._source_subelement = ET.SubElement(self._settings_file, "source") @@ -1311,17 +1321,10 @@ class SettingsFile(object): element.set("type", self._source_angle_type) subelement = ET.SubElement(element, "parameters") + subelement.text = ' '.join(map(str, self._source_angle_params)) - text = '' - for param in self._source_angle_params: - text += '{0} '.format(param) - subelement.text = text.rstrip(' ') - - - def create_source_energy_subelement(self): - + def _create_source_energy_subelement(self): if not self._source_energy_params is None: - if self._source_subelement is None: self._source_subelement = ET.SubElement(self._settings_file, "source") @@ -1330,15 +1333,9 @@ class SettingsFile(object): element.set("type", self._source_energy_type) subelement = ET.SubElement(element, "parameters") + subelement.text = ' '.join(map(str, self._source_energy_params)) - text = '' - for param in self._source_energy_params: - text += '{0} '.format(param) - subelement.text = text.rstrip(' ') - - - def create_output_subelement(self): - + def _create_output_subelement(self): if not self._output is None: element = ET.SubElement(self._settings_file, "output") @@ -1346,260 +1343,187 @@ class SettingsFile(object): subelement = ET.SubElement(element, key) subelement.text = str(self._output[key]).lower() - self.create_output_path_subelement() - - - def create_output_path_subelement(self): + self._create_output_path_subelement() + def _create_output_path_subelement(self): if not self._output_path is None: element = ET.SubElement(self._settings_file, "output_path") element.text = self._output_path - - def create_verbosity_subelement(self): - + def _create_verbosity_subelement(self): if not self._verbosity is None: element = ET.SubElement(self._settings_file, "verbosity") - element.text = '{0}'.format(self._verbosity) - - - def create_statepoint_subelement(self): + element.text = str(self._verbosity) + def _create_statepoint_subelement(self): # Batches subelement if not self._statepoint_batches is None: element = ET.SubElement(self._settings_file, "state_point") subelement = ET.SubElement(element, "batches") - text = '' - for batch in self._statepoint_batches: - text += '{0} '.format(batch) - subelement.text = text.rstrip(' ') + subelement.text = ' '.join(map(str, self._statepoint_batches)) # Interval subelement elif not self._statepoint_interval is None: element = ET.SubElement(self._settings_file, "state_point") subelement = ET.SubElement(element, "interval") - subelement.text = '{0}'.format(self._statepoint_interval) - - - def create_sourcepoint_subelement(self): + subelement.text = str(self._statepoint_interval) + def _create_sourcepoint_subelement(self): # Batches subelement if not self._sourcepoint_batches is None: element = ET.SubElement(self._settings_file, "source_point") subelement = ET.SubElement(element, "batches") - text = '' - for batch in self._sourcepoint_batches: - text += '{0} '.format(batch) - subelement.text = text.rstrip(' ') + subelement.text = ' '.join(map(str, self._sourcepoint_batches)) # Interval subelement elif not self._sourcepoint_interval is None: element = ET.SubElement(self._settings_file, "source_point") subelement = ET.SubElement(element, "interval") - subelement.text = '{0}'.format(self._sourcepoint_interval) + subelement.text = str(self._sourcepoint_interval) # Separate subelement if not self._sourcepoint_separate is None: subelement = ET.SubElement(element, "separate") - subelement.text = '{0}'.format(str(self._sourcepoint_separate).lower()) + subelement.text = str(self._sourcepoint_separate).lower() # Write subelement if not self._sourcepoint_write is None: subelement = ET.SubElement(element, "write") - subelement.text = '{0}'.format(str(self._sourcepoint_write).lower()) + subelement.text = str(self._sourcepoint_write).lower() # Overwrite latest subelement if not self._sourcepoint_overwrite is None: subelement = ET.SubElement(element, "overwrite_latest") - subelement.text = '{0}'.format(str(self._sourcepoint_overwrite).lower()) - - - def create_confidence_intervals(self): + subelement.text = str(self._sourcepoint_overwrite).lower() + def _create_confidence_intervals(self): if not self._confidence_intervals is None: element = ET.SubElement(self._settings_file, "confidence_intervals") - element.text = '{0}'.format(str(self._confidence_intervals).lower()) - - - def create_cross_sections_subelement(self): + element.text = str(self._confidence_intervals).lower() + def _create_cross_sections_subelement(self): if not self._cross_sections is None: element = ET.SubElement(self._settings_file, "cross_sections") - element.text = '{0}'.format(self._cross_sections) - - - def create_energy_grid_subelement(self): + element.text = str(self._cross_sections) + def _create_energy_grid_subelement(self): if not self._energy_grid is None: element = ET.SubElement(self._settings_file, "energy_grid") - element.text = '{0}'.format(self._energy_grid) - - - def create_ptables_subelement(self): + element.text = str(self._energy_grid) + def _create_ptables_subelement(self): if not self._ptables is None: element = ET.SubElement(self._settings_file, "ptables") - element.text = '{0}'.format(str(self._ptables).lower()) - - - def create_run_cmfd_subelement(self): + element.text = str(self._ptables).lower() + def _create_run_cmfd_subelement(self): if not self._run_cmfd is None: element = ET.SubElement(self._settings_file, "run_cmfd") - element.text = '{0}'.format(str(self._run_cmfd).lower()) - - - def create_seed_subelement(self): + element.text = str(self._run_cmfd).lower() + def _create_seed_subelement(self): if not self._seed is None: element = ET.SubElement(self._settings_file, "seed") - element.text = '{0}'.format(self._seed) - - - def create_survival_biasing_subelement(self): + element.text = str(self._seed) + def _create_survival_biasing_subelement(self): if not self._survival_biasing is None: element = ET.SubElement(self._settings_file, "survival_biasing") - element.text = '{0}'.format(str(self._survival_biasing).lower()) - - - def create_cutoff_subelement(self): + element.text = str(self._survival_biasing).lower() + def _create_cutoff_subelement(self): if not self._weight is None: element = ET.SubElement(self._settings_file, "cutoff") subelement = ET.SubElement(element, "weight") - subelement.text = '{0}'.format(self._weight) + subelement.text = str(self._weight) subelement = ET.SubElement(element, "weight_avg") - subelement.text = '{0}'.format(self._weight_avg) - - - def create_entropy_subelement(self): + subelement.text = str(self._weight_avg) + def _create_entropy_subelement(self): if not self._entropy_lower_left is None and \ not self._entropy_upper_right is None: element = ET.SubElement(self._settings_file, "entropy") subelement = ET.SubElement(element, "dimension") - subelement.text = '{0} {1} {2}'.format(self._entropy_dimension[0], - self._entropy_dimension[1], - self._entropy_dimension[2]) + subelement.text = ' '.join(map(str, self._entropy_dimension)) subelement = ET.SubElement(element, "lower_left") - subelement.text = '{0} {1} {2}'.format(self._entropy_lower_left[0], - self._entropy_lower_left[1], - self._entropy_lower_left[2]) + subelement.text = ' '.join(map(str, self._entropy_lower_left)) subelement = ET.SubElement(element, "upper_right") - subelement.text = '{0} {1} {2}'.format(self._entropy_upper_right[0], - self._entropy_upper_right[1], - self._entropy_upper_right[2]) + subelement.text = ' '.join(map(str, self._entropy_upper_right)) + def _create_trigger_subelement(self): + self._create_trigger_active_subelement() + self._create_trigger_max_batches_subelement() + self._create_trigger_batch_interval_subelement() - def create_trigger_subelement(self): - - self.create_trigger_active_subelement() - self.create_trigger_max_batches_subelement() - self.create_trigger_batch_interval_subelement() - - - def create_trigger_active_subelement(self): - + def _create_trigger_active_subelement(self): if not self._trigger_active is None: - if self._trigger_subelement is None: self._trigger_subelement = ET.SubElement(self._settings_file, "trigger") element = ET.SubElement(self._trigger_subelement, "active") - element.text = '{0}'.format(str(self._trigger_active).lower()) - - - def create_trigger_max_batches_subelement(self): + element.text = str(self._trigger_active).lower() + def _create_trigger_max_batches_subelement(self): if not self._trigger_max_batches is None: - if self._trigger_subelement is None: self._trigger_subelement = ET.SubElement(self._settings_file, "trigger") element = ET.SubElement(self._trigger_subelement, "max_batches") - element.text = '{0}'.format(self._trigger_max_batches) - - - def create_trigger_batch_interval_subelement(self): + element.text = str(self._trigger_max_batches) + def _create_trigger_batch_interval_subelement(self): if not self._trigger_batch_interval is None: - if self._trigger_subelement is None: self._trigger_subelement = ET.SubElement(self._settings_file, "trigger") element = ET.SubElement(self._trigger_subelement, "batch_interval") - element.text = '{0}'.format(self._trigger_batch_interval) - - - def create_no_reduce_subelement(self): + element.text = str(self._trigger_batch_interval) + def _create_no_reduce_subelement(self): if not self._no_reduce is None: element = ET.SubElement(self._settings_file, "no_reduce") - element.text = '{0}'.format(str(self._no_reduce).lower()) - - - def create_threads_subelement(self): + element.text = str(self._no_reduce).lower() + def _create_threads_subelement(self): if not self._threads is None: element = ET.SubElement(self._settings_file, "threads") - element.text = '{0}'.format(self._threads) - - - def create_trace_subelement(self): + element.text = str(self._threads) + def _create_trace_subelement(self): if not self._trace is None: element = ET.SubElement(self._settings_file, "trace") + element.text = ' '.join(map(str, self._trace)) - text = '' - for item in self._trace: - text += '{0} '.format(item) - element.text = text.rstrip(' ') - - - def create_track_subelement(self): - + def _create_track_subelement(self): if not self._track is None: element = ET.SubElement(self._settings_file, "track") + element.text = ' '.join(map(str, self._track)) - text = '' - for item in self._track: - text += '{0} '.format(item) - element.text = text.rstrip(' ') - - - def create_ufs_subelement(self): - + def _create_ufs_subelement(self): if not self._ufs_lower_left is None and \ not self._ufs_upper_right is None: element = ET.SubElement(self._settings_file, "uniform_fs") subelement = ET.SubElement(element, "dimension") - subelement.text = '{0}'.format(self._ufs_dimension) + subelement.text = str(self._ufs_dimension) subelement = ET.SubElement(element, "lower_left") - subelement.text = '{0} {1} {2}'.format(self._ufs_lower_left[0], - self._ufs_lower_left[1], - self._ufs_lower_left[2]) + subelement.text = ' '.join(map(str, self._ufs_lower_left)) subelement = ET.SubElement(element, "upper_right") - subelement.text = '{0} {1} {2}'.format(self._ufs_upper_right[0], - self._ufs_upper_right[1], - self._ufs_upper_right[2]) - - - def create_dd_subelement(self): + subelement.text = ' '.join(map(str, self._ufs_upper_right)) + def _create_dd_subelement(self): if not self._dd_mesh_lower_left is None and \ not self._dd_mesh_upper_right is None and \ not self._dd_mesh_dimension is None: @@ -1608,64 +1532,51 @@ class SettingsFile(object): subelement = ET.SubElement(element, "mesh") subsubelement = ET.SubElement(subelement, "dimension") - subsubelement.text = '{0} {1} {2}'.format( - self._dd_mesh_dimension[0], - self._dd_mesh_dimension[1], - self._dd_mesh_dimension[2]) + subsubelement.text = ' '.join(map(str, self._dd_mesh_dimension)) subsubelement = ET.SubElement(subelement, "lower_left") - subsubelement.text = '{0} {1} {2}'.format( - self._dd_mesh_lower_left[0], - self._dd_mesh_lower_left[1], - self._dd_mesh_lower_left[2]) + subsubelement.text = ' '.join(map(str, self._dd_mesh_lower_left)) subsubelement = ET.SubElement(subelement, "upper_right") - subsubelement.text = '{0} {1} {2}'.format( - self._dd_mesh_upper_right[0], - self._dd_mesh_upper_right[1], - self._dd_mesh_upper_right[2]) + subsubelement.text = ' '.join(map(str, self._dd_mesh_upper_right)) if not self._dd_nodemap is None: subelement = ET.SubElement(element, "nodemap") - subelement.text = ' '.join([str(n) for n in self._dd_nodemap]) + subelement.text = ' '.join(map(str, self._dd_nodemap)) subelement = ET.SubElement(element, "allow_leakage") - if self._dd_allow_leakage: - subelement.text = 'true' - else: - subelement.text = 'false' + subelement.text = str(self._dd_allow_leakage).lower() subelement = ET.SubElement(element, "count_interactions") - if self._dd_count_interactions: - subelement.text = 'true' - else: - subelement.text = 'false' - + subelement.text = str(self._dd_count_interactions).lower() def export_to_xml(self): + """Create a settings.xml file that can be used for a simulation. - self.create_eigenvalue_subelement() - self.create_source_subelement() - self.create_output_subelement() - self.create_statepoint_subelement() - self.create_sourcepoint_subelement() - self.create_confidence_intervals() - self.create_cross_sections_subelement() - self.create_energy_grid_subelement() - self.create_ptables_subelement() - self.create_run_cmfd_subelement() - self.create_seed_subelement() - self.create_survival_biasing_subelement() - self.create_cutoff_subelement() - self.create_entropy_subelement() - self.create_trigger_subelement() - self.create_no_reduce_subelement() - self.create_threads_subelement() - self.create_verbosity_subelement() - self.create_trace_subelement() - self.create_track_subelement() - self.create_ufs_subelement() - self.create_dd_subelement() + """ + + self._create_eigenvalue_subelement() + self._create_source_subelement() + self._create_output_subelement() + self._create_statepoint_subelement() + self._create_sourcepoint_subelement() + self._create_confidence_intervals() + self._create_cross_sections_subelement() + self._create_energy_grid_subelement() + self._create_ptables_subelement() + self._create_run_cmfd_subelement() + self._create_seed_subelement() + self._create_survival_biasing_subelement() + self._create_cutoff_subelement() + self._create_entropy_subelement() + self._create_trigger_subelement() + self._create_no_reduce_subelement() + self._create_threads_subelement() + self._create_verbosity_subelement() + self._create_trace_subelement() + self._create_track_subelement() + self._create_ufs_subelement() + self._create_dd_subelement() # Clean the indentation in the file to be user-readable clean_xml_indentation(self._settings_file) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index f5805f0e2..4655186b0 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -13,17 +13,28 @@ if sys.version > '3': class SourceSite(object): + """A single source site, usually resulting from fission. + + Attributes + ---------- + weight : float + Weight of the particle arising from the site + xyz : list of float + Cartesian coordinates of the site + uvw : list of float + Directional cosines for particles emerging from the site + E : float + Energy of the emerging particle in MeV + + """ def __init__(self): - self._weight = None self._xyz = None self._uvw = None self._E = None - def __repr__(self): - string = 'SourceSite\n' string += '{0: <16}{1}{2}\n'.format('\tweight', '=\t', self._weight) string += '{0: <16}{1}{2}\n'.format('\tE', '=\t', self._E) @@ -31,32 +42,56 @@ class SourceSite(object): string += '{0: <16}{1}{2}\n'.format('\t(u,v,w)', '=\t', self._uvw) return string - @property def weight(self): return self._weight - @property def xyz(self): return self._xyz - @property def uvw(self): return self._uvw - @property def E(self): return self._E - class StatePoint(object): + """State information on a simulation at a certain point in time (at the end of a + given batch). Statepoints can be used to analyze tally results as well as + restart a simulation. + + Attributes + ---------- + k_combined : list + Combined estimator for k-effective and its uncertainty + n_particles : int + Number of particles per generation + n_batches : int + Number of batches + current_batch : + Number of batches simulated + results : bool + Indicate whether tally results have been read + source : ndarray of SourceSite + Array of source sites + with_summary : bool + Indicate whether statepoint data has been linked against a summary file + tallies : dict + Dictionary whose keys are tally IDs and whose values are Tally objects + tallies_present : bool + Indicate whether user-defined tallies are present + global_tallies : ndarray + Global tallies and their uncertainties + n_realizations : int + Number of tally realizations + + """ def __init__(self, filename): - if filename.endswith('.h5'): import h5py self._f = h5py.File(filename, 'r') @@ -79,7 +114,6 @@ class StatePoint(object): # Read tally metadata self._read_tallies() - def close(self): self._f.close() @@ -128,7 +162,6 @@ class StatePoint(object): return self._n_realizations def _read_metadata(self): - # Read filetype self._filetype = self._get_int(path='filetype')[0] @@ -169,9 +202,7 @@ class StatePoint(object): if self._run_mode == 2: self._read_criticality() - def _read_criticality(self): - # Read criticality information if self._run_mode == 2: @@ -191,9 +222,7 @@ class StatePoint(object): # Read CMFD information (if used) self._read_cmfd() - def _read_cmfd(self): - base = 'cmfd' # Read CMFD information @@ -217,9 +246,7 @@ class StatePoint(object): self._cmfd_srccmp = self._get_double(self._current_batch, path='{0}/cmfd_srccmp'.format(base)) - def _read_meshes(self): - # Initialize dictionaries for the Meshes # Keys - Mesh IDs # Values - Mesh objects @@ -284,7 +311,6 @@ class StatePoint(object): def _read_tallies(self): - # Initialize dictionaries for the Tallies # Keys - Tally IDs # Values - Tally objects @@ -423,7 +449,6 @@ class StatePoint(object): # Add the scores to the Tally for j, score in enumerate(scores): - # If this is a scattering moment, insert the scattering order if '-n' in score: score = score.replace('-n', '-' + str(moments[j])) @@ -437,8 +462,11 @@ class StatePoint(object): # Add Tally to the global dictionary of all Tallies self.tallies[tally_key] = tally - def read_results(self): + """Read tally results and store them in the ``tallies`` attribute. By default, + when the statepoint is first loaded, no results are read. + + """ # Number of realizations for global Tallies self._n_realizations = self._get_int(path='n_realizations')[0] @@ -499,8 +527,11 @@ class StatePoint(object): # Indicate that Tally results have been read self._results = True - def read_source(self): + """Read and store source sites from the statepoint file. By default, source + sites are not loaded upon initialization. + + """ # Check whether Tally results have been read if not self._results: @@ -520,7 +551,6 @@ class StatePoint(object): # Initialize SourceSite object for each particle for i in range(self._n_particles): - # Initialize new source site site = SourceSite() @@ -536,9 +566,15 @@ class StatePoint(object): # Store the source site in the NumPy array self._source[i] = site - def compute_ci(self, confidence=0.95): - """Computes confidence intervals for each Tally bin.""" + """Computes confidence intervals for each Tally bin. + + Parameters + ---------- + confidence : float, optional + Confidence level. Defaults to 0.95. + + """ # Determine significance level and percentile for two-sided CI alpha = 1 - confidence @@ -548,11 +584,16 @@ class StatePoint(object): t_value = scipy.stats.t.ppf(percentile, self._n_realizations - 1) self.compute_stdev(t_value) - def compute_stdev(self, t_value=1.0): - """ - Computes the sample mean and the standard deviation of the mean + """Computes the sample mean and the standard deviation of the mean for each Tally bin. + + Parameters + ---------- + t_value : float, optional + Student's t-value applied to the uncertainty. Defaults to 1.0, + meaning the reported value is the sample standard deviation. + """ # Determine number of realizations @@ -572,12 +613,10 @@ class StatePoint(object): if s != 0.0: self._global_tallies[i, 1] = t_value * np.sqrt((s2 / n - s**2) / (n-1)) - # Calculate sample mean and standard deviation for user-defined Tallies for tally_id, tally in self.tallies.items(): tally.compute_std_dev(t_value) - def get_tally(self, scores=[], filters=[], nuclides=[], name=None, id=None, estimator=None): """Finds and returns a Tally object with certain properties. @@ -589,32 +628,30 @@ class StatePoint(object): Parameters ---------- - scores : list - A list of one or more score strings (default is []). - - filters : list - A list of Filter objects (default is []). - - nuclides : list - A list of Nuclide objects (default is []). - - name : str - The name specified for the Tally (default is None). - - id : int - The id specified for the Tally (default is None). - - estimator: str - The type of estimator ('tracklength', 'analog'; default is None). + scores : list, optional + A list of one or more score strings (default is []). + filters : list, optional + A list of Filter objects (default is []). + nuclides : list, optional + A list of Nuclide objects (default is []). + name : str, optional + The name specified for the Tally (default is None). + id : int, optional + The id specified for the Tally (default is None). + estimator: str, optional + The type of estimator ('tracklength', 'analog'; default is None). Returns ------- - A Tally object. + tally : Tally + A tally matching the specified criteria Raises ------ - LookupError : An error when a Tally meeting all of the input - parameters cannot be found in the statepoint. + LookupError + If a Tally meeting all of the input parameters cannot be found in + the statepoint. + """ tally = None @@ -683,7 +720,6 @@ class StatePoint(object): return tally - def link_with_summary(self, summary): """Links Tallies and Filters with Summary model information. @@ -699,8 +735,10 @@ class StatePoint(object): Raises ------ - ValueError : An error when the argument passed to the 'summary' - parameter is not an openmc.Summary object. + ValueError + An error when the argument passed to the 'summary' parameter is not + an openmc.Summary object. + """ if not isinstance(summary, openmc.summary.Summary): @@ -709,7 +747,6 @@ class StatePoint(object): raise ValueError(msg) for tally_id, tally in self.tallies.items(): - # Get the Tally name from the summary file tally.name = summary.tallies[tally_id].name tally.with_summary = True @@ -717,7 +754,6 @@ class StatePoint(object): nuclide_zaids = copy.deepcopy(tally.nuclides) for nuclide_zaid in nuclide_zaids: - tally.remove_nuclide(nuclide_zaid) if nuclide_zaid == -1: tally.add_nuclide(openmc.Nuclide('total')) @@ -725,7 +761,6 @@ class StatePoint(object): tally.add_nuclide(summary.nuclides[nuclide_zaid]) for filter in tally.filters: - if filter.type == 'surface': surface_ids = [] for bin in filter.bins: @@ -749,9 +784,8 @@ class StatePoint(object): for bin in filter.bins: material_ids.append(summary.materials[bin].id) filter.bins = material_ids - - self._with_summary = True + self._with_summary = True def _get_data(self, n, typeCode, size): return list(struct.unpack('={0}{1}'.format(n,typeCode), diff --git a/openmc/summary.py b/openmc/summary.py index 61fb89a1e..61cce8b5c 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -10,9 +10,12 @@ except ImportError: class Summary(object): + """Information summarizing the geometry, materials, and tallies used in a + simulation. + + """ def __init__(self, filename): - openmc.reset_auto_ids() if not filename.endswith(('.h5', '.hdf5')): @@ -27,9 +30,7 @@ class Summary(object): self._read_geometry() self._read_tallies() - def _read_metadata(self): - # Read OpenMC version self.version = [self._f['version_major'][0], self._f['version_minor'][0], @@ -44,9 +45,7 @@ class Summary(object): self.gen_per_batch = self._f['gen_per_batch'][0] self.n_procs = self._f['n_procs'][0] - def _read_geometry(self): - # Read in and initialize the Materials and Geometry self._read_nuclides() self._read_materials() @@ -56,9 +55,7 @@ class Summary(object): self._read_lattices() self._finalize_geometry() - def _read_nuclides(self): - self.n_nuclides = self._f['nuclides/n_nuclides'][0] # Initialize dictionary for each Nuclide @@ -67,7 +64,6 @@ class Summary(object): self.nuclides = {} for key in self._f['nuclides'].keys(): - if key == 'n_nuclides': continue @@ -88,9 +84,7 @@ class Summary(object): self.nuclides[zaid] = openmc.Nuclide(name=name, xs=xs) self.nuclides[zaid].zaid = zaid - def _read_materials(self): - self.n_materials = self._f['materials/n_materials'][0] # Initialize dictionary for each Material @@ -99,7 +93,6 @@ class Summary(object): self.materials = {} for key in self._f['materials'].keys(): - if key == 'n_materials': continue @@ -116,7 +109,6 @@ class Summary(object): # Read the names of the S(a,b) tables for this Material for i in range(1, n_sab+1): - sab_table = self._f['materials'][key]['sab_tables'][str(i)][0] # Read the cross-section identifiers for each S(a,b) table @@ -148,9 +140,7 @@ class Summary(object): # Add the Material to the global dictionary of all Materials self.materials[index] = material - def _read_surfaces(self): - self.n_surfaces = self._f['geometry/n_surfaces'][0] # Initialize dictionary for each Surface @@ -159,7 +149,6 @@ class Summary(object): self.surfaces = {} for key in self._f['geometry/surfaces'].keys(): - if key == 'n_surfaces': continue @@ -171,7 +160,6 @@ class Summary(object): coeffs = self._f['geometry/surfaces'][key]['coefficients'][...] # Create the Surface based on its type - if surf_type == 'X Plane': x0 = coeffs[0] surface = openmc.XPlane(surface_id, bc, x0, name) @@ -232,9 +220,7 @@ class Summary(object): # Add Surface to global dictionary of all Surfaces self.surfaces[index] = surface - def _read_cells(self): - self.n_cells = self._f['geometry/n_cells'][0] # Initialize dictionary for each Cell @@ -251,7 +237,6 @@ class Summary(object): self._cell_fills = {} for key in self._f['geometry/cells'].keys(): - if key == 'n_cells': continue @@ -310,9 +295,7 @@ class Summary(object): # Add the Cell to the global dictionary of all Cells self.cells[index] = cell - def _read_universes(self): - self.n_universes = self._f['geometry/n_universes'][0] # Initialize dictionary for each Universe @@ -321,7 +304,6 @@ class Summary(object): self.universes = {} for key in self._f['geometry/universes'].keys(): - if key == 'n_universes': continue @@ -340,9 +322,7 @@ class Summary(object): # Add the Universe to the global list of Universes self.universes[index] = universe - def _read_lattices(self): - self.n_lattices = self._f['geometry/n_lattices'][0] # Initialize lattices for each Lattice @@ -351,7 +331,6 @@ class Summary(object): self.lattices = {} for key in self._f['geometry/lattices'].keys(): - if key == 'n_lattices': continue @@ -451,15 +430,12 @@ class Summary(object): # Add the Lattice to the global dictionary of all Lattices self.lattices[index] = lattice - def _finalize_geometry(self): - # Initialize Geometry object self.openmc_geometry = openmc.Geometry() # Iterate over all Cells and add fill Materials, Universes and Lattices for cell_key in self._cell_fills.keys(): - # Determine fill type ('normal', 'universe', or 'lattice') and ID fill_type = self._cell_fills[cell_key][0] fill_id = self._cell_fills[cell_key][1] @@ -482,9 +458,7 @@ class Summary(object): root_universe = self.get_universe_by_id(0) self.openmc_geometry.root_universe = root_universe - def _read_tallies(self): - # Initialize dictionaries for the Tallies # Keys - Tally IDs # Values - Tally objects @@ -554,9 +528,7 @@ class Summary(object): # Add Tally to the global dictionary of all Tallies self.tallies[tally_id] = tally - def make_opencg_geometry(self): - try: from openmc.opencg_compatible import get_opencg_geometry except ImportError: @@ -567,54 +539,42 @@ class Summary(object): if self.opencg_geometry is None: self.opencg_geometry = get_opencg_geometry(self.openmc_geometry) - def get_nuclide_by_zaid(self, zaid): - for index, nuclide in self.nuclides.items(): if nuclide._zaid == zaid: return nuclide return None - def get_material_by_id(self, material_id): - for index, material in self.materials.items(): if material._id == material_id: return material return None - def get_surface_by_id(self, surface_id): - for index, surface in self.surfaces.items(): if surface._id == surface_id: return surface return None - def get_cell_by_id(self, cell_id): - for index, cell in self.cells.items(): if cell._id == cell_id: return cell return None - def get_universe_by_id(self, universe_id): - for index, universe in self.universes.items(): if universe._id == universe_id: return universe return None - def get_lattice_by_id(self, lattice_id): - for index, lattice in self.lattices.items(): if lattice._id == lattice_id: return lattice