diff --git a/openmc/settings.py b/openmc/settings.py index cb0207e71a..271932b84f 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -9,6 +9,7 @@ import numpy as np from openmc.clean_xml import * from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) +from openmc import Nuclide from openmc.source import Source if sys.version_info[0] >= 3: @@ -125,6 +126,8 @@ class SettingsFile(object): Coordinates of the lower-left point of the UFS mesh ufs_upper_right : tuple or list Coordinates of the upper-right point of the UFS mesh + resonance_scattering : ResonanceScattering or iterable of ResonanceScattering + The elastic scattering model to use for resonant isotopes """ @@ -205,6 +208,8 @@ class SettingsFile(object): self._run_mode_subelement = None self._source_element = None + self._resonance_scattering = None + @property def run_mode(self): return self._run_mode @@ -393,9 +398,13 @@ class SettingsFile(object): def dd_count_interactions(self): return self._dd_count_interactions + @property + def resonance_scattering(self): + return self._resonance_scattering + @run_mode.setter def run_mode(self, run_mode): - if 'run_mode' not in ['eigenvalue', 'fixed source']: + if run_mode not in ['eigenvalue', 'fixed source']: msg = 'Unable to set run mode to "{0}". Only "eigenvalue" ' \ 'and "fixed source" are supported."'.format(run_mode) raise ValueError(msg) @@ -764,6 +773,16 @@ class SettingsFile(object): self._dd_count_interactions = interactions + @resonance_scattering.setter + def resonance_scattering(self, res): + if isinstance(res, Iterable): + check_type('resonance_scattering', res, Iterable, + ResonanceScattering) + self._resonance_scattering = res + else: + check_type('resonance_scattering', res, ResonanceScattering) + self._resonance_scattering = [res] + def _create_run_mode_subelement(self): if self.run_mode == 'eigenvalue': @@ -1043,6 +1062,17 @@ class SettingsFile(object): subelement = ET.SubElement(element, "count_interactions") subelement.text = str(self._dd_count_interactions).lower() + def _create_resonance_scattering_element(self): + if self.resonance_scattering is None: return + + element = ET.SubElement(self._settings_file, "resonance_scattering") + + for r in self.resonance_scattering: + if r.nuclide.name != r.nuclide_0K.name: + raise ValueError("The nuclide and nuclide_0K attributes of " + "a ResonantScattering object must have identical names.") + r.create_xml_subelement(element) + def export_to_xml(self): """Create a settings.xml file that can be used for a simulation. @@ -1079,6 +1109,7 @@ class SettingsFile(object): self._create_track_subelement() self._create_ufs_subelement() self._create_dd_subelement() + self._create_resonance_scattering_element() # Clean the indentation in the file to be user-readable clean_xml_indentation(self._settings_file) @@ -1087,3 +1118,104 @@ class SettingsFile(object): tree = ET.ElementTree(self._settings_file) tree.write("settings.xml", xml_declaration=True, encoding='utf-8', method="xml") + + +class ResonanceScattering(object): + """Specification of the elastic scattering model for resonant isotopes + + Attributes + ---------- + nuclide : openmc.nuclide.Nuclide + The nuclide affected by this resonance scattering treatment. + nuclide_0K : openmc.nuclide.Nuclide + This should be the same isotope as the nuclide attribute above, but it + should have an xs attribute that identifies 0 Kelvin data. + method : str + The method used to sample outgoing scattering energies. Valid options + are 'ARES', 'CXS' (constant cross section), 'DBRC' (Doppler broadening + rejection correction), and 'WCM' (weight correction method). + E_min : Real + The minimum energy above which the specified method is applied. By + default, CXS will be used below E_min. + E_max : Real + The maximum energy below which the specified method is applied. By + default, the asymptotic target-at-rest model is applied above E_max. + + """ + + def __init__(self): + self._nuclide = None + self._nuclide_0K = None + self._method = None + self._E_min = None + self._E_max = None + + @property + def nuclide(self): + return self._nuclide + + @property + def nuclide_0K(self): + return self._nuclide_0K + + @property + def method(self): + return self._method + + @property + def E_min(self): + return self._E_min + + @property + def E_max(self): + return self._E_max + + @nuclide.setter + def nuclide(self, nuc): + check_type('nuclide', nuc, Nuclide) + if nuc.zaid == None: raise ValueError("The nuclide must have an " + "explicitly defined zaid attribute.") + self._nuclide = nuc + + @nuclide_0K.setter + def nuclide_0K(self, nuc): + check_type('nuclide_0K', nuc, Nuclide) + if nuc.zaid == None: raise ValueError("The nuclide_0K must have an " + "explicitly defined zaid attribute.") + self._nuclide_0K = nuc + + @method.setter + def method(self, m): + check_value('method', m, ('ARES', 'CXS', 'DBRC', 'WCM')) + self._method = m + + @E_min.setter + def E_min(self, E): + check_type('E_min', E, Real) + check_greater_than('E_min', E, 0, True) + self._E_min = E + + @E_max.setter + def E_max(self, E): + check_type('E_max', E, Real) + check_greater_than('E_max', E, 0, True) + self._E_max = E + + def create_xml_subelement(self, xml_element): + scatterer = ET.SubElement(xml_element, "scatterer") + subelement = ET.SubElement(scatterer, 'nuclide') + subelement.text = self.nuclide.name + if self.method is not None: + subelement = ET.SubElement(scatterer, 'method') + subelement.text = self.method + subelement = ET.SubElement(scatterer, 'xs_label') + subelement.text = str(self.nuclide.zaid) + '.' + str(self.nuclide.xs) + subelement = ET.SubElement(scatterer, 'xs_label_0K') + subelement.text = str(self.nuclide_0K.zaid) + '.' \ + + str(self.nuclide_0K.xs) + if self.E_min is not None: + subelement = ET.SubElement(scatterer, 'E_min') + subelement.text = str(self.E_min) + if self.E_max is not None: + subelement = ET.SubElement(scatterer, 'E_max') + subelement.text = str(self.E_max) diff --git a/tests/test_resonance_scattering/geometry.xml b/tests/test_resonance_scattering/geometry.xml deleted file mode 100644 index bc56030e18..0000000000 --- a/tests/test_resonance_scattering/geometry.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/tests/test_resonance_scattering/inputs_true.dat b/tests/test_resonance_scattering/inputs_true.dat new file mode 100644 index 0000000000..f2a875c7eb --- /dev/null +++ b/tests/test_resonance_scattering/inputs_true.dat @@ -0,0 +1 @@ +ece83bb075ed8144af89ce7cebf1577dcb2489d2e9ce4afbe61a3e4398837e7a9aaa2ae0cea0a6542f51ca5e0d119b570c675ed1dca0d74237cd5fdce0b606a3 \ No newline at end of file diff --git a/tests/test_resonance_scattering/materials.xml b/tests/test_resonance_scattering/materials.xml deleted file mode 100644 index 52a8c04be2..0000000000 --- a/tests/test_resonance_scattering/materials.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/tests/test_resonance_scattering/results_true.dat b/tests/test_resonance_scattering/results_true.dat index a649013c07..e7056e4fab 100644 --- a/tests/test_resonance_scattering/results_true.dat +++ b/tests/test_resonance_scattering/results_true.dat @@ -1,2 +1,2 @@ k-combined: -6.842159E-02 8.481029E-04 +1.440556E+00 6.383274E-02 diff --git a/tests/test_resonance_scattering/settings.xml b/tests/test_resonance_scattering/settings.xml deleted file mode 100644 index 7ce4f23ac7..0000000000 --- a/tests/test_resonance_scattering/settings.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - U-238 - cxs - 92238.71c - 92238.71c - 5.0e-6 - 40.0e-6 - - - - - 10 - 5 - 1000 - - - - - -4 -4 -4 4 4 4 - - - - diff --git a/tests/test_resonance_scattering/test_resonance_scattering.py b/tests/test_resonance_scattering/test_resonance_scattering.py index 2a595f3e66..d977488bfe 100644 --- a/tests/test_resonance_scattering/test_resonance_scattering.py +++ b/tests/test_resonance_scattering/test_resonance_scattering.py @@ -3,9 +3,81 @@ import os import sys sys.path.insert(0, os.pardir) -from testing_harness import TestHarness +from testing_harness import PyAPITestHarness +import openmc + + +class ResonanceScatteringTestHarness(PyAPITestHarness): + def _build_inputs(self): + # Materials + mat = openmc.Material(material_id=1) + mat.set_density('g/cc', 1.0) + mat.add_nuclide('U-238', 1.0) + mat.add_nuclide('U-235', 0.02) + mat.add_nuclide('Pu-239', 0.02) + mat.add_nuclide('H-1', 20.0) + + mats_file = openmc.MaterialsFile() + mats_file.default_xs = '71c' + mats_file.add_material(mat) + mats_file.export_to_xml() + + # Geometry + dumb_surface = openmc.XPlane(x0=100) + dumb_surface.boundary_type = 'reflective' + + c1 = openmc.Cell(cell_id=1) + c1.fill = mat + c1.region = -dumb_surface + + root_univ = openmc.Universe(universe_id=0) + root_univ.add_cell(c1) + + geometry = openmc.Geometry() + geometry.root_universe = root_univ + geo_file = openmc.GeometryFile() + geo_file.geometry = geometry + geo_file.export_to_xml() + + # Settings + nuclide = openmc.Nuclide('U-238', '71c') + nuclide.zaid = 92238 + res_scatt_dbrc = openmc.ResonanceScattering() + res_scatt_dbrc.nuclide = nuclide + res_scatt_dbrc.nuclide_0K = nuclide # This is a bad idea! Just for tests + res_scatt_dbrc.method = 'DBRC' + res_scatt_dbrc.E_min = 1e-6 + res_scatt_dbrc.E_max = 210e-6 + + nuclide = openmc.Nuclide('U-235', '71c') + nuclide.zaid = 92235 + res_scatt_wcm = openmc.ResonanceScattering() + res_scatt_wcm.nuclide = nuclide + res_scatt_wcm.nuclide_0K = nuclide + res_scatt_wcm.method = 'WCM' + res_scatt_wcm.E_min = 1e-6 + res_scatt_wcm.E_max = 210e-6 + + nuclide = openmc.Nuclide('Pu-239', '71c') + nuclide.zaid = 94239 + res_scatt_ares = openmc.ResonanceScattering() + res_scatt_ares.nuclide = nuclide + res_scatt_ares.nuclide_0K = nuclide + res_scatt_ares.method = 'ARES' + res_scatt_ares.E_min = 1e-6 + res_scatt_ares.E_max = 210e-6 + + sets_file = openmc.SettingsFile() + sets_file.batches = 10 + sets_file.inactive = 5 + sets_file.particles = 1000 + sets_file.source = openmc.source.Source( + space=openmc.stats.Box([-4, -4, -4], [4, 4, 4])) + sets_file.resonance_scattering = [res_scatt_dbrc, res_scatt_wcm, + res_scatt_ares] + sets_file.export_to_xml() if __name__ == '__main__': - harness = TestHarness('statepoint.10.*') + harness = ResonanceScatteringTestHarness('statepoint.10.*') harness.main()