From 94be20382267b9b279a0225e832b533f18e6b416 Mon Sep 17 00:00:00 2001 From: pranav Date: Mon, 24 Nov 2025 08:45:37 +0000 Subject: [PATCH 1/7] statepoint dump feature --- .../io_formats/settings_statepoint_latest.rst | 44 ++++++++++++++ include/openmc/settings.h | 1 + openmc/settings.py | 57 +++++++++++++++++++ src/settings.cpp | 24 ++++++++ src/simulation.cpp | 18 ++++++ .../statepoint_latest/__init__.py | 1 + .../statepoint_latest/geometry.xml | 14 +++++ .../statepoint_latest/materials.xml | 6 ++ .../statepoint_latest/settings.xml | 17 ++++++ .../statepoint_latest/test.py | 18 ++++++ .../statepoint_latest/test_pyapi.py | 42 ++++++++++++++ 11 files changed, 242 insertions(+) create mode 100644 docs/source/io_formats/settings_statepoint_latest.rst create mode 100644 tests/regression_tests/statepoint_latest/__init__.py create mode 100644 tests/regression_tests/statepoint_latest/geometry.xml create mode 100644 tests/regression_tests/statepoint_latest/materials.xml create mode 100644 tests/regression_tests/statepoint_latest/settings.xml create mode 100644 tests/regression_tests/statepoint_latest/test.py create mode 100644 tests/regression_tests/statepoint_latest/test_pyapi.py diff --git a/docs/source/io_formats/settings_statepoint_latest.rst b/docs/source/io_formats/settings_statepoint_latest.rst new file mode 100644 index 00000000000..7d4bd73240c --- /dev/null +++ b/docs/source/io_formats/settings_statepoint_latest.rst @@ -0,0 +1,44 @@ +.. _usersguide_statepoint_latest: + +Statepoint Overwrite +==================== + +OpenMC can be configured to write a continuously-overwritten "running" statepoint +file after every finished batch. This is useful when you want the most recent +results available even if the job is interrupted unexpectedly. + +To enable this behavior, set the ``overwrite_latest`` option inside the +```` element in the settings XML. The value may be either a +boolean or an integer: + +- ``true``: keep a single file named ``statepoint.running.h5`` that is + overwritten after every batch. +- ``false``: (default) do not write running statepoint files. +- ``N`` (integer > 1): keep ``N`` rotating running statepoint files named + ``statepoint.running.1.h5``, ``statepoint.running.2.h5``, ..., ``statepoint.running.N.h5``. + +Examples + +Keep a single running statepoint file: + +.. code-block:: xml + + + true + + +Keep the last two completed batches in rotating files: + +.. code-block:: xml + + + 2 + + +Remarks +------- + +The running statepoints are written in addition to any permanent statepoint +files you requested via the ``...`` +element. The rotating files form a circular buffer of the most recent +completed batches and are overwritten in batch order. diff --git a/include/openmc/settings.h b/include/openmc/settings.h index b369c99fef8..08ee8ea3a3e 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -86,6 +86,7 @@ extern bool source_separate; //!< write source to separate file? extern bool source_write; //!< write source in HDF5 files? extern bool source_mcpl_write; //!< write source in mcpl files? extern bool surf_source_write; //!< write surface source file? +extern int state_latest; //!< number of running statepoints to keep (0 = disabled) extern bool surf_mcpl_write; //!< write surface mcpl file? extern bool surf_source_read; //!< read surface source file? extern bool survival_biasing; //!< use survival biasing? diff --git a/openmc/settings.py b/openmc/settings.py index 289fb35b731..61d7b78cd80 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -262,6 +262,11 @@ class Settings: Options for writing state points. Acceptable keys are: :batches: list of batches at which to write statepoint files + :overwrite_latest: bool or int + If present and true, write a continuously-overwritten running + statepoint file after every batch. If an integer N > 1 is + provided, keep N rotating running statepoint files named + ``statepoint.running.1.h5`` ... ``statepoint.running.N.h5``. surf_source_read : dict Options for reading surface source points. Acceptable keys are: @@ -760,6 +765,35 @@ def output(self, output: dict): cv.check_type("output['path']", value, str) self._output = output + @property + def statepoint(self) -> dict: + """Dictionary of statepoint options. + + Acceptable keys: + - 'batches': list of batch integers + - 'overwrite_latest': bool or int (see docs) + """ + return self._statepoint + + @statepoint.setter + def statepoint(self, statepoint: dict): + cv.check_type('statepoint', statepoint, Mapping) + sp = {} + if 'batches' in statepoint: + batches = statepoint['batches'] + if not isinstance(batches, Sequence): + raise ValueError("statepoint['batches'] must be a sequence of ints") + sp['batches'] = [int(x) for x in batches] + if 'overwrite_latest' in statepoint: + val = statepoint['overwrite_latest'] + if isinstance(val, bool): + sp['overwrite_latest'] = 1 if val else 0 + elif isinstance(val, Integral): + sp['overwrite_latest'] = int(val) + else: + raise ValueError("statepoint['overwrite_latest'] must be bool or int") + self._statepoint = sp + @property def sourcepoint(self) -> dict: return self._sourcepoint @@ -1481,6 +1515,15 @@ def _create_statepoint_subelement(self, root): subelement = ET.SubElement(element, "batches") subelement.text = ' '.join( str(x) for x in self._statepoint['batches']) + # Overwrite latest / running statepoints + if 'overwrite_latest' in self._statepoint: + val = self._statepoint['overwrite_latest'] + subelement = ET.SubElement(element, "overwrite_latest") + # If boolean, write lowercase true/false, else write integer + if isinstance(val, bool): + subelement.text = str(val).lower() + else: + subelement.text = str(val) def _create_uniform_source_sampling_subelement(self, root): if self._uniform_source_sampling is not None: @@ -2001,6 +2044,20 @@ def _statepoint_from_xml_element(self, root): batches = get_elem_list(elem, "batches", int) if batches is not None: self.statepoint['batches'] = batches + # Read overwrite_latest which may be boolean or integer + val = get_text(elem, 'overwrite_latest') + if val is not None: + if val in ('true', '1'): + self.statepoint['overwrite_latest'] = 1 + elif val in ('false', '0'): + self.statepoint['overwrite_latest'] = 0 + else: + try: + self.statepoint['overwrite_latest'] = int(val) + except ValueError: + raise ValueError( + "Invalid value for : '" + val + + "'. Provide true/false or an integer.") def _sourcepoint_from_xml_element(self, root): elem = root.find('source_point') diff --git a/src/settings.cpp b/src/settings.cpp index 9dcf7c8dbc8..e4d03f8d4b3 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -4,6 +4,8 @@ #include // for ceil, pow #include // for numeric_limits #include +#include +#include #include #ifdef _OPENMP @@ -72,6 +74,7 @@ bool source_write {true}; bool source_mcpl_write {false}; bool surf_source_write {false}; bool surf_mcpl_write {false}; +int state_latest {0}; bool surf_source_read {false}; bool survival_biasing {false}; bool survival_normalization {false}; @@ -821,6 +824,27 @@ void read_settings_xml(pugi::xml_node root) // If neither were specified, write state point at last batch statepoint_batch.insert(n_batches); } + if (check_for_node(node_sp, "overwrite_latest")) { + // Support boolean or integer values for overwrite_latest. If boolean + // true -> keep 1 running statepoint; if integer -> keep that many. + std::string tmp = get_node_value(node_sp, "overwrite_latest"); + // lowercase + std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](unsigned char c) { + return std::tolower(c); + }); + if (tmp == "true") { + state_latest = 1; + } else if (tmp == "false") { + state_latest = 0; + } else { + try { + state_latest = std::stoi(tmp); + } catch (...) { + fatal_error("Invalid value for : '" + tmp + "'. Provide true/false or an integer."); + } + } + if (state_latest < 0) state_latest = 0; + } } else { // If no tag was present, by default write state point at // last batch only diff --git a/src/simulation.cpp b/src/simulation.cpp index b536ae5881f..aecbf2f87be 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -443,6 +443,24 @@ void finalize_batch() } } + // Write a continuously-overwritten running state point(s) if requested. + if (settings::state_latest > 0 && !settings::cmfd_run) { + bool b = false; + if (contains(settings::sourcepoint_batch, simulation::current_batch) && + settings::source_write && !settings::source_separate) { + b = (settings::run_mode == RunMode::EIGENVALUE); + } + int n = settings::state_latest; + std::string filename; + if (n == 1) { + filename = settings::path_output + "statepoint.running.h5"; + } else { + int idx = ((simulation::current_batch - 1) % n) + 1; + filename = fmt::format("{}statepoint.running.{}.h5", settings::path_output, idx); + } + openmc_statepoint_write(filename.c_str(), &b); + } + if (settings::run_mode == RunMode::EIGENVALUE) { // Write out a separate source point if it's been specified for this batch if (contains(settings::sourcepoint_batch, simulation::current_batch) && diff --git a/tests/regression_tests/statepoint_latest/__init__.py b/tests/regression_tests/statepoint_latest/__init__.py new file mode 100644 index 00000000000..d2af06a9a8b --- /dev/null +++ b/tests/regression_tests/statepoint_latest/__init__.py @@ -0,0 +1 @@ +# regression test package diff --git a/tests/regression_tests/statepoint_latest/geometry.xml b/tests/regression_tests/statepoint_latest/geometry.xml new file mode 100644 index 00000000000..90893d590dd --- /dev/null +++ b/tests/regression_tests/statepoint_latest/geometry.xml @@ -0,0 +1,14 @@ + + + + + -10 -10 -10 10 10 10 + + + + + 1 + 1 + + + diff --git a/tests/regression_tests/statepoint_latest/materials.xml b/tests/regression_tests/statepoint_latest/materials.xml new file mode 100644 index 00000000000..d6a07b746fa --- /dev/null +++ b/tests/regression_tests/statepoint_latest/materials.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/tests/regression_tests/statepoint_latest/settings.xml b/tests/regression_tests/statepoint_latest/settings.xml new file mode 100644 index 00000000000..5eff8fb0917 --- /dev/null +++ b/tests/regression_tests/statepoint_latest/settings.xml @@ -0,0 +1,17 @@ + + + + + + eigenvalue + 4 + 0 + 100 + + + + -1 -1 -1 1 1 1 + + + + diff --git a/tests/regression_tests/statepoint_latest/test.py b/tests/regression_tests/statepoint_latest/test.py new file mode 100644 index 00000000000..f80ca149db2 --- /dev/null +++ b/tests/regression_tests/statepoint_latest/test.py @@ -0,0 +1,18 @@ +import os +import glob + +from tests.testing_harness import TestHarness + + +class StatepointLatestTestHarness(TestHarness): + def _test_output_created(self): + """Make sure running statepoint files have been created/rotated.""" + TestHarness._test_output_created(self) + # Expect two rotating running files + running = glob.glob(os.path.join(os.getcwd(), 'statepoint.running.*.h5')) + assert len(running) == 2, 'Expected 2 running statepoint files to exist.' + + +def test_statepoint_latest(): + harness = TestHarness('statepoint.4.h5') + harness.main() diff --git a/tests/regression_tests/statepoint_latest/test_pyapi.py b/tests/regression_tests/statepoint_latest/test_pyapi.py new file mode 100644 index 00000000000..6afc2d27e17 --- /dev/null +++ b/tests/regression_tests/statepoint_latest/test_pyapi.py @@ -0,0 +1,42 @@ +import os +import glob + +import openmc + +from tests.testing_harness import PyAPITestHarness + + +class StatepointLatestPyAPITestHarness(PyAPITestHarness): + def _test_output_created(self): + """Make sure running statepoint files have been created/rotated.""" + # Call parent checks (writes inputs, etc.) + super()._test_output_created() + # Expect two rotating running files + running = glob.glob(os.path.join(os.getcwd(), 'statepoint.running.*.h5')) + assert len(running) == 2, 'Expected 2 running statepoint files to exist.' + + +def test_statepoint_latest_pyapi(): + # Build a minimal model programmatically + model = openmc.Model() + + mat = openmc.Material() + mat.set_density('g/cm3', 1.0) + mat.add_nuclide('H1', 1.0) + model.materials.append(mat) + + # Simple box cell + box = openmc.model.RectangularParallelepiped(-10, 10, -10, 10, -10, 10) + cell = openmc.Cell(fill=mat, region=box) + model.geometry = openmc.Geometry([cell]) + + # Settings: small run to exercise feature + model.settings.batches = 4 + model.settings.inactive = 0 + model.settings.particles = 100 + + # Use Python API to enable rotating running statepoints (keep 2) + model.settings.statepoint = {'overwrite_latest': 2} + + harness = StatepointLatestPyAPITestHarness('statepoint.4.h5', model) + harness.main() From 6724bffb8a9441d102b3d65305a58afb1b98b021 Mon Sep 17 00:00:00 2001 From: pranav Date: Mon, 24 Nov 2025 13:05:22 +0000 Subject: [PATCH 2/7] add fixes for statepoint file naming and a test script --- openmc/settings.py | 33 +++++---- src/simulation.cpp | 58 +++++++++++++--- .../statepoint_latest/test_flux_spectrum.py | 68 +++++++++++++++++++ 3 files changed, 133 insertions(+), 26 deletions(-) create mode 100644 tests/regression_tests/statepoint_latest/test_flux_spectrum.py diff --git a/openmc/settings.py b/openmc/settings.py index 61d7b78cd80..d0bdc8fca15 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -786,12 +786,24 @@ def statepoint(self, statepoint: dict): sp['batches'] = [int(x) for x in batches] if 'overwrite_latest' in statepoint: val = statepoint['overwrite_latest'] + # Accept bool, int, or string representations (e.g., "true", "false", "2") if isinstance(val, bool): sp['overwrite_latest'] = 1 if val else 0 elif isinstance(val, Integral): sp['overwrite_latest'] = int(val) + elif isinstance(val, str): + sval = val.strip().lower() + if sval in ('true', 't'): + sp['overwrite_latest'] = 1 + elif sval in ('false', 'f'): + sp['overwrite_latest'] = 0 + else: + try: + sp['overwrite_latest'] = int(sval) + except ValueError: + raise ValueError("statepoint['overwrite_latest'] must be bool, int, or numeric string") else: - raise ValueError("statepoint['overwrite_latest'] must be bool or int") + raise ValueError("statepoint['overwrite_latest'] must be bool, int, or numeric string") self._statepoint = sp @property @@ -819,22 +831,9 @@ def sourcepoint(self, sourcepoint: dict): "setting sourcepoint options.") self._sourcepoint = sourcepoint - @property - def statepoint(self) -> dict: - return self._statepoint - - @statepoint.setter - def statepoint(self, statepoint: dict): - cv.check_type('statepoint options', statepoint, Mapping) - for key, value in statepoint.items(): - if key == 'batches': - cv.check_type('statepoint batches', value, Iterable, Integral) - for batch in value: - cv.check_greater_than('statepoint batch', batch, 0) - else: - raise ValueError(f"Unknown key '{key}' encountered when " - "setting statepoint options.") - self._statepoint = statepoint + # The `statepoint` property (defined earlier) handles 'batches' and + # 'overwrite_latest' and normalizes values. This duplicate definition + # was removed to avoid shadowing the full implementation above. @property def surf_source_read(self) -> dict: diff --git a/src/simulation.cpp b/src/simulation.cpp index aecbf2f87be..376420cfcaa 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -41,6 +41,8 @@ #include #include #include +#include +#include //============================================================================== // C API functions @@ -450,15 +452,53 @@ void finalize_batch() settings::source_write && !settings::source_separate) { b = (settings::run_mode == RunMode::EIGENVALUE); } - int n = settings::state_latest; - std::string filename; - if (n == 1) { - filename = settings::path_output + "statepoint.running.h5"; - } else { - int idx = ((simulation::current_batch - 1) % n) + 1; - filename = fmt::format("{}statepoint.running.{}.h5", settings::path_output, idx); - } - openmc_statepoint_write(filename.c_str(), &b); + int n = settings::state_latest; + namespace fs = std::filesystem; + + // Construct output directory (use current path if none specified) + fs::path outdir = settings::path_output.empty() ? fs::current_path() : fs::path(settings::path_output); + + // Always include the batch number in the running statepoint filename. + fs::path sp_name = fmt::format("statepoint.running.{}.h5", simulation::current_batch); + fs::path sp_path = outdir / sp_name; + std::string filename = sp_path.string(); + openmc_statepoint_write(filename.c_str(), &b); + + // Prune older running statepoint files so that at most `n` remain. + // Match files like `statepoint.running.h5`, `statepoint.running..h5`. + if (n > 0) { + try { + std::vector> files; // pair(batchnum, path) + static const std::regex re(R"(^statepoint\.running(?:\.(\d+))?\.h5$)"); + if (fs::exists(outdir) && fs::is_directory(outdir)) { + for (auto &p : fs::directory_iterator(outdir)) { + auto name = p.path().filename().string(); + std::smatch m; + if (std::regex_match(name, m, re)) { + int batchnum = 0; + if (m.size() >= 2 && m[1].matched) batchnum = std::stoi(m[1].str()); + files.emplace_back(batchnum, p.path()); + } + } + } + + // Sort by batch number descending (newest first). Files without a + // batch number (batchnum == 0) will appear last. + std::sort(files.begin(), files.end(), + [](auto &a, auto &b) { + if (a.first != b.first) return a.first > b.first; + return a.second.string() > b.second.string(); + }); + + // Remove files older than the most recent `n` + for (size_t i = static_cast(n); i < files.size(); ++i) { + std::error_code ec; + fs::remove(files[i].second, ec); + } + } catch (...) { + // On any filesystem/regex error, ignore pruning so simulation can continue + } + } } if (settings::run_mode == RunMode::EIGENVALUE) { diff --git a/tests/regression_tests/statepoint_latest/test_flux_spectrum.py b/tests/regression_tests/statepoint_latest/test_flux_spectrum.py new file mode 100644 index 00000000000..0015e23f69d --- /dev/null +++ b/tests/regression_tests/statepoint_latest/test_flux_spectrum.py @@ -0,0 +1,68 @@ +import openmc.examples +import numpy as np +import matplotlib.pyplot as plt + +model = openmc.examples.pwr_pin_cell() + +model.tallies + +# Create equal-lethargy energies to put in filter +energies = np.logspace(np.log10(1e-5), np.log10(20.0e6), 501) +e_filter = openmc.EnergyFilter(energies) + +# Create tally with energy filter +tally = openmc.Tally() +tally.filters = [e_filter] +tally.scores = ['flux'] + +# Set model tallies +model.tallies = [tally] + +openmc.mgxs.GROUP_STRUCTURES.keys() + +# Create energy filter using SHEM-361 group structure +energies_shem = openmc.mgxs.GROUP_STRUCTURES['SHEM-361'] +shem_filter = openmc.EnergyFilter(openmc.mgxs.GROUP_STRUCTURES['SHEM-361']) + +tally_shem = openmc.Tally() +tally_shem.filters = [shem_filter] +tally_shem.scores = ['flux'] + +model.tallies.append(tally_shem) + +model.settings.particles = 10000 +model.settings.batches = 50 + +model.settings.statepoint = {"overwrite_latest" : "2"} + +print(model.settings.statepoint) +sp_path = model.run(output=True) + +with openmc.StatePoint(sp_path) as sp: + t = sp.tallies[tally.id] + flux500_mean = t.mean.ravel() + flux500_unc = t.std_dev.ravel() + + t_shem = sp.tallies[tally_shem.id] + flux_shem_mean = t_shem.mean.ravel() + flux_shem_unc = t_shem.std_dev.ravel() + + fig, ax = plt.subplots() + +ax.step(energies[:-1], flux500_mean/np.diff(energies), where='post', label='500 group') +ax.step(energies_shem[:-1], flux_shem_mean/np.diff(energies_shem), where='post', label='SHEM-361') +ax.set_xscale('log') +ax.set_yscale('log') +ax.set_xlabel('Energy [eV]') +ax.set_ylabel('Flux [n-cm/eV-src]') +ax.grid() +ax.legend() + + +fig, ax = plt.subplots() +ax.loglog(energies[:-1], flux500_mean, '.', color='C0', label='500 group') +ax.loglog(energies_shem[:-1], flux_shem_mean, '.', color='C1', label='SHEM-361') +ax.set_xlabel('Energy [eV]') +ax.set_ylabel('Flux [n-cm/src]') +ax.grid() +ax.legend() \ No newline at end of file From e2092a3640b65e56166e8d20cd0a959143ccaa14 Mon Sep 17 00:00:00 2001 From: pranav Date: Mon, 24 Nov 2025 13:05:59 +0000 Subject: [PATCH 3/7] add fixes for statepoint filename and purging old files --- src/simulation.cpp | 92 ++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 376420cfcaa..7a9a0d184ba 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -40,9 +40,9 @@ #include #include -#include #include #include +#include //============================================================================== // C API functions @@ -452,53 +452,59 @@ void finalize_batch() settings::source_write && !settings::source_separate) { b = (settings::run_mode == RunMode::EIGENVALUE); } - int n = settings::state_latest; - namespace fs = std::filesystem; - - // Construct output directory (use current path if none specified) - fs::path outdir = settings::path_output.empty() ? fs::current_path() : fs::path(settings::path_output); - - // Always include the batch number in the running statepoint filename. - fs::path sp_name = fmt::format("statepoint.running.{}.h5", simulation::current_batch); - fs::path sp_path = outdir / sp_name; - std::string filename = sp_path.string(); - openmc_statepoint_write(filename.c_str(), &b); - - // Prune older running statepoint files so that at most `n` remain. - // Match files like `statepoint.running.h5`, `statepoint.running..h5`. - if (n > 0) { - try { - std::vector> files; // pair(batchnum, path) - static const std::regex re(R"(^statepoint\.running(?:\.(\d+))?\.h5$)"); - if (fs::exists(outdir) && fs::is_directory(outdir)) { - for (auto &p : fs::directory_iterator(outdir)) { - auto name = p.path().filename().string(); - std::smatch m; - if (std::regex_match(name, m, re)) { - int batchnum = 0; - if (m.size() >= 2 && m[1].matched) batchnum = std::stoi(m[1].str()); - files.emplace_back(batchnum, p.path()); - } + int n = settings::state_latest; + namespace fs = std::filesystem; + + // Construct output directory (use current path if none specified) + fs::path outdir = settings::path_output.empty() + ? fs::current_path() + : fs::path(settings::path_output); + + // Always include the batch number in the running statepoint filename. + fs::path sp_name = + fmt::format("statepoint.running.{}.h5", simulation::current_batch); + fs::path sp_path = outdir / sp_name; + std::string filename = sp_path.string(); + openmc_statepoint_write(filename.c_str(), &b); + + // Prune older running statepoint files so that at most `n` remain. + // Match files like `statepoint.running.h5`, + // `statepoint.running..h5`. + if (n > 0) { + try { + std::vector> files; // pair(batchnum, path) + static const std::regex re(R"(^statepoint\.running(?:\.(\d+))?\.h5$)"); + if (fs::exists(outdir) && fs::is_directory(outdir)) { + for (auto& p : fs::directory_iterator(outdir)) { + auto name = p.path().filename().string(); + std::smatch m; + if (std::regex_match(name, m, re)) { + int batchnum = 0; + if (m.size() >= 2 && m[1].matched) + batchnum = std::stoi(m[1].str()); + files.emplace_back(batchnum, p.path()); } } + } - // Sort by batch number descending (newest first). Files without a - // batch number (batchnum == 0) will appear last. - std::sort(files.begin(), files.end(), - [](auto &a, auto &b) { - if (a.first != b.first) return a.first > b.first; - return a.second.string() > b.second.string(); - }); - - // Remove files older than the most recent `n` - for (size_t i = static_cast(n); i < files.size(); ++i) { - std::error_code ec; - fs::remove(files[i].second, ec); - } - } catch (...) { - // On any filesystem/regex error, ignore pruning so simulation can continue + // Sort by batch number descending (newest first). Files without a + // batch number (batchnum == 0) will appear last. + std::sort(files.begin(), files.end(), [](auto& a, auto& b) { + if (a.first != b.first) + return a.first > b.first; + return a.second.string() > b.second.string(); + }); + + // Remove files older than the most recent `n` + for (size_t i = static_cast(n); i < files.size(); ++i) { + std::error_code ec; + fs::remove(files[i].second, ec); } + } catch (...) { + // On any filesystem/regex error, ignore pruning so simulation can + // continue } + } } if (settings::run_mode == RunMode::EIGENVALUE) { From bb543b6200faa69636834908e2cf027b7422dc63 Mon Sep 17 00:00:00 2001 From: pranav Date: Tue, 25 Nov 2025 07:26:22 +0000 Subject: [PATCH 4/7] merge overwrite_existing key with the existing 'batches' --- .../io_formats/settings_statepoint_latest.rst | 77 ++++++++++++------ docs/source/usersguide/settings.rst | 13 +++ include/openmc/settings.h | 1 - openmc/settings.py | 38 +++------ src/settings.cpp | 22 ------ src/simulation.cpp | 76 +++++++++--------- .../statepoint_latest/model.xml | 79 +++++++++++++++++++ .../statepoint_latest/settings.xml | 4 +- .../statepoint_latest/test_flux_spectrum.py | 68 ---------------- .../statepoint_latest/test_pyapi.py | 6 +- 10 files changed, 202 insertions(+), 182 deletions(-) create mode 100644 tests/regression_tests/statepoint_latest/model.xml delete mode 100644 tests/regression_tests/statepoint_latest/test_flux_spectrum.py diff --git a/docs/source/io_formats/settings_statepoint_latest.rst b/docs/source/io_formats/settings_statepoint_latest.rst index 7d4bd73240c..7e46b30f004 100644 --- a/docs/source/io_formats/settings_statepoint_latest.rst +++ b/docs/source/io_formats/settings_statepoint_latest.rst @@ -1,44 +1,77 @@ .. _usersguide_statepoint_latest: -Statepoint Overwrite -==================== +Running Statepoints +=================== -OpenMC can be configured to write a continuously-overwritten "running" statepoint -file after every finished batch. This is useful when you want the most recent -results available even if the job is interrupted unexpectedly. +OpenMC can be configured to keep running statepoint files that capture the state +of the most recently completed batches. This is useful when you want access to +results from the most recent batches even if a simulation is interrupted +unexpectedly, allowing easy restart or analysis. -To enable this behavior, set the ``overwrite_latest`` option inside the -```` element in the settings XML. The value may be either a -boolean or an integer: - -- ``true``: keep a single file named ``statepoint.running.h5`` that is - overwritten after every batch. -- ``false``: (default) do not write running statepoint files. -- ``N`` (integer > 1): keep ``N`` rotating running statepoint files named - ``statepoint.running.1.h5``, ``statepoint.running.2.h5``, ..., ``statepoint.running.N.h5``. +To enable this feature, specify a negative batch number in the ```` +element within ````. A value of ``-N`` means "keep the last N +completed batches as running statepoint files." Examples -Keep a single running statepoint file: +Keep the last batch completed: + +.. code-block:: xml + + + -1 + + +Keep the last two completed batches: .. code-block:: xml - true + -2 -Keep the last two completed batches in rotating files: +You can also mix positive and negative batches. Positive batches specify +explicit intervals to write statepoints, while negative values keep a rolling +window of recent batches: .. code-block:: xml - 2 + 10 20 30 -3 +In this case, statepoints are written at batches 10, 20, and 30, and the last 3 +completed batches are also retained as running statepoints. + +File Naming +----------- + +Running statepoint files are named ``statepoint.running..h5``, where +```` is the batch number. For example: + +- Batch 1: ``statepoint.running.1.h5`` +- Batch 2: ``statepoint.running.2.h5`` +- Batch 5: ``statepoint.running.5.h5`` + +Pruning +------- + +When you specify ``-N``, OpenMC automatically keeps only the most recent ``N`` +running statepoints. Older running statepoint files are automatically deleted +when a new batch completes and the number of running statepoints exceeds ``N``. + +For example, with ``-2`` and if batches 1 through 5 complete: + +- After batch 1 completes: ``statepoint.running.1.h5`` exists +- After batch 2 completes: ``statepoint.running.1.h5``, ``statepoint.running.2.h5`` exist +- After batch 3 completes: ``statepoint.running.2.h5``, ``statepoint.running.3.h5`` exist (batch 1 file deleted) +- After batch 4 completes: ``statepoint.running.3.h5``, ``statepoint.running.4.h5`` exist (batch 2 file deleted) +- After batch 5 completes: ``statepoint.running.4.h5``, ``statepoint.running.5.h5`` exist (batch 3 file deleted) + Remarks ------- -The running statepoints are written in addition to any permanent statepoint -files you requested via the ``...`` -element. The rotating files form a circular buffer of the most recent -completed batches and are overwritten in batch order. +Running statepoints are written in addition to any permanent statepoint files +specified by positive batch numbers. Each running statepoint file is named with +its batch number, making it easy to identify which batch's results are +contained in each file. diff --git a/docs/source/usersguide/settings.rst b/docs/source/usersguide/settings.rst index f973f146552..a8b81135f21 100644 --- a/docs/source/usersguide/settings.rst +++ b/docs/source/usersguide/settings.rst @@ -651,6 +651,19 @@ As an example, to write a statepoint file every five batches:: settings.batches = n settings.statepoint = {'batches': range(5, n + 5, 5)} +Additionally, you can specify negative batch numbers to keep running statepoints +for the most recent batches. For example, to keep the last 2 completed batches:: + + settings.statepoint = {'batches': -2} + +Or to write statepoints at specific batches and also keep the last 3 completed +batches, you can mix positive and negative values:: + + settings.statepoint = {'batches': [10, 20, 30, -3]} + +See :ref:`usersguide_statepoint_latest` for detailed information on running +statepoints, file naming, and automatic pruning behavior. + Particle Track Files -------------------- diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 08ee8ea3a3e..b369c99fef8 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -86,7 +86,6 @@ extern bool source_separate; //!< write source to separate file? extern bool source_write; //!< write source in HDF5 files? extern bool source_mcpl_write; //!< write source in mcpl files? extern bool surf_source_write; //!< write surface source file? -extern int state_latest; //!< number of running statepoints to keep (0 = disabled) extern bool surf_mcpl_write; //!< write surface mcpl file? extern bool surf_source_read; //!< read surface source file? extern bool survival_biasing; //!< use survival biasing? diff --git a/openmc/settings.py b/openmc/settings.py index d0bdc8fca15..a8463222584 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -770,8 +770,9 @@ def statepoint(self) -> dict: """Dictionary of statepoint options. Acceptable keys: - - 'batches': list of batch integers - - 'overwrite_latest': bool or int (see docs) + - 'batches': list of batch integers or single integer. + Positive integers: write statepoints at specified batches. + Negative integer -N: keep running statepoints for the last N batches. """ return self._statepoint @@ -781,29 +782,14 @@ def statepoint(self, statepoint: dict): sp = {} if 'batches' in statepoint: batches = statepoint['batches'] - if not isinstance(batches, Sequence): - raise ValueError("statepoint['batches'] must be a sequence of ints") - sp['batches'] = [int(x) for x in batches] - if 'overwrite_latest' in statepoint: - val = statepoint['overwrite_latest'] - # Accept bool, int, or string representations (e.g., "true", "false", "2") - if isinstance(val, bool): - sp['overwrite_latest'] = 1 if val else 0 - elif isinstance(val, Integral): - sp['overwrite_latest'] = int(val) - elif isinstance(val, str): - sval = val.strip().lower() - if sval in ('true', 't'): - sp['overwrite_latest'] = 1 - elif sval in ('false', 'f'): - sp['overwrite_latest'] = 0 - else: - try: - sp['overwrite_latest'] = int(sval) - except ValueError: - raise ValueError("statepoint['overwrite_latest'] must be bool, int, or numeric string") + if isinstance(batches, Integral): + # Single integer: positive (single batch) or negative (keep last N) + sp['batches'] = [int(batches)] + elif isinstance(batches, Sequence): + # Sequence of integers + sp['batches'] = [int(x) for x in batches] else: - raise ValueError("statepoint['overwrite_latest'] must be bool, int, or numeric string") + raise ValueError("statepoint['batches'] must be a sequence or single integer") self._statepoint = sp @property @@ -831,10 +817,6 @@ def sourcepoint(self, sourcepoint: dict): "setting sourcepoint options.") self._sourcepoint = sourcepoint - # The `statepoint` property (defined earlier) handles 'batches' and - # 'overwrite_latest' and normalizes values. This duplicate definition - # was removed to avoid shadowing the full implementation above. - @property def surf_source_read(self) -> dict: return self._surf_source_read diff --git a/src/settings.cpp b/src/settings.cpp index e4d03f8d4b3..05efbbbcab4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -74,7 +74,6 @@ bool source_write {true}; bool source_mcpl_write {false}; bool surf_source_write {false}; bool surf_mcpl_write {false}; -int state_latest {0}; bool surf_source_read {false}; bool survival_biasing {false}; bool survival_normalization {false}; @@ -824,27 +823,6 @@ void read_settings_xml(pugi::xml_node root) // If neither were specified, write state point at last batch statepoint_batch.insert(n_batches); } - if (check_for_node(node_sp, "overwrite_latest")) { - // Support boolean or integer values for overwrite_latest. If boolean - // true -> keep 1 running statepoint; if integer -> keep that many. - std::string tmp = get_node_value(node_sp, "overwrite_latest"); - // lowercase - std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](unsigned char c) { - return std::tolower(c); - }); - if (tmp == "true") { - state_latest = 1; - } else if (tmp == "false") { - state_latest = 0; - } else { - try { - state_latest = std::stoi(tmp); - } catch (...) { - fatal_error("Invalid value for : '" + tmp + "'. Provide true/false or an integer."); - } - } - if (state_latest < 0) state_latest = 0; - } } else { // If no tag was present, by default write state point at // last batch only diff --git a/src/simulation.cpp b/src/simulation.cpp index 7a9a0d184ba..21d71410f9e 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -445,14 +445,23 @@ void finalize_batch() } } - // Write a continuously-overwritten running state point(s) if requested. - if (settings::state_latest > 0 && !settings::cmfd_run) { + // Write running statepoints if user specified negative batch(es) in statepoint_batch. + // A negative value -N means "keep the last N completed batches as running statepoints". + // Check if there's a negative batch number in statepoint_batch. + int keep_last_n = 0; + for (int b : settings::statepoint_batch) { + if (b < 0) { + keep_last_n = -b; // Convert to positive count + break; + } + } + + if (keep_last_n > 0 && !settings::cmfd_run) { bool b = false; if (contains(settings::sourcepoint_batch, simulation::current_batch) && settings::source_write && !settings::source_separate) { b = (settings::run_mode == RunMode::EIGENVALUE); } - int n = settings::state_latest; namespace fs = std::filesystem; // Construct output directory (use current path if none specified) @@ -460,50 +469,43 @@ void finalize_batch() ? fs::current_path() : fs::path(settings::path_output); - // Always include the batch number in the running statepoint filename. + // Write running statepoint with batch number in filename. fs::path sp_name = fmt::format("statepoint.running.{}.h5", simulation::current_batch); fs::path sp_path = outdir / sp_name; std::string filename = sp_path.string(); openmc_statepoint_write(filename.c_str(), &b); - // Prune older running statepoint files so that at most `n` remain. - // Match files like `statepoint.running.h5`, - // `statepoint.running..h5`. - if (n > 0) { - try { - std::vector> files; // pair(batchnum, path) - static const std::regex re(R"(^statepoint\.running(?:\.(\d+))?\.h5$)"); - if (fs::exists(outdir) && fs::is_directory(outdir)) { - for (auto& p : fs::directory_iterator(outdir)) { - auto name = p.path().filename().string(); - std::smatch m; - if (std::regex_match(name, m, re)) { - int batchnum = 0; - if (m.size() >= 2 && m[1].matched) - batchnum = std::stoi(m[1].str()); - files.emplace_back(batchnum, p.path()); - } + // Prune older running statepoint files so that at most `keep_last_n` remain. + // Match files like `statepoint.running..h5`. + try { + std::vector> files; // pair(batchnum, path) + static const std::regex re(R"(^statepoint\.running\.(\d+)\.h5$)"); + if (fs::exists(outdir) && fs::is_directory(outdir)) { + for (auto& p : fs::directory_iterator(outdir)) { + auto name = p.path().filename().string(); + std::smatch m; + if (std::regex_match(name, m, re)) { + int batchnum = std::stoi(m[1].str()); + files.emplace_back(batchnum, p.path()); } } + } - // Sort by batch number descending (newest first). Files without a - // batch number (batchnum == 0) will appear last. - std::sort(files.begin(), files.end(), [](auto& a, auto& b) { - if (a.first != b.first) - return a.first > b.first; - return a.second.string() > b.second.string(); - }); - - // Remove files older than the most recent `n` - for (size_t i = static_cast(n); i < files.size(); ++i) { - std::error_code ec; - fs::remove(files[i].second, ec); - } - } catch (...) { - // On any filesystem/regex error, ignore pruning so simulation can - // continue + // Sort by batch number descending (newest first) + std::sort(files.begin(), files.end(), [](auto& a, auto& b) { + if (a.first != b.first) + return a.first > b.first; + return a.second.string() > b.second.string(); + }); + + // Remove files older than the most recent `keep_last_n` + for (size_t i = static_cast(keep_last_n); i < files.size(); ++i) { + std::error_code ec; + fs::remove(files[i].second, ec); } + } catch (...) { + // On any filesystem/regex error, ignore pruning so simulation can continue } } diff --git a/tests/regression_tests/statepoint_latest/model.xml b/tests/regression_tests/statepoint_latest/model.xml new file mode 100644 index 00000000000..2dec6b033ce --- /dev/null +++ b/tests/regression_tests/statepoint_latest/model.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 10000 + 50 + 5 + + + -0.63 -0.63 -1 0.63 0.63 1 + + + true + + + + 5 10 15 -3 + + + + + 1e-05 1.058283585339154e-05 1.1199641469982942e-05 1.1852396729366596e-05 1.254319690561614e-05 1.3274259392890431e-05 1.4047930823030026e-05 1.4866694597992626e-05 1.5733178861305835e-05 1.6650164934124926e-05 1.7620596242973984e-05 1.864758776782813e-05 1.9734436040863702e-05 2.0884629727971414e-05 2.2101860826998267e-05 2.3390036518662722e-05 2.4753291708184126e-05 2.6196002297883046e-05 2.772279923335633e-05 2.933858336831388e-05 3.1048541195790885e-05 3.2858161496232e-05 3.477325295588533e-05 3.679996281205958e-05 3.894479658509395e-05 4.121463896037726e-05 4.3616775887446824e-05 4.6158917967101585e-05 4.884922520160005e-05 5.169633318738906e-05 5.4709380834437574e-05 5.789803970115379e-05 6.127254503904571e-05 6.484372864677595e-05 6.862305363906924e-05 7.262265124207526e-05 7.685535973329837e-05 8.133476565108544e-05 8.607524740595039e-05 9.109202143372388e-05 9.640119103867236e-05 0.00010201979808337089 0.00010796587769124629 0.00011425851613738057 0.00012091791211339856 0.00012796544156309215 0.00013542372629689713 0.00014331670660546855 0.00015166971810543468 0.00016050957306399803 0.00016986464646342474 0.00017976496708168094 0.0001902423138815763 0.00020133031801781103 0.0002130645707893609 0.0002254827378837128 0.00023862468027966425 0.0002525325821967724 0.00026725108650215454 0.0002828274380092842 0.00029931163512875256 0.0003167565903577809 0.00033521830012363805 0.0003547560245261398 0.00037543247755618766 0.0003973140284009237 0.00042047091448167196 0.00044497746690849656 0.00047091234907505796 0.000498358809159635 0.0005274049473428096 0.0005581439985995563 0.0005906746319734701 0.0006251012672937686 0.0006615344103516975 0.0007000910076122174 0.0007408948215995583 0.0007840768281615934 0.0008297756368882018 0.0008781379360331252 0.0009293189633674604 0.0009834830044761819 0.0010408039200971756 0.0011014657041954854 0.0011656630745641142 0.0012336020978471708 0.0013055008509916058 0.0013815901212507115 0.0014621141469863593 0.001547331401647823 0.0016375154234437147 0.0017329556933701772 0.0018339585644136886 0.001940848244911166 0.002053967839223793 0.0021736804490650684 0.0023003703390182023 0.0024344441699840256 0.002576332304518695 0.0027264901882511298 0.002885399811814427 0.0030535712579838918 0.0032315443389877804 0.003419890329246434 0.0036192137991016156 0.003830154555422194 0.0040533896953152924 0.004289635779535044 0.0045396511325654625 0.004804238276760328 0.0050842465083535135 0.0053805746236084305 0.005694173803857192 0.0060260506686902774 0.006377270507096952 0.0067489606969282 0.00714231432365821 0.007558594010060197 0.007999135969089558 0.008465354292983484 0.00895874549234475 0.009480893299779585 0.01003347375350869 0.010618260577269476 0.011237130873778135 0.011892071150027212 0.012585183693759112 0.013318693321583235 0.01409495452039775 0.014916459005038846 0.015785843716417003 0.016705899285813338 0.017679578992505333 0.018710008243475327 0.019800494605630194 0.020954538422734877 0.022175844051138923 0.023468331750361223 0.024836150266700975 0.02628369015026629 0.027815597848167187 0.029436790619110424 0.031152472317270117 0.03296815009609936 0.0348896520856994 0.036923146100489614 0.039075159437227544 0.041352599826928196 0.04376277760793685 0.04631342919132745 0.04901274189394899 0.05186938021883089 0.054892513667304084 0.058091846172113076 0.06147764724599442 0.06506078494570668 0.0688527607573221 0.07286574651475776 0.0771126234700518 0.0816070236407946 0.08636337356743712 0.09139694062093207 0.09672388200934963 0.10236129664077592 0.10832728000896481 0.11464098227792573 0.12132266975188563 0.12839378992794365 0.13587704034022635 0.14379644141652728 0.1521774135812941 0.16104685885245115 0.17043324719398067 0.18036670790144016 0.1908791263137556 0.2020042461617265 0.21377777788176494 0.22623751324255145 0.23942344665254167 0.2533779035377089 0.26814567620160484 0.28377416760382623 0.3003135435184112 0.3178168935605702 0.3363404015986318 0.35594352609821095 0.3766891909774754 0.3986439875861479 0.4218783884565657 0.4464669735129178 0.4724886694647717 0.500027003153305 0.529170369663472 0.5600123160627045 0.5926518416769212 0.6271937158677048 0.6637488143306611 0.7024344749944644 0.743374874662968 0.7867014276093683 0.8325532074018732 0.8810773933148666 0.9324297427585329 0.986775091243365 1.044287881484397 1.105152723343537 1.1695649864073283 1.2377314271022861 1.309870852360755 1.3862148219675905 1.4670083918421386 1.5525109006413247 1.64299680220882 1.7387565465423147 1.8400975121067225 1.9473449924859594 2.060843240540289 2.180956573420937 2.3080705419889047 2.4425931683916975 2.5849562557704893 2.735616774301668 2.8950583280219 3.0637927071449926 3.2423615308533487 3.4313379858372293 3.6313286661622537 3.8429755203710374 4.066957912068862 4.303994800607666 4.554847048868157 4.820319865547658 5.101265389793324 5.398585426477014 5.7132343408917885 6.04622212216174 6.398617625198233 6.771552001609089 7.16622233057339 7.5838954613367 8.025912079660738 8.493691011280191 8.988733776180544 9.512629408315497 10.06705955623478 10.653803880994934 11.27474576867951 11.931878375865606 12.627311027441774 13.363275987313688 14.142135623730951 14.966389992234562 15.838684860566024 16.76182020129679 17.73875917943859 18.772637663884094 19.8667742932081 21.02468112814 22.250074924900446 23.546889065588402 24.919286183914213 26.371671526805173 27.90870709477386 29.53532660643756 31.25675133522358 33.078506869094795 35.00644084709147 37.04674172962297 39.205958662759045 41.491022500283215 43.90926805098722 46.46845762261672 49.17680593804336 52.04300650364032 55.07625951450127 58.28630138607608 61.683436007015096 65.2785678135422 69.08323679152052 73.10965551856394 77.3707483650963 81.88019298018759 86.65246419533474 91.70288048711151 97.0476531478281 102.70393832203415 108.68989207589362 115.0247286762024 121.72878226611486 128.82357213555298 136.33187179581014 144.27778208006782 152.6868085044753 161.58594313810892 171.0037512446062 180.97046297358665 191.5180703961739 202.68043019609937 214.49337234601015 226.99481511782128 240.22488679628626 254.22605448646618 269.0432604285645 284.72406625767695 301.3188056715166 318.88074599616436 337.4662591684448 357.1350026837739 377.9501110902926 399.97839864396553 423.2905737751492 447.96146605503253 474.0702663905035 501.7007812184298 530.9417015152933 561.8868874856754 594.6356698433983 629.293168652421 665.9706307509209 704.7857868416609 745.8632293948696 789.3348125766423 835.3400754866182 884.0266900634576 935.550935095859 990.0781978606437 1047.7835049980924 1108.8520843286042 1173.4799594140666 1241.8745787723658 1314.255481764768 1390.8550032936585 1471.9190195725107 1557.7077373620855 1648.4965292060924 1744.576817347371 1846.2570091619498 1953.8634871134473 2067.7416564056766 2188.2570536961243 2315.796520429223 2450.7694445557804 2593.609074624133 2744.773910461387 2904.749174908452 3074.0483713330595 3253.2149319203436 3442.8239620315253 3643.4840862302663 3855.8394019019247 4080.5715467367395 4318.401886713502 4570.093831606522 4836.455285448892 5118.341239817365 5416.656518263159 5732.358680698247 6066.461097059349 6420.036200116452 6794.218927866411 7190.210366561592 7609.28160606757 8052.777819924446 8522.12258320924 9018.822442058465 9544.471749518836 10100.757783249082 10689.46616149918 11312.486574752893 11971.81885143056 12669.579377122778 13408.007887960524 14189.47465992649 15016.488117186047 15891.702883858483 16817.928305074292 17798.137464670897 18835.476728470996 19933.275843778443 21095.058627508242 22324.554277259027 23625.709341636273 25002.700388247526 26459.94741003519 28002.12801297756 29634.192430699786 31361.37941419145 33189.23304763199 35123.62054430465 37170.751079718764 39337.19572239392 41629.90852628314 44056.24885253585 46624.004992255526 49341.41916607489 52217.21398079567 55260.62042801835 58481.40751462922 61889.913620261825 65497.07968238135 69314.48431551465 73354.38097735753 77629.73730105201 82154.2767198938 86942.52251807437 92009.84444885768 97372.50806983514 103047.72695361062 109053.71794151698 115409.75961771364 122136.25419136364 129254.79298553085 136788.22574299743 144760.73397148054 153197.90856366622 162126.8319412162 171576.1649864291 181576.23904058 192159.15326426385 203358.87767224156 215211.36217352576 227754.65196672245 241029.00966101373 255077.04451480263 269943.84920684085 285677.14457886684 302327.4328143757 319948.159545179 338595.88540613494 358330.4675886905 379215.2519760141 401317.27647646767 424707.4862080598 449460.96122464386 475657.1575147995 503380.16204699164 532718.9626796957 563767.7338028221 596626.1386273791 631399.6490936391 668199.8844246987 707144.9694121798 748359.9135840663 791977.0124718435 838136.2722648961 886985.8592152856 938682.5752354843 993392.3612155964 1051290.829675767 1112563.8284634468 1177408.0373049495 1246031.5991262207 1318654.7881691733 1395510.7170483125 1476846.0850171046 1562921.969845991 1654014.6658539518 1750416.570783459 1852437.1243657821 1960403.801589176 2074663.1638582961 2195581.9714190345 2323548.360619339 2458973.0897851423 2602290.857710323 2753961.698992977 2914472.460697001 3084338.365078642 3264104.663394523 3454348.386099415 3655680.195051801 3868746.3436727654 4094230.751349748 4332857.198744221 4585391.651049606 4852644.716656989 5135474.249120872 5434788.100776524 5751547.036848338 6086767.819402659 6441526.471044417 6816961.728833966 7214278.699510192 7634752.727753518 8079733.489904895 8550649.326281369 9049011.825994886 9576420.678990252 10134568.810877798 10725247.817042144 11350353.713470269 12011893.022758922 12711989.214835651 13452889.523068901 14236972.157644926 15066753.93936615 15944898.378375264 16874224.2237354 17857714.511311464 18898526.13899377 20000000.000000004 + + + 0.0 0.0024999 0.00455602 0.00714526 0.0104505 0.01483 0.0200104 0.0249394 0.0292989 0.0343998 0.0402999 0.0473019 0.0554982 0.0651999 0.0764969 0.0897968 0.104298 0.119995 0.137999 0.161895 0.190005 0.20961 0.231192 0.254997 0.279989 0.305012 0.325008 0.352994 0.390001 0.431579 0.475017 0.520011 0.55499 0.594993 0.624999 0.719999 0.800371 0.880024 0.919978 0.944022 0.96396 0.981959 0.996501 1.00904 1.02101 1.03499 1.07799 1.09198 1.10395 1.11605 1.12997 1.14797 1.16999 1.21397 1.25094 1.29304 1.33095 1.38098 1.41001 1.44397 1.51998 1.58803 1.66895 1.77997 1.90008 1.98992 2.0701 2.15695 2.21709 2.27299 2.33006 2.46994 2.55 2.59009 2.62005 2.64004 2.70012 2.7199 2.74092 2.77512 2.88405 3.14211 3.54307 3.71209 3.88217 4.0 4.21983 4.30981 4.4198 4.76785 4.93323 5.10997 5.21008 5.32011 5.38003 5.41025 5.48817 5.53004 5.61979 5.72015 5.80021 5.96014 6.05991 6.16011 6.28016 6.35978 6.43206 6.48178 6.51492 6.53907 6.55609 6.57184 6.58829 6.60611 6.63126 6.71668 6.74225 6.75981 6.77605 6.79165 6.8107 6.83526 6.87021 6.91778 6.99429 7.13987 7.38015 7.60035 7.73994 7.83965 7.97008 8.13027 8.30032 8.52407 8.67369 8.80038 8.97995 9.14031 9.50002 10.5793 10.8038 11.0529 11.2694 11.5894 11.7094 11.8153 11.9795 12.1302 12.3086 12.4721 12.6 13.3297 13.546 14.0496 14.2505 14.4702 14.5952 14.7301 14.8662 15.7792 16.0498 16.5501 16.8305 17.4457 17.5648 17.759 17.9591 19.0848 19.1997 19.3927 19.5974 20.0734 20.2751 20.4175 20.5199 20.6021 20.6847 20.7676 20.9763 21.0604 21.1448 21.2296 21.336 21.4859 21.7018 22.0011 22.1557 22.3788 22.5356 24.6578 27.8852 31.693 33.0855 34.5392 35.698 36.0568 36.4191 36.8588 37.3038 37.7919 38.7874 39.7295 41.227 42.1441 43.1246 44.1721 45.2904 46.2053 47.5173 49.2591 51.7847 52.9895 54.06 57.0595 59.925 62.3083 63.6306 64.5923 65.046 65.5029 65.8312 66.1612 66.4929 66.8261 69.0682 71.8869 73.5595 76.3322 79.3679 83.9393 88.7741 93.3256 97.3287 100.594 101.098 101.605 102.115 103.038 105.646 110.288 112.854 115.48 116.524 117.577 120.554 126.229 132.701 139.504 146.657 154.176 163.056 167.519 175.229 183.295 184.952 186.251 187.559 188.877 190.204 193.078 195.996 200.958 212.108 224.325 235.59 241.796 256.748 268.297 276.468 284.888 288.327 295.922 319.928 335.323 353.575 371.703 390.76 419.094 453.999 501.746 539.204 577.146 592.941 600.099 612.834 646.837 677.287 748.517 832.218 909.681 982.494 1064.32 1134.67 1343.58 1586.2 1811.83 2084.1 2397.29 2700.24 2996.18 3481.07 4097.35 5004.51 6112.52 7465.85 9118.81 11137.7 13603.7 14899.7 16200.5 18584.7 22699.4 24999.1 26100.1 27394.4 29281.0 33459.6 36978.6 40867.7 49915.9 55165.6 67379.4 82297.4 94664.5 115624.0 122773.0 140000.0 164999.0 195008.0 230014.0 267826.0 320646.0 383884.0 412501.0 456021.0 494002.0 578443.0 706511.0 860006.0 951119.0 1051150.0 1162050.0 1286960.0 1336940.0 1405770.0 1636540.0 1901390.0 2231300.0 2725310.0 3328710.0 4065690.0 4965850.0 6065300.0 6703190.0 7408170.0 8187300.0 9048360.0 9999990.0 11618300.0 13840300.0 14918200.0 19640300.0 + + + 1 + flux + + + 2 + flux + + + + + 300 300 + 0.0 0.0 0.0 + 1.26 1.26 + + + diff --git a/tests/regression_tests/statepoint_latest/settings.xml b/tests/regression_tests/statepoint_latest/settings.xml index 5eff8fb0917..d51065a9c66 100644 --- a/tests/regression_tests/statepoint_latest/settings.xml +++ b/tests/regression_tests/statepoint_latest/settings.xml @@ -1,7 +1,9 @@ - + + -2 + eigenvalue 4 diff --git a/tests/regression_tests/statepoint_latest/test_flux_spectrum.py b/tests/regression_tests/statepoint_latest/test_flux_spectrum.py deleted file mode 100644 index 0015e23f69d..00000000000 --- a/tests/regression_tests/statepoint_latest/test_flux_spectrum.py +++ /dev/null @@ -1,68 +0,0 @@ -import openmc.examples -import numpy as np -import matplotlib.pyplot as plt - -model = openmc.examples.pwr_pin_cell() - -model.tallies - -# Create equal-lethargy energies to put in filter -energies = np.logspace(np.log10(1e-5), np.log10(20.0e6), 501) -e_filter = openmc.EnergyFilter(energies) - -# Create tally with energy filter -tally = openmc.Tally() -tally.filters = [e_filter] -tally.scores = ['flux'] - -# Set model tallies -model.tallies = [tally] - -openmc.mgxs.GROUP_STRUCTURES.keys() - -# Create energy filter using SHEM-361 group structure -energies_shem = openmc.mgxs.GROUP_STRUCTURES['SHEM-361'] -shem_filter = openmc.EnergyFilter(openmc.mgxs.GROUP_STRUCTURES['SHEM-361']) - -tally_shem = openmc.Tally() -tally_shem.filters = [shem_filter] -tally_shem.scores = ['flux'] - -model.tallies.append(tally_shem) - -model.settings.particles = 10000 -model.settings.batches = 50 - -model.settings.statepoint = {"overwrite_latest" : "2"} - -print(model.settings.statepoint) -sp_path = model.run(output=True) - -with openmc.StatePoint(sp_path) as sp: - t = sp.tallies[tally.id] - flux500_mean = t.mean.ravel() - flux500_unc = t.std_dev.ravel() - - t_shem = sp.tallies[tally_shem.id] - flux_shem_mean = t_shem.mean.ravel() - flux_shem_unc = t_shem.std_dev.ravel() - - fig, ax = plt.subplots() - -ax.step(energies[:-1], flux500_mean/np.diff(energies), where='post', label='500 group') -ax.step(energies_shem[:-1], flux_shem_mean/np.diff(energies_shem), where='post', label='SHEM-361') -ax.set_xscale('log') -ax.set_yscale('log') -ax.set_xlabel('Energy [eV]') -ax.set_ylabel('Flux [n-cm/eV-src]') -ax.grid() -ax.legend() - - -fig, ax = plt.subplots() -ax.loglog(energies[:-1], flux500_mean, '.', color='C0', label='500 group') -ax.loglog(energies_shem[:-1], flux_shem_mean, '.', color='C1', label='SHEM-361') -ax.set_xlabel('Energy [eV]') -ax.set_ylabel('Flux [n-cm/src]') -ax.grid() -ax.legend() \ No newline at end of file diff --git a/tests/regression_tests/statepoint_latest/test_pyapi.py b/tests/regression_tests/statepoint_latest/test_pyapi.py index 6afc2d27e17..a9b6634331c 100644 --- a/tests/regression_tests/statepoint_latest/test_pyapi.py +++ b/tests/regression_tests/statepoint_latest/test_pyapi.py @@ -11,7 +11,7 @@ def _test_output_created(self): """Make sure running statepoint files have been created/rotated.""" # Call parent checks (writes inputs, etc.) super()._test_output_created() - # Expect two rotating running files + # Expect two running files (batches 3 and 4 when -2 is specified) running = glob.glob(os.path.join(os.getcwd(), 'statepoint.running.*.h5')) assert len(running) == 2, 'Expected 2 running statepoint files to exist.' @@ -35,8 +35,8 @@ def test_statepoint_latest_pyapi(): model.settings.inactive = 0 model.settings.particles = 100 - # Use Python API to enable rotating running statepoints (keep 2) - model.settings.statepoint = {'overwrite_latest': 2} + # Use Python API to keep running statepoints for last 2 batches (using negative batch number) + model.settings.statepoint = {'batches': -2} harness = StatepointLatestPyAPITestHarness('statepoint.4.h5', model) harness.main() From 336e0ebe94283e6277612910327e275c97224a86 Mon Sep 17 00:00:00 2001 From: pranav Date: Tue, 25 Nov 2025 07:29:05 +0000 Subject: [PATCH 5/7] update comments --- src/simulation.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 21d71410f9e..c3c5f8dbdfc 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -445,9 +445,10 @@ void finalize_batch() } } - // Write running statepoints if user specified negative batch(es) in statepoint_batch. - // A negative value -N means "keep the last N completed batches as running statepoints". - // Check if there's a negative batch number in statepoint_batch. + // Write running statepoints if user specified negative batch(es) in + // statepoint_batch. A negative value -N means "keep the last N completed + // batches as running statepoints". Check if there's a negative batch number + // in statepoint_batch. int keep_last_n = 0; for (int b : settings::statepoint_batch) { if (b < 0) { @@ -476,8 +477,8 @@ void finalize_batch() std::string filename = sp_path.string(); openmc_statepoint_write(filename.c_str(), &b); - // Prune older running statepoint files so that at most `keep_last_n` remain. - // Match files like `statepoint.running..h5`. + // Prune older running statepoint files so that at most `keep_last_n` + // remain. Match files like `statepoint.running..h5`. try { std::vector> files; // pair(batchnum, path) static const std::regex re(R"(^statepoint\.running\.(\d+)\.h5$)"); @@ -505,7 +506,8 @@ void finalize_batch() fs::remove(files[i].second, ec); } } catch (...) { - // On any filesystem/regex error, ignore pruning so simulation can continue + // On any filesystem/regex error, ignore pruning so simulation can + // continue } } From cce0a29d700e80f7a8da6ef55e0b65bbd5249d2b Mon Sep 17 00:00:00 2001 From: pranav Date: Tue, 25 Nov 2025 08:31:09 +0000 Subject: [PATCH 6/7] fixes tests --- .../statepoint_latest/geometry.xml | 14 ----- .../statepoint_latest/materials.xml | 6 -- .../statepoint_latest/model.xml | 24 ++------ .../statepoint_latest/settings.xml | 19 ------ .../statepoint_latest/test.py | 48 +++++++++++---- .../statepoint_latest/test_pyapi.py | 61 ++++++++----------- 6 files changed, 68 insertions(+), 104 deletions(-) delete mode 100644 tests/regression_tests/statepoint_latest/geometry.xml delete mode 100644 tests/regression_tests/statepoint_latest/materials.xml delete mode 100644 tests/regression_tests/statepoint_latest/settings.xml diff --git a/tests/regression_tests/statepoint_latest/geometry.xml b/tests/regression_tests/statepoint_latest/geometry.xml deleted file mode 100644 index 90893d590dd..00000000000 --- a/tests/regression_tests/statepoint_latest/geometry.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - -10 -10 -10 10 10 10 - - - - - 1 - 1 - - - diff --git a/tests/regression_tests/statepoint_latest/materials.xml b/tests/regression_tests/statepoint_latest/materials.xml deleted file mode 100644 index d6a07b746fa..00000000000 --- a/tests/regression_tests/statepoint_latest/materials.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/tests/regression_tests/statepoint_latest/model.xml b/tests/regression_tests/statepoint_latest/model.xml index 2dec6b033ce..cc1a595ef99 100644 --- a/tests/regression_tests/statepoint_latest/model.xml +++ b/tests/regression_tests/statepoint_latest/model.xml @@ -38,9 +38,9 @@ eigenvalue - 10000 - 50 - 5 + 1000 + 5 + 1 -0.63 -0.63 -1 0.63 0.63 1 @@ -50,25 +50,9 @@ - 5 10 15 -3 + -2 - - - 1e-05 1.058283585339154e-05 1.1199641469982942e-05 1.1852396729366596e-05 1.254319690561614e-05 1.3274259392890431e-05 1.4047930823030026e-05 1.4866694597992626e-05 1.5733178861305835e-05 1.6650164934124926e-05 1.7620596242973984e-05 1.864758776782813e-05 1.9734436040863702e-05 2.0884629727971414e-05 2.2101860826998267e-05 2.3390036518662722e-05 2.4753291708184126e-05 2.6196002297883046e-05 2.772279923335633e-05 2.933858336831388e-05 3.1048541195790885e-05 3.2858161496232e-05 3.477325295588533e-05 3.679996281205958e-05 3.894479658509395e-05 4.121463896037726e-05 4.3616775887446824e-05 4.6158917967101585e-05 4.884922520160005e-05 5.169633318738906e-05 5.4709380834437574e-05 5.789803970115379e-05 6.127254503904571e-05 6.484372864677595e-05 6.862305363906924e-05 7.262265124207526e-05 7.685535973329837e-05 8.133476565108544e-05 8.607524740595039e-05 9.109202143372388e-05 9.640119103867236e-05 0.00010201979808337089 0.00010796587769124629 0.00011425851613738057 0.00012091791211339856 0.00012796544156309215 0.00013542372629689713 0.00014331670660546855 0.00015166971810543468 0.00016050957306399803 0.00016986464646342474 0.00017976496708168094 0.0001902423138815763 0.00020133031801781103 0.0002130645707893609 0.0002254827378837128 0.00023862468027966425 0.0002525325821967724 0.00026725108650215454 0.0002828274380092842 0.00029931163512875256 0.0003167565903577809 0.00033521830012363805 0.0003547560245261398 0.00037543247755618766 0.0003973140284009237 0.00042047091448167196 0.00044497746690849656 0.00047091234907505796 0.000498358809159635 0.0005274049473428096 0.0005581439985995563 0.0005906746319734701 0.0006251012672937686 0.0006615344103516975 0.0007000910076122174 0.0007408948215995583 0.0007840768281615934 0.0008297756368882018 0.0008781379360331252 0.0009293189633674604 0.0009834830044761819 0.0010408039200971756 0.0011014657041954854 0.0011656630745641142 0.0012336020978471708 0.0013055008509916058 0.0013815901212507115 0.0014621141469863593 0.001547331401647823 0.0016375154234437147 0.0017329556933701772 0.0018339585644136886 0.001940848244911166 0.002053967839223793 0.0021736804490650684 0.0023003703390182023 0.0024344441699840256 0.002576332304518695 0.0027264901882511298 0.002885399811814427 0.0030535712579838918 0.0032315443389877804 0.003419890329246434 0.0036192137991016156 0.003830154555422194 0.0040533896953152924 0.004289635779535044 0.0045396511325654625 0.004804238276760328 0.0050842465083535135 0.0053805746236084305 0.005694173803857192 0.0060260506686902774 0.006377270507096952 0.0067489606969282 0.00714231432365821 0.007558594010060197 0.007999135969089558 0.008465354292983484 0.00895874549234475 0.009480893299779585 0.01003347375350869 0.010618260577269476 0.011237130873778135 0.011892071150027212 0.012585183693759112 0.013318693321583235 0.01409495452039775 0.014916459005038846 0.015785843716417003 0.016705899285813338 0.017679578992505333 0.018710008243475327 0.019800494605630194 0.020954538422734877 0.022175844051138923 0.023468331750361223 0.024836150266700975 0.02628369015026629 0.027815597848167187 0.029436790619110424 0.031152472317270117 0.03296815009609936 0.0348896520856994 0.036923146100489614 0.039075159437227544 0.041352599826928196 0.04376277760793685 0.04631342919132745 0.04901274189394899 0.05186938021883089 0.054892513667304084 0.058091846172113076 0.06147764724599442 0.06506078494570668 0.0688527607573221 0.07286574651475776 0.0771126234700518 0.0816070236407946 0.08636337356743712 0.09139694062093207 0.09672388200934963 0.10236129664077592 0.10832728000896481 0.11464098227792573 0.12132266975188563 0.12839378992794365 0.13587704034022635 0.14379644141652728 0.1521774135812941 0.16104685885245115 0.17043324719398067 0.18036670790144016 0.1908791263137556 0.2020042461617265 0.21377777788176494 0.22623751324255145 0.23942344665254167 0.2533779035377089 0.26814567620160484 0.28377416760382623 0.3003135435184112 0.3178168935605702 0.3363404015986318 0.35594352609821095 0.3766891909774754 0.3986439875861479 0.4218783884565657 0.4464669735129178 0.4724886694647717 0.500027003153305 0.529170369663472 0.5600123160627045 0.5926518416769212 0.6271937158677048 0.6637488143306611 0.7024344749944644 0.743374874662968 0.7867014276093683 0.8325532074018732 0.8810773933148666 0.9324297427585329 0.986775091243365 1.044287881484397 1.105152723343537 1.1695649864073283 1.2377314271022861 1.309870852360755 1.3862148219675905 1.4670083918421386 1.5525109006413247 1.64299680220882 1.7387565465423147 1.8400975121067225 1.9473449924859594 2.060843240540289 2.180956573420937 2.3080705419889047 2.4425931683916975 2.5849562557704893 2.735616774301668 2.8950583280219 3.0637927071449926 3.2423615308533487 3.4313379858372293 3.6313286661622537 3.8429755203710374 4.066957912068862 4.303994800607666 4.554847048868157 4.820319865547658 5.101265389793324 5.398585426477014 5.7132343408917885 6.04622212216174 6.398617625198233 6.771552001609089 7.16622233057339 7.5838954613367 8.025912079660738 8.493691011280191 8.988733776180544 9.512629408315497 10.06705955623478 10.653803880994934 11.27474576867951 11.931878375865606 12.627311027441774 13.363275987313688 14.142135623730951 14.966389992234562 15.838684860566024 16.76182020129679 17.73875917943859 18.772637663884094 19.8667742932081 21.02468112814 22.250074924900446 23.546889065588402 24.919286183914213 26.371671526805173 27.90870709477386 29.53532660643756 31.25675133522358 33.078506869094795 35.00644084709147 37.04674172962297 39.205958662759045 41.491022500283215 43.90926805098722 46.46845762261672 49.17680593804336 52.04300650364032 55.07625951450127 58.28630138607608 61.683436007015096 65.2785678135422 69.08323679152052 73.10965551856394 77.3707483650963 81.88019298018759 86.65246419533474 91.70288048711151 97.0476531478281 102.70393832203415 108.68989207589362 115.0247286762024 121.72878226611486 128.82357213555298 136.33187179581014 144.27778208006782 152.6868085044753 161.58594313810892 171.0037512446062 180.97046297358665 191.5180703961739 202.68043019609937 214.49337234601015 226.99481511782128 240.22488679628626 254.22605448646618 269.0432604285645 284.72406625767695 301.3188056715166 318.88074599616436 337.4662591684448 357.1350026837739 377.9501110902926 399.97839864396553 423.2905737751492 447.96146605503253 474.0702663905035 501.7007812184298 530.9417015152933 561.8868874856754 594.6356698433983 629.293168652421 665.9706307509209 704.7857868416609 745.8632293948696 789.3348125766423 835.3400754866182 884.0266900634576 935.550935095859 990.0781978606437 1047.7835049980924 1108.8520843286042 1173.4799594140666 1241.8745787723658 1314.255481764768 1390.8550032936585 1471.9190195725107 1557.7077373620855 1648.4965292060924 1744.576817347371 1846.2570091619498 1953.8634871134473 2067.7416564056766 2188.2570536961243 2315.796520429223 2450.7694445557804 2593.609074624133 2744.773910461387 2904.749174908452 3074.0483713330595 3253.2149319203436 3442.8239620315253 3643.4840862302663 3855.8394019019247 4080.5715467367395 4318.401886713502 4570.093831606522 4836.455285448892 5118.341239817365 5416.656518263159 5732.358680698247 6066.461097059349 6420.036200116452 6794.218927866411 7190.210366561592 7609.28160606757 8052.777819924446 8522.12258320924 9018.822442058465 9544.471749518836 10100.757783249082 10689.46616149918 11312.486574752893 11971.81885143056 12669.579377122778 13408.007887960524 14189.47465992649 15016.488117186047 15891.702883858483 16817.928305074292 17798.137464670897 18835.476728470996 19933.275843778443 21095.058627508242 22324.554277259027 23625.709341636273 25002.700388247526 26459.94741003519 28002.12801297756 29634.192430699786 31361.37941419145 33189.23304763199 35123.62054430465 37170.751079718764 39337.19572239392 41629.90852628314 44056.24885253585 46624.004992255526 49341.41916607489 52217.21398079567 55260.62042801835 58481.40751462922 61889.913620261825 65497.07968238135 69314.48431551465 73354.38097735753 77629.73730105201 82154.2767198938 86942.52251807437 92009.84444885768 97372.50806983514 103047.72695361062 109053.71794151698 115409.75961771364 122136.25419136364 129254.79298553085 136788.22574299743 144760.73397148054 153197.90856366622 162126.8319412162 171576.1649864291 181576.23904058 192159.15326426385 203358.87767224156 215211.36217352576 227754.65196672245 241029.00966101373 255077.04451480263 269943.84920684085 285677.14457886684 302327.4328143757 319948.159545179 338595.88540613494 358330.4675886905 379215.2519760141 401317.27647646767 424707.4862080598 449460.96122464386 475657.1575147995 503380.16204699164 532718.9626796957 563767.7338028221 596626.1386273791 631399.6490936391 668199.8844246987 707144.9694121798 748359.9135840663 791977.0124718435 838136.2722648961 886985.8592152856 938682.5752354843 993392.3612155964 1051290.829675767 1112563.8284634468 1177408.0373049495 1246031.5991262207 1318654.7881691733 1395510.7170483125 1476846.0850171046 1562921.969845991 1654014.6658539518 1750416.570783459 1852437.1243657821 1960403.801589176 2074663.1638582961 2195581.9714190345 2323548.360619339 2458973.0897851423 2602290.857710323 2753961.698992977 2914472.460697001 3084338.365078642 3264104.663394523 3454348.386099415 3655680.195051801 3868746.3436727654 4094230.751349748 4332857.198744221 4585391.651049606 4852644.716656989 5135474.249120872 5434788.100776524 5751547.036848338 6086767.819402659 6441526.471044417 6816961.728833966 7214278.699510192 7634752.727753518 8079733.489904895 8550649.326281369 9049011.825994886 9576420.678990252 10134568.810877798 10725247.817042144 11350353.713470269 12011893.022758922 12711989.214835651 13452889.523068901 14236972.157644926 15066753.93936615 15944898.378375264 16874224.2237354 17857714.511311464 18898526.13899377 20000000.000000004 - - - 0.0 0.0024999 0.00455602 0.00714526 0.0104505 0.01483 0.0200104 0.0249394 0.0292989 0.0343998 0.0402999 0.0473019 0.0554982 0.0651999 0.0764969 0.0897968 0.104298 0.119995 0.137999 0.161895 0.190005 0.20961 0.231192 0.254997 0.279989 0.305012 0.325008 0.352994 0.390001 0.431579 0.475017 0.520011 0.55499 0.594993 0.624999 0.719999 0.800371 0.880024 0.919978 0.944022 0.96396 0.981959 0.996501 1.00904 1.02101 1.03499 1.07799 1.09198 1.10395 1.11605 1.12997 1.14797 1.16999 1.21397 1.25094 1.29304 1.33095 1.38098 1.41001 1.44397 1.51998 1.58803 1.66895 1.77997 1.90008 1.98992 2.0701 2.15695 2.21709 2.27299 2.33006 2.46994 2.55 2.59009 2.62005 2.64004 2.70012 2.7199 2.74092 2.77512 2.88405 3.14211 3.54307 3.71209 3.88217 4.0 4.21983 4.30981 4.4198 4.76785 4.93323 5.10997 5.21008 5.32011 5.38003 5.41025 5.48817 5.53004 5.61979 5.72015 5.80021 5.96014 6.05991 6.16011 6.28016 6.35978 6.43206 6.48178 6.51492 6.53907 6.55609 6.57184 6.58829 6.60611 6.63126 6.71668 6.74225 6.75981 6.77605 6.79165 6.8107 6.83526 6.87021 6.91778 6.99429 7.13987 7.38015 7.60035 7.73994 7.83965 7.97008 8.13027 8.30032 8.52407 8.67369 8.80038 8.97995 9.14031 9.50002 10.5793 10.8038 11.0529 11.2694 11.5894 11.7094 11.8153 11.9795 12.1302 12.3086 12.4721 12.6 13.3297 13.546 14.0496 14.2505 14.4702 14.5952 14.7301 14.8662 15.7792 16.0498 16.5501 16.8305 17.4457 17.5648 17.759 17.9591 19.0848 19.1997 19.3927 19.5974 20.0734 20.2751 20.4175 20.5199 20.6021 20.6847 20.7676 20.9763 21.0604 21.1448 21.2296 21.336 21.4859 21.7018 22.0011 22.1557 22.3788 22.5356 24.6578 27.8852 31.693 33.0855 34.5392 35.698 36.0568 36.4191 36.8588 37.3038 37.7919 38.7874 39.7295 41.227 42.1441 43.1246 44.1721 45.2904 46.2053 47.5173 49.2591 51.7847 52.9895 54.06 57.0595 59.925 62.3083 63.6306 64.5923 65.046 65.5029 65.8312 66.1612 66.4929 66.8261 69.0682 71.8869 73.5595 76.3322 79.3679 83.9393 88.7741 93.3256 97.3287 100.594 101.098 101.605 102.115 103.038 105.646 110.288 112.854 115.48 116.524 117.577 120.554 126.229 132.701 139.504 146.657 154.176 163.056 167.519 175.229 183.295 184.952 186.251 187.559 188.877 190.204 193.078 195.996 200.958 212.108 224.325 235.59 241.796 256.748 268.297 276.468 284.888 288.327 295.922 319.928 335.323 353.575 371.703 390.76 419.094 453.999 501.746 539.204 577.146 592.941 600.099 612.834 646.837 677.287 748.517 832.218 909.681 982.494 1064.32 1134.67 1343.58 1586.2 1811.83 2084.1 2397.29 2700.24 2996.18 3481.07 4097.35 5004.51 6112.52 7465.85 9118.81 11137.7 13603.7 14899.7 16200.5 18584.7 22699.4 24999.1 26100.1 27394.4 29281.0 33459.6 36978.6 40867.7 49915.9 55165.6 67379.4 82297.4 94664.5 115624.0 122773.0 140000.0 164999.0 195008.0 230014.0 267826.0 320646.0 383884.0 412501.0 456021.0 494002.0 578443.0 706511.0 860006.0 951119.0 1051150.0 1162050.0 1286960.0 1336940.0 1405770.0 1636540.0 1901390.0 2231300.0 2725310.0 3328710.0 4065690.0 4965850.0 6065300.0 6703190.0 7408170.0 8187300.0 9048360.0 9999990.0 11618300.0 13840300.0 14918200.0 19640300.0 - - - 1 - flux - - - 2 - flux - - 300 300 diff --git a/tests/regression_tests/statepoint_latest/settings.xml b/tests/regression_tests/statepoint_latest/settings.xml deleted file mode 100644 index d51065a9c66..00000000000 --- a/tests/regression_tests/statepoint_latest/settings.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - -2 - - - eigenvalue - 4 - 0 - 100 - - - - -1 -1 -1 1 1 1 - - - - diff --git a/tests/regression_tests/statepoint_latest/test.py b/tests/regression_tests/statepoint_latest/test.py index f80ca149db2..3c5bf754df8 100644 --- a/tests/regression_tests/statepoint_latest/test.py +++ b/tests/regression_tests/statepoint_latest/test.py @@ -1,18 +1,44 @@ import os import glob -from tests.testing_harness import TestHarness +import tempfile - -class StatepointLatestTestHarness(TestHarness): - def _test_output_created(self): - """Make sure running statepoint files have been created/rotated.""" - TestHarness._test_output_created(self) - # Expect two rotating running files - running = glob.glob(os.path.join(os.getcwd(), 'statepoint.running.*.h5')) - assert len(running) == 2, 'Expected 2 running statepoint files to exist.' +import openmc def test_statepoint_latest(): - harness = TestHarness('statepoint.4.h5') - harness.main() + """Test that negative batch numbers keep running statepoints for last N batches.""" + # Create a temporary directory for this test + with tempfile.TemporaryDirectory() as tmpdir: + # Save current directory and change to temp + original_dir = os.getcwd() + os.chdir(tmpdir) + + try: + # Copy all supporting files from the test directory into temp + import shutil + src_dir = os.path.dirname(__file__) + for fname in os.listdir(src_dir): + if fname == '__pycache__': + continue + src_path = os.path.join(src_dir, fname) + # copy files (XML, HDF5, etc.) into the temp working dir + if os.path.isfile(src_path): + shutil.copy(src_path, fname) + + # Load the model from the single `model.xml` file + model = openmc.Model.from_model_xml('model.xml') + + # Run the model + model.run(output=False) + + # Check that exactly 2 running statepoint files exist + running = sorted(glob.glob('statepoint.running.*.h5')) + assert len(running) == 2, f'Expected 2 running statepoint files, found {len(running)}: {running}' + + # Verify the batch numbers are 4 and 5 (last 2 of 5 batches) + batch_nums = sorted([int(f.split('.')[2]) for f in running]) + assert batch_nums == [4, 5], f'Expected batches [4, 5], found {batch_nums}' + + finally: + os.chdir(original_dir) diff --git a/tests/regression_tests/statepoint_latest/test_pyapi.py b/tests/regression_tests/statepoint_latest/test_pyapi.py index a9b6634331c..51c1e87607a 100644 --- a/tests/regression_tests/statepoint_latest/test_pyapi.py +++ b/tests/regression_tests/statepoint_latest/test_pyapi.py @@ -2,41 +2,34 @@ import glob import openmc - -from tests.testing_harness import PyAPITestHarness - - -class StatepointLatestPyAPITestHarness(PyAPITestHarness): - def _test_output_created(self): - """Make sure running statepoint files have been created/rotated.""" - # Call parent checks (writes inputs, etc.) - super()._test_output_created() - # Expect two running files (batches 3 and 4 when -2 is specified) - running = glob.glob(os.path.join(os.getcwd(), 'statepoint.running.*.h5')) - assert len(running) == 2, 'Expected 2 running statepoint files to exist.' +import openmc.examples def test_statepoint_latest_pyapi(): - # Build a minimal model programmatically - model = openmc.Model() - - mat = openmc.Material() - mat.set_density('g/cm3', 1.0) - mat.add_nuclide('H1', 1.0) - model.materials.append(mat) - - # Simple box cell - box = openmc.model.RectangularParallelepiped(-10, 10, -10, 10, -10, 10) - cell = openmc.Cell(fill=mat, region=box) - model.geometry = openmc.Geometry([cell]) - - # Settings: small run to exercise feature - model.settings.batches = 4 - model.settings.inactive = 0 - model.settings.particles = 100 - - # Use Python API to keep running statepoints for last 2 batches (using negative batch number) + """Test that negative batch numbers keep running statepoints for last N batches.""" + # Use the PWR pin cell example model which is known to work + model = openmc.examples.pwr_pin_cell() + + # Set batches: need at least 1 inactive + 1 active + model.settings.inactive = 1 + model.settings.batches = 5 + model.settings.particles = 1000 + + # Enable running statepoints: keep last 2 batches model.settings.statepoint = {'batches': -2} - - harness = StatepointLatestPyAPITestHarness('statepoint.4.h5', model) - harness.main() + + # Run the model + sp_path = model.run(output=False) + + # Check that exactly 2 running statepoint files exist + running = sorted(glob.glob('statepoint.running.*.h5')) + assert len(running) == 2, f'Expected 2 running statepoint files, found {len(running)}: {running}' + + # Verify the batch numbers are 4 and 5 (last 2 of 5 batches) + batch_nums = sorted([int(f.split('.')[2]) for f in running]) + assert batch_nums == [4, 5], f'Expected batches [4, 5], found {batch_nums}' + + # Clean up the running statepoint files + for f in running: + if os.path.exists(f): + os.remove(f) From 7ded6df6559e3085c78545abac5a804096f55153 Mon Sep 17 00:00:00 2001 From: pranav Date: Tue, 25 Nov 2025 10:31:38 +0000 Subject: [PATCH 7/7] remove references to stale key 'overwrite_latest' --- openmc/settings.py | 41 ++++------------------------------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index a8463222584..a35f33fcd5b 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -262,11 +262,8 @@ class Settings: Options for writing state points. Acceptable keys are: :batches: list of batches at which to write statepoint files - :overwrite_latest: bool or int - If present and true, write a continuously-overwritten running - statepoint file after every batch. If an integer N > 1 is - provided, keep N rotating running statepoint files named - ``statepoint.running.1.h5`` ... ``statepoint.running.N.h5``. + Positive integers write statepoints at specified batches. + Negative integer -N keeps running statepoints for the last N batches. surf_source_read : dict Options for reading surface source points. Acceptable keys are: @@ -1496,15 +1493,6 @@ def _create_statepoint_subelement(self, root): subelement = ET.SubElement(element, "batches") subelement.text = ' '.join( str(x) for x in self._statepoint['batches']) - # Overwrite latest / running statepoints - if 'overwrite_latest' in self._statepoint: - val = self._statepoint['overwrite_latest'] - subelement = ET.SubElement(element, "overwrite_latest") - # If boolean, write lowercase true/false, else write integer - if isinstance(val, bool): - subelement.text = str(val).lower() - else: - subelement.text = str(val) def _create_uniform_source_sampling_subelement(self, root): if self._uniform_source_sampling is not None: @@ -1528,11 +1516,6 @@ def _create_sourcepoint_subelement(self, root): subelement = ET.SubElement(element, "write") subelement.text = str(self._sourcepoint['write']).lower() - # Overwrite latest subelement - if 'overwrite' in self._sourcepoint: - subelement = ET.SubElement(element, "overwrite_latest") - subelement.text = str(self._sourcepoint['overwrite']).lower() - if 'mcpl' in self._sourcepoint: subelement = ET.SubElement(element, "mcpl") subelement.text = str(self._sourcepoint['mcpl']).lower() @@ -2025,29 +2008,13 @@ def _statepoint_from_xml_element(self, root): batches = get_elem_list(elem, "batches", int) if batches is not None: self.statepoint['batches'] = batches - # Read overwrite_latest which may be boolean or integer - val = get_text(elem, 'overwrite_latest') - if val is not None: - if val in ('true', '1'): - self.statepoint['overwrite_latest'] = 1 - elif val in ('false', '0'): - self.statepoint['overwrite_latest'] = 0 - else: - try: - self.statepoint['overwrite_latest'] = int(val) - except ValueError: - raise ValueError( - "Invalid value for : '" + val + - "'. Provide true/false or an integer.") def _sourcepoint_from_xml_element(self, root): elem = root.find('source_point') if elem is not None: - for key in ('separate', 'write', 'overwrite_latest', 'batches', 'mcpl'): - if key in ('separate', 'write', 'mcpl', 'overwrite_latest'): + for key in ('separate', 'write', 'batches', 'mcpl'): + if key in ('separate', 'write', 'mcpl'): value = get_text(elem, key) in ('true', '1') - if key == 'overwrite_latest': - key = 'overwrite' else: value = get_elem_list(elem, key, int) if value is not None: