Change how volume is stored

This commit is contained in:
Paul Romano 2017-02-22 13:06:07 -06:00
parent acdf287c01
commit cfb6339698
6 changed files with 114 additions and 68 deletions

View file

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

View file

@ -61,15 +61,15 @@ 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.results:
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.results:
if universe.id in volume_calc.volumes:
universe.add_volume_information(volume_calc)
def export_to_xml(self, path='geometry.xml'):

View file

@ -71,9 +71,9 @@ 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_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 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.
"""
@ -86,7 +86,8 @@ class Material(object):
self._density = None
self._density_units = ''
self._depletable = False
self._volume_information = None
self._volume = None
self._atoms = {}
# A list of tuples (nuclide, percent, percent type)
self._nuclides = []
@ -232,8 +233,8 @@ class Material(object):
return mass / moles
@property
def volume_information(self):
return self._volume_information
def volume(self):
return self._volume
@id.setter
def id(self, material_id):
@ -268,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
@ -321,10 +328,9 @@ class Material(object):
"""
if volume_calc.domain_type == 'material':
for mat_id in volume_calc.results:
if mat_id == self.id:
self._volume_information = volume_calc.results[mat_id]
break
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:

View file

@ -1,5 +1,5 @@
from collections import OrderedDict, Iterable
from numbers import Integral
from numbers import Integral, Real
import random
import sys
@ -42,9 +42,9 @@ class Universe(object):
cells : collections.OrderedDict
Dictionary whose keys are cell IDs and values are :class:`Cell`
instances
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 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.
"""
@ -53,7 +53,8 @@ class Universe(object):
# Initialize Cell class attributes
self.id = universe_id
self.name = name
self._volume_information = None
self._volume = None
self._atoms = {}
# Keys - Cell IDs
# Values - Cells
@ -105,8 +106,8 @@ class Universe(object):
return self._cells
@property
def volume_information(self):
return self._volume_information
def volume(self):
return self._volume
@id.setter
def id(self, universe_id):
@ -127,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
@ -166,10 +173,9 @@ class Universe(object):
"""
if volume_calc.domain_type == 'cell':
for univ_id in volume_calc.results:
if univ_id == self.id:
self._volume_information = volume_calc.results[univ_id]
break
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:

View file

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

View file

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