From 8b1b8dc13e4446e0c1026c8d025c2b4b9ab25e9a Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Sun, 15 Feb 2026 22:03:34 -0600 Subject: [PATCH 01/38] add properties file path to settings with documentation and loading from c++ and python round trip test --- docs/source/io_formats/settings.rst | 10 ++++++++++ include/openmc/settings.h | 2 ++ openmc/settings.py | 29 +++++++++++++++++++++++++++++ src/finalize.cpp | 1 + src/settings.cpp | 6 ++++++ tests/unit_tests/test_settings.py | 2 ++ 6 files changed, 50 insertions(+) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index ed7c6273aaa..288d8412b83 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -1340,6 +1340,16 @@ despite not being bounded on both sides. *Default*: 10 K +.. _properties_file: + +-------------------------------------- +```` Element +-------------------------------------- + + The ``properties_file`` element has no attributes and contains the path to + an HDF5 properties file to load during simulation initialization. + + .. _trace: ------------------- diff --git a/include/openmc/settings.h b/include/openmc/settings.h index b369c99fef8..d0b681ad302 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -114,6 +114,8 @@ extern std::string path_sourcepoint; //!< path to a source file extern std::string path_statepoint; //!< path to a statepoint file extern std::string weight_windows_file; //!< Location of weight window file to //!< load on simulation initialization +extern std::string properties_file; //!< Location of properties file to + //!< load on simulation initialization // This is required because the c_str() may not be the first thing in // std::string. Sometimes it is, but it seems libc++ may not be like that diff --git a/openmc/settings.py b/openmc/settings.py index 3cf662e311e..4e25e957635 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -179,6 +179,9 @@ class Settings: Initial seed for randomly generated plot colors. ptables : bool Determine whether probability tables are used. + properties_file : Pathlike + Location of the properties file to load cell temperatures/densities + and materials random_ray : dict Options for configuring the random ray solver. Acceptable keys are: @@ -392,6 +395,7 @@ def __init__(self, **kwargs): self._photon_transport = None self._plot_seed = None self._ptables = None + self._properties_file = None self._uniform_source_sampling = None self._seed = None self._stride = None @@ -1018,6 +1022,18 @@ def temperature(self, temperature: dict): self._temperature = temperature + @property + def properties_file(self) -> PathLike | None: + return self._properties_file + + @properties_file.setter + def properties_file(self, value: PathLike | None): + if value is None: + self._properties_file = None + else: + cv.check_type('weight windows file', value, PathLike) + self._properties_file = input_path(value) + @property def trace(self) -> Iterable: return self._trace @@ -1708,6 +1724,12 @@ def _create_temperature_subelements(self, root): else: element.text = str(value) + def _create_properties_file_element(self, root): + if self.properties_file is not None: + element = ET.Element("properties_file") + element.text = str(self.properties_file) + root.append(element) + def _create_trace_subelement(self, root): if self._trace is not None: element = ET.SubElement(root, "trace") @@ -2205,6 +2227,11 @@ def _temperature_from_xml_element(self, root): if text is not None: self.temperature['multipole'] = text in ('true', '1') + def _properties_file_from_xml_element(self, root): + text = get_text(root, 'properties_file') + if text is not None: + self.properties_file = text + def _trace_from_xml_element(self, root): text = get_elem_list(root, "trace", int) if text is not None: @@ -2440,6 +2467,7 @@ def to_xml_element(self, mesh_memo=None): self._create_ifp_n_generation_subelement(element) self._create_tabular_legendre_subelements(element) self._create_temperature_subelements(element) + self._create_properties_file_element(element) self._create_trace_subelement(element) self._create_track_subelement(element) self._create_ufs_mesh_subelement(element, mesh_memo) @@ -2554,6 +2582,7 @@ def from_xml_element(cls, elem, meshes=None): settings._ifp_n_generation_from_xml_element(elem) settings._tabular_legendre_from_xml_element(elem) settings._temperature_from_xml_element(elem) + settings._properties_file_from_xml_element(elem) settings._trace_from_xml_element(elem) settings._track_from_xml_element(elem) settings._ufs_mesh_from_xml_element(elem, meshes) diff --git a/src/finalize.cpp b/src/finalize.cpp index 4ac6d09f321..13230bc17cf 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -138,6 +138,7 @@ int openmc_finalize() settings::temperature_multipole = false; settings::temperature_range = {0.0, 0.0}; settings::temperature_tolerance = 10.0; + settings::properties_file.clear(); settings::trigger_on = false; settings::trigger_predict = false; settings::trigger_batch_interval = 1; diff --git a/src/settings.cpp b/src/settings.cpp index 5b472468fc4..3fc9f487b62 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -96,6 +96,7 @@ std::string path_sourcepoint; std::string path_statepoint; const char* path_statepoint_c {path_statepoint.c_str()}; std::string weight_windows_file; +std::string properties_file; int32_t n_inactive {0}; int32_t max_lost_particles {10}; @@ -733,6 +734,11 @@ void read_settings_xml(pugi::xml_node root) } } + // read properties from file + if (check_for_node(root, "properties_file")) { + properties_file = get_node_value(root, "properties_file"); + } + // Particle trace if (check_for_node(root, "trace")) { auto temp = get_node_array(root, "trace"); diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index fe618fd2d65..2457cfae1ae 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -43,6 +43,7 @@ def test_export_to_xml(run_in_tmpdir): s.tabular_legendre = {'enable': True, 'num_points': 50} s.temperature = {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': (200., 1000.)} + s.properties_file = 'properties_test.h5' s.trace = (10, 1, 20) s.track = [(1, 1, 1), (2, 1, 1)] s.ufs_mesh = mesh @@ -129,6 +130,7 @@ def test_export_to_xml(run_in_tmpdir): assert s.tabular_legendre == {'enable': True, 'num_points': 50} assert s.temperature == {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': [200., 1000.]} + assert s.properties_file = "properties_test.h5" assert s.trace == [10, 1, 20] assert s.track == [(1, 1, 1), (2, 1, 1)] assert isinstance(s.ufs_mesh, openmc.RegularMesh) From 8713fe9acfdb4fe34a4fd1be9dfca1670eb6fc69 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Wed, 18 Feb 2026 17:37:23 -0600 Subject: [PATCH 02/38] add flags to indicate which properties to read from properties file --- openmc/settings.py | 71 ++++++++++++++++++++++++++++--- tests/unit_tests/test_settings.py | 4 +- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 4e25e957635..0a611ddf5e6 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -182,6 +182,10 @@ class Settings: properties_file : Pathlike Location of the properties file to load cell temperatures/densities and materials + read_temperatures_from_properties : bool + Whether to read cell temperatures from the properties file + read_densities_from_properties : bool + Whether to read densities from the properties file random_ray : dict Options for configuring the random ray solver. Acceptable keys are: @@ -396,6 +400,8 @@ def __init__(self, **kwargs): self._plot_seed = None self._ptables = None self._properties_file = None + self._read_temperatures_from_properties = None + self._read_densities_from_properties = None self._uniform_source_sampling = None self._seed = None self._stride = None @@ -1031,8 +1037,30 @@ def properties_file(self, value: PathLike | None): if value is None: self._properties_file = None else: - cv.check_type('weight windows file', value, PathLike) + cv.check_type('properties file', value, PathLike) self._properties_file = input_path(value) + self.read_temperatures_from_properties = True + self.read_densities_from_properties = True + + @property + def read_temperatures_from_properties(self) -> bool: + return self._read_temperatures_from_properties + + @read_temperatures_from_properties.setter + def read_temperatures_from_properties(self, read_temperatures_from_properties : bool): + cv.check_type('Whether read temperatures from properties ', + read_temperatures_from_properties, bool) + self._read_temperatures_from_properties = read_temperatures_from_properties + + @property + def read_densities_from_properties(self) -> bool: + return self._read_densities_from_properties + + @read_densities_from_properties.setter + def read_densities_from_properties(self, read_densities_from_properties : bool): + cv.check_type('Whether read temperatures from properties ', + read_densities_from_properties, bool) + self._read_densities_from_properties = read_densities_from_properties @property def trace(self) -> Iterable: @@ -1725,11 +1753,24 @@ def _create_temperature_subelements(self, root): element.text = str(value) def _create_properties_file_element(self, root): + if ((self.read_densities_from_properties or + self.read_temperatures_from_properties) and + self.properties_file is None): + # build warning that no properties file is specified + msg = (f'Flag to read densities or temperatures was set without providing ' + 'a properties file.') + warnings.warn(msg) + if self.properties_file is not None: - element = ET.Element("properties_file") - element.text = str(self.properties_file) + element = ET.Element("properties") + subelement = ET.SubElement(element, "filepath") + subelement.text = str(self.properties_file) + subelement = ET.SubElement(element, "temperatures") + subelement.text = str(self.read_temperatures_from_properties).lower() + subelement = ET.SubElement(element, "densities") + subelement.text = str(self.read_densities_from_properties).lower() root.append(element) - + def _create_trace_subelement(self, root): if self._trace is not None: element = ET.SubElement(root, "trace") @@ -2228,9 +2269,25 @@ def _temperature_from_xml_element(self, root): self.temperature['multipole'] = text in ('true', '1') def _properties_file_from_xml_element(self, root): - text = get_text(root, 'properties_file') - if text is not None: - self.properties_file = text + elem = root.find('properties') + if elem is not None: + self.properties_file = get_text(elem, 'filepath') + text = get_text(elem, 'temperatures') + self.read_temperatures_from_properties = text in ('true', '1') + text = get_text(elem, 'densities') + self.read_densities_from_properties = text in ('true', '1') + + # def _weight_window_checkpoints_from_xml_element(self, root): + # elem = root.find('weight_window_checkpoints') + # if elem is None: + # return + # for key in ('collision', 'surface'): + # value = get_text(elem, key) + # if value is not None: + # value = value in ('true', '1') + # self.weight_window_checkpoints[key] = value + + def _trace_from_xml_element(self, root): text = get_elem_list(root, "trace", int) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 2457cfae1ae..97a12a48f7f 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -130,7 +130,9 @@ def test_export_to_xml(run_in_tmpdir): assert s.tabular_legendre == {'enable': True, 'num_points': 50} assert s.temperature == {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': [200., 1000.]} - assert s.properties_file = "properties_test.h5" + assert s.properties_file == "properties_test.h5" + assert s.read_temperatures_from_properties + assert s.read_densities_from_properties assert s.trace == [10, 1, 20] assert s.track == [(1, 1, 1), (2, 1, 1)] assert isinstance(s.ufs_mesh, openmc.RegularMesh) From 76c258efd2b2bb7a9ad2245e36c0f9a7719211d4 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Wed, 18 Feb 2026 17:38:17 -0600 Subject: [PATCH 03/38] Add new XML reading method to check existence of file path --- src/xml_interface.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index 840d3f5b871..53d4388b90d 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -36,6 +36,21 @@ std::string get_node_value( return value; } +std::string get_node_path( + pugi::xml_node node, const char* name, bool lowercase, bool strip) +{ + + std::string filename = get_node_value(node, name, lowercase, strip); + + if (!file_exists(filename)) { + set_errmsg(fmt::format("File '{}' does not exist.", filename)); + return OPENMC_E_INVALID_ARGUMENT; + } + + return filename; +} + + bool get_node_value_bool(pugi::xml_node node, const char* name) { if (node.attribute(name)) { From 0a7636bd53603a236834d9c4ee8f50f905c12665 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Wed, 18 Feb 2026 17:44:21 -0600 Subject: [PATCH 04/38] add flags for which properties to read from file --- src/settings.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 3fc9f487b62..f6918e50841 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -87,6 +87,8 @@ bool weight_window_checkpoint_surface {false}; bool weight_window_checkpoint_collision {true}; bool write_all_tracks {false}; bool write_initial_source {false}; +bool read_temperatures_from_properties {true}; +bool read_densities_from_properties {true}; std::string path_cross_sections; std::string path_input; @@ -735,8 +737,15 @@ void read_settings_xml(pugi::xml_node root) } // read properties from file - if (check_for_node(root, "properties_file")) { - properties_file = get_node_value(root, "properties_file"); + if (check_for_node(root, "properties")) { + // Get pointer to properties node + xml_node node_props = root.child("properties"); + + properties_file = get_node_path(node_props, "filepath"); + read_temperatures_from_properties = get_node_value_bool(node_props, + "temperatures"); + read_densities_from_properties = get_node_value_bool(node_props, + "densities"); } // Particle trace From 9942d78e51d5df8af37919ce5ca1d4a09698b0dc Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Wed, 18 Feb 2026 17:46:51 -0600 Subject: [PATCH 05/38] read properties file after other initialization --- src/initialize.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/initialize.cpp b/src/initialize.cpp index a2269ed1ea9..06b6ebf8b17 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -502,6 +502,12 @@ void read_separate_xml_files() read_plots_xml(); finalize_variance_reduction(); + + if (!settings::properties_file.empty()) { + openmc_properties_import(settings::properties_file, + settings::read_temperatures_from_properties, + settings::read_densities_from_properties); + } } void initial_output() From 886c641fb668326f7fe703ac62fbc0caf0f01409 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Wed, 18 Feb 2026 17:55:18 -0600 Subject: [PATCH 06/38] propagate boolean for property reads into C API and related python calls --- include/openmc/capi.h | 4 +++- openmc/lib/core.py | 4 ++-- src/summary.cpp | 15 ++++++++++++--- tests/unit_tests/test_lib.py | 10 +++++----- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 911654d318f..7ba6cdcc58a 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -318,7 +318,9 @@ int openmc_properties_export(const char* filename); //! Import physical properties for model //! \param[in] filename Filename to read from // \return Error code -int openmc_properties_import(const char* filename); +int openmc_properties_import(const char* filename, + bool read_temperatures_from_properties, + bool read_densities_from_properties); // Error codes extern int OPENMC_E_UNASSIGNED; diff --git a/openmc/lib/core.py b/openmc/lib/core.py index 2764ddff1a6..4013e37341a 100644 --- a/openmc/lib/core.py +++ b/openmc/lib/core.py @@ -77,7 +77,7 @@ class _SourceSite(Structure): _dll.openmc_properties_export.argtypes = [c_char_p] _dll.openmc_properties_export.restype = c_int _dll.openmc_properties_export.errcheck = _error_handler -_dll.openmc_properties_import.argtypes = [c_char_p] +_dll.openmc_properties_import.argtypes = [c_char_p, c_bool, c_bool] _dll.openmc_properties_import.restype = c_int _dll.openmc_properties_import.errcheck = _error_handler _dll.openmc_run.restype = c_int @@ -304,7 +304,7 @@ def import_properties(filename): openmc.lib.export_properties """ - _dll.openmc_properties_import(filename.encode()) + _dll.openmc_properties_import(filename.encode(), True, True) def init(args=None, intracomm=None, output=True): diff --git a/src/summary.cpp b/src/summary.cpp index b3ea0254e6e..a77b4ee57b9 100644 --- a/src/summary.cpp +++ b/src/summary.cpp @@ -190,8 +190,14 @@ extern "C" int openmc_properties_export(const char* filename) return 0; } -extern "C" int openmc_properties_import(const char* filename) +extern "C" int openmc_properties_import(const char* filename, + bool read_temperatures_from_properties, + bool read_densities_from_properties) { + if (!read_temperatures_from_properties && + !read_densities_from_properties) + return 0; + // Display output message auto msg = fmt::format("Importing properties from {}...", filename); write_message(msg, 5); @@ -228,7 +234,9 @@ extern "C" int openmc_properties_import(const char* filename) auto cells_group = open_group(geom_group, "cells"); try { for (const auto& c : model::cells) { - c->import_properties_hdf5(cells_group); + c->import_properties_hdf5(cells_group, + read_temperatures_from_properties, + read_densities_from_properties); } } catch (const std::exception& e) { set_errmsg(e.what()); @@ -250,7 +258,8 @@ extern "C" int openmc_properties_import(const char* filename) // Read material properties for (const auto& mat : model::materials) { - mat->import_properties_hdf5(materials_group); + mat->import_properties_hdf5(materials_group, + read_densities_from_properties); } close_group(materials_group); diff --git a/tests/unit_tests/test_lib.py b/tests/unit_tests/test_lib.py index 8ef8d592715..a05a8d1f084 100644 --- a/tests/unit_tests/test_lib.py +++ b/tests/unit_tests/test_lib.py @@ -169,7 +169,7 @@ def test_properties_temperature(lib_init): assert cell.get_temperature() == pytest.approx(300.0) # Import properties and check that temperature is restored - openmc.lib.import_properties('properties.h5') + openmc.lib.import_properties('properties.h5', True, True) assert cell.get_temperature() == pytest.approx(200.0) @@ -197,7 +197,7 @@ def test_properties_cell_density(lib_init): assert cell.get_density() == pytest.approx(3.0) # Import properties and check that density is restored - openmc.lib.import_properties('properties.h5') + openmc.lib.import_properties('properties.h5', True, True) assert cell.get_density() == pytest.approx(orig_density) @@ -213,7 +213,7 @@ def test_properties_fail_cell(lib_init): # The number of cells was changed in the previous test, so the properties # file is no longer valid with pytest.raises(exc.GeometryError, match="Number of cells"): - openmc.lib.import_properties("properties.h5") + openmc.lib.import_properties("properties.h5", True, True) def test_material_mapping(lib_init): @@ -267,7 +267,7 @@ def test_properties_density(lib_init): assert m.get_density() == pytest.approx(orig_density*2) # Import properties and check that density was restored - openmc.lib.import_properties('properties.h5') + openmc.lib.import_properties('properties.h5', True, True) assert m.get_density() == pytest.approx(orig_density) with pytest.raises(ValueError): @@ -293,7 +293,7 @@ def test_properties_fail_material(lib_init): # The number of materials was changed in the previous test, so the properties # file is no longer valid with pytest.raises(exc.GeometryError, match="Number of materials"): - openmc.lib.import_properties("properties.h5") + openmc.lib.import_properties("properties.h5", True, True) def test_nuclide_mapping(lib_init): From fd1594af56667fd2e1af71bb8d283caffd111f57 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Wed, 18 Feb 2026 19:27:22 -0600 Subject: [PATCH 07/38] propagate read properties flags to cells --- include/openmc/cell.h | 6 +++- src/cell.cpp | 70 +++++++++++++++++++++++-------------------- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/include/openmc/cell.h b/include/openmc/cell.h index 26c34ebd41d..02c180bc362 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -196,7 +196,11 @@ class Cell { //! Import physical properties from HDF5 //! \param[in] group HDF5 group to write to - void import_properties_hdf5(hid_t group); + //! \param[in] read_temperatures_from_properties whether to read temperatures + //! \param[in] read_densities_from_properties whether to read densities + void import_properties_hdf5(hid_t group, + bool read_temperatures_from_properties, + bool read_densities_from_properties); //! Get the BoundingBox for this cell. virtual BoundingBox bounding_box() const = 0; diff --git a/src/cell.cpp b/src/cell.cpp index ebe28c3d2ce..91c823d7d71 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -234,45 +234,51 @@ void Cell::export_properties_hdf5(hid_t group) const close_group(cell_group); } -void Cell::import_properties_hdf5(hid_t group) +void Cell::import_properties_hdf5(hid_t group, + bool read_temperatures_from_properties, + bool read_densities_from_properties) { auto cell_group = open_group(group, fmt::format("cell {}", id_)); - // Read temperatures from file - vector temps; - read_dataset(cell_group, "temperature", temps); - - // Ensure number of temperatures makes sense - auto n_temps = temps.size(); - if (n_temps > 1 && n_temps != n_instances()) { - fatal_error(fmt::format( - "Number of temperatures for cell {} doesn't match number of instances", - id_)); - } - - // Modify temperatures for the cell - sqrtkT_.clear(); - sqrtkT_.resize(temps.size()); - for (int64_t i = 0; i < temps.size(); ++i) { - this->set_temperature(temps[i], i); - } - - // Read densities - if (object_exists(cell_group, "density")) { - vector density; - read_dataset(cell_group, "density", density); + if (read_temperatures_from_properties) { + // Read temperatures from file + vector temps; + read_dataset(cell_group, "temperature", temps); - // Ensure number of densities makes sense - auto n_density = density.size(); - if (n_density > 1 && n_density != n_instances()) { - fatal_error(fmt::format("Number of densities for cell {} " - "doesn't match number of instances", + // Ensure number of temperatures makes sense + auto n_temps = temps.size(); + if (n_temps > 1 && n_temps != n_instances()) { + fatal_error(fmt::format( + "Number of temperatures for cell {} doesn't match number of instances", id_)); } - // Set densities. - for (int32_t i = 0; i < n_density; ++i) { - this->set_density(density[i], i); + // Modify temperatures for the cell + sqrtkT_.clear(); + sqrtkT_.resize(temps.size()); + for (int64_t i = 0; i < temps.size(); ++i) { + this->set_temperature(temps[i], i); + } + } + + if (read_densities_from_properties) {} + // Read densities + if (object_exists(cell_group, "density")) { + vector density; + read_dataset(cell_group, "density", density); + + // Ensure number of densities makes sense + auto n_density = density.size(); + if (n_density > 1 && n_density != n_instances()) { + fatal_error(fmt::format("Number of densities for cell {} " + "doesn't match number of instances", + id_)); + } + + // Set densities. + for (int32_t i = 0; i < n_density; ++i) { + this->set_density(density[i], i); + } } } From 5852ec01851677112d4fda3754cd37d5a2f37f99 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Wed, 18 Feb 2026 19:29:00 -0600 Subject: [PATCH 08/38] propagate read properties flags to material --- include/openmc/material.h | 3 ++- src/material.cpp | 15 +++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/openmc/material.h b/include/openmc/material.h index 3967c36878f..667aeaa51e1 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -76,7 +76,8 @@ class Material { //! Import physical properties from HDF5 //! \param[in] group HDF5 group to read from - void import_properties_hdf5(hid_t group); + void import_properties_hdf5(hid_t group, + bool read_densities_from_properties); //! Add nuclide to the material // diff --git a/src/material.cpp b/src/material.cpp index 21b11b8b9ce..1e4f07c1b28 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1138,13 +1138,16 @@ void Material::export_properties_hdf5(hid_t group) const close_group(material_group); } -void Material::import_properties_hdf5(hid_t group) +void Material::import_properties_hdf5(hid_t group, + bool read_densities_from_properties) { - hid_t material_group = open_group(group, "material " + std::to_string(id_)); - double density; - read_attribute(material_group, "atom_density", density); - this->set_density(density, "atom/b-cm"); - close_group(material_group); + if (read_densities_from_properties) { + hid_t material_group = open_group(group, "material " + std::to_string(id_)); + double density; + read_attribute(material_group, "atom_density", density); + this->set_density(density, "atom/b-cm"); + close_group(material_group); + } } void Material::add_nuclide(const std::string& name, double density) From a9b3f28b70f0b5b297da2904c909baebd3a1e7d7 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Thu, 19 Feb 2026 06:28:31 -0500 Subject: [PATCH 09/38] add new booleans to header --- include/openmc/settings.h | 50 +++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index d0b681ad302..bad07b6ffed 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -35,15 +35,15 @@ enum class IFPParameter { struct CollisionTrackConfig { bool mcpl_write {false}; //!< Write collision tracks using MCPL? std::unordered_set - cell_ids; //!< Cell ids where collisions will be written + cell_ids; //!< Cell ids where collisions will be written std::unordered_set - mt_numbers; //!< MT Numbers where collisions will be written + mt_numbers; //!< MT Numbers where collisions will be written std::unordered_set - universe_ids; //!< Universe IDs where collisions will be written + universe_ids; //!< Universe IDs where collisions will be written std::unordered_set - material_ids; //!< Material IDs where collisions will be written + material_ids; //!< Material IDs where collisions will be written std::unordered_set - nuclides; //!< Nuclides where collisions will be written + nuclides; //!< Nuclides where collisions will be written double deposited_energy_threshold {0.0}; //!< Minimum deposited energy [eV] int64_t max_collisions { 1000}; //!< Maximum events recorded per collision track file @@ -62,15 +62,15 @@ extern bool check_overlaps; //!< check overlaps in geometry? extern bool collision_track; //!< flag to use collision track feature? extern bool confidence_intervals; //!< use confidence intervals for results? extern bool - create_fission_neutrons; //!< create fission neutrons (fixed source)? + create_fission_neutrons; //!< create fission neutrons (fixed source)? extern bool create_delayed_neutrons; //!< create delayed fission neutrons? extern "C" bool cmfd_run; //!< is a CMFD run? extern bool delayed_photon_scaling; //!< Scale fission photon yield to include delayed extern "C" bool entropy_on; //!< calculate Shannon entropy? extern "C" bool - event_based; //!< use event-based mode (instead of history-based) -extern bool ifp_on; //!< Use IFP for kinetics parameters? + event_based; //!< use event-based mode (instead of history-based) +extern bool ifp_on; //!< Use IFP for kinetics parameters? extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular? extern bool material_cell_offsets; //!< create material cells offsets? extern "C" bool output_summary; //!< write summary.h5? @@ -104,6 +104,10 @@ extern bool weight_window_checkpoint_collision; //!< enable weight window check //!< upon collision? extern bool write_all_tracks; //!< write track files for every particle? extern bool write_initial_source; //!< write out initial source file? +extern bool read_temperatures_from_properties; //!< read cell temperatures from + //!< properties file? +extern bool read_densities_from_properties; //!< read cell/material densities + //!< from properties file? // Paths to various files extern std::string path_cross_sections; //!< path to cross_sections.xml @@ -122,8 +126,8 @@ extern std::string properties_file; //!< Location of properties file to // on some computers, like the intel Mac. extern "C" const char* path_statepoint_c; //!< C pointer to statepoint file name -extern "C" int32_t n_inactive; //!< number of inactive batches -extern "C" int32_t max_lost_particles; //!< maximum number of lost particles +extern "C" int32_t n_inactive; //!< number of inactive batches +extern "C" int32_t max_lost_particles; //!< maximum number of lost particles extern double rel_max_lost_particles; //!< maximum number of lost particles, relative to the //!< total number of particles @@ -137,15 +141,15 @@ extern int64_t max_particles_in_flight; //!< Max num. event-based particles in flight extern int max_particle_events; //!< Maximum number of particle events extern ElectronTreatment - electron_treatment; //!< how to treat secondary electrons + electron_treatment; //!< how to treat secondary electrons extern array - energy_cutoff; //!< Energy cutoff in [eV] for each particle type + energy_cutoff; //!< Energy cutoff in [eV] for each particle type extern array - time_cutoff; //!< Time cutoff in [s] for each particle type + time_cutoff; //!< Time cutoff in [s] for each particle type extern int ifp_n_generation; //!< Number of generation for Iterated Fission Probability extern IFPParameter - ifp_parameter; //!< Parameter to calculate for Iterated Fission Probability + ifp_parameter; //!< Parameter to calculate for Iterated Fission Probability extern int legendre_to_tabular_points; //!< number of points to convert Legendres extern int max_order; //!< Maximum Legendre order for multigroup data @@ -161,11 +165,11 @@ extern vector extern RunMode run_mode; //!< Run mode (eigenvalue, fixed src, etc.) extern SolverType solver_type; //!< Solver Type (Monte Carlo or Random Ray) extern std::unordered_set - sourcepoint_batch; //!< Batches when source should be written + sourcepoint_batch; //!< Batches when source should be written extern std::unordered_set - statepoint_batch; //!< Batches when state should be written + statepoint_batch; //!< Batches when state should be written extern std::unordered_set - source_write_surf_id; //!< Surface ids where sources will be written + source_write_surf_id; //!< Surface ids where sources will be written extern CollisionTrackConfig collision_track_config; extern double source_rejection_fraction; //!< Minimum fraction of source sites //!< that must be accepted @@ -184,15 +188,15 @@ extern int64_t ssw_cell_id; //!< Cell id for the surface source extern SSWCellType ssw_cell_type; //!< Type of option for the cell //!< argument of surface source write extern TemperatureMethod - temperature_method; //!< method for choosing temperatures + temperature_method; //!< method for choosing temperatures extern double - temperature_tolerance; //!< Tolerance in [K] on choosing temperatures + temperature_tolerance; //!< Tolerance in [K] on choosing temperatures extern double temperature_default; //!< Default T in [K] extern array - temperature_range; //!< Min/max T in [K] over which to load xs -extern int trace_batch; //!< Batch to trace particle on -extern int trace_gen; //!< Generation to trace particle on -extern int64_t trace_particle; //!< Particle ID to enable trace on + temperature_range; //!< Min/max T in [K] over which to load xs +extern int trace_batch; //!< Batch to trace particle on +extern int trace_gen; //!< Generation to trace particle on +extern int64_t trace_particle; //!< Particle ID to enable trace on extern vector> track_identifiers; //!< Particle numbers for writing tracks extern int trigger_batch_interval; //!< Batch interval for triggers From 1b78b84dae68397aae3560a557381dd45c6e647a Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Thu, 19 Feb 2026 06:32:45 -0500 Subject: [PATCH 10/38] apply clang formatting --- include/openmc/capi.h | 3 +-- include/openmc/material.h | 5 ++--- src/cell.cpp | 7 +++---- src/initialize.cpp | 2 +- src/material.cpp | 4 ++-- src/settings.cpp | 8 ++++---- src/summary.cpp | 15 ++++++--------- src/xml_interface.cpp | 1 - 8 files changed, 19 insertions(+), 26 deletions(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 7ba6cdcc58a..61cde08fd81 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -319,8 +319,7 @@ int openmc_properties_export(const char* filename); //! \param[in] filename Filename to read from // \return Error code int openmc_properties_import(const char* filename, - bool read_temperatures_from_properties, - bool read_densities_from_properties); + bool read_temperatures_from_properties, bool read_densities_from_properties); // Error codes extern int OPENMC_E_UNASSIGNED; diff --git a/include/openmc/material.h b/include/openmc/material.h index 667aeaa51e1..f91e2f3a6b0 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -76,8 +76,7 @@ class Material { //! Import physical properties from HDF5 //! \param[in] group HDF5 group to read from - void import_properties_hdf5(hid_t group, - bool read_densities_from_properties); + void import_properties_hdf5(hid_t group, bool read_densities_from_properties); //! Add nuclide to the material // @@ -231,7 +230,7 @@ class Material { bool depletable_ {false}; //!< Is the material depletable? bool fissionable_ { - false}; //!< Does this material contain fissionable nuclides + false}; //!< Does this material contain fissionable nuclides //! \brief Default temperature for cells containing this material. //! //! A negative value indicates no default temperature was specified. diff --git a/src/cell.cpp b/src/cell.cpp index 91c823d7d71..c2a1f23e2c3 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -235,8 +235,7 @@ void Cell::export_properties_hdf5(hid_t group) const } void Cell::import_properties_hdf5(hid_t group, - bool read_temperatures_from_properties, - bool read_densities_from_properties) + bool read_temperatures_from_properties, bool read_densities_from_properties) { auto cell_group = open_group(group, fmt::format("cell {}", id_)); @@ -260,8 +259,8 @@ void Cell::import_properties_hdf5(hid_t group, this->set_temperature(temps[i], i); } } - - if (read_densities_from_properties) {} + + if (read_densities_from_properties) { // Read densities if (object_exists(cell_group, "density")) { vector density; diff --git a/src/initialize.cpp b/src/initialize.cpp index 06b6ebf8b17..84bda735d8a 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -504,7 +504,7 @@ void read_separate_xml_files() finalize_variance_reduction(); if (!settings::properties_file.empty()) { - openmc_properties_import(settings::properties_file, + openmc_properties_import(settings::properties_file, settings::read_temperatures_from_properties, settings::read_densities_from_properties); } diff --git a/src/material.cpp b/src/material.cpp index 1e4f07c1b28..0be2f4d7112 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1138,8 +1138,8 @@ void Material::export_properties_hdf5(hid_t group) const close_group(material_group); } -void Material::import_properties_hdf5(hid_t group, - bool read_densities_from_properties) +void Material::import_properties_hdf5( + hid_t group, bool read_densities_from_properties) { if (read_densities_from_properties) { hid_t material_group = open_group(group, "material " + std::to_string(id_)); diff --git a/src/settings.cpp b/src/settings.cpp index f6918e50841..95e9b4191e4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -742,10 +742,10 @@ void read_settings_xml(pugi::xml_node root) xml_node node_props = root.child("properties"); properties_file = get_node_path(node_props, "filepath"); - read_temperatures_from_properties = get_node_value_bool(node_props, - "temperatures"); - read_densities_from_properties = get_node_value_bool(node_props, - "densities"); + read_temperatures_from_properties = + get_node_value_bool(node_props, "temperatures"); + read_densities_from_properties = + get_node_value_bool(node_props, "densities"); } // Particle trace diff --git a/src/summary.cpp b/src/summary.cpp index a77b4ee57b9..e043a5e7c15 100644 --- a/src/summary.cpp +++ b/src/summary.cpp @@ -191,12 +191,10 @@ extern "C" int openmc_properties_export(const char* filename) } extern "C" int openmc_properties_import(const char* filename, - bool read_temperatures_from_properties, - bool read_densities_from_properties) + bool read_temperatures_from_properties, bool read_densities_from_properties) { - if (!read_temperatures_from_properties && - !read_densities_from_properties) - return 0; + if (!read_temperatures_from_properties && !read_densities_from_properties) + return 0; // Display output message auto msg = fmt::format("Importing properties from {}...", filename); @@ -234,8 +232,7 @@ extern "C" int openmc_properties_import(const char* filename, auto cells_group = open_group(geom_group, "cells"); try { for (const auto& c : model::cells) { - c->import_properties_hdf5(cells_group, - read_temperatures_from_properties, + c->import_properties_hdf5(cells_group, read_temperatures_from_properties, read_densities_from_properties); } } catch (const std::exception& e) { @@ -258,8 +255,8 @@ extern "C" int openmc_properties_import(const char* filename, // Read material properties for (const auto& mat : model::materials) { - mat->import_properties_hdf5(materials_group, - read_densities_from_properties); + mat->import_properties_hdf5( + materials_group, read_densities_from_properties); } close_group(materials_group); diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index 53d4388b90d..f6af471e490 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -50,7 +50,6 @@ std::string get_node_path( return filename; } - bool get_node_value_bool(pugi::xml_node node, const char* name) { if (node.attribute(name)) { From cc10db69262ae894f14b656c1e6cc73800ebc7bd Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Thu, 19 Feb 2026 20:05:13 -0500 Subject: [PATCH 11/38] fix formatting --- include/openmc/material.h | 2 +- include/openmc/settings.h | 54 +++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/include/openmc/material.h b/include/openmc/material.h index f91e2f3a6b0..2eaf6e4bb27 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -230,7 +230,7 @@ class Material { bool depletable_ {false}; //!< Is the material depletable? bool fissionable_ { - false}; //!< Does this material contain fissionable nuclides + false}; //!< Does this material contain fissionable nuclides //! \brief Default temperature for cells containing this material. //! //! A negative value indicates no default temperature was specified. diff --git a/include/openmc/settings.h b/include/openmc/settings.h index bad07b6ffed..a45067bf2d5 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -35,15 +35,15 @@ enum class IFPParameter { struct CollisionTrackConfig { bool mcpl_write {false}; //!< Write collision tracks using MCPL? std::unordered_set - cell_ids; //!< Cell ids where collisions will be written + cell_ids; //!< Cell ids where collisions will be written std::unordered_set - mt_numbers; //!< MT Numbers where collisions will be written + mt_numbers; //!< MT Numbers where collisions will be written std::unordered_set - universe_ids; //!< Universe IDs where collisions will be written + universe_ids; //!< Universe IDs where collisions will be written std::unordered_set - material_ids; //!< Material IDs where collisions will be written + material_ids; //!< Material IDs where collisions will be written std::unordered_set - nuclides; //!< Nuclides where collisions will be written + nuclides; //!< Nuclides where collisions will be written double deposited_energy_threshold {0.0}; //!< Minimum deposited energy [eV] int64_t max_collisions { 1000}; //!< Maximum events recorded per collision track file @@ -62,15 +62,15 @@ extern bool check_overlaps; //!< check overlaps in geometry? extern bool collision_track; //!< flag to use collision track feature? extern bool confidence_intervals; //!< use confidence intervals for results? extern bool - create_fission_neutrons; //!< create fission neutrons (fixed source)? + create_fission_neutrons; //!< create fission neutrons (fixed source)? extern bool create_delayed_neutrons; //!< create delayed fission neutrons? extern "C" bool cmfd_run; //!< is a CMFD run? extern bool delayed_photon_scaling; //!< Scale fission photon yield to include delayed extern "C" bool entropy_on; //!< calculate Shannon entropy? extern "C" bool - event_based; //!< use event-based mode (instead of history-based) -extern bool ifp_on; //!< Use IFP for kinetics parameters? + event_based; //!< use event-based mode (instead of history-based) +extern bool ifp_on; //!< Use IFP for kinetics parameters? extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular? extern bool material_cell_offsets; //!< create material cells offsets? extern "C" bool output_summary; //!< write summary.h5? @@ -106,8 +106,8 @@ extern bool write_all_tracks; //!< write track files for every particle? extern bool write_initial_source; //!< write out initial source file? extern bool read_temperatures_from_properties; //!< read cell temperatures from //!< properties file? -extern bool read_densities_from_properties; //!< read cell/material densities - //!< from properties file? +extern bool read_densities_from_properties; //!< read cell/material densities + //!< from properties file? // Paths to various files extern std::string path_cross_sections; //!< path to cross_sections.xml @@ -118,16 +118,16 @@ extern std::string path_sourcepoint; //!< path to a source file extern std::string path_statepoint; //!< path to a statepoint file extern std::string weight_windows_file; //!< Location of weight window file to //!< load on simulation initialization -extern std::string properties_file; //!< Location of properties file to - //!< load on simulation initialization +extern std::string properties_file; //!< Location of properties file to + //!< load on simulation initialization // This is required because the c_str() may not be the first thing in // std::string. Sometimes it is, but it seems libc++ may not be like that // on some computers, like the intel Mac. extern "C" const char* path_statepoint_c; //!< C pointer to statepoint file name -extern "C" int32_t n_inactive; //!< number of inactive batches -extern "C" int32_t max_lost_particles; //!< maximum number of lost particles +extern "C" int32_t n_inactive; //!< number of inactive batches +extern "C" int32_t max_lost_particles; //!< maximum number of lost particles extern double rel_max_lost_particles; //!< maximum number of lost particles, relative to the //!< total number of particles @@ -141,15 +141,15 @@ extern int64_t max_particles_in_flight; //!< Max num. event-based particles in flight extern int max_particle_events; //!< Maximum number of particle events extern ElectronTreatment - electron_treatment; //!< how to treat secondary electrons + electron_treatment; //!< how to treat secondary electrons extern array - energy_cutoff; //!< Energy cutoff in [eV] for each particle type + energy_cutoff; //!< Energy cutoff in [eV] for each particle type extern array - time_cutoff; //!< Time cutoff in [s] for each particle type + time_cutoff; //!< Time cutoff in [s] for each particle type extern int ifp_n_generation; //!< Number of generation for Iterated Fission Probability extern IFPParameter - ifp_parameter; //!< Parameter to calculate for Iterated Fission Probability + ifp_parameter; //!< Parameter to calculate for Iterated Fission Probability extern int legendre_to_tabular_points; //!< number of points to convert Legendres extern int max_order; //!< Maximum Legendre order for multigroup data @@ -165,11 +165,11 @@ extern vector extern RunMode run_mode; //!< Run mode (eigenvalue, fixed src, etc.) extern SolverType solver_type; //!< Solver Type (Monte Carlo or Random Ray) extern std::unordered_set - sourcepoint_batch; //!< Batches when source should be written + sourcepoint_batch; //!< Batches when source should be written extern std::unordered_set - statepoint_batch; //!< Batches when state should be written + statepoint_batch; //!< Batches when state should be written extern std::unordered_set - source_write_surf_id; //!< Surface ids where sources will be written + source_write_surf_id; //!< Surface ids where sources will be written extern CollisionTrackConfig collision_track_config; extern double source_rejection_fraction; //!< Minimum fraction of source sites //!< that must be accepted @@ -188,15 +188,15 @@ extern int64_t ssw_cell_id; //!< Cell id for the surface source extern SSWCellType ssw_cell_type; //!< Type of option for the cell //!< argument of surface source write extern TemperatureMethod - temperature_method; //!< method for choosing temperatures + temperature_method; //!< method for choosing temperatures extern double - temperature_tolerance; //!< Tolerance in [K] on choosing temperatures + temperature_tolerance; //!< Tolerance in [K] on choosing temperatures extern double temperature_default; //!< Default T in [K] extern array - temperature_range; //!< Min/max T in [K] over which to load xs -extern int trace_batch; //!< Batch to trace particle on -extern int trace_gen; //!< Generation to trace particle on -extern int64_t trace_particle; //!< Particle ID to enable trace on + temperature_range; //!< Min/max T in [K] over which to load xs +extern int trace_batch; //!< Batch to trace particle on +extern int trace_gen; //!< Generation to trace particle on +extern int64_t trace_particle; //!< Particle ID to enable trace on extern vector> track_identifiers; //!< Particle numbers for writing tracks extern int trigger_batch_interval; //!< Batch interval for triggers From cf9a27eaecb1f9928cc7a8964702006359713562 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Thu, 19 Feb 2026 20:33:54 -0500 Subject: [PATCH 12/38] manually fix clang-format inconsistency --- include/openmc/settings.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/openmc/settings.h b/include/openmc/settings.h index a45067bf2d5..6abc978fd9e 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -106,8 +106,8 @@ extern bool write_all_tracks; //!< write track files for every particle? extern bool write_initial_source; //!< write out initial source file? extern bool read_temperatures_from_properties; //!< read cell temperatures from //!< properties file? -extern bool read_densities_from_properties; //!< read cell/material densities - //!< from properties file? +extern bool read_densities_from_properties; //!< read cell/material densities + //!< from properties file? // Paths to various files extern std::string path_cross_sections; //!< path to cross_sections.xml @@ -118,8 +118,8 @@ extern std::string path_sourcepoint; //!< path to a source file extern std::string path_statepoint; //!< path to a statepoint file extern std::string weight_windows_file; //!< Location of weight window file to //!< load on simulation initialization -extern std::string properties_file; //!< Location of properties file to - //!< load on simulation initialization +extern std::string properties_file; //!< Location of properties file to + //!< load on simulation initialization // This is required because the c_str() may not be the first thing in // std::string. Sometimes it is, but it seems libc++ may not be like that From 4f796fb663b15721433ea994f1499307871a14f7 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Thu, 19 Feb 2026 20:42:58 -0500 Subject: [PATCH 13/38] fix string to char* conversion --- src/initialize.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/initialize.cpp b/src/initialize.cpp index 84bda735d8a..8933d734e49 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -504,7 +504,7 @@ void read_separate_xml_files() finalize_variance_reduction(); if (!settings::properties_file.empty()) { - openmc_properties_import(settings::properties_file, + openmc_properties_import(settings::properties_file.c_str(), settings::read_temperatures_from_properties, settings::read_densities_from_properties); } From 6a862b6f5ca175da9df2f743ed8fe57c0cb812eb Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Thu, 19 Feb 2026 20:50:01 -0500 Subject: [PATCH 14/38] declare new XML reader --- include/openmc/xml_interface.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index 17a34e5c77a..87fd37fecba 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -21,6 +21,9 @@ inline bool check_for_node(pugi::xml_node node, const char* name) std::string get_node_value(pugi::xml_node node, const char* name, bool lowercase = false, bool strip = false); +std::string get_node_path(pugi::xml_node node, const char* name, + bool lowercase, bool strip) + bool get_node_value_bool(pugi::xml_node node, const char* name); template From cfcbfc2391a32621dba2c0afe5a55244a6411df1 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Thu, 19 Feb 2026 20:52:56 -0500 Subject: [PATCH 15/38] add missing semi-colon and guess at formatting --- include/openmc/xml_interface.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index 87fd37fecba..9499f106bf1 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -18,11 +18,11 @@ inline bool check_for_node(pugi::xml_node node, const char* name) return node.attribute(name) || node.child(name); } -std::string get_node_value(pugi::xml_node node, const char* name, - bool lowercase = false, bool strip = false); +std::string get_node_value( + pugi::xml_node node, const char* name, bool lowercase = false, bool strip = false); std::string get_node_path(pugi::xml_node node, const char* name, - bool lowercase, bool strip) + bool lowercase, bool strip = false); bool get_node_value_bool(pugi::xml_node node, const char* name); From fb22bbaf14173eeb692aa236f44d93116e9e524d Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Thu, 19 Feb 2026 22:02:38 -0500 Subject: [PATCH 16/38] add default value to signature --- include/openmc/xml_interface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index 9499f106bf1..1c7a238a842 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -22,7 +22,7 @@ std::string get_node_value( pugi::xml_node node, const char* name, bool lowercase = false, bool strip = false); std::string get_node_path(pugi::xml_node node, const char* name, - bool lowercase, bool strip = false); + bool lowercase = false, bool strip = false); bool get_node_value_bool(pugi::xml_node node, const char* name); From 9a20d6ed6133aa2e7dbb7b28a0fb9cdf6dffd52b Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Fri, 20 Feb 2026 14:19:27 -0600 Subject: [PATCH 17/38] clang-format-15 --- include/openmc/xml_interface.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index 1c7a238a842..e148f038342 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -18,10 +18,10 @@ inline bool check_for_node(pugi::xml_node node, const char* name) return node.attribute(name) || node.child(name); } -std::string get_node_value( - pugi::xml_node node, const char* name, bool lowercase = false, bool strip = false); +std::string get_node_value(pugi::xml_node node, const char* name, + bool lowercase = false, bool strip = false); -std::string get_node_path(pugi::xml_node node, const char* name, +std::string get_node_path(pugi::xml_node node, const char* name, bool lowercase = false, bool strip = false); bool get_node_value_bool(pugi::xml_node node, const char* name); From 015df875377f998524f6f789882c5c3d6fd1cd7d Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Fri, 20 Feb 2026 14:27:02 -0600 Subject: [PATCH 18/38] add file_utils to xml_interface --- src/xml_interface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index f6af471e490..c8bc06dd53b 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -3,6 +3,7 @@ #include #include "openmc/error.h" +#include "openmc/file_utils.h" #include "openmc/string_utils.h" #include "openmc/vector.h" From d8c92fdc210125214bfce6e58ae0331fcbdbed68 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Sat, 21 Feb 2026 10:31:27 -0600 Subject: [PATCH 19/38] back away from new XML reader for valid file paths --- include/openmc/xml_interface.h | 3 --- src/settings.cpp | 6 +++++- src/xml_interface.cpp | 15 --------------- 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/include/openmc/xml_interface.h b/include/openmc/xml_interface.h index e148f038342..17a34e5c77a 100644 --- a/include/openmc/xml_interface.h +++ b/include/openmc/xml_interface.h @@ -21,9 +21,6 @@ inline bool check_for_node(pugi::xml_node node, const char* name) std::string get_node_value(pugi::xml_node node, const char* name, bool lowercase = false, bool strip = false); -std::string get_node_path(pugi::xml_node node, const char* name, - bool lowercase = false, bool strip = false); - bool get_node_value_bool(pugi::xml_node node, const char* name); template diff --git a/src/settings.cpp b/src/settings.cpp index 95e9b4191e4..9f23c4f4d80 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -741,7 +741,11 @@ void read_settings_xml(pugi::xml_node root) // Get pointer to properties node xml_node node_props = root.child("properties"); - properties_file = get_node_path(node_props, "filepath"); + properties_file = get_node_value(node_props, "filepath"); + if (!file_exists(properties_file)) { + set_errmsg(fmt::format("File '{}' does not exist.", properties_file)); + return OPENMC_E_INVALID_ARGUMENT; + } read_temperatures_from_properties = get_node_value_bool(node_props, "temperatures"); read_densities_from_properties = diff --git a/src/xml_interface.cpp b/src/xml_interface.cpp index c8bc06dd53b..840d3f5b871 100644 --- a/src/xml_interface.cpp +++ b/src/xml_interface.cpp @@ -3,7 +3,6 @@ #include #include "openmc/error.h" -#include "openmc/file_utils.h" #include "openmc/string_utils.h" #include "openmc/vector.h" @@ -37,20 +36,6 @@ std::string get_node_value( return value; } -std::string get_node_path( - pugi::xml_node node, const char* name, bool lowercase, bool strip) -{ - - std::string filename = get_node_value(node, name, lowercase, strip); - - if (!file_exists(filename)) { - set_errmsg(fmt::format("File '{}' does not exist.", filename)); - return OPENMC_E_INVALID_ARGUMENT; - } - - return filename; -} - bool get_node_value_bool(pugi::xml_node node, const char* name) { if (node.attribute(name)) { From 99ca76f1939e78c553730dacd812d56960803b0e Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Sat, 21 Feb 2026 16:03:23 -0600 Subject: [PATCH 20/38] Improve documentation --- docs/source/io_formats/settings.rst | 35 ++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 288d8412b83..c17c9f5c0b1 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -542,6 +542,31 @@ generator during generation of colors in plots. *Default*: 1 + +.. _properties_file: + +-------------------------------------- +```` Element +-------------------------------------- + + The ``properties_file`` element indicates whether temperatures and densities + should be read from the properties file instead of from the XML file. It contains + the following parameters: + + :filepath: + The path to the properties file to be used to read temperatures and/or densities + + :temperatures: + Boolean to indicate whether or not to read cell temperatures from the properties file. + + *Default*: True + + :densities: + Boolean to indicate whether or not to read cell or material densities from the properties file. + + *Default*: True + + --------------------- ```` Element --------------------- @@ -1340,16 +1365,6 @@ despite not being bounded on both sides. *Default*: 10 K -.. _properties_file: - --------------------------------------- -```` Element --------------------------------------- - - The ``properties_file`` element has no attributes and contains the path to - an HDF5 properties file to load during simulation initialization. - - .. _trace: ------------------- From a86f00ecd50d204bc1f5038d75568bfe1c625086 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Sat, 21 Feb 2026 16:15:48 -0600 Subject: [PATCH 21/38] switch to C++ error handling --- src/settings.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 9f23c4f4d80..b4e123694e5 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -743,8 +743,7 @@ void read_settings_xml(pugi::xml_node root) properties_file = get_node_value(node_props, "filepath"); if (!file_exists(properties_file)) { - set_errmsg(fmt::format("File '{}' does not exist.", properties_file)); - return OPENMC_E_INVALID_ARGUMENT; + fatal_error(fmt::format("File '{}' does not exist.", properties_file)); } read_temperatures_from_properties = get_node_value_bool(node_props, "temperatures"); From 5474147fbba60678104565d7acd826fc1714e8e2 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Sun, 22 Feb 2026 10:49:21 -0600 Subject: [PATCH 22/38] cleanup lib call tests --- tests/unit_tests/test_lib.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit_tests/test_lib.py b/tests/unit_tests/test_lib.py index a05a8d1f084..8ef8d592715 100644 --- a/tests/unit_tests/test_lib.py +++ b/tests/unit_tests/test_lib.py @@ -169,7 +169,7 @@ def test_properties_temperature(lib_init): assert cell.get_temperature() == pytest.approx(300.0) # Import properties and check that temperature is restored - openmc.lib.import_properties('properties.h5', True, True) + openmc.lib.import_properties('properties.h5') assert cell.get_temperature() == pytest.approx(200.0) @@ -197,7 +197,7 @@ def test_properties_cell_density(lib_init): assert cell.get_density() == pytest.approx(3.0) # Import properties and check that density is restored - openmc.lib.import_properties('properties.h5', True, True) + openmc.lib.import_properties('properties.h5') assert cell.get_density() == pytest.approx(orig_density) @@ -213,7 +213,7 @@ def test_properties_fail_cell(lib_init): # The number of cells was changed in the previous test, so the properties # file is no longer valid with pytest.raises(exc.GeometryError, match="Number of cells"): - openmc.lib.import_properties("properties.h5", True, True) + openmc.lib.import_properties("properties.h5") def test_material_mapping(lib_init): @@ -267,7 +267,7 @@ def test_properties_density(lib_init): assert m.get_density() == pytest.approx(orig_density*2) # Import properties and check that density was restored - openmc.lib.import_properties('properties.h5', True, True) + openmc.lib.import_properties('properties.h5') assert m.get_density() == pytest.approx(orig_density) with pytest.raises(ValueError): @@ -293,7 +293,7 @@ def test_properties_fail_material(lib_init): # The number of materials was changed in the previous test, so the properties # file is no longer valid with pytest.raises(exc.GeometryError, match="Number of materials"): - openmc.lib.import_properties("properties.h5", True, True) + openmc.lib.import_properties("properties.h5") def test_nuclide_mapping(lib_init): From 902e749059afbe08bdc5cd3fe8ec27751c1e6542 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Sun, 22 Feb 2026 14:41:52 -0600 Subject: [PATCH 23/38] add temporary file for testing --- tests/unit_tests/test_settings.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 97a12a48f7f..32129ffb4c2 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -1,8 +1,14 @@ +from pathlib import Path + import openmc import openmc.stats def test_export_to_xml(run_in_tmpdir): + + tmp_properties_file = Path('properties_test.h5') + tmp_properties_file.touch() + s = openmc.Settings(run_mode='fixed source', batches=1000, seed=17) s.generations_per_batch = 10 s.inactive = 100 @@ -43,7 +49,7 @@ def test_export_to_xml(run_in_tmpdir): s.tabular_legendre = {'enable': True, 'num_points': 50} s.temperature = {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': (200., 1000.)} - s.properties_file = 'properties_test.h5' + s.properties_file = str(tmp_properties_file) s.trace = (10, 1, 20) s.track = [(1, 1, 1), (2, 1, 1)] s.ufs_mesh = mesh @@ -86,6 +92,7 @@ def test_export_to_xml(run_in_tmpdir): # Make sure exporting XML works s.export_to_xml() + # Generate settings from XML s = openmc.Settings.from_xml() assert s.run_mode == 'fixed source' @@ -130,7 +137,7 @@ def test_export_to_xml(run_in_tmpdir): assert s.tabular_legendre == {'enable': True, 'num_points': 50} assert s.temperature == {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': [200., 1000.]} - assert s.properties_file == "properties_test.h5" + assert s.properties_file == str(tmp_properties_file) assert s.read_temperatures_from_properties assert s.read_densities_from_properties assert s.trace == [10, 1, 20] From a62b545e22c025d648fd258f292270fce24a158b Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Sun, 22 Feb 2026 16:21:42 -0600 Subject: [PATCH 24/38] don't convert back to string in test --- tests/unit_tests/test_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 32129ffb4c2..de71aeca106 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -137,7 +137,7 @@ def test_export_to_xml(run_in_tmpdir): assert s.tabular_legendre == {'enable': True, 'num_points': 50} assert s.temperature == {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': [200., 1000.]} - assert s.properties_file == str(tmp_properties_file) + assert s.properties_file == tmp_properties_file assert s.read_temperatures_from_properties assert s.read_densities_from_properties assert s.trace == [10, 1, 20] From c7a8090cd4d68dfe2477d2447f72e98f0bfe6c2a Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Sun, 22 Feb 2026 16:26:16 -0600 Subject: [PATCH 25/38] Don't generate file, but do test against Path --- tests/unit_tests/test_settings.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index de71aeca106..0f0f8f08d90 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -6,8 +6,7 @@ def test_export_to_xml(run_in_tmpdir): - tmp_properties_file = Path('properties_test.h5') - tmp_properties_file.touch() + tmp_properties_file = 'properties_test.h5' s = openmc.Settings(run_mode='fixed source', batches=1000, seed=17) s.generations_per_batch = 10 @@ -49,7 +48,7 @@ def test_export_to_xml(run_in_tmpdir): s.tabular_legendre = {'enable': True, 'num_points': 50} s.temperature = {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': (200., 1000.)} - s.properties_file = str(tmp_properties_file) + s.properties_file = tmp_properties_file s.trace = (10, 1, 20) s.track = [(1, 1, 1), (2, 1, 1)] s.ufs_mesh = mesh @@ -137,7 +136,7 @@ def test_export_to_xml(run_in_tmpdir): assert s.tabular_legendre == {'enable': True, 'num_points': 50} assert s.temperature == {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': [200., 1000.]} - assert s.properties_file == tmp_properties_file + assert s.properties_file == Path(tmp_properties_file) assert s.read_temperatures_from_properties assert s.read_densities_from_properties assert s.trace == [10, 1, 20] From ebbdc1a68caa1d02984ef7d148402bd799fa260f Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Mon, 23 Feb 2026 13:21:20 -0600 Subject: [PATCH 26/38] abbreviate names --- include/openmc/capi.h | 4 +-- include/openmc/cell.h | 9 +++--- include/openmc/material.h | 2 +- include/openmc/settings.h | 8 +++--- openmc/settings.py | 48 +++++++++++++++---------------- src/cell.cpp | 8 +++--- src/initialize.cpp | 3 +- src/material.cpp | 5 ++-- src/settings.cpp | 10 +++---- src/summary.cpp | 12 ++++---- tests/unit_tests/test_settings.py | 4 +-- 11 files changed, 53 insertions(+), 60 deletions(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 61cde08fd81..d6dc7698547 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -318,8 +318,8 @@ int openmc_properties_export(const char* filename); //! Import physical properties for model //! \param[in] filename Filename to read from // \return Error code -int openmc_properties_import(const char* filename, - bool read_temperatures_from_properties, bool read_densities_from_properties); +int openmc_properties_import( + const char* filename, bool read_temperatures, bool read_densities); // Error codes extern int OPENMC_E_UNASSIGNED; diff --git a/include/openmc/cell.h b/include/openmc/cell.h index 02c180bc362..86459182bf7 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -196,11 +196,10 @@ class Cell { //! Import physical properties from HDF5 //! \param[in] group HDF5 group to write to - //! \param[in] read_temperatures_from_properties whether to read temperatures - //! \param[in] read_densities_from_properties whether to read densities - void import_properties_hdf5(hid_t group, - bool read_temperatures_from_properties, - bool read_densities_from_properties); + //! \param[in] read_temperatures whether to read temperatures + //! \param[in] read_densities whether to read densities + void import_properties_hdf5( + hid_t group, bool read_temperatures, bool read_densities); //! Get the BoundingBox for this cell. virtual BoundingBox bounding_box() const = 0; diff --git a/include/openmc/material.h b/include/openmc/material.h index 2eaf6e4bb27..d5622dd83ca 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -76,7 +76,7 @@ class Material { //! Import physical properties from HDF5 //! \param[in] group HDF5 group to read from - void import_properties_hdf5(hid_t group, bool read_densities_from_properties); + void import_properties_hdf5(hid_t group, bool read_densities); //! Add nuclide to the material // diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 6abc978fd9e..1a65ee9d1ab 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -104,10 +104,10 @@ extern bool weight_window_checkpoint_collision; //!< enable weight window check //!< upon collision? extern bool write_all_tracks; //!< write track files for every particle? extern bool write_initial_source; //!< write out initial source file? -extern bool read_temperatures_from_properties; //!< read cell temperatures from - //!< properties file? -extern bool read_densities_from_properties; //!< read cell/material densities - //!< from properties file? +extern bool read_temperatures; //!< read cell temperatures from + //!< properties file? +extern bool read_densities; //!< read cell/material densities + //!< from properties file? // Paths to various files extern std::string path_cross_sections; //!< path to cross_sections.xml diff --git a/openmc/settings.py b/openmc/settings.py index 0a611ddf5e6..23952303f32 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -182,9 +182,9 @@ class Settings: properties_file : Pathlike Location of the properties file to load cell temperatures/densities and materials - read_temperatures_from_properties : bool + read_temperatures : bool Whether to read cell temperatures from the properties file - read_densities_from_properties : bool + read_densities : bool Whether to read densities from the properties file random_ray : dict Options for configuring the random ray solver. Acceptable keys are: @@ -400,8 +400,8 @@ def __init__(self, **kwargs): self._plot_seed = None self._ptables = None self._properties_file = None - self._read_temperatures_from_properties = None - self._read_densities_from_properties = None + self._read_temperatures = None + self._read_densities = None self._uniform_source_sampling = None self._seed = None self._stride = None @@ -1039,28 +1039,28 @@ def properties_file(self, value: PathLike | None): else: cv.check_type('properties file', value, PathLike) self._properties_file = input_path(value) - self.read_temperatures_from_properties = True - self.read_densities_from_properties = True + self.read_temperatures = True + self.read_densities = True @property - def read_temperatures_from_properties(self) -> bool: - return self._read_temperatures_from_properties + def read_temperatures(self) -> bool: + return self._read_temperatures - @read_temperatures_from_properties.setter - def read_temperatures_from_properties(self, read_temperatures_from_properties : bool): + @read_temperatures.setter + def read_temperatures(self, read_temperatures : bool): cv.check_type('Whether read temperatures from properties ', - read_temperatures_from_properties, bool) - self._read_temperatures_from_properties = read_temperatures_from_properties + read_temperatures, bool) + self._read_temperatures = read_temperatures @property - def read_densities_from_properties(self) -> bool: - return self._read_densities_from_properties + def read_densities(self) -> bool: + return self._read_densities - @read_densities_from_properties.setter - def read_densities_from_properties(self, read_densities_from_properties : bool): + @read_densities.setter + def read_densities(self, read_densities : bool): cv.check_type('Whether read temperatures from properties ', - read_densities_from_properties, bool) - self._read_densities_from_properties = read_densities_from_properties + read_densities, bool) + self._read_densities = read_densities @property def trace(self) -> Iterable: @@ -1753,8 +1753,8 @@ def _create_temperature_subelements(self, root): element.text = str(value) def _create_properties_file_element(self, root): - if ((self.read_densities_from_properties or - self.read_temperatures_from_properties) and + if ((self.read_densities or + self.read_temperatures) and self.properties_file is None): # build warning that no properties file is specified msg = (f'Flag to read densities or temperatures was set without providing ' @@ -1766,9 +1766,9 @@ def _create_properties_file_element(self, root): subelement = ET.SubElement(element, "filepath") subelement.text = str(self.properties_file) subelement = ET.SubElement(element, "temperatures") - subelement.text = str(self.read_temperatures_from_properties).lower() + subelement.text = str(self.read_temperatures).lower() subelement = ET.SubElement(element, "densities") - subelement.text = str(self.read_densities_from_properties).lower() + subelement.text = str(self.read_densities).lower() root.append(element) def _create_trace_subelement(self, root): @@ -2273,9 +2273,9 @@ def _properties_file_from_xml_element(self, root): if elem is not None: self.properties_file = get_text(elem, 'filepath') text = get_text(elem, 'temperatures') - self.read_temperatures_from_properties = text in ('true', '1') + self.read_temperatures = text in ('true', '1') text = get_text(elem, 'densities') - self.read_densities_from_properties = text in ('true', '1') + self.read_densities = text in ('true', '1') # def _weight_window_checkpoints_from_xml_element(self, root): # elem = root.find('weight_window_checkpoints') diff --git a/src/cell.cpp b/src/cell.cpp index c2a1f23e2c3..498727f438c 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -234,12 +234,12 @@ void Cell::export_properties_hdf5(hid_t group) const close_group(cell_group); } -void Cell::import_properties_hdf5(hid_t group, - bool read_temperatures_from_properties, bool read_densities_from_properties) +void Cell::import_properties_hdf5( + hid_t group, bool read_temperatures, bool read_densities) { auto cell_group = open_group(group, fmt::format("cell {}", id_)); - if (read_temperatures_from_properties) { + if (read_temperatures) { // Read temperatures from file vector temps; read_dataset(cell_group, "temperature", temps); @@ -260,7 +260,7 @@ void Cell::import_properties_hdf5(hid_t group, } } - if (read_densities_from_properties) { + if (read_densities) { // Read densities if (object_exists(cell_group, "density")) { vector density; diff --git a/src/initialize.cpp b/src/initialize.cpp index 8933d734e49..97381410481 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -505,8 +505,7 @@ void read_separate_xml_files() if (!settings::properties_file.empty()) { openmc_properties_import(settings::properties_file.c_str(), - settings::read_temperatures_from_properties, - settings::read_densities_from_properties); + settings::read_temperatures, settings::read_densities); } } diff --git a/src/material.cpp b/src/material.cpp index 0be2f4d7112..4ff40b9a324 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1138,10 +1138,9 @@ void Material::export_properties_hdf5(hid_t group) const close_group(material_group); } -void Material::import_properties_hdf5( - hid_t group, bool read_densities_from_properties) +void Material::import_properties_hdf5(hid_t group, bool read_densities) { - if (read_densities_from_properties) { + if (read_densities) { hid_t material_group = open_group(group, "material " + std::to_string(id_)); double density; read_attribute(material_group, "atom_density", density); diff --git a/src/settings.cpp b/src/settings.cpp index b4e123694e5..c71a104f2d4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -87,8 +87,8 @@ bool weight_window_checkpoint_surface {false}; bool weight_window_checkpoint_collision {true}; bool write_all_tracks {false}; bool write_initial_source {false}; -bool read_temperatures_from_properties {true}; -bool read_densities_from_properties {true}; +bool read_temperatures {true}; +bool read_densities {true}; std::string path_cross_sections; std::string path_input; @@ -745,10 +745,8 @@ void read_settings_xml(pugi::xml_node root) if (!file_exists(properties_file)) { fatal_error(fmt::format("File '{}' does not exist.", properties_file)); } - read_temperatures_from_properties = - get_node_value_bool(node_props, "temperatures"); - read_densities_from_properties = - get_node_value_bool(node_props, "densities"); + read_temperatures = get_node_value_bool(node_props, "temperatures"); + read_densities = get_node_value_bool(node_props, "densities"); } // Particle trace diff --git a/src/summary.cpp b/src/summary.cpp index e043a5e7c15..56072e56e26 100644 --- a/src/summary.cpp +++ b/src/summary.cpp @@ -190,10 +190,10 @@ extern "C" int openmc_properties_export(const char* filename) return 0; } -extern "C" int openmc_properties_import(const char* filename, - bool read_temperatures_from_properties, bool read_densities_from_properties) +extern "C" int openmc_properties_import( + const char* filename, bool read_temperatures, bool read_densities) { - if (!read_temperatures_from_properties && !read_densities_from_properties) + if (!read_temperatures && !read_densities) return 0; // Display output message @@ -232,8 +232,7 @@ extern "C" int openmc_properties_import(const char* filename, auto cells_group = open_group(geom_group, "cells"); try { for (const auto& c : model::cells) { - c->import_properties_hdf5(cells_group, read_temperatures_from_properties, - read_densities_from_properties); + c->import_properties_hdf5(cells_group, read_temperatures, read_densities); } } catch (const std::exception& e) { set_errmsg(e.what()); @@ -255,8 +254,7 @@ extern "C" int openmc_properties_import(const char* filename, // Read material properties for (const auto& mat : model::materials) { - mat->import_properties_hdf5( - materials_group, read_densities_from_properties); + mat->import_properties_hdf5(materials_group, read_densities); } close_group(materials_group); diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 0f0f8f08d90..8fb5ac751d9 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -137,8 +137,8 @@ def test_export_to_xml(run_in_tmpdir): assert s.temperature == {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': [200., 1000.]} assert s.properties_file == Path(tmp_properties_file) - assert s.read_temperatures_from_properties - assert s.read_densities_from_properties + assert s.read_temperatures + assert s.read_densities assert s.trace == [10, 1, 20] assert s.track == [(1, 1, 1), (2, 1, 1)] assert isinstance(s.ufs_mesh, openmc.RegularMesh) From 6355305f3a936a81015d12850a23ce42fe8f18bd Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Mon, 23 Feb 2026 13:34:02 -0600 Subject: [PATCH 27/38] update documentation --- docs/source/io_formats/settings.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index c17c9f5c0b1..6529d1a3fd7 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -550,11 +550,11 @@ generator during generation of colors in plots. -------------------------------------- The ``properties_file`` element indicates whether temperatures and densities - should be read from the properties file instead of from the XML file. It contains + from a properties file should override the properties present in the XML file. It contains the following parameters: :filepath: - The path to the properties file to be used to read temperatures and/or densities + The path to the properties file :temperatures: Boolean to indicate whether or not to read cell temperatures from the properties file. @@ -562,7 +562,7 @@ generator during generation of colors in plots. *Default*: True :densities: - Boolean to indicate whether or not to read cell or material densities from the properties file. + Boolean to indicate whether or not to read cell and material densities from the properties file. *Default*: True From 19abae7b06f72af39ec8e50045f3eeb9b273a8e7 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Mon, 23 Feb 2026 13:34:37 -0600 Subject: [PATCH 28/38] cleanup comments and error messages --- openmc/settings.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 23952303f32..87d1e1dcd50 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1048,7 +1048,7 @@ def read_temperatures(self) -> bool: @read_temperatures.setter def read_temperatures(self, read_temperatures : bool): - cv.check_type('Whether read temperatures from properties ', + cv.check_type('read temperatures from properties ', read_temperatures, bool) self._read_temperatures = read_temperatures @@ -1058,7 +1058,7 @@ def read_densities(self) -> bool: @read_densities.setter def read_densities(self, read_densities : bool): - cv.check_type('Whether read temperatures from properties ', + cv.check_type('read temperatures from properties ', read_densities, bool) self._read_densities = read_densities @@ -1757,7 +1757,7 @@ def _create_properties_file_element(self, root): self.read_temperatures) and self.properties_file is None): # build warning that no properties file is specified - msg = (f'Flag to read densities or temperatures was set without providing ' + msg = ('Flag to read densities or temperatures was set without providing ' 'a properties file.') warnings.warn(msg) @@ -2277,18 +2277,6 @@ def _properties_file_from_xml_element(self, root): text = get_text(elem, 'densities') self.read_densities = text in ('true', '1') - # def _weight_window_checkpoints_from_xml_element(self, root): - # elem = root.find('weight_window_checkpoints') - # if elem is None: - # return - # for key in ('collision', 'surface'): - # value = get_text(elem, key) - # if value is not None: - # value = value in ('true', '1') - # self.weight_window_checkpoints[key] = value - - - def _trace_from_xml_element(self, root): text = get_elem_list(root, "trace", int) if text is not None: From 172660243fcd4eea36ae8241a25881d809c02655 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Mon, 23 Feb 2026 13:34:58 -0600 Subject: [PATCH 29/38] simplify early exit logic --- src/material.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/material.cpp b/src/material.cpp index 4ff40b9a324..a13fdd458b4 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1140,13 +1140,13 @@ void Material::export_properties_hdf5(hid_t group) const void Material::import_properties_hdf5(hid_t group, bool read_densities) { - if (read_densities) { - hid_t material_group = open_group(group, "material " + std::to_string(id_)); - double density; - read_attribute(material_group, "atom_density", density); - this->set_density(density, "atom/b-cm"); - close_group(material_group); - } + if (!read_densities) return; + + hid_t material_group = open_group(group, "material " + std::to_string(id_)); + double density; + read_attribute(material_group, "atom_density", density); + this->set_density(density, "atom/b-cm"); + close_group(material_group); } void Material::add_nuclide(const std::string& name, double density) From 0e9eea39a8243b2223bb345d01fd091906a629f8 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Mon, 23 Feb 2026 13:37:32 -0600 Subject: [PATCH 30/38] style update --- src/material.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/material.cpp b/src/material.cpp index a13fdd458b4..49d7c7ca8a7 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1140,7 +1140,8 @@ void Material::export_properties_hdf5(hid_t group) const void Material::import_properties_hdf5(hid_t group, bool read_densities) { - if (!read_densities) return; + if (!read_densities) + return; hid_t material_group = open_group(group, "material " + std::to_string(id_)); double density; From 41317cfa2d4958dccbff6fef63577e8b15ea8abe Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Wed, 4 Mar 2026 17:39:46 -0600 Subject: [PATCH 31/38] read the properties in the right place --- src/initialize.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/initialize.cpp b/src/initialize.cpp index 97381410481..eccdb8081b9 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -118,6 +118,11 @@ int openmc_init(int argc, char* argv[], const void* intracomm) if (!read_model_xml()) read_separate_xml_files(); + if (!settings::properties_file.empty()) { + openmc_properties_import(settings::properties_file.c_str(), + settings::read_temperatures, settings::read_densities); + } + // Reset locale to previous state if (std::setlocale(LC_ALL, prev_locale.c_str()) == NULL) { fatal_error("Cannot reset locale."); @@ -502,11 +507,6 @@ void read_separate_xml_files() read_plots_xml(); finalize_variance_reduction(); - - if (!settings::properties_file.empty()) { - openmc_properties_import(settings::properties_file.c_str(), - settings::read_temperatures, settings::read_densities); - } } void initial_output() From cde42e1c4c1d4343807e644a2043de347abbe7d3 Mon Sep 17 00:00:00 2001 From: "Paul P.H. Wilson" Date: Thu, 5 Mar 2026 06:03:27 -0600 Subject: [PATCH 32/38] unwind fine-grained import booleans --- docs/source/io_formats/settings.rst | 18 +------- include/openmc/capi.h | 3 +- include/openmc/cell.h | 5 +-- include/openmc/material.h | 2 +- include/openmc/settings.h | 4 -- openmc/lib/core.py | 4 +- openmc/settings.py | 53 ++--------------------- src/cell.cpp | 65 +++++++++++++---------------- src/initialize.cpp | 3 +- src/material.cpp | 5 +-- src/settings.cpp | 9 +--- src/summary.cpp | 10 ++--- tests/unit_tests/test_settings.py | 2 - 13 files changed, 47 insertions(+), 136 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 6529d1a3fd7..fa813928bc1 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -549,22 +549,8 @@ generator during generation of colors in plots. ```` Element -------------------------------------- - The ``properties_file`` element indicates whether temperatures and densities - from a properties file should override the properties present in the XML file. It contains - the following parameters: - - :filepath: - The path to the properties file - - :temperatures: - Boolean to indicate whether or not to read cell temperatures from the properties file. - - *Default*: True - - :densities: - Boolean to indicate whether or not to read cell and material densities from the properties file. - - *Default*: True + The ``properties_file`` element has no attributes and contains the path to + a properties HDF5 file to load cell temperatures/densities and material densities. --------------------- diff --git a/include/openmc/capi.h b/include/openmc/capi.h index d6dc7698547..911654d318f 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -318,8 +318,7 @@ int openmc_properties_export(const char* filename); //! Import physical properties for model //! \param[in] filename Filename to read from // \return Error code -int openmc_properties_import( - const char* filename, bool read_temperatures, bool read_densities); +int openmc_properties_import(const char* filename); // Error codes extern int OPENMC_E_UNASSIGNED; diff --git a/include/openmc/cell.h b/include/openmc/cell.h index 86459182bf7..26c34ebd41d 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -196,10 +196,7 @@ class Cell { //! Import physical properties from HDF5 //! \param[in] group HDF5 group to write to - //! \param[in] read_temperatures whether to read temperatures - //! \param[in] read_densities whether to read densities - void import_properties_hdf5( - hid_t group, bool read_temperatures, bool read_densities); + void import_properties_hdf5(hid_t group); //! Get the BoundingBox for this cell. virtual BoundingBox bounding_box() const = 0; diff --git a/include/openmc/material.h b/include/openmc/material.h index d5622dd83ca..3967c36878f 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -76,7 +76,7 @@ class Material { //! Import physical properties from HDF5 //! \param[in] group HDF5 group to read from - void import_properties_hdf5(hid_t group, bool read_densities); + void import_properties_hdf5(hid_t group); //! Add nuclide to the material // diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 1a65ee9d1ab..d0b681ad302 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -104,10 +104,6 @@ extern bool weight_window_checkpoint_collision; //!< enable weight window check //!< upon collision? extern bool write_all_tracks; //!< write track files for every particle? extern bool write_initial_source; //!< write out initial source file? -extern bool read_temperatures; //!< read cell temperatures from - //!< properties file? -extern bool read_densities; //!< read cell/material densities - //!< from properties file? // Paths to various files extern std::string path_cross_sections; //!< path to cross_sections.xml diff --git a/openmc/lib/core.py b/openmc/lib/core.py index 4013e37341a..2764ddff1a6 100644 --- a/openmc/lib/core.py +++ b/openmc/lib/core.py @@ -77,7 +77,7 @@ class _SourceSite(Structure): _dll.openmc_properties_export.argtypes = [c_char_p] _dll.openmc_properties_export.restype = c_int _dll.openmc_properties_export.errcheck = _error_handler -_dll.openmc_properties_import.argtypes = [c_char_p, c_bool, c_bool] +_dll.openmc_properties_import.argtypes = [c_char_p] _dll.openmc_properties_import.restype = c_int _dll.openmc_properties_import.errcheck = _error_handler _dll.openmc_run.restype = c_int @@ -304,7 +304,7 @@ def import_properties(filename): openmc.lib.export_properties """ - _dll.openmc_properties_import(filename.encode(), True, True) + _dll.openmc_properties_import(filename.encode()) def init(args=None, intracomm=None, output=True): diff --git a/openmc/settings.py b/openmc/settings.py index 87d1e1dcd50..9798c40a1e1 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -182,10 +182,6 @@ class Settings: properties_file : Pathlike Location of the properties file to load cell temperatures/densities and materials - read_temperatures : bool - Whether to read cell temperatures from the properties file - read_densities : bool - Whether to read densities from the properties file random_ray : dict Options for configuring the random ray solver. Acceptable keys are: @@ -400,8 +396,6 @@ def __init__(self, **kwargs): self._plot_seed = None self._ptables = None self._properties_file = None - self._read_temperatures = None - self._read_densities = None self._uniform_source_sampling = None self._seed = None self._stride = None @@ -1039,28 +1033,6 @@ def properties_file(self, value: PathLike | None): else: cv.check_type('properties file', value, PathLike) self._properties_file = input_path(value) - self.read_temperatures = True - self.read_densities = True - - @property - def read_temperatures(self) -> bool: - return self._read_temperatures - - @read_temperatures.setter - def read_temperatures(self, read_temperatures : bool): - cv.check_type('read temperatures from properties ', - read_temperatures, bool) - self._read_temperatures = read_temperatures - - @property - def read_densities(self) -> bool: - return self._read_densities - - @read_densities.setter - def read_densities(self, read_densities : bool): - cv.check_type('read temperatures from properties ', - read_densities, bool) - self._read_densities = read_densities @property def trace(self) -> Iterable: @@ -1753,22 +1725,9 @@ def _create_temperature_subelements(self, root): element.text = str(value) def _create_properties_file_element(self, root): - if ((self.read_densities or - self.read_temperatures) and - self.properties_file is None): - # build warning that no properties file is specified - msg = ('Flag to read densities or temperatures was set without providing ' - 'a properties file.') - warnings.warn(msg) - if self.properties_file is not None: element = ET.Element("properties") - subelement = ET.SubElement(element, "filepath") - subelement.text = str(self.properties_file) - subelement = ET.SubElement(element, "temperatures") - subelement.text = str(self.read_temperatures).lower() - subelement = ET.SubElement(element, "densities") - subelement.text = str(self.read_densities).lower() + element.text = str(self.properties_file) root.append(element) def _create_trace_subelement(self, root): @@ -2269,13 +2228,9 @@ def _temperature_from_xml_element(self, root): self.temperature['multipole'] = text in ('true', '1') def _properties_file_from_xml_element(self, root): - elem = root.find('properties') - if elem is not None: - self.properties_file = get_text(elem, 'filepath') - text = get_text(elem, 'temperatures') - self.read_temperatures = text in ('true', '1') - text = get_text(elem, 'densities') - self.read_densities = text in ('true', '1') + text = get_text(root, 'properties') + if text is not None: + self.properties_file = text def _trace_from_xml_element(self, root): text = get_elem_list(root, "trace", int) diff --git a/src/cell.cpp b/src/cell.cpp index 498727f438c..ebe28c3d2ce 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -234,50 +234,45 @@ void Cell::export_properties_hdf5(hid_t group) const close_group(cell_group); } -void Cell::import_properties_hdf5( - hid_t group, bool read_temperatures, bool read_densities) +void Cell::import_properties_hdf5(hid_t group) { auto cell_group = open_group(group, fmt::format("cell {}", id_)); - if (read_temperatures) { - // Read temperatures from file - vector temps; - read_dataset(cell_group, "temperature", temps); + // Read temperatures from file + vector temps; + read_dataset(cell_group, "temperature", temps); - // Ensure number of temperatures makes sense - auto n_temps = temps.size(); - if (n_temps > 1 && n_temps != n_instances()) { - fatal_error(fmt::format( - "Number of temperatures for cell {} doesn't match number of instances", - id_)); - } + // Ensure number of temperatures makes sense + auto n_temps = temps.size(); + if (n_temps > 1 && n_temps != n_instances()) { + fatal_error(fmt::format( + "Number of temperatures for cell {} doesn't match number of instances", + id_)); + } - // Modify temperatures for the cell - sqrtkT_.clear(); - sqrtkT_.resize(temps.size()); - for (int64_t i = 0; i < temps.size(); ++i) { - this->set_temperature(temps[i], i); - } + // Modify temperatures for the cell + sqrtkT_.clear(); + sqrtkT_.resize(temps.size()); + for (int64_t i = 0; i < temps.size(); ++i) { + this->set_temperature(temps[i], i); } - if (read_densities) { - // Read densities - if (object_exists(cell_group, "density")) { - vector density; - read_dataset(cell_group, "density", density); + // Read densities + if (object_exists(cell_group, "density")) { + vector density; + read_dataset(cell_group, "density", density); - // Ensure number of densities makes sense - auto n_density = density.size(); - if (n_density > 1 && n_density != n_instances()) { - fatal_error(fmt::format("Number of densities for cell {} " - "doesn't match number of instances", - id_)); - } + // Ensure number of densities makes sense + auto n_density = density.size(); + if (n_density > 1 && n_density != n_instances()) { + fatal_error(fmt::format("Number of densities for cell {} " + "doesn't match number of instances", + id_)); + } - // Set densities. - for (int32_t i = 0; i < n_density; ++i) { - this->set_density(density[i], i); - } + // Set densities. + for (int32_t i = 0; i < n_density; ++i) { + this->set_density(density[i], i); } } diff --git a/src/initialize.cpp b/src/initialize.cpp index eccdb8081b9..efb462f5c01 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -119,8 +119,7 @@ int openmc_init(int argc, char* argv[], const void* intracomm) read_separate_xml_files(); if (!settings::properties_file.empty()) { - openmc_properties_import(settings::properties_file.c_str(), - settings::read_temperatures, settings::read_densities); + openmc_properties_import(settings::properties_file.c_str()); } // Reset locale to previous state diff --git a/src/material.cpp b/src/material.cpp index 49d7c7ca8a7..21b11b8b9ce 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1138,11 +1138,8 @@ void Material::export_properties_hdf5(hid_t group) const close_group(material_group); } -void Material::import_properties_hdf5(hid_t group, bool read_densities) +void Material::import_properties_hdf5(hid_t group) { - if (!read_densities) - return; - hid_t material_group = open_group(group, "material " + std::to_string(id_)); double density; read_attribute(material_group, "atom_density", density); diff --git a/src/settings.cpp b/src/settings.cpp index c71a104f2d4..5dd8a0ac9a4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -87,8 +87,6 @@ bool weight_window_checkpoint_surface {false}; bool weight_window_checkpoint_collision {true}; bool write_all_tracks {false}; bool write_initial_source {false}; -bool read_temperatures {true}; -bool read_densities {true}; std::string path_cross_sections; std::string path_input; @@ -738,15 +736,10 @@ void read_settings_xml(pugi::xml_node root) // read properties from file if (check_for_node(root, "properties")) { - // Get pointer to properties node - xml_node node_props = root.child("properties"); - - properties_file = get_node_value(node_props, "filepath"); + properties_file = get_node_value(root, "properties"); if (!file_exists(properties_file)) { fatal_error(fmt::format("File '{}' does not exist.", properties_file)); } - read_temperatures = get_node_value_bool(node_props, "temperatures"); - read_densities = get_node_value_bool(node_props, "densities"); } // Particle trace diff --git a/src/summary.cpp b/src/summary.cpp index 56072e56e26..b3ea0254e6e 100644 --- a/src/summary.cpp +++ b/src/summary.cpp @@ -190,12 +190,8 @@ extern "C" int openmc_properties_export(const char* filename) return 0; } -extern "C" int openmc_properties_import( - const char* filename, bool read_temperatures, bool read_densities) +extern "C" int openmc_properties_import(const char* filename) { - if (!read_temperatures && !read_densities) - return 0; - // Display output message auto msg = fmt::format("Importing properties from {}...", filename); write_message(msg, 5); @@ -232,7 +228,7 @@ extern "C" int openmc_properties_import( auto cells_group = open_group(geom_group, "cells"); try { for (const auto& c : model::cells) { - c->import_properties_hdf5(cells_group, read_temperatures, read_densities); + c->import_properties_hdf5(cells_group); } } catch (const std::exception& e) { set_errmsg(e.what()); @@ -254,7 +250,7 @@ extern "C" int openmc_properties_import( // Read material properties for (const auto& mat : model::materials) { - mat->import_properties_hdf5(materials_group, read_densities); + mat->import_properties_hdf5(materials_group); } close_group(materials_group); diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 8fb5ac751d9..690aaec2bbc 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -137,8 +137,6 @@ def test_export_to_xml(run_in_tmpdir): assert s.temperature == {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': [200., 1000.]} assert s.properties_file == Path(tmp_properties_file) - assert s.read_temperatures - assert s.read_densities assert s.trace == [10, 1, 20] assert s.track == [(1, 1, 1), (2, 1, 1)] assert isinstance(s.ufs_mesh, openmc.RegularMesh) From 17aa7ef805dc562a17774f837f7b852856afdb4b Mon Sep 17 00:00:00 2001 From: Patrick C Shriwise Date: Sun, 8 Mar 2026 05:04:37 +0000 Subject: [PATCH 33/38] Adding manual test for properties load via settings --- tests/unit_tests/test_settings.py | 73 +++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 690aaec2bbc..6fd433cad8b 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -1,6 +1,10 @@ from pathlib import Path +import h5py +import pytest + import openmc +import openmc.lib import openmc.stats @@ -180,3 +184,72 @@ def test_export_to_xml(run_in_tmpdir): assert s.max_secondaries == 1_000_000 assert s.source_rejection_fraction == 0.01 assert s.free_gas_threshold == 800.0 + + +@pytest.fixture +def pwr_assembly_properties(tmp_path, mpi_intracomm): + """Build a pwr_assembly properties.h5 file and return associated data. + + Returns (model, props_path, cell_instances, mat_densities, density_factor). + """ + model = openmc.examples.pwr_assembly() + density_factor = 0.75 + + # Session 1: export a structurally valid properties file via the C++ API, + # then collect the cell/material structure so we can patch it with h5py. + # Using export_properties avoids HDF5 dtype mismatches that arise when + # writing attributes by hand (e.g. uint64 n_cells, fixed-length filetype). + cell_instances = {} # {cell_id: n_instances} — material cells only + mat_densities = {} # {mat_id: original atom/b-cm density} + + props_path = tmp_path / 'properties.h5' + comm_kwargs = {'intracomm': mpi_intracomm} if mpi_intracomm is not None else {} + with openmc.lib.TemporarySession(model, **comm_kwargs): + openmc.lib.export_properties(str(props_path)) + for cell_id, cell in openmc.lib.cells.items(): + try: + cell.fill # raises NotImplementedError for non-material cells + cell_instances[cell_id] = cell.num_instances + except NotImplementedError: + pass + for mat_id, mat in openmc.lib.materials.items(): + mat_densities[mat_id] = mat.get_density('atom/b-cm') + + # Patch the exported file: overwrite temperatures with per-instance values + # and scale material atom densities. + with h5py.File(props_path, 'r+') as f: + cells_grp = f['geometry/cells'] + for cell_id, n in cell_instances.items(): + cell_grp = cells_grp[f'cell {cell_id}'] + del cell_grp['temperature'] + cell_grp.create_dataset( + 'temperature', data=[500.0 + 5.0 * i for i in range(n)] + ) + + for mat_id, orig_density in mat_densities.items(): + f['materials'][f'material {mat_id}'].attrs['atom_density'] = \ + orig_density * density_factor + + return model, props_path, cell_instances, mat_densities, density_factor + + +def test_properties_file_load(pwr_assembly_properties, mpi_intracomm): + model, props_path, cell_instances, mat_densities, density_factor = \ + pwr_assembly_properties + + assert any(n > 1 for n in cell_instances.values()) + + model.settings.properties_file = props_path + + comm_kwargs = {'intracomm': mpi_intracomm} if mpi_intracomm is not None else {} + with openmc.lib.TemporarySession(model, **comm_kwargs): + for cell_id, n in cell_instances.items(): + cell = openmc.lib.cells[cell_id] + for i in range(n): + assert cell.get_temperature(i) == pytest.approx(500.0 + 5.0 * i) + + for mat_id, orig_density in mat_densities.items(): + mat = openmc.lib.materials[mat_id] + assert mat.get_density('atom/b-cm') == pytest.approx( + orig_density * density_factor, rel=1e-5 + ) From 86ec40c66e3462417f248974a6fbc15931522930 Mon Sep 17 00:00:00 2001 From: Patrick C Shriwise Date: Sun, 8 Mar 2026 13:58:16 +0000 Subject: [PATCH 34/38] Abandoning the test fixture as it adds complexity --- tests/unit_tests/test_settings.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 6fd433cad8b..2329e94bb32 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -186,12 +186,7 @@ def test_export_to_xml(run_in_tmpdir): assert s.free_gas_threshold == 800.0 -@pytest.fixture -def pwr_assembly_properties(tmp_path, mpi_intracomm): - """Build a pwr_assembly properties.h5 file and return associated data. - - Returns (model, props_path, cell_instances, mat_densities, density_factor). - """ +def test_properties_file_load(tmp_path, mpi_intracomm): model = openmc.examples.pwr_assembly() density_factor = 0.75 @@ -215,6 +210,8 @@ def pwr_assembly_properties(tmp_path, mpi_intracomm): for mat_id, mat in openmc.lib.materials.items(): mat_densities[mat_id] = mat.get_density('atom/b-cm') + assert any(n > 1 for n in cell_instances.values()) + # Patch the exported file: overwrite temperatures with per-instance values # and scale material atom densities. with h5py.File(props_path, 'r+') as f: @@ -230,18 +227,8 @@ def pwr_assembly_properties(tmp_path, mpi_intracomm): f['materials'][f'material {mat_id}'].attrs['atom_density'] = \ orig_density * density_factor - return model, props_path, cell_instances, mat_densities, density_factor - - -def test_properties_file_load(pwr_assembly_properties, mpi_intracomm): - model, props_path, cell_instances, mat_densities, density_factor = \ - pwr_assembly_properties - - assert any(n > 1 for n in cell_instances.values()) - model.settings.properties_file = props_path - comm_kwargs = {'intracomm': mpi_intracomm} if mpi_intracomm is not None else {} with openmc.lib.TemporarySession(model, **comm_kwargs): for cell_id, n in cell_instances.items(): cell = openmc.lib.cells[cell_id] From 14df9796b10543f3b11f72dfc9b119583df157c3 Mon Sep 17 00:00:00 2001 From: Patrick C Shriwise Date: Sun, 8 Mar 2026 15:22:13 +0000 Subject: [PATCH 35/38] Altering TemporarySession intracomm handling for cleaner testing --- openmc/lib/core.py | 4 ++-- tests/unit_tests/test_settings.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openmc/lib/core.py b/openmc/lib/core.py index 2764ddff1a6..f95df18ded8 100644 --- a/openmc/lib/core.py +++ b/openmc/lib/core.py @@ -674,8 +674,8 @@ def __init__(self, model=None, cwd=None, **init_kwargs): self.model = model # Determine MPI intercommunicator - self.init_kwargs.setdefault('intracomm', comm) - self.comm = self.init_kwargs['intracomm'] + self.comm = self.init_kwargs.get('intracomm') or comm + self.init_kwargs['intracomm'] = self.comm def __enter__(self): """Initialize the OpenMC library in a temporary directory.""" diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 2329e94bb32..4a5d24071c4 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -198,8 +198,7 @@ def test_properties_file_load(tmp_path, mpi_intracomm): mat_densities = {} # {mat_id: original atom/b-cm density} props_path = tmp_path / 'properties.h5' - comm_kwargs = {'intracomm': mpi_intracomm} if mpi_intracomm is not None else {} - with openmc.lib.TemporarySession(model, **comm_kwargs): + with openmc.lib.TemporarySession(model, intracomm=mpi_intracomm): openmc.lib.export_properties(str(props_path)) for cell_id, cell in openmc.lib.cells.items(): try: @@ -229,7 +228,7 @@ def test_properties_file_load(tmp_path, mpi_intracomm): model.settings.properties_file = props_path - with openmc.lib.TemporarySession(model, **comm_kwargs): + with openmc.lib.TemporarySession(model, intracomm=mpi_intracomm): for cell_id, n in cell_instances.items(): cell = openmc.lib.cells[cell_id] for i in range(n): From bcea94f230305933dad5ca019ac1fc1f70effef9 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Sun, 8 Mar 2026 11:17:25 -0500 Subject: [PATCH 36/38] Test code cleanup --- tests/unit_tests/test_settings.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 4a5d24071c4..e34baa320ea 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -188,12 +188,9 @@ def test_export_to_xml(run_in_tmpdir): def test_properties_file_load(tmp_path, mpi_intracomm): model = openmc.examples.pwr_assembly() - density_factor = 0.75 # Session 1: export a structurally valid properties file via the C++ API, # then collect the cell/material structure so we can patch it with h5py. - # Using export_properties avoids HDF5 dtype mismatches that arise when - # writing attributes by hand (e.g. uint64 n_cells, fixed-length filetype). cell_instances = {} # {cell_id: n_instances} — material cells only mat_densities = {} # {mat_id: original atom/b-cm density} @@ -211,8 +208,9 @@ def test_properties_file_load(tmp_path, mpi_intracomm): assert any(n > 1 for n in cell_instances.values()) - # Patch the exported file: overwrite temperatures with per-instance values - # and scale material atom densities. + # Patch the exported properties file overwriting temperatures + # with per-instance values and scale material atom densities. + density_factor = 0.75 with h5py.File(props_path, 'r+') as f: cells_grp = f['geometry/cells'] for cell_id, n in cell_instances.items(): @@ -226,6 +224,9 @@ def test_properties_file_load(tmp_path, mpi_intracomm): f['materials'][f'material {mat_id}'].attrs['atom_density'] = \ orig_density * density_factor + # now apply the newly patched properties file using the settings + # and load the model again, checking that the new temperature and + # density values match those in the new file model.settings.properties_file = props_path with openmc.lib.TemporarySession(model, intracomm=mpi_intracomm): @@ -238,4 +239,4 @@ def test_properties_file_load(tmp_path, mpi_intracomm): mat = openmc.lib.materials[mat_id] assert mat.get_density('atom/b-cm') == pytest.approx( orig_density * density_factor, rel=1e-5 - ) + ) \ No newline at end of file From f74b535c5a6af708495e031ecc2a293e4023217b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 13 Mar 2026 14:36:43 -0500 Subject: [PATCH 37/38] Doc fix --- docs/source/io_formats/settings.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index fa813928bc1..1774d1cd9e3 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -542,16 +542,15 @@ generator during generation of colors in plots. *Default*: 1 - .. _properties_file: --------------------------------------- +----------------------------- ```` Element --------------------------------------- - - The ``properties_file`` element has no attributes and contains the path to - a properties HDF5 file to load cell temperatures/densities and material densities. +----------------------------- + The ``properties_file`` element has no attributes and contains the path to a + properties HDF5 file to load cell temperatures/densities and material + densities. --------------------- ```` Element From ab73212e545cf0b018d7c528125882c1569a93ee Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 13 Mar 2026 15:56:19 -0500 Subject: [PATCH 38/38] Use properties_file consistently, small fixes --- docs/source/io_formats/settings.rst | 2 ++ openmc/settings.py | 10 +++++----- src/settings.cpp | 4 ++-- tests/unit_tests/test_settings.py | 6 +++--- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 8633304938a..a50922b041e 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -565,6 +565,8 @@ generator during generation of colors in plots. properties HDF5 file to load cell temperatures/densities and material densities. + *Default*: None + --------------------- ```` Element --------------------- diff --git a/openmc/settings.py b/openmc/settings.py index 5b96b5dff47..6919afca4cc 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -183,9 +183,9 @@ class Settings: Initial seed for randomly generated plot colors. ptables : bool Determine whether probability tables are used. - properties_file : Pathlike + properties_file : PathLike Location of the properties file to load cell temperatures/densities - and materials + and material densities random_ray : dict Options for configuring the random ray solver. Acceptable keys are: @@ -1790,10 +1790,10 @@ def _create_temperature_subelements(self, root): def _create_properties_file_element(self, root): if self.properties_file is not None: - element = ET.Element("properties") + element = ET.Element("properties_file") element.text = str(self.properties_file) root.append(element) - + def _create_trace_subelement(self, root): if self._trace is not None: element = ET.SubElement(root, "trace") @@ -2307,7 +2307,7 @@ def _temperature_from_xml_element(self, root): self.temperature['multipole'] = text in ('true', '1') def _properties_file_from_xml_element(self, root): - text = get_text(root, 'properties') + text = get_text(root, 'properties_file') if text is not None: self.properties_file = text diff --git a/src/settings.cpp b/src/settings.cpp index ca59707e0a9..ab9f9a5aafa 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -753,8 +753,8 @@ void read_settings_xml(pugi::xml_node root) } // read properties from file - if (check_for_node(root, "properties")) { - properties_file = get_node_value(root, "properties"); + if (check_for_node(root, "properties_file")) { + properties_file = get_node_value(root, "properties_file"); if (!file_exists(properties_file)) { fatal_error(fmt::format("File '{}' does not exist.", properties_file)); } diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 75e9b6a3769..bdb3ea8fe9f 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -31,7 +31,7 @@ def test_export_to_xml(run_in_tmpdir): s.surf_source_read = {'path': 'surface_source_1.h5'} s.surf_source_write = {'surface_ids': [2], 'max_particles': 200} s.surface_grazing_ratio = 0.7 - s.surface_grazing_cutoff = 0.1 + s.surface_grazing_cutoff = 0.1 s.confidence_intervals = True s.ptables = True s.plot_seed = 100 @@ -122,7 +122,7 @@ def test_export_to_xml(run_in_tmpdir): assert s.surf_source_read['path'].name == 'surface_source_1.h5' assert s.surf_source_write == {'surface_ids': [2], 'max_particles': 200} assert s.surface_grazing_ratio == 0.7 - assert s.surface_grazing_cutoff == 0.1 + assert s.surface_grazing_cutoff == 0.1 assert s.confidence_intervals assert s.ptables assert s.plot_seed == 100 @@ -245,4 +245,4 @@ def test_properties_file_load(tmp_path, mpi_intracomm): mat = openmc.lib.materials[mat_id] assert mat.get_density('atom/b-cm') == pytest.approx( orig_density * density_factor, rel=1e-5 - ) \ No newline at end of file + )