mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-26 21:25:36 -04:00
Merge pull request #800 from paulromano/volume
Change how volume is handled
This commit is contained in:
commit
b1fbef35e9
17 changed files with 385 additions and 271 deletions
|
|
@ -146,7 +146,7 @@ The current version of the statepoint file format is 16.0.
|
|||
All values are given in seconds and are measured on the master process.
|
||||
|
||||
:Datasets: - **total initialization** (*double*) -- Time spent reading inputs,
|
||||
allocating arrays, etc.
|
||||
allocating arrays, etc.
|
||||
- **reading cross sections** (*double*) -- Time spent loading cross
|
||||
section libraries (this is a subset of initialization).
|
||||
- **simulation** (*double*) -- Time spent between initialization and
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ Simulation Settings
|
|||
|
||||
openmc.Source
|
||||
openmc.ResonanceScattering
|
||||
openmc.VolumeCalculation
|
||||
openmc.Settings
|
||||
|
||||
Material Specification
|
||||
|
|
@ -185,6 +186,7 @@ Running OpenMC
|
|||
:template: myfunction.rst
|
||||
|
||||
openmc.run
|
||||
openmc.calculate_volumes
|
||||
openmc.plot_geometry
|
||||
|
||||
Post-processing
|
||||
|
|
@ -286,17 +288,11 @@ Multi-group Cross Sections
|
|||
openmc.mgxs.AbsorptionXS
|
||||
openmc.mgxs.CaptureXS
|
||||
openmc.mgxs.Chi
|
||||
openmc.mgxs.ChiPrompt
|
||||
openmc.mgxs.FissionXS
|
||||
openmc.mgxs.InverseVelocity
|
||||
openmc.mgxs.KappaFissionXS
|
||||
openmc.mgxs.MultiplicityMatrixXS
|
||||
openmc.mgxs.NuFissionXS
|
||||
openmc.mgxs.NuFissionMatrixXS
|
||||
openmc.mgxs.NuScatterXS
|
||||
openmc.mgxs.NuScatterMatrixXS
|
||||
openmc.mgxs.PromptNuFissionXS
|
||||
openmc.mgxs.PromptNuFissionMatrixXS
|
||||
openmc.mgxs.ScatterXS
|
||||
openmc.mgxs.ScatterMatrixXS
|
||||
openmc.mgxs.TotalXS
|
||||
|
|
|
|||
|
|
@ -13,9 +13,12 @@ It is assumed that if no
|
|||
is specified, the XML input files are present in the current directory.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B "\-c\fR, \fP\-\-volume"
|
||||
Run in stochastic volume calculation mode
|
||||
.TP
|
||||
.B "\-g\fR, \fP\-\-geometry-debug"
|
||||
Run in geometry debugging mode, where cell overlaps are checked for after each
|
||||
move of a particle
|
||||
Run with geometry debugging turned on, where cell overlaps are checked for after
|
||||
each move of a particle
|
||||
.TP
|
||||
.B "\-p\fR, \fP\-\-plot"
|
||||
Run in plotting mode
|
||||
|
|
|
|||
|
|
@ -86,9 +86,9 @@ class Cell(object):
|
|||
distribcell_paths : list of str
|
||||
The paths traversed through the CSG tree to reach each distribcell
|
||||
instance
|
||||
volume_information : dict
|
||||
Estimate of the volume and total number of atoms of each nuclide from a
|
||||
stochastic volume calculation. This information is set with the
|
||||
volume : float
|
||||
Volume of the cell in cm^3. This can either be set manually or
|
||||
calculated in a stochastic volume calculation and added via the
|
||||
:meth:`Cell.add_volume_information` method.
|
||||
|
||||
"""
|
||||
|
|
@ -106,7 +106,8 @@ class Cell(object):
|
|||
self._offsets = None
|
||||
self._distribcell_index = None
|
||||
self._distribcell_paths = None
|
||||
self._volume_information = None
|
||||
self._volume = None
|
||||
self._atoms = None
|
||||
|
||||
def __contains__(self, point):
|
||||
if self.region is None:
|
||||
|
|
@ -224,8 +225,8 @@ class Cell(object):
|
|||
return self._distribcell_paths
|
||||
|
||||
@property
|
||||
def volume_information(self):
|
||||
return self._volume_information
|
||||
def volume(self):
|
||||
return self._volume
|
||||
|
||||
@id.setter
|
||||
def id(self, cell_id):
|
||||
|
|
@ -326,6 +327,12 @@ class Cell(object):
|
|||
cv.check_type('cell region', region, Region)
|
||||
self._region = region
|
||||
|
||||
@volume.setter
|
||||
def volume(self, volume):
|
||||
if volume is not None:
|
||||
cv.check_type('cell volume', volume, Real)
|
||||
self._volume = volume
|
||||
|
||||
@distribcell_index.setter
|
||||
def distribcell_index(self, ind):
|
||||
cv.check_type('distribcell index', ind, Integral)
|
||||
|
|
@ -391,10 +398,9 @@ class Cell(object):
|
|||
|
||||
"""
|
||||
if volume_calc.domain_type == 'cell':
|
||||
for cell_id in volume_calc.results:
|
||||
if cell_id == self.id:
|
||||
self._volume_information = volume_calc.results[cell_id]
|
||||
break
|
||||
if self.id in volume_calc.volumes:
|
||||
self._volume = volume_calc.volumes[self.id][0]
|
||||
self._atoms = volume_calc.atoms[self.id]
|
||||
else:
|
||||
raise ValueError('No volume information found for this cell.')
|
||||
else:
|
||||
|
|
@ -446,9 +452,9 @@ class Cell(object):
|
|||
elif self.fill_type == 'void':
|
||||
pass
|
||||
else:
|
||||
if self.volume_information is not None:
|
||||
volume = self.volume_information['volume'][0]
|
||||
for name, atoms in self.volume_information['atoms']:
|
||||
if self._atoms is not None:
|
||||
volume = self.volume
|
||||
for name, atoms in self._atoms.items():
|
||||
nuclide = openmc.Nuclide(name)
|
||||
density = 1.0e-24 * atoms[0]/volume # density in atoms/b-cm
|
||||
nuclides[name] = (nuclide, density)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
from __future__ import print_function
|
||||
import subprocess
|
||||
from numbers import Integral
|
||||
import sys
|
||||
|
||||
from six import string_types
|
||||
|
||||
from openmc import VolumeCalculation
|
||||
|
||||
_summary_indicator = "TIMING STATISTICS"
|
||||
|
||||
|
||||
def _run(command, output, cwd):
|
||||
def _run(args, output, cwd):
|
||||
# Launch a subprocess
|
||||
p = subprocess.Popen(command, shell=True, cwd=cwd, stdout=subprocess.PIPE,
|
||||
p = subprocess.Popen(args, cwd=cwd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, universal_newlines=True)
|
||||
|
||||
storage_flag = False
|
||||
|
|
@ -51,8 +52,59 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.'):
|
|||
Path to working directory to run in. Defaults to the current working directory.
|
||||
|
||||
"""
|
||||
if output:
|
||||
output = 'full'
|
||||
return _run([openmc_exec, '-p'], output, cwd)
|
||||
|
||||
return _run(openmc_exec + ' -p', output, cwd)
|
||||
|
||||
def calculate_volumes(threads=None, output=True, cwd='.',
|
||||
openmc_exec='openmc', mpi_args=None):
|
||||
"""Run stochastic volume calculations in OpenMC.
|
||||
|
||||
This function runs OpenMC in stochastic volume calculation mode. To specify
|
||||
the parameters of a volume calculation, one must first create a
|
||||
:class:`openmc.VolumeCalculation` instance and assign it to
|
||||
:attr:`openmc.Settings.volume_calculations`. For example:
|
||||
|
||||
>>> vol = openmc.VolumeCalculation(domains=[cell1, cell2], samples=100000)
|
||||
>>> settings = openmc.Settings()
|
||||
>>> settings.volume_calculations = [vol]
|
||||
>>> settings.export_to_xml()
|
||||
>>> openmc.calculate_volumes()
|
||||
|
||||
Parameters
|
||||
----------
|
||||
threads : int, optional
|
||||
Number of OpenMP threads. If OpenMC is compiled with OpenMP threading
|
||||
enabled, the default is implementation-dependent but is usually equal to
|
||||
the number of hardware threads available (or a value set by the
|
||||
:envvar:`OMP_NUM_THREADS` environment variable).
|
||||
output : bool, optional
|
||||
Capture OpenMC output from standard out
|
||||
openmc_exec : str, optional
|
||||
Path to OpenMC executable. Defaults to 'openmc'.
|
||||
mpi_args : list of str, optional
|
||||
MPI execute command and any additional MPI arguments to pass,
|
||||
e.g. ['mpiexec', '-n', '8'].
|
||||
cwd : str, optional
|
||||
Path to working directory to run in. Defaults to the current working
|
||||
directory.
|
||||
|
||||
See Also
|
||||
--------
|
||||
openmc.VolumeCalculation
|
||||
|
||||
"""
|
||||
args = [openmc_exec, '--volume']
|
||||
|
||||
if isinstance(threads, Integral) and threads > 0:
|
||||
args += ['-s', str(threads)]
|
||||
if mpi_args is not None:
|
||||
args = mpi_args + args
|
||||
|
||||
if output:
|
||||
output = 'full'
|
||||
return _run(args, output, cwd)
|
||||
|
||||
|
||||
def run(particles=None, threads=None, geometry_debug=False,
|
||||
|
|
@ -90,27 +142,24 @@ def run(particles=None, threads=None, geometry_debug=False,
|
|||
|
||||
"""
|
||||
|
||||
post_args = ' '
|
||||
pre_args = ''
|
||||
args = [openmc_exec]
|
||||
|
||||
if isinstance(particles, Integral) and particles > 0:
|
||||
post_args += '-n {0} '.format(particles)
|
||||
args += ['-n', str(particles)]
|
||||
|
||||
if isinstance(threads, Integral) and threads > 0:
|
||||
post_args += '-s {0} '.format(threads)
|
||||
args += ['-s', str(threads)]
|
||||
|
||||
if geometry_debug:
|
||||
post_args += '-g '
|
||||
args.append('-g')
|
||||
|
||||
if isinstance(restart_file, string_types):
|
||||
post_args += '-r {0} '.format(restart_file)
|
||||
args += ['-r', restart_file]
|
||||
|
||||
if tracks:
|
||||
post_args += '-t'
|
||||
args.append('-t')
|
||||
|
||||
if mpi_args is not None:
|
||||
pre_args = ' '.join(mpi_args) + ' '
|
||||
args = mpi_args + args
|
||||
|
||||
command = pre_args + openmc_exec + ' ' + post_args
|
||||
|
||||
return _run(command, output, cwd)
|
||||
return _run(args, output, cwd)
|
||||
|
|
|
|||
|
|
@ -61,8 +61,16 @@ class Geometry(object):
|
|||
"""
|
||||
if volume_calc.domain_type == 'cell':
|
||||
for cell in self.get_all_cells():
|
||||
if cell.id in volume_calc.results:
|
||||
if cell.id in volume_calc.volumes:
|
||||
cell.add_volume_information(volume_calc)
|
||||
elif volume_calc.domain_type == 'material':
|
||||
for material in self.get_all_materials():
|
||||
if material.id in volume_calc.volumes:
|
||||
material.add_volume_information(volume_calc)
|
||||
elif volume_calc.domain_type == 'universe':
|
||||
for universe in self.get_all_universes():
|
||||
if universe.id in volume_calc.volumes:
|
||||
universe.add_volume_information(volume_calc)
|
||||
|
||||
def export_to_xml(self, path='geometry.xml'):
|
||||
"""Export geometry to an XML file.
|
||||
|
|
|
|||
|
|
@ -71,6 +71,10 @@ class Material(object):
|
|||
The average molar mass of nuclides in the material in units of grams per
|
||||
mol. For example, UO2 with 3 nuclides will have an average molar mass
|
||||
of 270 / 3 = 90 g / mol.
|
||||
volume : float
|
||||
Volume of the material in cm^3. This can either be set manually or
|
||||
calculated in a stochastic volume calculation and added via the
|
||||
:meth:`Material.add_volume_information` method.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -82,6 +86,8 @@ class Material(object):
|
|||
self._density = None
|
||||
self._density_units = ''
|
||||
self._depletable = False
|
||||
self._volume = None
|
||||
self._atoms = {}
|
||||
|
||||
# A list of tuples (nuclide, percent, percent type)
|
||||
self._nuclides = []
|
||||
|
|
@ -226,6 +232,10 @@ class Material(object):
|
|||
# Compute and return the molar mass
|
||||
return mass / moles
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
return self._volume
|
||||
|
||||
@id.setter
|
||||
def id(self, material_id):
|
||||
|
||||
|
|
@ -259,6 +269,12 @@ class Material(object):
|
|||
depletable, bool)
|
||||
self._depletable = depletable
|
||||
|
||||
@volume.setter
|
||||
def volume(self, volume):
|
||||
if volume is not None:
|
||||
cv.check_type('material volume', volume, Real)
|
||||
self._volume = volume
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group):
|
||||
"""Create material from HDF5 group
|
||||
|
|
@ -302,6 +318,24 @@ class Material(object):
|
|||
|
||||
return material
|
||||
|
||||
def add_volume_information(self, volume_calc):
|
||||
"""Add volume information to a material.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
volume_calc : openmc.VolumeCalculation
|
||||
Results from a stochastic volume calculation
|
||||
|
||||
"""
|
||||
if volume_calc.domain_type == 'material':
|
||||
if self.id in volume_calc.volumes:
|
||||
self._volume = volume_calc.volumes[self.id]
|
||||
self._atoms = volume_calc.atoms[self.id]
|
||||
else:
|
||||
raise ValueError('No volume information found for this material.')
|
||||
else:
|
||||
raise ValueError('No volume information found for this material.')
|
||||
|
||||
def set_density(self, units, density=None):
|
||||
"""Set the density of the material
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ _DOMAINS = (openmc.Cell,
|
|||
openmc.Material,
|
||||
openmc.Mesh)
|
||||
|
||||
# Supported ScatterMatrixXS and NuScatterMatrixXS angular distribution types
|
||||
# Supported ScatterMatrixXS angular distribution types
|
||||
MU_TREATMENTS = ('legendre', 'histogram')
|
||||
|
||||
# Maximum Legendre order supported by OpenMC
|
||||
|
|
|
|||
|
|
@ -1563,19 +1563,17 @@ class XSdata(object):
|
|||
temperature=294., nuclide='total',
|
||||
xs_type='macro', subdomain=None):
|
||||
"""This method allows for either the direct use of only an
|
||||
openmc.mgxs.MultiplicityMatrixXS OR
|
||||
an openmc.mgxs.NuScatterMatrixXS and
|
||||
openmc.mgxs.MultiplicityMatrixXS or an openmc.mgxs.ScatterMatrixXS and
|
||||
openmc.mgxs.ScatterMatrixXS to be used to set the scattering
|
||||
multiplicity for this XSdata object. Multiplicity,
|
||||
in OpenMC parlance, is a factor used to account for the production
|
||||
of neutrons introduced by scattering multiplication reactions, i.e.,
|
||||
(n,xn) events. In this sense, the multiplication matrix is simply
|
||||
defined as the ratio of the nu-scatter and scatter matrices.
|
||||
multiplicity for this XSdata object. Multiplicity, in OpenMC parlance,
|
||||
is a factor used to account for the production of neutrons introduced by
|
||||
scattering multiplication reactions, i.e., (n,xn) events. In this sense,
|
||||
the multiplication matrix is simply defined as the ratio of the
|
||||
nu-scatter and scatter matrices.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
nuscatter: {openmc.mgxs.NuScatterMatrixXS,
|
||||
openmc.mgxs.MultiplicityMatrixXS}
|
||||
nuscatter: openmc.mgxs.ScatterMatrixXS or openmc.mgxs.MultiplicityMatrixXS
|
||||
MGXS Object containing the matrix cross section for the domain
|
||||
of interest.
|
||||
scatter: openmc.mgxs.ScatterMatrixXS
|
||||
|
|
@ -1701,19 +1699,21 @@ class XSdata(object):
|
|||
Representation of the MGXS (isotropic or angle-dependent flux
|
||||
weighting).
|
||||
num_polar : int, optional
|
||||
Number of equal width angular bins that the polar angular
|
||||
domain is subdivided into. This is required when
|
||||
:param:`target_representation` is "angle".
|
||||
Number of equal width angular bins that the polar angular domain is
|
||||
subdivided into. This is required when `target_representation` is
|
||||
"angle".
|
||||
|
||||
num_azimuthal : int, optional
|
||||
Number of equal width angular bins that the azimuthal angular
|
||||
domain is subdivided into. This is required when
|
||||
:param:`target_representation` is "angle".
|
||||
Number of equal width angular bins that the azimuthal angular domain
|
||||
is subdivided into. This is required when `target_representation` is
|
||||
"angle".
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.XSdata
|
||||
|
||||
Multi-group cross section data with the same data as self, but
|
||||
represented as specified in :param:`target_representation`.
|
||||
represented as specified in `target_representation`.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -1798,7 +1798,7 @@ class XSdata(object):
|
|||
-------
|
||||
openmc.XSdata
|
||||
Multi-group cross section data with the same data as in self, but
|
||||
represented as specified in :param:`target_format`.
|
||||
represented as specified in `target_format`.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -2447,19 +2447,19 @@ class MGXSLibrary(object):
|
|||
Representation of the MGXS (isotropic or angle-dependent flux
|
||||
weighting).
|
||||
num_polar : int, optional
|
||||
Number of equal width angular bins that the polar angular
|
||||
domain is subdivided into. This is required when
|
||||
:param:`target_representation` is "angle".
|
||||
Number of equal width angular bins that the polar angular domain is
|
||||
subdivided into. This is required when `target_representation` is
|
||||
"angle".
|
||||
num_azimuthal : int, optional
|
||||
Number of equal width angular bins that the azimuthal angular
|
||||
domain is subdivided into. This is required when
|
||||
:param:`target_representation` is "angle".
|
||||
Number of equal width angular bins that the azimuthal angular domain
|
||||
is subdivided into. This is required when `target_representation` is
|
||||
"angle".
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.MGXSLibrary
|
||||
Multi-group Library with the same data as self, but represented as
|
||||
specified in :param:`target_representation`.
|
||||
specified in `target_representation`.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -2486,9 +2486,9 @@ class MGXSLibrary(object):
|
|||
Returns
|
||||
-------
|
||||
openmc.MGXSLibrary
|
||||
Multi-group Library with the same data as self, but with the
|
||||
scatter format represented as specified in :param:`target_format`
|
||||
and :param:`target_order`.
|
||||
Multi-group Library with the same data as self, but with the scatter
|
||||
format represented as specified in `target_format` and
|
||||
`target_order`.
|
||||
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from collections import OrderedDict, Iterable
|
||||
from numbers import Integral
|
||||
from numbers import Integral, Real
|
||||
import random
|
||||
import sys
|
||||
|
||||
|
|
@ -42,6 +42,10 @@ class Universe(object):
|
|||
cells : collections.OrderedDict
|
||||
Dictionary whose keys are cell IDs and values are :class:`Cell`
|
||||
instances
|
||||
volume : float
|
||||
Volume of the universe in cm^3. This can either be set manually or
|
||||
calculated in a stochastic volume calculation and added via the
|
||||
:meth:`Universe.add_volume_information` method.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -49,6 +53,8 @@ class Universe(object):
|
|||
# Initialize Cell class attributes
|
||||
self.id = universe_id
|
||||
self.name = name
|
||||
self._volume = None
|
||||
self._atoms = {}
|
||||
|
||||
# Keys - Cell IDs
|
||||
# Values - Cells
|
||||
|
|
@ -99,6 +105,10 @@ class Universe(object):
|
|||
def cells(self):
|
||||
return self._cells
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
return self._volume
|
||||
|
||||
@id.setter
|
||||
def id(self, universe_id):
|
||||
if universe_id is None:
|
||||
|
|
@ -118,6 +128,12 @@ class Universe(object):
|
|||
else:
|
||||
self._name = ''
|
||||
|
||||
@volume.setter
|
||||
def volume(self, volume):
|
||||
if volume is not None:
|
||||
cv.check_type('universe volume', volume, Real)
|
||||
self._volume = volume
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group, cells):
|
||||
"""Create universe from HDF5 group
|
||||
|
|
@ -147,6 +163,24 @@ class Universe(object):
|
|||
|
||||
return universe
|
||||
|
||||
def add_volume_information(self, volume_calc):
|
||||
"""Add volume information to a universe.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
volume_calc : openmc.VolumeCalculation
|
||||
Results from a stochastic volume calculation
|
||||
|
||||
"""
|
||||
if volume_calc.domain_type == 'cell':
|
||||
if self.id in volume_calc.volumes:
|
||||
self._volume = volume_calc.volumes[self.id]
|
||||
self._atoms = volume_calc.atoms[self.id]
|
||||
else:
|
||||
raise ValueError('No volume information found for this universe.')
|
||||
else:
|
||||
raise ValueError('No volume information found for this universe.')
|
||||
|
||||
def find(self, point):
|
||||
"""Find cells/universes/lattices which contain a given point
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from collections import Iterable, Mapping
|
||||
from collections import Iterable, Mapping, OrderedDict
|
||||
from numbers import Real, Integral
|
||||
from xml.etree import ElementTree as ET
|
||||
from warnings import warn
|
||||
|
|
@ -43,21 +43,21 @@ class VolumeCalculation(object):
|
|||
Lower-left coordinates of bounding box used to sample points
|
||||
upper_right : Iterable of float
|
||||
Upper-right coordinates of bounding box used to sample points
|
||||
results : dict
|
||||
Dictionary whose keys are unique IDs of domains and values are
|
||||
dictionaries with calculated volumes and total number of atoms for each
|
||||
nuclide present in the domain.
|
||||
volumes : dict
|
||||
Dictionary whose keys are unique IDs of domains and values are the
|
||||
estimated volumes
|
||||
atoms : dict
|
||||
Dictionary mapping unique IDs of domains to a mapping of nuclides to
|
||||
total number of atoms for each nuclide present in the domain. For
|
||||
example, {10: {'U235': 1.0e22, 'U238': 5.0e22, ...}}.
|
||||
atoms_dataframe : pandas.DataFrame
|
||||
DataFrame showing the estimated number of atoms for each nuclide present
|
||||
in each domain specified.
|
||||
volumes : dict
|
||||
Dictionary mapping unique IDs of domains to estimated volumes in cm^3.
|
||||
|
||||
"""
|
||||
def __init__(self, domains, samples, lower_left=None,
|
||||
upper_right=None):
|
||||
self._results = None
|
||||
self._atoms = {}
|
||||
self._volumes = {}
|
||||
|
||||
cv.check_type('domains', domains, Iterable,
|
||||
(openmc.Cell, openmc.Material, openmc.Universe))
|
||||
|
|
@ -122,25 +122,25 @@ class VolumeCalculation(object):
|
|||
def upper_right(self):
|
||||
return self._upper_right
|
||||
|
||||
@property
|
||||
def results(self):
|
||||
return self._results
|
||||
|
||||
@property
|
||||
def domain_type(self):
|
||||
return self._domain_type
|
||||
|
||||
@property
|
||||
def atoms(self):
|
||||
return self._atoms
|
||||
|
||||
@property
|
||||
def volumes(self):
|
||||
return {uid: results['volume'] for uid, results in self.results.items()}
|
||||
return self._volumes
|
||||
|
||||
@property
|
||||
def atoms_dataframe(self):
|
||||
items = []
|
||||
columns = [self.domain_type.capitalize(), 'Nuclide', 'Atoms',
|
||||
'Uncertainty']
|
||||
for uid, results in self.results.items():
|
||||
for name, atoms in results['atoms']:
|
||||
for uid, atoms_dict in self.atoms.items():
|
||||
for name, atoms in atoms_dict.items():
|
||||
items.append((uid, name, atoms[0], atoms[1]))
|
||||
|
||||
return pd.DataFrame.from_records(items, columns=columns)
|
||||
|
|
@ -170,10 +170,15 @@ class VolumeCalculation(object):
|
|||
cv.check_length(name, upper_right, 3)
|
||||
self._upper_right = upper_right
|
||||
|
||||
@results.setter
|
||||
def results(self, results):
|
||||
cv.check_type('results', results, Mapping)
|
||||
self._results = results
|
||||
@volumes.setter
|
||||
def volumes(self, volumes):
|
||||
cv.check_type('volumes', volumes, Mapping)
|
||||
self._volumes = volumes
|
||||
|
||||
@atoms.setter
|
||||
def atoms(self, atoms):
|
||||
cv.check_type('atoms', atoms, Mapping)
|
||||
self._atoms = atoms
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, filename):
|
||||
|
|
@ -198,7 +203,8 @@ class VolumeCalculation(object):
|
|||
lower_left = f.attrs['lower_left']
|
||||
upper_right = f.attrs['upper_right']
|
||||
|
||||
results = {}
|
||||
volumes = {}
|
||||
atoms = {}
|
||||
ids = []
|
||||
for obj_name in f:
|
||||
if obj_name.startswith('domain_'):
|
||||
|
|
@ -207,12 +213,13 @@ class VolumeCalculation(object):
|
|||
group = f[obj_name]
|
||||
volume = tuple(group['volume'].value)
|
||||
nucnames = group['nuclides'].value
|
||||
atoms = group['atoms'].value
|
||||
atoms_ = group['atoms'].value
|
||||
|
||||
atom_list = []
|
||||
for name_i, atoms_i in zip(nucnames, atoms):
|
||||
atom_list.append((name_i.decode(), tuple(atoms_i)))
|
||||
results[domain_id] = {'volume': volume, 'atoms': atom_list}
|
||||
atom_dict = OrderedDict()
|
||||
for name_i, atoms_i in zip(nucnames, atoms_):
|
||||
atom_dict[name_i.decode()] = tuple(atoms_i)
|
||||
volumes[domain_id] = volume
|
||||
atoms[domain_id] = atom_dict
|
||||
|
||||
# Instantiate some throw-away domains that are used by the constructor
|
||||
# to assign IDs
|
||||
|
|
@ -225,9 +232,30 @@ class VolumeCalculation(object):
|
|||
|
||||
# Instantiate the class and assign results
|
||||
vol = cls(domains, samples, lower_left, upper_right)
|
||||
vol.results = results
|
||||
vol.volumes = volumes
|
||||
vol.atoms = atoms
|
||||
return vol
|
||||
|
||||
def load_results(self, filename):
|
||||
"""Load stochastic volume calculation results from an HDF5 file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filename : str
|
||||
Path to volume.h5 file
|
||||
|
||||
"""
|
||||
results = type(self).from_hdf5(filename)
|
||||
|
||||
# Make sure properties match
|
||||
assert self.domains == results.domains
|
||||
assert self.lower_left == results.lower_left
|
||||
assert self.upper_right == results.upper_right
|
||||
|
||||
# Copy results
|
||||
self.volumes = results.volumes
|
||||
self.atoms = results.atoms
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the volume calculation
|
||||
|
||||
|
|
|
|||
|
|
@ -365,6 +365,9 @@ contains
|
|||
case ('-g', '-geometry-debug', '--geometry-debug')
|
||||
check_overlaps = .true.
|
||||
|
||||
case ('-c', '--volume')
|
||||
run_mode = MODE_VOLUME
|
||||
|
||||
case ('-s', '--threads')
|
||||
! Read number of threads
|
||||
i = i + 1
|
||||
|
|
|
|||
|
|
@ -194,7 +194,8 @@ contains
|
|||
write(OUTPUT_UNIT,*) 'Usage: openmc [options] [directory]'
|
||||
write(OUTPUT_UNIT,*)
|
||||
write(OUTPUT_UNIT,*) 'Options:'
|
||||
write(OUTPUT_UNIT,*) ' -g, --geometry-debug Run in geometry debugging mode'
|
||||
write(OUTPUT_UNIT,*) ' -c, --volume Run in stochastic volume calculation mode'
|
||||
write(OUTPUT_UNIT,*) ' -g, --geometry-debug Run with geometry debugging on'
|
||||
write(OUTPUT_UNIT,*) ' -n, --particles Number of particles per generation'
|
||||
write(OUTPUT_UNIT,*) ' -p, --plot Run in plotting mode'
|
||||
write(OUTPUT_UNIT,*) ' -r, --restart Restart a previous run from a state point'
|
||||
|
|
|
|||
|
|
@ -27,23 +27,12 @@ element materials {
|
|||
attribute name { xsd:string })
|
||||
}* &
|
||||
|
||||
element element {
|
||||
(element name { xsd:string { maxLength = "2" } } |
|
||||
attribute name { xsd:string { maxLength = "2" } }) &
|
||||
(element scattering { ( "data" | "iso-in-lab" ) } |
|
||||
attribute scattering { ( "data" | "iso-in-lab" ) })? &
|
||||
(
|
||||
(element ao { xsd:double } | attribute ao { xsd:double }) |
|
||||
(element wo { xsd:double } | attribute wo { xsd:double })
|
||||
)
|
||||
}* &
|
||||
|
||||
element sab {
|
||||
(element name { xsd:string } | attribute name { xsd:string })
|
||||
}*
|
||||
}+
|
||||
}+ &
|
||||
|
||||
element cross_sections { xsd:string { maxLength = "255" } }? &
|
||||
|
||||
element multipole_library { xsd:string { maxLength = "255" } }? &
|
||||
element multipole_library { xsd:string { maxLength = "255" } }?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +1,112 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<element name="materials" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
|
||||
<oneOrMore>
|
||||
<element name="material">
|
||||
<interleave>
|
||||
<choice>
|
||||
<element name="id">
|
||||
<data type="int"/>
|
||||
</element>
|
||||
<attribute name="id">
|
||||
<data type="int"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
<optional>
|
||||
<interleave>
|
||||
<oneOrMore>
|
||||
<element name="material">
|
||||
<interleave>
|
||||
<choice>
|
||||
<element name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">52</param>
|
||||
</data>
|
||||
<element name="id">
|
||||
<data type="int"/>
|
||||
</element>
|
||||
<attribute name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">52</param>
|
||||
</data>
|
||||
<attribute name="id">
|
||||
<data type="int"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="temperature">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
</optional>
|
||||
<element name="density">
|
||||
<interleave>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="value">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="value">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="units">
|
||||
<element name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">10</param>
|
||||
<param name="maxLength">52</param>
|
||||
</data>
|
||||
</element>
|
||||
<attribute name="units">
|
||||
<attribute name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">10</param>
|
||||
<param name="maxLength">52</param>
|
||||
</data>
|
||||
</attribute>
|
||||
</choice>
|
||||
</interleave>
|
||||
</element>
|
||||
<zeroOrMore>
|
||||
<element name="nuclide">
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="temperature">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
</optional>
|
||||
<element name="density">
|
||||
<interleave>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="value">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="value">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<choice>
|
||||
<element name="units">
|
||||
<data type="string">
|
||||
<param name="maxLength">10</param>
|
||||
</data>
|
||||
</element>
|
||||
<attribute name="units">
|
||||
<data type="string">
|
||||
<param name="maxLength">10</param>
|
||||
</data>
|
||||
</attribute>
|
||||
</choice>
|
||||
</interleave>
|
||||
</element>
|
||||
<zeroOrMore>
|
||||
<element name="nuclide">
|
||||
<interleave>
|
||||
<choice>
|
||||
<element name="name">
|
||||
<data type="string"/>
|
||||
</element>
|
||||
<attribute name="name">
|
||||
<data type="string"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="scattering">
|
||||
<choice>
|
||||
<value>data</value>
|
||||
<value>iso-in-lab</value>
|
||||
</choice>
|
||||
</element>
|
||||
<attribute name="scattering">
|
||||
<choice>
|
||||
<value>data</value>
|
||||
<value>iso-in-lab</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<choice>
|
||||
<choice>
|
||||
<element name="ao">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="ao">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
<choice>
|
||||
<element name="wo">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="wo">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</choice>
|
||||
</interleave>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<element name="macroscopic">
|
||||
<choice>
|
||||
<element name="name">
|
||||
<data type="string"/>
|
||||
|
|
@ -67,120 +115,36 @@
|
|||
<data type="string"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="scattering">
|
||||
<choice>
|
||||
<value>data</value>
|
||||
<value>iso-in-lab</value>
|
||||
</choice>
|
||||
</element>
|
||||
<attribute name="scattering">
|
||||
<choice>
|
||||
<value>data</value>
|
||||
<value>iso-in-lab</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<choice>
|
||||
<choice>
|
||||
<element name="ao">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="ao">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
<choice>
|
||||
<element name="wo">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="wo">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</choice>
|
||||
</interleave>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<element name="macroscopic">
|
||||
<choice>
|
||||
<element name="name">
|
||||
<data type="string"/>
|
||||
</element>
|
||||
<attribute name="name">
|
||||
<data type="string"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<element name="element">
|
||||
<interleave>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<element name="sab">
|
||||
<choice>
|
||||
<element name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">2</param>
|
||||
</data>
|
||||
<data type="string"/>
|
||||
</element>
|
||||
<attribute name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">2</param>
|
||||
</data>
|
||||
<data type="string"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="scattering">
|
||||
<choice>
|
||||
<value>data</value>
|
||||
<value>iso-in-lab</value>
|
||||
</choice>
|
||||
</element>
|
||||
<attribute name="scattering">
|
||||
<choice>
|
||||
<value>data</value>
|
||||
<value>iso-in-lab</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<choice>
|
||||
<choice>
|
||||
<element name="ao">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="ao">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
<choice>
|
||||
<element name="wo">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="wo">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</choice>
|
||||
</interleave>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
<zeroOrMore>
|
||||
<element name="sab">
|
||||
<choice>
|
||||
<element name="name">
|
||||
<data type="string"/>
|
||||
</element>
|
||||
<attribute name="name">
|
||||
<data type="string"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</interleave>
|
||||
</element>
|
||||
</oneOrMore>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</interleave>
|
||||
</element>
|
||||
</oneOrMore>
|
||||
<optional>
|
||||
<element name="cross_sections">
|
||||
<data type="string">
|
||||
<param name="maxLength">255</param>
|
||||
</data>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="multipole_library">
|
||||
<data type="string">
|
||||
<param name="maxLength">255</param>
|
||||
</data>
|
||||
</element>
|
||||
</optional>
|
||||
</interleave>
|
||||
</element>
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ from testing_harness import ParticleRestartTestHarness
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
harness = ParticleRestartTestHarness('particle_10_1030.*')
|
||||
harness = ParticleRestartTestHarness('particle_10_1030.h5')
|
||||
harness.main()
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ class VolumeTest(PyAPITestHarness):
|
|||
bottom_hemisphere = openmc.Cell(3, fill=water, region=-bottom_sphere & -top_plane)
|
||||
root = openmc.Universe(0, cells=(inside_cyl, top_hemisphere, bottom_hemisphere))
|
||||
|
||||
geometry = openmc.Geometry()
|
||||
geometry.root_universe = root
|
||||
geometry = openmc.Geometry(root)
|
||||
geometry.export_to_xml()
|
||||
|
||||
# Set up stochastic volume calculation
|
||||
|
|
@ -63,13 +62,13 @@ class VolumeTest(PyAPITestHarness):
|
|||
outstr += 'Volume calculation {}\n'.format(i)
|
||||
|
||||
# Read volume calculation results
|
||||
vol = openmc.VolumeCalculation.from_hdf5(filename)
|
||||
volume_calc = openmc.VolumeCalculation.from_hdf5(filename)
|
||||
|
||||
# Write cell volumes and total # of atoms for each nuclide
|
||||
for uid, results in sorted(vol.results.items()):
|
||||
for uid, volume in sorted(volume_calc.volumes.items()):
|
||||
outstr += 'Domain {0}: {1[0]:.4f} +/- {1[1]:.4f} cm^3\n'.format(
|
||||
uid, results['volume'])
|
||||
outstr += str(vol.atoms_dataframe) + '\n'
|
||||
uid, volume)
|
||||
outstr += str(volume_calc.atoms_dataframe) + '\n'
|
||||
|
||||
return outstr
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue