From 8f48ecf7dec4ee6a36b431b965ce23c91429f096 Mon Sep 17 00:00:00 2001 From: amandalund Date: Thu, 18 Apr 2019 08:22:40 -0500 Subject: [PATCH 1/5] Add method for generating settings from an XML file --- openmc/mesh.py | 37 +++++ openmc/settings.py | 255 +++++++++++++++++++++++++++++++++-- openmc/source.py | 76 ++++++++++- openmc/stats/multivariate.py | 130 ++++++++++++++++++ openmc/stats/univariate.py | 155 +++++++++++++++++++++ 5 files changed, 641 insertions(+), 12 deletions(-) diff --git a/openmc/mesh.py b/openmc/mesh.py index ba07ec178b..a30b333fb3 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -246,6 +246,43 @@ class Mesh(IDManagerMixin): return element + @classmethod + def from_xml_element(cls, elem): + """Generate mesh from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Mesh + Mesh generated from XML element + + """ + mesh_id = int(elem.get('id')) + mesh = cls(mesh_id) + mesh.type = elem.get('type') + + dimension = elem.findtext('dimension') + if dimension is not None: + mesh.dimension = [int(x) for x in dimension.split()] + + lower_left = elem.findtext('lower_left') + if lower_left is not None: + mesh.lower_left = [float(x) for x in lower_left.split()] + + upper_right = elem.findtext('upper_right') + if upper_right is not None: + mesh.upper_right = [float(x) for x in upper_right.split()] + + width = elem.findtext('width') + if width is not None: + mesh.width = [float(x) for x in width.split()] + + return mesh + def build_cells(self, bc=['reflective'] * 6): """Generates a lattice of universes with the same dimensionality as the mesh object. The individual cells/universes produced diff --git a/openmc/settings.py b/openmc/settings.py index f348bc6cb0..a7ec086792 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -693,29 +693,29 @@ class Settings(object): elem = ET.SubElement(root, "run_mode") elem.text = self._run_mode - def _create_batches_subelement(self, run_mode_element): + def _create_batches_subelement(self, root): if self._batches is not None: - element = ET.SubElement(run_mode_element, "batches") + element = ET.SubElement(root, "batches") element.text = str(self._batches) - def _create_generations_per_batch_subelement(self, run_mode_element): + def _create_generations_per_batch_subelement(self, root): if self._generations_per_batch is not None: - element = ET.SubElement(run_mode_element, "generations_per_batch") + element = ET.SubElement(root, "generations_per_batch") element.text = str(self._generations_per_batch) - def _create_inactive_subelement(self, run_mode_element): + def _create_inactive_subelement(self, root): if self._inactive is not None: - element = ET.SubElement(run_mode_element, "inactive") + element = ET.SubElement(root, "inactive") element.text = str(self._inactive) - def _create_particles_subelement(self, run_mode_element): + def _create_particles_subelement(self, root): if self._particles is not None: - element = ET.SubElement(run_mode_element, "particles") + element = ET.SubElement(root, "particles") element.text = str(self._particles) - def _create_keff_trigger_subelement(self, run_mode_element): + def _create_keff_trigger_subelement(self, root): if self._keff_trigger is not None: - element = ET.SubElement(run_mode_element, "keff_trigger") + element = ET.SubElement(root, "keff_trigger") for key in self._keff_trigger: subelement = ET.SubElement(element, key) @@ -985,3 +985,238 @@ class Settings(object): # Write the XML Tree to the settings.xml file tree = ET.ElementTree(root_element) tree.write(str(p), xml_declaration=True, encoding='utf-8') + + @classmethod + def from_xml(cls, path='settings.xml'): + """Generate settings from XML file + + Parameters + ---------- + path : str, optional + Path to settings XML file + + Returns + ------- + openmc.Settings + Settings object + + """ + tree = ET.parse(path) + root = tree.getroot() + + settings = cls() + + # Get the run mode + elem = root.find('run_mode') + if elem is not None: + settings.run_mode = elem.text + + # Get number of particles + elem = root.find('particles') + if elem is not None: + settings.particles = int(elem.text) + + # Get number of batches + elem = root.find('batches') + if elem is not None: + settings.batches = int(elem.text) + + # Get number of inactive batches + elem = root.find('inactive') + if elem is not None: + settings.inactive = int(elem.text) + + # Get number of generations per batch + elem = root.find('generations_per_batch') + if elem is not None: + settings.generations_per_batch = int(elem.text) + + # Get keff trigger + elem = root.find('keff_trigger') + if elem is not None: + trigger = elem.findtext('type') + threshold = float(elem.findtext('threshold')) + settings.keff_trigger = {'type': trigger, 'threshold': threshold} + + # Get the source + for elem in root.findall('source'): + settings.source.append(Source.from_xml_element(elem)) + + # Get the output + elem = root.find('output') + if elem is not None: + settings.output = {} + for entry in elem: + key = entry.tag + if key in ('summary', 'tallies'): + value = entry.text == 'true' + else: + value = entry.text + settings.output[key] = value + + # Get the statepoint + elem = root.find('state_point') + if elem is not None: + batches = elem.findtext('batches') + if batches is not None: + settings.statepoint['batches'] = [int(x) for x in batches.split()] + + # Get the sourcepoint + elem = root.find('source_point') + if elem is not None: + for entry in elem: + key = entry.tag + if key in ('separate', 'write', 'overwrite'): + value = entry.text == 'true' + else: + value = [int(x) for x in entry.text.split()] + settings.sourcepoint[key] = value + + # Get confidence intervals + elem = root.find('confidence_intervals') + if elem is not None: + settings.confidence_intervals = elem.text == 'true' + + # Get electron treatment + elem = root.find('electron_treatment') + if elem is not None: + settings.electron_treatment = elem.text + + # Get energy mode + elem = root.find('energy_mode') + if elem is not None: + settings.energy_mode = elem.text + + # Get max order + elem = root.find('max_order') + if elem is not None: + settings.max_order = int(elem.text) + + # Get photon transport + elem = root.find('photon_transport') + if elem is not None: + settings.photon_transport = elem.text == 'true' + + # Get probability tables + elem = root.find('ptables') + if elem is not None: + settings.ptables = elem.text == 'true' + + # Get seed + elem = root.find('seed') + if elem is not None: + settings.seed = int(elem.text) + + # Get survival biasing + elem = root.find('survival_biasing') + if elem is not None: + settings.survival_biasing = elem.text == 'true' + + # Get cutoff + elem = root.find('cutoff') + if elem is not None: + settings.cutoff = {x.tag: float(x.text) for x in elem} + + # Get entropy mesh + elem = root.find('entropy_mesh') + if elem is not None: + settings.entropy_mesh = Mesh.from_xml_element(elem) + + # Get trigger + elem = root.find('trigger') + if elem is not None: + active = elem.find('active') + settings.trigger_active = active.text == 'true' + max_batches = elem.find('max_batches') + if max_batches is not None: + settings.trigger_max_batches = int(max_batches.text) + batch_interval = elem.find('batch_interval') + if batch_interval is not None: + settings.trigger_batch_interval = int(batch_interval.text) + + # Get no reduce + elem = root.find('no_reduce') + if elem is not None: + settings.no_reduce = elem.text == 'true' + + # Get verbosity + elem = root.find('verbosity') + if elem is not None: + settings.verbosity = int(elem.text) + + # Get tabular legendre + elem = root.find('tabular_legendre') + if elem is not None: + enable = elem.findtext('eneable') + settings.tabular_legendre['enable'] = enable == 'true' + num_points = elem.findtext('num_points') + if num_points is not None: + settings.tabular_legendre['num_points'] = int(num_points) + + # Get temperature + elem = root.findtext('temperature_default') + if elem is not None: + settings.temperature['default'] = float(elem) + elem = root.findtext('temperature_tolerance') + if elem is not None: + settings.temperature['tolerance'] = float(elem) + elem = root.findtext('temperature_method') + if elem is not None: + settings.temperature['method'] = elem + elem = root.findtext('temperature_range') + if elem is not None: + settings.temperature['range'] = [float(x) for x in elem.split()] + elem = root.findtext('temperature_multipole') + if elem is not None: + settings.temperature['multipole'] = elem == 'true' + + # Get trace + elem = root.find('trace') + if elem is not None: + settings.trace = [int(x) for x in elem.text.split()] + + # Get track + elem = root.find('track') + if elem is not None: + settings.track = [int(x) for x in elem.text.split()] + + # Get UFS mesh + elem = root.find('ufs_mesh') + if elem is not None: + settings.ufs_mesh = Mesh.from_xml_element(elem) + + # Get resonance scattering + elem = root.find('resonance_scattering') + if elem is not None: + for entry in elem: + key = entry.tag + if key == 'enable': + value = entry.text == 'true' + elif key == 'method': + value = entry.text + elif key == 'energy_min': + value = float(entry.text) + elif key == 'energy_max': + value = float(entry.text) + elif key == 'nuclides': + value = entry.text.split() + settings.resonance_scattering[key] = value + + # TODO: Get volume calculations + + # Get fission neutrons + elem = root.find('create_fission_neutrons') + if elem is not None: + settings.create_fission_neutrons = elem.text == 'true' + + # Get log grid bins + elem = root.find('log_grid_bins') + if elem is not None: + settings.log_grid_bins = int(elem.text) + + # Get dagmc + elem = root.find('dagmc') + if elem is not None: + settings.dagmc = elem.text == 'true' + + return settings diff --git a/openmc/source.py b/openmc/source.py index 6ec882ca6a..e278d80892 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -2,8 +2,11 @@ from numbers import Real import sys from xml.etree import ElementTree as ET -from openmc.stats.univariate import Univariate -from openmc.stats.multivariate import UnitSphere, Spatial +from openmc.stats.univariate import (Univariate, Discrete, Uniform, Maxwell, + Watt, Normal, Muir, Tabular) +from openmc.stats.multivariate import (UnitSphere, Spatial, PolarAzimuthal, + Isotropic, Monodirectional, Box, Point, + CartesianIndependent) import openmc.checkvalue as cv @@ -137,3 +140,72 @@ class Source(object): if self.energy is not None: element.append(self.energy.to_xml_element('energy')) return element + + @classmethod + def from_xml_element(cls, elem): + """Generate source from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Source + Source generated from XML element + + """ + source = cls() + + strength = elem.find('strength') + if strength is not None: + source.strength = float(strength.text) + + particle = elem.find('particle') + if particle is not None: + source.particle = particle.text + + filename = elem.find('file') + if filename is not None: + source.file = filename.text + + space = elem.find('space') + if space is not None: + space_type = space.get('type') + if space_type == 'cartesian': + source.space = CartesianIndependent.from_xml_element(space) + elif space_type == 'box' or space_type == 'fission': + source.space = Box.from_xml_element(space) + elif space_type == 'point': + source.space = Point.from_xml_element(space) + + angle = elem.find('angle') + if angle is not None: + angle_type = angle.get('type') + if angle_type == 'mu-phi': + source.angle = PolarAzimuthal.from_xml_element(angle) + elif angle_type == 'isotropic': + source.angle = Isotropic.from_xml_element(angle) + elif angle_type == 'monodirectional': + source.angle = Monodirectional.from_xml_element(angle) + + energy = elem.find('energy') + if energy is not None: + energy_type = energy.get('type') + if energy_type == 'discrete': + source.energy = Discrete.from_xml_element(energy) + elif energy_type == 'uniform': + source.energy = Uniform.from_xml_element(energy) + elif energy_type == 'maxwell': + source.energy = Maxwell.from_xml_element(energy) + elif energy_type == 'watt': + source.energy = Watt.from_xml_element(energy) + elif energy_type == 'normal': + source.energy = Normal.from_xml_element(energy) + elif energy_type == 'muir': + source.energy = Muir.from_xml_element(energy) + elif energy_type == 'tabular': + source.energy = Tabular.from_xml_element(energy) + + return source diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index ac788c3443..61122ada21 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -47,6 +47,11 @@ class UnitSphere(metaclass=ABCMeta): def to_xml_element(self): return '' + @classmethod + @abstractmethod + def from_xml_element(cls, elem): + pass + class PolarAzimuthal(UnitSphere): """Angular distribution represented by polar and azimuthal angles @@ -121,6 +126,29 @@ class PolarAzimuthal(UnitSphere): element.append(self.phi.to_xml_element('phi')) return element + @classmethod + def from_xml_element(cls, elem): + """Generate angular distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.PolarAzimuthal + Angular distribution generated from XML element + + """ + mu_phi = cls() + params = elem.findtext('parameters') + if params is not None: + mu_phi.reference_uvw = [float(x) for x in params.split()] + mu_phi.mu = openmc.stats.Univariate.from_xml_element(elem.find('mu')) + mu_phi.phi = openmc.stats.Univariate.from_xml_element(elem.find('phi')) + return mu_phi + class Isotropic(UnitSphere): """Isotropic angular distribution. @@ -143,6 +171,23 @@ class Isotropic(UnitSphere): element.set("type", "isotropic") return element + @classmethod + def from_xml_element(cls, elem): + """Generate isotropic distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Isotropic + Isotropic distribution generated from XML element + + """ + return cls() + class Monodirectional(UnitSphere): """Monodirectional angular distribution. @@ -178,6 +223,27 @@ class Monodirectional(UnitSphere): element.set("reference_uvw", ' '.join(map(str, self.reference_uvw))) return element + @classmethod + def from_xml_element(cls, elem): + """Generate monodirectional distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Monodirectional + Monodirectional distribution generated from XML element + + """ + monodirectional = cls() + params = elem.findtext('parameters') + if params is not None: + monodirectional.reference_uvw = [float(x) for x in params.split()] + return monodirectional + class Spatial(metaclass=ABCMeta): """Distribution of locations in three-dimensional Euclidean space. @@ -193,6 +259,11 @@ class Spatial(metaclass=ABCMeta): def to_xml_element(self): return '' + @classmethod + @abstractmethod + def from_xml_element(cls, elem): + pass + class CartesianIndependent(Spatial): """Spatial distribution with independent x, y, and z distributions. @@ -270,6 +341,26 @@ class CartesianIndependent(Spatial): element.append(self.z.to_xml_element('z')) return element + @classmethod + def from_xml_element(cls, elem): + """Generate spatial distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.CartesianIndependent + Spatial distribution generated from XML element + + """ + x = openmc.stats.Univariate.from_xml_element(elem.find('x')) + y = openmc.stats.Univariate.from_xml_element(elem.find('y')) + z = openmc.stats.Univariate.from_xml_element(elem.find('z')) + return cls(x, y, z) + class Box(Spatial): """Uniform distribution of coordinates in a rectangular cuboid. @@ -351,6 +442,27 @@ class Box(Spatial): ' '.join(map(str, self.upper_right)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate box distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Box + Box distribution generated from XML element + + """ + only_fissionable = elem.get('type') == 'fission' + params = [float(x) for x in elem.findtext('parameters').split()] + lower_left = params[:len(params)//2] + upper_right = paramx[len(params)//2:] + return cls(lower_left, upper_right, only_fissionable) + class Point(Spatial): """Delta function in three dimensions. @@ -398,3 +510,21 @@ class Point(Spatial): params = ET.SubElement(element, "parameters") params.text = ' '.join(map(str, self.xyz)) return element + + @classmethod + def from_xml_element(cls, elem): + """Generate point distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Point + Point distribution generated from XML element + + """ + xyz = [float(x) for x in elem.findtext('parameters').split()] + return cls(xyz) diff --git a/openmc/stats/univariate.py b/openmc/stats/univariate.py index e61f216ef0..6a83cc780e 100644 --- a/openmc/stats/univariate.py +++ b/openmc/stats/univariate.py @@ -32,6 +32,11 @@ class Univariate(EqualityMixin, metaclass=ABCMeta): def __len__(self): return 0 + @classmethod + @abstractmethod + def from_xml_element(cls, elem): + pass + class Discrete(Univariate): """Distribution characterized by a probability mass function. @@ -110,6 +115,26 @@ class Discrete(Univariate): return element + @classmethod + def from_xml_element(cls, elem): + """Generate discrete distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Discrete + Discrete distribution generated from XML element + + """ + params = [float(x) for x in elem.findtext('parameters').split()] + x = params[:len(params)//2] + p = params[len(params)//2:] + return cls(x, p) + class Uniform(Univariate): """Distribution with constant probability over a finite interval [a,b] @@ -181,6 +206,26 @@ class Uniform(Univariate): element.set("parameters", '{} {}'.format(self.a, self.b)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate uniform distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Uniform + Uniform distribution generated from XML element + + """ + params = elem.findtext('parameters').split() + a = float(params[0]) + b = float(params[1]) + return cls(a, b) + class Maxwell(Univariate): """Maxwellian distribution in energy. @@ -237,6 +282,24 @@ class Maxwell(Univariate): element.set("parameters", str(self.theta)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate Maxwellian distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Maxwell + Maxwellian distribution generated from XML element + + """ + theta = float(elem.findtext('parameters')) + return cls(theta) + class Watt(Univariate): r"""Watt fission energy spectrum. @@ -308,6 +371,27 @@ class Watt(Univariate): element.set("parameters", '{} {}'.format(self.a, self.b)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate Watt distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Watt + Watt distribution generated from XML element + + """ + params = elem.findtext('parameters').split() + a = float(params[0]) + b = float(params[1]) + return watt(a, b) + + class Normal(Univariate): r"""Normally distributed sampling. @@ -377,6 +461,27 @@ class Normal(Univariate): element.set("parameters", '{} {}'.format(self.mean_value, self.std_dev)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate Normal distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Normal + Normal distribution generated from XML element + + """ + params = elem.findtext('parameters').split() + mean_value = float(params[0]) + std_dev = float(params[1]) + return cls(mean_value, std_dev) + + class Muir(Univariate): """Muir energy spectrum. @@ -465,6 +570,27 @@ class Muir(Univariate): element.set("parameters", '{} {} {}'.format(self._e0, self._m_rat, self._kt)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate Muir distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Muir + Muir distribution generated from XML element + + """ + params = elem.findtext('parameters').split() + e0 = float(params[0]) + m_rat = float(params[1]) + kt = float(params[2]) + return muir(e0, m_rat, kt) + class Tabular(Univariate): """Piecewise continuous probability distribution. @@ -561,6 +687,27 @@ class Tabular(Univariate): return element + @classmethod + def from_xml_element(cls, elem): + """Generate tabular distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Tabular + Tabular distribution generated from XML element + + """ + interpolation = elem.get('interpolation') + params = [float(x) for x in elem.findtext('parameters').split()] + x = params[:len(params)//2] + p = paramx[len(params)//2:] + return cls(x, p, interpolation) + class Legendre(Univariate): r"""Probability density given by a Legendre polynomial expansion @@ -607,6 +754,10 @@ class Legendre(Univariate): def to_xml_element(self, element_name): raise NotImplementedError + @classmethod + def from_xml_element(cls, elem): + raise NotImplementedError + class Mixture(Univariate): """Probability distribution characterized by a mixture of random variables. @@ -660,3 +811,7 @@ class Mixture(Univariate): def to_xml_element(self, element_name): raise NotImplementedError + + @classmethod + def from_xml_element(cls, elem): + raise NotImplementedError From 489a03c648bb7914b69c2813f996ac37c1f65b77 Mon Sep 17 00:00:00 2001 From: amandalund Date: Thu, 25 Apr 2019 20:07:53 -0500 Subject: [PATCH 2/5] Get value from either attribute or element when reading settings from XML --- openmc/mesh.py | 13 +- openmc/settings.py | 468 +++++++++++++++++++---------------- openmc/source.py | 19 +- openmc/stats/multivariate.py | 13 +- openmc/stats/univariate.py | 17 +- 5 files changed, 289 insertions(+), 241 deletions(-) diff --git a/openmc/mesh.py b/openmc/mesh.py index a30b333fb3..fb59053aea 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -7,6 +7,7 @@ import numpy as np import openmc.checkvalue as cv import openmc +from openmc._xml import get_text from openmc.mixin import EqualityMixin, IDManagerMixin @@ -261,23 +262,23 @@ class Mesh(IDManagerMixin): Mesh generated from XML element """ - mesh_id = int(elem.get('id')) + mesh_id = int(get_text(elem, 'id')) mesh = cls(mesh_id) - mesh.type = elem.get('type') + mesh.type = get_text(elem, 'type') - dimension = elem.findtext('dimension') + dimension = get_text(elem, 'dimension') if dimension is not None: mesh.dimension = [int(x) for x in dimension.split()] - lower_left = elem.findtext('lower_left') + lower_left = get_text(elem, 'lower_left') if lower_left is not None: mesh.lower_left = [float(x) for x in lower_left.split()] - upper_right = elem.findtext('upper_right') + upper_right = get_text(elem, 'upper_right') if upper_right is not None: mesh.upper_right = [float(x) for x in upper_right.split()] - width = elem.findtext('width') + width = get_text(elem, 'width') if width is not None: mesh.width = [float(x) for x in width.split()] diff --git a/openmc/settings.py b/openmc/settings.py index a7ec086792..39312899b1 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -7,7 +7,7 @@ import sys import numpy as np -from openmc._xml import clean_indentation +from openmc._xml import clean_indentation, get_text import openmc.checkvalue as cv from openmc import VolumeCalculation, Source, Mesh @@ -927,6 +927,228 @@ class Settings(object): elem = ET.SubElement(root, "dagmc") elem.text = str(self._dagmc).lower() + def _eigenvalue_from_xml_element(self, root): + elem = root.find('eigenvalue') + if elem is not None: + self._run_mode_from_xml_element(elem) + self._particles_from_xml_element(elem) + self._batches_from_xml_element(elem) + self._inactive_from_xml_element(elem) + self._generations_per_batch_from_xml_element(elem) + + def _run_mode_from_xml_element(self, root): + text = get_text(root, 'run_mode') + if text is not None: + self.run_mode = text + + def _particles_from_xml_element(self, root): + text = get_text(root, 'particles') + if text is not None: + self.particles = int(text) + + def _batches_from_xml_element(self, root): + text = get_text(root, 'batches') + if text is not None: + self.batches = int(text) + + def _inactive_from_xml_element(self, root): + text = get_text(root, 'inactive') + if text is not None: + self.inactive = int(text) + + def _generations_per_batch_from_xml_element(self, root): + text = get_text(root, 'generations_per_batch') + if text is not None: + self.generations_per_batch = int(text) + + def _keff_trigger_from_xml_element(self, root): + elem = root.find('keff_trigger') + if elem is not None: + trigger = get_text(elem, 'type') + threshold = float(get_text(elem, 'threshold')) + self.keff_trigger = {'type': trigger, 'threshold': threshold} + + def _source_from_xml_element(self, root): + for elem in root.findall('source'): + self.source.append(Source.from_xml_element(elem)) + + def _output_from_xml_element(self, root): + elem = root.find('output') + if elem is not None: + self.output = {} + for key in ('summary', 'tallies', 'path'): + value = get_text(elem, key) + if value is not None: + if key in ('summary', 'tallies'): + value = value == 'true' + self.output[key] = value + + def _statepoint_from_xml_element(self, root): + elem = root.find('state_point') + if elem is not None: + text = get_text(elem, 'batches') + if text is not None: + self.statepoint['batches'] = [int(x) for x in text.split()] + + def _sourcepoint_from_xml_element(self, root): + elem = root.find('source_point') + if elem is not None: + for key in ('separate', 'write', 'overwrite', 'batches'): + value = get_text(elem, key) + if value is not None: + if key in ('separate', 'write', 'overwrite'): + value = value == 'true' + else: + value = [int(x) for x in value.split()] + self.sourcepoint[key] = value + + def _confidence_intervals_from_xml_element(self, root): + text = get_text(root, 'confidence_intervals') + if text is not None: + self.confidence_intervals = text == 'true' + + def _electron_treatment_from_xml_element(self, root): + text = get_text(root, 'electron_treatment') + if text is not None: + self.electron_treatment = text + + def _energy_mode_from_xml_element(self, root): + text = get_text(root, 'energy_mode') + if text is not None: + self.energy_mode = text + + def _max_order_from_xml_element(self, root): + text = get_text(root, 'max_order') + if text is not None: + self.max_order = int(text) + + def _photon_transport_from_xml_element(self, root): + text = get_text(root, 'photon_transport') + if text is not None: + self.photon_transport = text == 'true' + + def _ptables_from_xml_element(self, root): + text = get_text(root, 'ptables') + if text is not None: + self.ptables = text == 'true' + + def _seed_from_xml_element(self, root): + text = get_text(root, 'seed') + if text is not None: + self.seed = int(text) + + def _survival_biasing_from_xml_element(self, root): + text = get_text(root, 'survival_biasing') + if text is not None: + self.survival_biasing = text == 'true' + + def _cutoff_from_xml_element(self, root): + elem = root.find('cutoff') + if elem is not None: + self.cutoff = {} + for key in ('energy_neutron', 'energy_photon', 'energy_electron', + 'energy_positron', 'weight', 'weight_avg'): + value = get_text(elem, key) + if value is not None: + self.cutoff[key] = float(value) + + def _entropy_mesh_from_xml_element(self, root): + elem = root.find('entropy_mesh') + if elem is not None: + self.entropy_mesh = Mesh.from_xml_element(elem) + + def _trigger_from_xml_element(self, root): + elem = root.find('trigger') + if elem is not None: + self.trigger_active = get_text(elem, 'active') == 'true' + text = get_text(elem, 'max_batches') + if text is not None: + self.trigger_max_batches = int(text) + text = get_text(elem, 'batch_interval') + if text is not None: + self.trigger_batch_interval = int(text) + + def _no_reduce_from_xml_element(self, root): + text = get_text(root, 'no_reduce') + if text is not None: + self.no_reduce = text == 'true' + + def _verbosity_from_xml_element(self, root): + text = get_text(root, 'verbosity') + if text is not None: + self.verbosity = int(text) + + def _tabular_legendre_from_xml_element(self, root): + elem = root.find('tabular_legendre') + if elem is not None: + text = get_text(elem, 'enable') + self.tabular_legendre['enable'] = text == 'true' + text = get_text(elem, 'num_points') + if text is not None: + self.tabular_legendre['num_points'] = int(text) + + def _temperature_from_xml_element(self, root): + text = get_text(root, 'temperature_default') + if text is not None: + self.temperature['default'] = float(text) + text = get_text(root, 'temperature_tolerance') + if text is not None: + self.temperature['tolerance'] = float(text) + text = get_text(root, 'temperature_method') + if text is not None: + self.temperature['method'] = text + text = get_text(root, 'temperature_range') + if text is not None: + self.temperature['range'] = [float(x) for x in text.split()] + text = get_text(root, 'temperature_multipole') + if text is not None: + self.temperature['multipole'] = text == 'true' + + def _trace_from_xml_element(self, root): + text = get_text(root, 'trace') + if text is not None: + self.trace = [int(x) for x in text.split()] + + def _track_from_xml_element(self, root): + text = get_text(root, 'track') + if text is not None: + self.track = [int(x) for x in text.split()] + + def _ufs_mesh_from_xml_element(self, root): + elem = root.find('ufs_mesh') + if elem is not None: + self.ufs_mesh = Mesh.from_xml_element(elem) + + def _resonance_scattering_from_xml_element(self, root): + elem = root.find('resonance_scattering') + if elem is not None: + keys = ('enable', 'method', 'energy_min', 'energy_max', 'nuclides') + for key in keys: + value = get_text(elem, key) + if value is not None: + if key == 'enable': + value = value == 'true' + elif key in ('energy_min', 'energy_max'): + value = float(value) + elif key == 'nuclides': + value = value.split() + self.resonance_scattering[key] = value + + def _create_fission_neutrons_from_xml_element(self, root): + text = get_text(root, 'create_fission_neutrons') + if text is not None: + self.create_fission_neutrons = text == 'true' + + def _log_grid_bins_from_xml_element(self, root): + text = get_text(root, 'log_grid_bins') + if text is not None: + self.log_grid_bins = int(text) + + def _dagmc_from_xml_element(self, root): + text = get_text(root, 'dagmc') + if text is not None: + self.dagmc = text == 'true' + def export_to_xml(self, path='settings.xml'): """Export simulation settings to an XML file. @@ -1005,218 +1227,40 @@ class Settings(object): root = tree.getroot() settings = cls() - - # Get the run mode - elem = root.find('run_mode') - if elem is not None: - settings.run_mode = elem.text - - # Get number of particles - elem = root.find('particles') - if elem is not None: - settings.particles = int(elem.text) - - # Get number of batches - elem = root.find('batches') - if elem is not None: - settings.batches = int(elem.text) - - # Get number of inactive batches - elem = root.find('inactive') - if elem is not None: - settings.inactive = int(elem.text) - - # Get number of generations per batch - elem = root.find('generations_per_batch') - if elem is not None: - settings.generations_per_batch = int(elem.text) - - # Get keff trigger - elem = root.find('keff_trigger') - if elem is not None: - trigger = elem.findtext('type') - threshold = float(elem.findtext('threshold')) - settings.keff_trigger = {'type': trigger, 'threshold': threshold} - - # Get the source - for elem in root.findall('source'): - settings.source.append(Source.from_xml_element(elem)) - - # Get the output - elem = root.find('output') - if elem is not None: - settings.output = {} - for entry in elem: - key = entry.tag - if key in ('summary', 'tallies'): - value = entry.text == 'true' - else: - value = entry.text - settings.output[key] = value - - # Get the statepoint - elem = root.find('state_point') - if elem is not None: - batches = elem.findtext('batches') - if batches is not None: - settings.statepoint['batches'] = [int(x) for x in batches.split()] - - # Get the sourcepoint - elem = root.find('source_point') - if elem is not None: - for entry in elem: - key = entry.tag - if key in ('separate', 'write', 'overwrite'): - value = entry.text == 'true' - else: - value = [int(x) for x in entry.text.split()] - settings.sourcepoint[key] = value - - # Get confidence intervals - elem = root.find('confidence_intervals') - if elem is not None: - settings.confidence_intervals = elem.text == 'true' - - # Get electron treatment - elem = root.find('electron_treatment') - if elem is not None: - settings.electron_treatment = elem.text - - # Get energy mode - elem = root.find('energy_mode') - if elem is not None: - settings.energy_mode = elem.text - - # Get max order - elem = root.find('max_order') - if elem is not None: - settings.max_order = int(elem.text) - - # Get photon transport - elem = root.find('photon_transport') - if elem is not None: - settings.photon_transport = elem.text == 'true' - - # Get probability tables - elem = root.find('ptables') - if elem is not None: - settings.ptables = elem.text == 'true' - - # Get seed - elem = root.find('seed') - if elem is not None: - settings.seed = int(elem.text) - - # Get survival biasing - elem = root.find('survival_biasing') - if elem is not None: - settings.survival_biasing = elem.text == 'true' - - # Get cutoff - elem = root.find('cutoff') - if elem is not None: - settings.cutoff = {x.tag: float(x.text) for x in elem} - - # Get entropy mesh - elem = root.find('entropy_mesh') - if elem is not None: - settings.entropy_mesh = Mesh.from_xml_element(elem) - - # Get trigger - elem = root.find('trigger') - if elem is not None: - active = elem.find('active') - settings.trigger_active = active.text == 'true' - max_batches = elem.find('max_batches') - if max_batches is not None: - settings.trigger_max_batches = int(max_batches.text) - batch_interval = elem.find('batch_interval') - if batch_interval is not None: - settings.trigger_batch_interval = int(batch_interval.text) - - # Get no reduce - elem = root.find('no_reduce') - if elem is not None: - settings.no_reduce = elem.text == 'true' - - # Get verbosity - elem = root.find('verbosity') - if elem is not None: - settings.verbosity = int(elem.text) - - # Get tabular legendre - elem = root.find('tabular_legendre') - if elem is not None: - enable = elem.findtext('eneable') - settings.tabular_legendre['enable'] = enable == 'true' - num_points = elem.findtext('num_points') - if num_points is not None: - settings.tabular_legendre['num_points'] = int(num_points) - - # Get temperature - elem = root.findtext('temperature_default') - if elem is not None: - settings.temperature['default'] = float(elem) - elem = root.findtext('temperature_tolerance') - if elem is not None: - settings.temperature['tolerance'] = float(elem) - elem = root.findtext('temperature_method') - if elem is not None: - settings.temperature['method'] = elem - elem = root.findtext('temperature_range') - if elem is not None: - settings.temperature['range'] = [float(x) for x in elem.split()] - elem = root.findtext('temperature_multipole') - if elem is not None: - settings.temperature['multipole'] = elem == 'true' - - # Get trace - elem = root.find('trace') - if elem is not None: - settings.trace = [int(x) for x in elem.text.split()] - - # Get track - elem = root.find('track') - if elem is not None: - settings.track = [int(x) for x in elem.text.split()] - - # Get UFS mesh - elem = root.find('ufs_mesh') - if elem is not None: - settings.ufs_mesh = Mesh.from_xml_element(elem) - - # Get resonance scattering - elem = root.find('resonance_scattering') - if elem is not None: - for entry in elem: - key = entry.tag - if key == 'enable': - value = entry.text == 'true' - elif key == 'method': - value = entry.text - elif key == 'energy_min': - value = float(entry.text) - elif key == 'energy_max': - value = float(entry.text) - elif key == 'nuclides': - value = entry.text.split() - settings.resonance_scattering[key] = value + settings._eigenvalue_from_xml_element(root) + settings._run_mode_from_xml_element(root) + settings._particles_from_xml_element(root) + settings._batches_from_xml_element(root) + settings._inactive_from_xml_element(root) + settings._generations_per_batch_from_xml_element(root) + settings._keff_trigger_from_xml_element(root) + settings._source_from_xml_element(root) + settings._output_from_xml_element(root) + settings._statepoint_from_xml_element(root) + settings._sourcepoint_from_xml_element(root) + settings._confidence_intervals_from_xml_element(root) + settings._electron_treatment_from_xml_element(root) + settings._energy_mode_from_xml_element(root) + settings._max_order_from_xml_element(root) + settings._photon_transport_from_xml_element(root) + settings._ptables_from_xml_element(root) + settings._seed_from_xml_element(root) + settings._survival_biasing_from_xml_element(root) + settings._cutoff_from_xml_element(root) + settings._entropy_mesh_from_xml_element(root) + settings._trigger_from_xml_element(root) + settings._no_reduce_from_xml_element(root) + settings._verbosity_from_xml_element(root) + settings._tabular_legendre_from_xml_element(root) + settings._temperature_from_xml_element(root) + settings._trace_from_xml_element(root) + settings._track_from_xml_element(root) + settings._ufs_mesh_from_xml_element(root) + settings._resonance_scattering_from_xml_element(root) + settings._create_fission_neutrons_from_xml_element(root) + settings._log_grid_bins_from_xml_element(root) + settings._dagmc_from_xml_element(root) # TODO: Get volume calculations - # Get fission neutrons - elem = root.find('create_fission_neutrons') - if elem is not None: - settings.create_fission_neutrons = elem.text == 'true' - - # Get log grid bins - elem = root.find('log_grid_bins') - if elem is not None: - settings.log_grid_bins = int(elem.text) - - # Get dagmc - elem = root.find('dagmc') - if elem is not None: - settings.dagmc = elem.text == 'true' - return settings diff --git a/openmc/source.py b/openmc/source.py index e278d80892..5bafaf6984 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -2,6 +2,7 @@ from numbers import Real import sys from xml.etree import ElementTree as ET +from openmc._xml import get_text from openmc.stats.univariate import (Univariate, Discrete, Uniform, Maxwell, Watt, Normal, Muir, Tabular) from openmc.stats.multivariate import (UnitSphere, Spatial, PolarAzimuthal, @@ -158,21 +159,21 @@ class Source(object): """ source = cls() - strength = elem.find('strength') + strength = get_text(elem, 'strength') if strength is not None: - source.strength = float(strength.text) + source.strength = float(strength) - particle = elem.find('particle') + particle = get_text(elem, 'particle') if particle is not None: - source.particle = particle.text + source.particle = particle - filename = elem.find('file') + filename = get_text(elem, 'file') if filename is not None: - source.file = filename.text + source.file = filename space = elem.find('space') if space is not None: - space_type = space.get('type') + space_type = get_text(space, 'type') if space_type == 'cartesian': source.space = CartesianIndependent.from_xml_element(space) elif space_type == 'box' or space_type == 'fission': @@ -182,7 +183,7 @@ class Source(object): angle = elem.find('angle') if angle is not None: - angle_type = angle.get('type') + angle_type = get_text(angle, 'type') if angle_type == 'mu-phi': source.angle = PolarAzimuthal.from_xml_element(angle) elif angle_type == 'isotropic': @@ -192,7 +193,7 @@ class Source(object): energy = elem.find('energy') if energy is not None: - energy_type = energy.get('type') + energy_type = get_text(energy, 'type') if energy_type == 'discrete': source.energy = Discrete.from_xml_element(energy) elif energy_type == 'uniform': diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index 61122ada21..5a836ab63b 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -8,6 +8,7 @@ from xml.etree import ElementTree as ET import numpy as np import openmc.checkvalue as cv +from openmc._xml import get_text from openmc.stats.univariate import Univariate, Uniform @@ -142,7 +143,7 @@ class PolarAzimuthal(UnitSphere): """ mu_phi = cls() - params = elem.findtext('parameters') + params = get_text(elem, 'parameters') if params is not None: mu_phi.reference_uvw = [float(x) for x in params.split()] mu_phi.mu = openmc.stats.Univariate.from_xml_element(elem.find('mu')) @@ -239,7 +240,7 @@ class Monodirectional(UnitSphere): """ monodirectional = cls() - params = elem.findtext('parameters') + params = get_text(elem, 'parameters') if params is not None: monodirectional.reference_uvw = [float(x) for x in params.split()] return monodirectional @@ -457,10 +458,10 @@ class Box(Spatial): Box distribution generated from XML element """ - only_fissionable = elem.get('type') == 'fission' - params = [float(x) for x in elem.findtext('parameters').split()] + only_fissionable = get_text(elem, 'type') == 'fission' + params = [float(x) for x in get_text(elem, 'parameters').split()] lower_left = params[:len(params)//2] - upper_right = paramx[len(params)//2:] + upper_right = params[len(params)//2:] return cls(lower_left, upper_right, only_fissionable) @@ -526,5 +527,5 @@ class Point(Spatial): Point distribution generated from XML element """ - xyz = [float(x) for x in elem.findtext('parameters').split()] + xyz = [float(x) for x in get_text(elem, 'parameters').split()] return cls(xyz) diff --git a/openmc/stats/univariate.py b/openmc/stats/univariate.py index 6a83cc780e..3088f8c779 100644 --- a/openmc/stats/univariate.py +++ b/openmc/stats/univariate.py @@ -7,6 +7,7 @@ from xml.etree import ElementTree as ET import numpy as np import openmc.checkvalue as cv +from openmc._xml import get_text from openmc.mixin import EqualityMixin @@ -130,7 +131,7 @@ class Discrete(Univariate): Discrete distribution generated from XML element """ - params = [float(x) for x in elem.findtext('parameters').split()] + params = [float(x) for x in get_text(elem, 'parameters').split()] x = params[:len(params)//2] p = params[len(params)//2:] return cls(x, p) @@ -221,7 +222,7 @@ class Uniform(Univariate): Uniform distribution generated from XML element """ - params = elem.findtext('parameters').split() + params = get_text(elem, 'parameters').split() a = float(params[0]) b = float(params[1]) return cls(a, b) @@ -297,7 +298,7 @@ class Maxwell(Univariate): Maxwellian distribution generated from XML element """ - theta = float(elem.findtext('parameters')) + theta = float(get_text(elem, 'parameters')) return cls(theta) @@ -386,7 +387,7 @@ class Watt(Univariate): Watt distribution generated from XML element """ - params = elem.findtext('parameters').split() + params = get_text(elem, 'parameters').split() a = float(params[0]) b = float(params[1]) return watt(a, b) @@ -476,7 +477,7 @@ class Normal(Univariate): Normal distribution generated from XML element """ - params = elem.findtext('parameters').split() + params = get_text(elem, 'parameters').split() mean_value = float(params[0]) std_dev = float(params[1]) return cls(mean_value, std_dev) @@ -585,7 +586,7 @@ class Muir(Univariate): Muir distribution generated from XML element """ - params = elem.findtext('parameters').split() + params = get_text(elem, 'parameters').split() e0 = float(params[0]) m_rat = float(params[1]) kt = float(params[2]) @@ -702,8 +703,8 @@ class Tabular(Univariate): Tabular distribution generated from XML element """ - interpolation = elem.get('interpolation') - params = [float(x) for x in elem.findtext('parameters').split()] + interpolation = get_text(elem, 'interpolation') + params = [float(x) for x in get_text(elem, 'parameters').split()] x = params[:len(params)//2] p = paramx[len(params)//2:] return cls(x, p, interpolation) From c0ede1ec8dbd930d6c16bd8c0b72a9b722566f6d Mon Sep 17 00:00:00 2001 From: amandalund Date: Tue, 30 Apr 2019 11:39:17 -0400 Subject: [PATCH 3/5] Add unit tests for settings.from_xml() and a few fixes --- openmc/settings.py | 26 +++++++++----- tests/unit_tests/test_settings.py | 58 +++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 11 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 39312899b1..802046c4e2 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -174,7 +174,6 @@ class Settings(object): self._source = cv.CheckedList(Source, 'source distributions') self._confidence_intervals = None - self._cross_sections = None self._electron_treatment = None self._photon_transport = None self._ptables = None @@ -993,11 +992,14 @@ class Settings(object): def _sourcepoint_from_xml_element(self, root): elem = root.find('source_point') if elem is not None: - for key in ('separate', 'write', 'overwrite', 'batches'): + for key in ('separate', 'write', 'overwrite_latest', 'batches'): value = get_text(elem, key) if value is not None: - if key in ('separate', 'write', 'overwrite'): + if key in ('separate', 'write'): value = value == 'true' + elif key == 'overwrite_latest': + value = value == 'true' + key = 'overwrite' else: value = [int(x) for x in value.split()] self.sourcepoint[key] = value @@ -1053,9 +1055,12 @@ class Settings(object): self.cutoff[key] = float(value) def _entropy_mesh_from_xml_element(self, root): - elem = root.find('entropy_mesh') - if elem is not None: - self.entropy_mesh = Mesh.from_xml_element(elem) + text = get_text(root, 'entropy_mesh') + if text is not None: + path = "./mesh[@id='{}']".format(int(text)) + elem = root.find(path) + if elem is not None: + self.entropy_mesh = Mesh.from_xml_element(elem) def _trigger_from_xml_element(self, root): elem = root.find('trigger') @@ -1115,9 +1120,12 @@ class Settings(object): self.track = [int(x) for x in text.split()] def _ufs_mesh_from_xml_element(self, root): - elem = root.find('ufs_mesh') - if elem is not None: - self.ufs_mesh = Mesh.from_xml_element(elem) + text = get_text(root, 'ufs_mesh') + if text is not None: + path = "./mesh[@id='{}']".format(int(text)) + elem = root.find(path) + if elem is not None: + self.ufs_mesh = Mesh.from_xml_element(elem) def _resonance_scattering_from_xml_element(self, root): elem = root.find('resonance_scattering') diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index e42f6240f5..f99a32a52c 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -19,11 +19,12 @@ def test_export_to_xml(run_in_tmpdir): 'write': True, 'overwrite': True} s.statepoint = {'batches': [50, 150, 500, 1000]} s.confidence_intervals = True - s.cross_sections = '/path/to/cross_sections.xml' s.ptables = True s.seed = 17 s.survival_biasing = True - s.cutoff = {'weight': 0.25, 'weight_avg': 0.5, 'energy': 1.0e-5} + s.cutoff = {'weight': 0.25, 'weight_avg': 0.5, 'energy_neutron': 1.0e-5, + 'energy_photon': 1000.0, 'energy_electron': 1.0e-5, + 'energy_positron': 1.0e-5} mesh = openmc.Mesh() mesh.lower_left = (-10., -10., -10.) mesh.upper_right = (10., 10., 10.) @@ -47,6 +48,59 @@ def test_export_to_xml(run_in_tmpdir): upper_right = (10., 10., 10.)) s.create_fission_neutrons = True s.log_grid_bins = 2000 + s.photon_transport = False + s.electron_treatment = 'led' + s.dagmc = False # Make sure exporting XML works s.export_to_xml() + + # Generate settings from XML + s = openmc.Settings.from_xml() + assert s.run_mode == 'fixed source' + assert s.batches == 1000 + assert s.generations_per_batch == 10 + assert s.inactive == 100 + assert s.particles == 1000000 + assert s.keff_trigger == {'type': 'std_dev', 'threshold': 0.001} + assert s.energy_mode == 'continuous-energy' + assert s.max_order == 5 + assert isinstance(s.source[0], openmc.Source) + assert isinstance(s.source[0].space, openmc.stats.Point) + assert s.output == {'summary': True, 'tallies': False, 'path': 'here'} + assert s.verbosity == 7 + assert s.sourcepoint == {'batches': [50, 150, 500, 1000], 'separate': True, + 'write': True, 'overwrite': True} + assert s.statepoint == {'batches': [50, 150, 500, 1000]} + assert s.confidence_intervals + assert s.ptables + assert s.seed == 17 + assert s.survival_biasing + assert s.cutoff == {'weight': 0.25, 'weight_avg': 0.5, + 'energy_neutron': 1.0e-5, 'energy_photon': 1000.0, + 'energy_electron': 1.0e-5, 'energy_positron': 1.0e-5} + assert isinstance(s.entropy_mesh, openmc.Mesh) + assert s.entropy_mesh.lower_left == [-10., -10., -10.] + assert s.entropy_mesh.upper_right == [10., 10., 10.] + assert s.entropy_mesh.dimension == [5, 5, 5] + assert s.trigger_active + assert s.trigger_max_batches == 10000 + assert s.trigger_batch_interval == 50 + assert not s.no_reduce + assert s.tabular_legendre == {'enable': True, 'num_points': 50} + assert s.temperature == {'default': 293.6, 'method': 'interpolation', + 'multipole': True, 'range': [200., 1000.]} + assert s.trace == [10, 1, 20] + assert s.track == [1, 1, 1, 2, 1, 1] + assert isinstance(s.ufs_mesh, openmc.Mesh) + assert s.ufs_mesh.lower_left == [-10., -10., -10.] + assert s.ufs_mesh.upper_right == [10., 10., 10.] + assert s.ufs_mesh.dimension == [5, 5, 5] + assert s.resonance_scattering == {'enable': True, 'method': 'rvs', + 'energy_min': 1.0, 'energy_max': 1000.0, + 'nuclides': ['U235', 'U238', 'Pu239']} + assert s.create_fission_neutrons + assert s.log_grid_bins == 2000 + assert not s.photon_transport + assert s.electron_treatment == 'led' + assert not s.dagmc From 81674a1c2d5ee2e1c97d02dd1c84e5b936b4ca1a Mon Sep 17 00:00:00 2001 From: amandalund Date: Wed, 1 May 2019 12:20:46 -0400 Subject: [PATCH 4/5] A couple more fixes for mesh to/from XML --- openmc/mesh.py | 10 +++++++--- openmc/settings.py | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/openmc/mesh.py b/openmc/mesh.py index fb59053aea..2489d5faa7 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -231,8 +231,9 @@ class Mesh(IDManagerMixin): element.set("id", str(self._id)) element.set("type", self._type) - subelement = ET.SubElement(element, "dimension") - subelement.text = ' '.join(map(str, self._dimension)) + if self._dimension is not None: + subelement = ET.SubElement(element, "dimension") + subelement.text = ' '.join(map(str, self._dimension)) subelement = ET.SubElement(element, "lower_left") subelement.text = ' '.join(map(str, self._lower_left)) @@ -264,7 +265,10 @@ class Mesh(IDManagerMixin): """ mesh_id = int(get_text(elem, 'id')) mesh = cls(mesh_id) - mesh.type = get_text(elem, 'type') + + mesh_type = get_text(elem, 'type') + if mesh_type is not None: + mesh.type = mesh_type dimension = get_text(elem, 'dimension') if dimension is not None: diff --git a/openmc/settings.py b/openmc/settings.py index 802046c4e2..0636775755 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -551,7 +551,8 @@ class Settings(object): @entropy_mesh.setter def entropy_mesh(self, entropy): cv.check_type('entropy mesh', entropy, Mesh) - cv.check_length('entropy mesh dimension', entropy.dimension, 3) + if entropy.dimension: + cv.check_length('entropy mesh dimension', entropy.dimension, 3) cv.check_length('entropy mesh lower-left corner', entropy.lower_left, 3) cv.check_length('entropy mesh upper-right corner', entropy.upper_right, 3) self._entropy_mesh = entropy From 2f09b7ca854ba9a4370687f2bd0521a174c2407f Mon Sep 17 00:00:00 2001 From: amandalund Date: Wed, 22 May 2019 19:26:15 -0600 Subject: [PATCH 5/5] Fixed some univariate/multivariate from_xml_element issues and updated unit tests --- openmc/settings.py | 28 ++++++------ openmc/source.py | 39 +++------------- openmc/stats/multivariate.py | 26 ++++++++--- openmc/stats/univariate.py | 39 +++++++++------- tests/unit_tests/test_source.py | 8 +++- tests/unit_tests/test_stats.py | 80 +++++++++++++++++++++++++-------- 6 files changed, 130 insertions(+), 90 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 0636775755..4fe87d908c 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -980,7 +980,7 @@ class Settings(object): value = get_text(elem, key) if value is not None: if key in ('summary', 'tallies'): - value = value == 'true' + value = value in ('true', '1') self.output[key] = value def _statepoint_from_xml_element(self, root): @@ -997,9 +997,9 @@ class Settings(object): value = get_text(elem, key) if value is not None: if key in ('separate', 'write'): - value = value == 'true' + value = value in ('true', '1') elif key == 'overwrite_latest': - value = value == 'true' + value = value in ('true', '1') key = 'overwrite' else: value = [int(x) for x in value.split()] @@ -1008,7 +1008,7 @@ class Settings(object): def _confidence_intervals_from_xml_element(self, root): text = get_text(root, 'confidence_intervals') if text is not None: - self.confidence_intervals = text == 'true' + self.confidence_intervals = text in ('true', '1') def _electron_treatment_from_xml_element(self, root): text = get_text(root, 'electron_treatment') @@ -1028,12 +1028,12 @@ class Settings(object): def _photon_transport_from_xml_element(self, root): text = get_text(root, 'photon_transport') if text is not None: - self.photon_transport = text == 'true' + self.photon_transport = text in ('true', '1') def _ptables_from_xml_element(self, root): text = get_text(root, 'ptables') if text is not None: - self.ptables = text == 'true' + self.ptables = text in ('true', '1') def _seed_from_xml_element(self, root): text = get_text(root, 'seed') @@ -1043,7 +1043,7 @@ class Settings(object): def _survival_biasing_from_xml_element(self, root): text = get_text(root, 'survival_biasing') if text is not None: - self.survival_biasing = text == 'true' + self.survival_biasing = text in ('true', '1') def _cutoff_from_xml_element(self, root): elem = root.find('cutoff') @@ -1066,7 +1066,7 @@ class Settings(object): def _trigger_from_xml_element(self, root): elem = root.find('trigger') if elem is not None: - self.trigger_active = get_text(elem, 'active') == 'true' + self.trigger_active = get_text(elem, 'active') in ('true', '1') text = get_text(elem, 'max_batches') if text is not None: self.trigger_max_batches = int(text) @@ -1077,7 +1077,7 @@ class Settings(object): def _no_reduce_from_xml_element(self, root): text = get_text(root, 'no_reduce') if text is not None: - self.no_reduce = text == 'true' + self.no_reduce = text in ('true', '1') def _verbosity_from_xml_element(self, root): text = get_text(root, 'verbosity') @@ -1088,7 +1088,7 @@ class Settings(object): elem = root.find('tabular_legendre') if elem is not None: text = get_text(elem, 'enable') - self.tabular_legendre['enable'] = text == 'true' + self.tabular_legendre['enable'] = text in ('true', '1') text = get_text(elem, 'num_points') if text is not None: self.tabular_legendre['num_points'] = int(text) @@ -1108,7 +1108,7 @@ class Settings(object): self.temperature['range'] = [float(x) for x in text.split()] text = get_text(root, 'temperature_multipole') if text is not None: - self.temperature['multipole'] = text == 'true' + self.temperature['multipole'] = text in ('true', '1') def _trace_from_xml_element(self, root): text = get_text(root, 'trace') @@ -1136,7 +1136,7 @@ class Settings(object): value = get_text(elem, key) if value is not None: if key == 'enable': - value = value == 'true' + value = value in ('true', '1') elif key in ('energy_min', 'energy_max'): value = float(value) elif key == 'nuclides': @@ -1146,7 +1146,7 @@ class Settings(object): def _create_fission_neutrons_from_xml_element(self, root): text = get_text(root, 'create_fission_neutrons') if text is not None: - self.create_fission_neutrons = text == 'true' + self.create_fission_neutrons = text in ('true', '1') def _log_grid_bins_from_xml_element(self, root): text = get_text(root, 'log_grid_bins') @@ -1156,7 +1156,7 @@ class Settings(object): def _dagmc_from_xml_element(self, root): text = get_text(root, 'dagmc') if text is not None: - self.dagmc = text == 'true' + self.dagmc = text in ('true', '1') def export_to_xml(self, path='settings.xml'): """Export simulation settings to an XML file. diff --git a/openmc/source.py b/openmc/source.py index 5bafaf6984..88c2f86119 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -3,11 +3,8 @@ import sys from xml.etree import ElementTree as ET from openmc._xml import get_text -from openmc.stats.univariate import (Univariate, Discrete, Uniform, Maxwell, - Watt, Normal, Muir, Tabular) -from openmc.stats.multivariate import (UnitSphere, Spatial, PolarAzimuthal, - Isotropic, Monodirectional, Box, Point, - CartesianIndependent) +from openmc.stats.univariate import Univariate +from openmc.stats.multivariate import UnitSphere, Spatial import openmc.checkvalue as cv @@ -173,40 +170,14 @@ class Source(object): space = elem.find('space') if space is not None: - space_type = get_text(space, 'type') - if space_type == 'cartesian': - source.space = CartesianIndependent.from_xml_element(space) - elif space_type == 'box' or space_type == 'fission': - source.space = Box.from_xml_element(space) - elif space_type == 'point': - source.space = Point.from_xml_element(space) + source.space = Spatial.from_xml_element(space) angle = elem.find('angle') if angle is not None: - angle_type = get_text(angle, 'type') - if angle_type == 'mu-phi': - source.angle = PolarAzimuthal.from_xml_element(angle) - elif angle_type == 'isotropic': - source.angle = Isotropic.from_xml_element(angle) - elif angle_type == 'monodirectional': - source.angle = Monodirectional.from_xml_element(angle) + source.angle = UnitSphere.from_xml_element(angle) energy = elem.find('energy') if energy is not None: - energy_type = get_text(energy, 'type') - if energy_type == 'discrete': - source.energy = Discrete.from_xml_element(energy) - elif energy_type == 'uniform': - source.energy = Uniform.from_xml_element(energy) - elif energy_type == 'maxwell': - source.energy = Maxwell.from_xml_element(energy) - elif energy_type == 'watt': - source.energy = Watt.from_xml_element(energy) - elif energy_type == 'normal': - source.energy = Normal.from_xml_element(energy) - elif energy_type == 'muir': - source.energy = Muir.from_xml_element(energy) - elif energy_type == 'tabular': - source.energy = Tabular.from_xml_element(energy) + source.energy = Univariate.from_xml_element(energy) return source diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index 5a836ab63b..35afc21dd1 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -51,7 +51,13 @@ class UnitSphere(metaclass=ABCMeta): @classmethod @abstractmethod def from_xml_element(cls, elem): - pass + distribution = get_text(elem, 'type') + if distribution == 'mu-phi': + return PolarAzimuthal.from_xml_element(elem) + elif distribution == 'isotropic': + return Isotropic.from_xml_element(elem) + elif distribution == 'monodirectional': + return Monodirectional.from_xml_element(elem) class PolarAzimuthal(UnitSphere): @@ -146,8 +152,8 @@ class PolarAzimuthal(UnitSphere): params = get_text(elem, 'parameters') if params is not None: mu_phi.reference_uvw = [float(x) for x in params.split()] - mu_phi.mu = openmc.stats.Univariate.from_xml_element(elem.find('mu')) - mu_phi.phi = openmc.stats.Univariate.from_xml_element(elem.find('phi')) + mu_phi.mu = Univariate.from_xml_element(elem.find('mu')) + mu_phi.phi = Univariate.from_xml_element(elem.find('phi')) return mu_phi @@ -263,7 +269,13 @@ class Spatial(metaclass=ABCMeta): @classmethod @abstractmethod def from_xml_element(cls, elem): - pass + distribution = get_text(elem, 'type') + if distribution == 'cartesian': + return CartesianIndependent.from_xml_element(elem) + elif distribution == 'box' or distribution == 'fission': + return Box.from_xml_element(elem) + elif distribution == 'point': + return Point.from_xml_element(elem) class CartesianIndependent(Spatial): @@ -357,9 +369,9 @@ class CartesianIndependent(Spatial): Spatial distribution generated from XML element """ - x = openmc.stats.Univariate.from_xml_element(elem.find('x')) - y = openmc.stats.Univariate.from_xml_element(elem.find('y')) - z = openmc.stats.Univariate.from_xml_element(elem.find('z')) + x = Univariate.from_xml_element(elem.find('x')) + y = Univariate.from_xml_element(elem.find('y')) + z = Univariate.from_xml_element(elem.find('z')) return cls(x, y, z) diff --git a/openmc/stats/univariate.py b/openmc/stats/univariate.py index 3088f8c779..363dd2ee35 100644 --- a/openmc/stats/univariate.py +++ b/openmc/stats/univariate.py @@ -36,7 +36,25 @@ class Univariate(EqualityMixin, metaclass=ABCMeta): @classmethod @abstractmethod def from_xml_element(cls, elem): - pass + distribution = get_text(elem, 'type') + if distribution == 'discrete': + return Discrete.from_xml_element(elem) + elif distribution == 'uniform': + return Uniform.from_xml_element(elem) + elif distribution == 'maxwell': + return Maxwell.from_xml_element(elem) + elif distribution == 'watt': + return Watt.from_xml_element(elem) + elif distribution == 'normal': + return Normal.from_xml_element(elem) + elif distribution == 'muir': + return Muir.from_xml_element(elem) + elif distribution == 'tabular': + return Tabular.from_xml_element(elem) + elif distribution == 'legendre': + return Legendre.from_xml_element(elem) + elif distribution == 'mixture': + return Mixture.from_xml_element(elem) class Discrete(Univariate): @@ -223,9 +241,7 @@ class Uniform(Univariate): """ params = get_text(elem, 'parameters').split() - a = float(params[0]) - b = float(params[1]) - return cls(a, b) + return cls(*map(float, params)) class Maxwell(Univariate): @@ -388,9 +404,7 @@ class Watt(Univariate): """ params = get_text(elem, 'parameters').split() - a = float(params[0]) - b = float(params[1]) - return watt(a, b) + return cls(*map(float, params)) class Normal(Univariate): @@ -478,9 +492,7 @@ class Normal(Univariate): """ params = get_text(elem, 'parameters').split() - mean_value = float(params[0]) - std_dev = float(params[1]) - return cls(mean_value, std_dev) + return cls(*map(float, params)) class Muir(Univariate): @@ -587,10 +599,7 @@ class Muir(Univariate): """ params = get_text(elem, 'parameters').split() - e0 = float(params[0]) - m_rat = float(params[1]) - kt = float(params[2]) - return muir(e0, m_rat, kt) + return cls(*map(float, params)) class Tabular(Univariate): @@ -706,7 +715,7 @@ class Tabular(Univariate): interpolation = get_text(elem, 'interpolation') params = [float(x) for x in get_text(elem, 'parameters').split()] x = params[:len(params)//2] - p = paramx[len(params)//2:] + p = params[len(params)//2:] return cls(x, p, interpolation) diff --git a/tests/unit_tests/test_source.py b/tests/unit_tests/test_source.py index 3c963d0527..1c70e159d2 100644 --- a/tests/unit_tests/test_source.py +++ b/tests/unit_tests/test_source.py @@ -11,7 +11,6 @@ def test_source(): assert src.space == space assert src.angle == angle assert src.energy == energy - assert src.strength == 1.0 elem = src.to_xml_element() assert 'strength' in elem.attrib @@ -19,6 +18,13 @@ def test_source(): assert elem.find('angle') is not None assert elem.find('energy') is not None + src = openmc.Source.from_xml_element(elem) + assert isinstance(src.angle, openmc.stats.Isotropic) + assert src.space.xyz == [0.0, 0.0, 0.0] + assert src.energy.x == [1.0e6] + assert src.energy.p == [1.0] + assert src.strength == 1.0 + def test_source_file(): filename = 'source.h5' diff --git a/tests/unit_tests/test_stats.py b/tests/unit_tests/test_stats.py index 553f2410e4..e159584782 100644 --- a/tests/unit_tests/test_stats.py +++ b/tests/unit_tests/test_stats.py @@ -10,10 +10,15 @@ def test_discrete(): x = [0.0, 1.0, 10.0] p = [0.3, 0.2, 0.5] d = openmc.stats.Discrete(x, p) + elem = d.to_xml_element('distribution') + + d = openmc.stats.Discrete.from_xml_element(elem) assert d.x == x assert d.p == p assert len(d) == len(x) - d.to_xml_element('distribution') + + d = openmc.stats.Univariate.from_xml_element(elem) + assert isinstance(d, openmc.stats.Discrete) # Single point d2 = openmc.stats.Discrete(1e6, 1.0) @@ -25,6 +30,9 @@ def test_discrete(): def test_uniform(): a, b = 10.0, 20.0 d = openmc.stats.Uniform(a, b) + elem = d.to_xml_element('distribution') + + d = openmc.stats.Uniform.from_xml_element(elem) assert d.a == a assert d.b == b assert len(d) == 2 @@ -34,35 +42,39 @@ def test_uniform(): assert t.p == [1/(b-a), 1/(b-a)] assert t.interpolation == 'histogram' - d.to_xml_element('distribution') - def test_maxwell(): theta = 1.2895e6 d = openmc.stats.Maxwell(theta) + elem = d.to_xml_element('distribution') + + d = openmc.stats.Maxwell.from_xml_element(elem) assert d.theta == theta assert len(d) == 1 - d.to_xml_element('distribution') def test_watt(): a, b = 0.965e6, 2.29e-6 d = openmc.stats.Watt(a, b) + elem = d.to_xml_element('distribution') + + d = openmc.stats.Watt.from_xml_element(elem) assert d.a == a assert d.b == b assert len(d) == 2 - d.to_xml_element('distribution') def test_tabular(): x = [0.0, 5.0, 7.0] p = [0.1, 0.2, 0.05] d = openmc.stats.Tabular(x, p, 'linear-linear') + elem = d.to_xml_element('distribution') + + d = openmc.stats.Tabular.from_xml_element(elem) assert d.x == x assert d.p == p assert d.interpolation == 'linear-linear' assert len(d) == len(x) - d.to_xml_element('distribution') def test_legendre(): @@ -115,6 +127,15 @@ def test_polar_azimuthal(): assert elem.find('mu') is not None assert elem.find('phi') is not None + d = openmc.stats.PolarAzimuthal.from_xml_element(elem) + assert d.mu.x == [1.] + assert d.mu.p == [1.] + assert d.phi.x == [0.] + assert d.phi.p == [1.] + + d = openmc.stats.UnitSphere.from_xml_element(elem) + assert isinstance(d, openmc.stats.PolarAzimuthal) + def test_isotropic(): d = openmc.stats.Isotropic() @@ -122,24 +143,25 @@ def test_isotropic(): assert elem.tag == 'angle' assert elem.attrib['type'] == 'isotropic' + d = openmc.stats.Isotropic.from_xml_element(elem) + assert isinstance(d, openmc.stats.Isotropic) + def test_monodirectional(): d = openmc.stats.Monodirectional((1., 0., 0.)) - assert d.reference_uvw == pytest.approx((1., 0., 0.)) - elem = d.to_xml_element() assert elem.tag == 'angle' assert elem.attrib['type'] == 'monodirectional' + d = openmc.stats.Monodirectional.from_xml_element(elem) + assert d.reference_uvw == pytest.approx((1., 0., 0.)) + def test_cartesian(): x = openmc.stats.Uniform(-10., 10.) y = openmc.stats.Uniform(-10., 10.) z = openmc.stats.Uniform(0., 20.) d = openmc.stats.CartesianIndependent(x, y, z) - assert d.x == x - assert d.y == y - assert d.z == z elem = d.to_xml_element() assert elem.tag == 'space' @@ -147,55 +169,75 @@ def test_cartesian(): assert elem.find('x') is not None assert elem.find('y') is not None + d = openmc.stats.CartesianIndependent.from_xml_element(elem) + assert d.x == x + assert d.y == y + assert d.z == z + + d = openmc.stats.Spatial.from_xml_element(elem) + assert isinstance(d, openmc.stats.CartesianIndependent) + def test_box(): lower_left = (-10., -10., -10.) upper_right = (10., 10., 10.) d = openmc.stats.Box(lower_left, upper_right) - assert d.lower_left == pytest.approx(lower_left) - assert d.upper_right == pytest.approx(upper_right) - assert not d.only_fissionable elem = d.to_xml_element() assert elem.tag == 'space' assert elem.attrib['type'] == 'box' assert elem.find('parameters') is not None + d = openmc.stats.Box.from_xml_element(elem) + assert d.lower_left == pytest.approx(lower_left) + assert d.upper_right == pytest.approx(upper_right) + assert not d.only_fissionable + # only fissionable parameter d2 = openmc.stats.Box(lower_left, upper_right, True) assert d2.only_fissionable elem = d2.to_xml_element() assert elem.attrib['type'] == 'fission' + d = openmc.stats.Spatial.from_xml_element(elem) + assert isinstance(d, openmc.stats.Box) def test_point(): p = (-4., 2., 10.) d = openmc.stats.Point(p) - assert d.xyz == pytest.approx(p) elem = d.to_xml_element() assert elem.tag == 'space' assert elem.attrib['type'] == 'point' assert elem.find('parameters') is not None + d = openmc.stats.Point.from_xml_element(elem) + assert d.xyz == pytest.approx(p) + def test_normal(): mean = 10.0 std_dev = 2.0 d = openmc.stats.Normal(mean,std_dev) + + elem = d.to_xml_element('distribution') + assert elem.attrib['type'] == 'normal' + + d = openmc.stats.Normal.from_xml_element(elem) assert d.mean_value == pytest.approx(mean) assert d.std_dev == pytest.approx(std_dev) assert len(d) == 2 - elem = d.to_xml_element('distribution') - assert elem.attrib['type'] == 'normal' def test_muir(): mean = 10.0 mass = 5.0 temp = 20000. d = openmc.stats.Muir(mean,mass,temp) + + elem = d.to_xml_element('energy') + assert elem.attrib['type'] == 'muir' + + d = openmc.stats.Muir.from_xml_element(elem) assert d.e0 == pytest.approx(mean) assert d.m_rat == pytest.approx(mass) assert d.kt == pytest.approx(temp) assert len(d) == 3 - elem = d.to_xml_element('energy') - assert elem.attrib['type'] == 'muir'