Get rid of extra Settings class

This commit is contained in:
Paul Romano 2018-02-19 17:52:17 -06:00
parent ccfee55115
commit a460782691
8 changed files with 77 additions and 151 deletions

View file

@ -29,7 +29,6 @@ Metaclasses
:toctree: generated
:nosignatures:
openmc.deplete.Settings
openmc.deplete.Operator
OpenMC Classes
@ -39,7 +38,6 @@ OpenMC Classes
:toctree: generated
:nosignatures:
openmc.deplete.OpenMCSettings
openmc.deplete.Materials
openmc.deplete.OpenMCOperator

View file

@ -7,44 +7,9 @@ to run a full depletion simulation.
from collections import namedtuple
import os
from pathlib import Path
from abc import ABCMeta, abstractmethod
class Settings(object):
"""The Settings class.
Contains all parameters necessary for the integrator.
Attributes
----------
output_dir : pathlib.Path
Path to output directory to save results.
chain_file : str
Path to the depletion chain XML file. Defaults to the
:envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists.
dilute_initial : float
Initial atom density to add for nuclides that are zero in initial
condition to ensure they exist in the decay chain. Only done for
nuclides with reaction rates. Defaults to 1.0e3.
"""
def __init__(self):
try:
self.chain_file = os.environ["OPENMC_DEPLETE_CHAIN"]
except KeyError:
self.chain_file = None
self.output_dir = '.'
self.dilute_initial = 1.0e3
@property
def output_dir(self):
return self._output_dir
@output_dir.setter
def output_dir(self, output_dir):
self._output_dir = Path(output_dir)
from .chain import Chain
OperatorResult = namedtuple('OperatorResult', ['k', 'rates'])
@ -52,14 +17,30 @@ OperatorResult = namedtuple('OperatorResult', ['k', 'rates'])
class Operator(metaclass=ABCMeta):
"""Abstract class defining a transport operator
Parameters
----------
chain_file : str, optional
Attributes
----------
settings : Settings
Settings object.
dilute_initial : float
Initial atom density to add for nuclides that are zero in initial
condition to ensure they exist in the decay chain. Only done for
nuclides with reaction rates. Defaults to 1.0e3.
"""
def __init__(self, settings):
self.settings = settings
def __init__(self, chain_file=None):
self.dilute_initial = 1.0e3
self.output_dir = '.'
# Read depletion chain
if chain_file is None:
chain_file = os.environ.get("OPENMC_DEPLETE_CHAIN", None)
if chain_file is None:
raise IOError("No chain specified, either manually or in "
"environment variable OPENMC_DEPLETE_CHAIN.")
self.chain = Chain.from_xml(chain_file)
@abstractmethod
def __call__(self, vec, print_out=True):
@ -83,11 +64,11 @@ class Operator(metaclass=ABCMeta):
def __enter__(self):
# Save current directory and move to specific output directory
self._orig_dir = os.getcwd()
if not self.settings.output_dir.exists():
self.settings.output_dir.mkdir() # exist_ok parameter is 3.5+
if not self.output_dir.exists():
self.output_dir.mkdir() # exist_ok parameter is 3.5+
# In Python 3.6+, chdir accepts a Path directly
os.chdir(str(self.settings.output_dir))
os.chdir(str(self.output_dir))
return self.initial_condition()
@ -95,6 +76,14 @@ class Operator(metaclass=ABCMeta):
self.finalize()
os.chdir(self._orig_dir)
@property
def output_dir(self):
return self._output_dir
@output_dir.setter
def output_dir(self, output_dir):
self._output_dir = Path(output_dir)
@abstractmethod
def initial_condition(self):
"""Performs final setup and returns initial condition.

View file

@ -328,15 +328,7 @@ class Chain(object):
chain = cls()
# Load XML tree
try:
root = ET.parse(filename)
except Exception:
if filename is None:
msg = ("No chain specified, either manually or in environment "
"variable OPENMC_DEPLETE_CHAIN.")
else:
msg = 'Decay chain "{}" is invalid.'.format(filename)
raise IOError(msg)
root = ET.parse(str(filename))
for i, nuclide_elem in enumerate(root.findall('nuclide_table')):
nuc = Nuclide.from_xml(nuclide_elem)
@ -367,10 +359,10 @@ class Chain(object):
tree = ET.ElementTree(root_elem)
if _have_lxml:
tree.write(filename, encoding='utf-8', pretty_print=True)
tree.write(str(filename), encoding='utf-8', pretty_print=True)
else:
clean_xml_indentation(root_elem)
tree.write(filename, encoding='utf-8')
tree.write(str(filename), encoding='utf-8')
def form_matrix(self, rates):
"""Forms depletion matrix.

View file

@ -24,9 +24,8 @@ import openmc
import openmc.capi
from openmc.data import JOULE_PER_EV
from . import comm
from .abc import Settings, Operator, OperatorResult
from .abc import Operator, OperatorResult
from .atom_number import AtomNumber
from .chain import Chain
from .reaction_rates import ReactionRates
@ -40,77 +39,34 @@ def _distribute(items):
j += chunk_size
class OpenMCSettings(Settings):
"""Extends Settings to provide information OpenMC needs to run.
Attributes
----------
output_dir : pathlib.Path
Path to output directory to save results.
chain_file : str
Path to the depletion chain XML file. Defaults to the
:envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists.
dilute_initial : float
Initial atom density to add for nuclides that are zero in initial
condition to ensure they exist in the decay chain. Only done for
nuclides with reaction rates. Defaults to 1.0e3.
power : float
Power of the reactor in [W]. For a 2D problem, the power can be given in
W/cm as long as the "volume" assigned to a depletion material is
actually an area in cm^2.
round_number : bool
Whether or not to round output to OpenMC to 8 digits.
Useful in testing, as OpenMC is incredibly sensitive to exact values.
settings : openmc.Settings
Settings for OpenMC simulations
"""
_depletion_attrs = {'_output_dir', 'chain_file', 'dilute_initial',
'round_number'}
def __init__(self):
super().__init__()
self.round_number = False
# Avoid setattr to create OpenMC settings
self.__dict__['settings'] = openmc.Settings()
def __setattr__(self, name, value):
if hasattr(self.__class__, name):
# Use properties when appropriate
prop = getattr(self.__class__, name)
prop.fset(self, value)
elif name in self._depletion_attrs:
# For known attributes, store in dictionary
self.__dict__[name] = value
else:
# otherwise, delegate to openmc.Settings
setattr(self.__dict__['settings'], name, value)
def __getattr__(self, name):
if name in self._depletion_attrs:
return self.__dict__[name]
else:
return getattr(self.__dict__['settings'], name)
class OpenMCOperator(Operator):
"""OpenMC transport operator
Parameters
----------
geometry : openmc.Geometry
The OpenMC geometry object.
settings : openmc.deplete.OpenMCSettings
Settings object.
OpenMC geometry object
settings : openmc.Settings
OpenMC Settings object
chain_file : str, optional
Path to the depletion chain XML file. Defaults to the
:envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists.
Attributes
----------
settings : OpenMCSettings
Settings object. (From Operator)
geometry : openmc.Geometry
The OpenMC geometry object.
OpenMC geometry object
settings : openmc.Settings
OpenMC settings object
dilute_initial : float
Initial atom density to add for nuclides that are zero in initial
condition to ensure they exist in the decay chain. Only done for
nuclides with reaction rates. Defaults to 1.0e3.
output_dir : pathlib.Path
Path to output directory to save results.
round_number : bool
Whether or not to round output to OpenMC to 8 digits.
Useful in testing, as OpenMC is incredibly sensitive to exact values.
number : openmc.deplete.AtomNumber
Total number of atoms in simulation.
nuclides_with_data : set of str
@ -125,13 +81,12 @@ class OpenMCOperator(Operator):
All burnable material IDs being managed by a single process
"""
def __init__(self, geometry, settings):
super().__init__(settings)
def __init__(self, geometry, settings, chain_file=None):
super().__init__(chain_file)
self.round_number = False
self.settings = settings
self.geometry = geometry
# Read depletion chain
self.chain = Chain.from_xml(settings.chain_file)
# Clear out OpenMC, create task lists, distribute
openmc.reset_auto_ids()
self.burnable_mats, volume, nuclides = self._get_burnable_mats()
@ -253,10 +208,9 @@ class OpenMCOperator(Operator):
"""
self.number = AtomNumber(local_mats, nuclides, volume, len(self.chain))
if self.settings.dilute_initial != 0.0:
if self.dilute_initial != 0.0:
for nuc in self._burnable_nucs:
self.number.set_atom_density(np.s_[:], nuc,
self.settings.dilute_initial)
self.number.set_atom_density(np.s_[:], nuc, self.dilute_initial)
# Now extract the number densities and store
for mat in self.geometry.get_all_materials().values():
@ -290,7 +244,7 @@ class OpenMCOperator(Operator):
# Create XML files
if comm.rank == 0:
self.geometry.export_to_xml()
self.settings.settings.export_to_xml()
self.settings.export_to_xml()
self._generate_materials_xml()
# Initialize OpenMC library
@ -322,7 +276,7 @@ class OpenMCOperator(Operator):
# If nuclide is zero, do not add to the problem.
if val > 0.0:
if self.settings.round_number:
if self.round_number:
val_magnitude = np.floor(np.log10(val))
val_scaled = val / 10**val_magnitude
val_round = round(val_scaled, 8)

View file

@ -17,9 +17,8 @@ class DummyGeometry(Operator):
y_2(1.5) ~ 3.1726475740397628
"""
def __init__(self, settings):
super().__init__(settings)
def __init__(self):
pass
def __call__(self, vec, power, print_out=False):
"""Evaluates F(y)

View file

@ -32,13 +32,8 @@ def test_full(run_in_tmpdir):
# Load geometry from example
geometry, lower_left, upper_right = generate_problem(n_rings, n_wedges)
# Depletion settings
settings = openmc.deplete.OpenMCSettings()
settings.chain_file = str(Path(__file__).parents[2] / 'chains' /
'chain_simple.xml')
settings.round_number = True
# Add OpenMC-specific settings
# OpenMC-specific settings
settings = openmc.Settings()
settings.particles = 100
settings.batches = 100
settings.inactive = 40
@ -47,7 +42,10 @@ def test_full(run_in_tmpdir):
settings.seed = 1
settings.verbosity = 3
op = openmc.deplete.OpenMCOperator(geometry, settings)
# Create operator
chain_file = Path(__file__).parents[2] / 'chains' / 'chain_simple.xml'
op = openmc.deplete.OpenMCOperator(geometry, settings, chain_file)
op.round_number = True
# Power and timesteps
dt1 = 15.*24*60*60 # 15 days
@ -60,7 +58,7 @@ def test_full(run_in_tmpdir):
openmc.deplete.integrator.predictor(op, dt, power)
# Get path to test and reference results
path_test = settings.output_dir / 'depletion_results.h5'
path_test = op.output_dir / 'depletion_results.h5'
path_reference = Path(__file__).with_name('test_reference.h5')
# If updating results, do so and return

View file

@ -14,10 +14,8 @@ from tests import dummy_geometry
def test_cecm(run_in_tmpdir):
"""Integral regression test of integrator algorithm using CE/CM."""
settings = openmc.deplete.Settings()
settings.output_dir = "test_integrator_regression"
op = dummy_geometry.DummyGeometry(settings)
op = dummy_geometry.DummyGeometry()
op.output_dir = "test_integrator_regression"
# Perform simulation using the MCNPX/MCNP6 algorithm
dt = [0.75, 0.75]
@ -25,7 +23,7 @@ def test_cecm(run_in_tmpdir):
openmc.deplete.cecm(op, dt, power, print_out=False)
# Load the files
res = results.read_results(settings.output_dir / "depletion_results.h5")
res = results.read_results(op.output_dir / "depletion_results.h5")
_, y1 = utilities.evaluate_single_nuclide(res, "1", "1")
_, y2 = utilities.evaluate_single_nuclide(res, "1", "2")

View file

@ -14,10 +14,8 @@ from tests import dummy_geometry
def test_predictor(run_in_tmpdir):
"""Integral regression test of integrator algorithm using predictor/corrector"""
settings = openmc.deplete.Settings()
settings.output_dir = "test_integrator_regression"
op = dummy_geometry.DummyGeometry(settings)
op = dummy_geometry.DummyGeometry()
op.output_dir = "test_integrator_regression"
# Perform simulation using the predictor algorithm
dt = [0.75, 0.75]
@ -25,7 +23,7 @@ def test_predictor(run_in_tmpdir):
openmc.deplete.predictor(op, dt, power, print_out=False)
# Load the files
res = results.read_results(settings.output_dir / "depletion_results.h5")
res = results.read_results(op.output_dir / "depletion_results.h5")
_, y1 = utilities.evaluate_single_nuclide(res, "1", "1")
_, y2 = utilities.evaluate_single_nuclide(res, "1", "2")