Skip to content

Commit

Permalink
Write surface source files per batch (#3124)
Browse files Browse the repository at this point in the history
Co-authored-by: Patrick Shriwise <[email protected]>
Co-authored-by: Paul Romano <[email protected]>
  • Loading branch information
3 people authored Oct 3, 2024
1 parent b54de4d commit 9686851
Show file tree
Hide file tree
Showing 14 changed files with 167 additions and 48 deletions.
9 changes: 9 additions & 0 deletions docs/source/io_formats/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,15 @@ attributes/sub-elements:

*Default*: None

:max_source_files:
An integer value indicating the number of surface source files to be written
containing the maximum number of particles each. The surface source bank
will be cleared in simulation memory each time a surface source file is
written. By default a ``surface_source.h5`` file will be created when the
maximum number of saved particles is reached.

*Default*: 1

:mcpl:
An optional boolean which indicates if the banked particles should be
written to a file in the MCPL_-format instead of the native HDF5-based
Expand Down
11 changes: 11 additions & 0 deletions docs/source/usersguide/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,17 @@ or particles going to a cell::
.. note:: The ``cell``, ``cellfrom`` and ``cellto`` attributes cannot be
used simultaneously.

To generate more than one surface source files when the maximum number of stored
particles is reached, ``max_source_files`` is available. The surface source bank
will be cleared in simulation memory each time a surface source file is written.
As an example, to write a maximum of three surface source files:::

settings.surf_source_write = {
'surfaces_ids': [1, 2, 3],
'max_particles': 10000,
'max_source_files': 3
}

.. _compiled_source:

Compiled Sources
Expand Down
15 changes: 9 additions & 6 deletions include/openmc/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,17 @@ extern std::unordered_set<int>
statepoint_batch; //!< Batches when state should be written
extern std::unordered_set<int>
source_write_surf_id; //!< Surface ids where sources will be written

extern int
max_history_splits; //!< maximum number of particle splits for weight windows
extern int64_t max_surface_particles; //!< maximum number of particles to be
//!< banked on surfaces per process
extern int64_t ssw_cell_id; //!< Cell id for the surface source
//!< write setting
extern SSWCellType ssw_cell_type; //!< Type of option for the cell
//!< argument of surface source write
extern int64_t ssw_max_particles; //!< maximum number of particles to be
//!< banked on surfaces per process
extern int64_t ssw_max_files; //!< maximum number of surface source files
//!< to be created
extern int64_t ssw_cell_id; //!< Cell id for the surface source
//!< write setting
extern SSWCellType ssw_cell_type; //!< Type of option for the cell
//!< argument of surface source write
extern TemperatureMethod
temperature_method; //!< method for choosing temperatures
extern double
Expand Down
1 change: 1 addition & 0 deletions include/openmc/simulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ extern "C" int n_lost_particles; //!< cumulative number of lost particles
extern "C" bool need_depletion_rx; //!< need to calculate depletion rx?
extern "C" int restart_batch; //!< batch at which a restart job resumed
extern "C" bool satisfy_triggers; //!< have tally triggers been satisfied?
extern int ssw_current_file; //!< current surface source file
extern "C" int total_gen; //!< total number of generations simulated
extern double total_weight; //!< Total source weight in a batch
extern int64_t work_per_rank; //!< number of particles per MPI rank
Expand Down
8 changes: 6 additions & 2 deletions include/openmc/state_point.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define OPENMC_STATE_POINT_H

#include <cstdint>
#include <string>

#include <gsl/gsl-lite.hpp>

Expand Down Expand Up @@ -33,8 +34,11 @@ void load_state_point();
// values on each rank, used to create global indexing. This vector
// can be created by calling calculate_parallel_index_vector on
// source_bank.size() if such a vector is not already available.
void write_source_point(const char* filename, gsl::span<SourceSite> source_bank,
const vector<int64_t>& bank_index);
void write_h5_source_point(const char* filename,
gsl::span<SourceSite> source_bank, const vector<int64_t>& bank_index);

void write_source_point(std::string, gsl::span<SourceSite> source_bank,
const vector<int64_t>& bank_index, bool use_mcpl);

// This appends a source bank specification to an HDF5 file
// that's already open. It is used internally by write_source_point.
Expand Down
13 changes: 8 additions & 5 deletions openmc/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ class Settings:
banked (int)
:max_particles: Maximum number of particles to be banked on surfaces per
process (int)
:max_source_files: Maximum number of surface source files to be created (int)
:mcpl: Output in the form of an MCPL-file (bool)
:cell: Cell ID used to determine if particles crossing identified
surfaces are to be banked. Particles coming from or going to this
Expand Down Expand Up @@ -718,19 +719,21 @@ def surf_source_write(self, surf_source_write: dict):
cv.check_value(
"surface source writing key",
key,
("surface_ids", "max_particles", "mcpl", "cell", "cellfrom", "cellto"),
("surface_ids", "max_particles", "max_source_files", "mcpl", "cell", "cellfrom", "cellto"),
)
if key == "surface_ids":
cv.check_type(
"surface ids for source banking", value, Iterable, Integral
)
for surf_id in value:
cv.check_greater_than("surface id for source banking", surf_id, 0)

elif key == "mcpl":
cv.check_type("write to an MCPL-format file", value, bool)
elif key in ("max_particles", "cell", "cellfrom", "cellto"):
elif key in ("max_particles", "max_source_files", "cell", "cellfrom", "cellto"):
name = {
"max_particles": "maximum particle banks on surfaces per process",
"max_source_files": "maximun surface source files to be written",
"cell": "Cell ID for source banking (from or to)",
"cellfrom": "Cell ID for source banking (from only)",
"cellto": "Cell ID for source banking (to only)",
Expand Down Expand Up @@ -1251,7 +1254,7 @@ def _create_surf_source_write_subelement(self, root):
if "mcpl" in self._surf_source_write:
subelement = ET.SubElement(element, "mcpl")
subelement.text = str(self._surf_source_write["mcpl"]).lower()
for key in ("max_particles", "cell", "cellfrom", "cellto"):
for key in ("max_particles", "max_source_files", "cell", "cellfrom", "cellto"):
if key in self._surf_source_write:
subelement = ET.SubElement(element, key)
subelement.text = str(self._surf_source_write[key])
Expand Down Expand Up @@ -1650,14 +1653,14 @@ def _surf_source_write_from_xml_element(self, root):
elem = root.find('surf_source_write')
if elem is None:
return
for key in ('surface_ids', 'max_particles', 'mcpl', 'cell', 'cellto', 'cellfrom'):
for key in ('surface_ids', 'max_particles', 'max_source_files', 'mcpl', 'cell', 'cellto', 'cellfrom'):
value = get_text(elem, key)
if value is not None:
if key == 'surface_ids':
value = [int(x) for x in value.split()]
elif key == 'mcpl':
value = value in ('true', '1')
elif key in ('max_particles', 'cell', 'cellfrom', 'cellto'):
elif key in ('max_particles', 'max_source_files', 'cell', 'cellfrom', 'cellto'):
value = int(value)
self.surf_source_write[key] = value

Expand Down
5 changes: 5 additions & 0 deletions src/finalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ int openmc_finalize()
settings::source_latest = false;
settings::source_separate = false;
settings::source_write = true;
settings::ssw_cell_id = C_NONE;
settings::ssw_cell_type = SSWCellType::None;
settings::ssw_max_particles = 0;
settings::ssw_max_files = 1;
settings::survival_biasing = false;
settings::temperature_default = 293.6;
settings::temperature_method = TemperatureMethod::NEAREST;
Expand All @@ -141,6 +145,7 @@ int openmc_finalize()

simulation::keff = 1.0;
simulation::need_depletion_rx = false;
simulation::ssw_current_file = 1;
simulation::total_gen = 0;

simulation::entropy_mesh = nullptr;
Expand Down
13 changes: 10 additions & 3 deletions src/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ SolverType solver_type {SolverType::MONTE_CARLO};
std::unordered_set<int> sourcepoint_batch;
std::unordered_set<int> statepoint_batch;
std::unordered_set<int> source_write_surf_id;
int64_t max_surface_particles;
int64_t ssw_max_particles;
int64_t ssw_max_files;
int64_t ssw_cell_id {C_NONE};
SSWCellType ssw_cell_type {SSWCellType::None};
TemperatureMethod temperature_method {TemperatureMethod::NEAREST};
Expand Down Expand Up @@ -799,14 +800,20 @@ void read_settings_xml(pugi::xml_node root)

// Get maximum number of particles to be banked per surface
if (check_for_node(node_ssw, "max_particles")) {
max_surface_particles =
std::stoll(get_node_value(node_ssw, "max_particles"));
ssw_max_particles = std::stoll(get_node_value(node_ssw, "max_particles"));
} else {
fatal_error("A maximum number of particles needs to be specified "
"using the 'max_particles' parameter to store surface "
"source points.");
}

// Get maximum number of surface source files to be created
if (check_for_node(node_ssw, "max_source_files")) {
ssw_max_files = std::stoll(get_node_value(node_ssw, "max_source_files"));
} else {
ssw_max_files = 1;
}

if (check_for_node(node_ssw, "mcpl")) {
surf_mcpl_write = get_node_value_bool(node_ssw, "mcpl");

Expand Down
60 changes: 34 additions & 26 deletions src/simulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ int openmc_simulation_init()
// Reset global variables -- this is done before loading state point (as that
// will potentially populate k_generation and entropy)
simulation::current_batch = 0;
simulation::ssw_current_file = 1;
simulation::k_generation.clear();
simulation::entropy.clear();
openmc_reset();
Expand Down Expand Up @@ -308,6 +309,7 @@ int n_lost_particles {0};
bool need_depletion_rx {false};
int restart_batch;
bool satisfy_triggers {false};
int ssw_current_file;
int total_gen {0};
double total_weight;
int64_t work_per_rank;
Expand Down Expand Up @@ -337,15 +339,14 @@ void allocate_banks()

if (settings::surf_source_write) {
// Allocate surface source bank
simulation::surf_source_bank.reserve(settings::max_surface_particles);
simulation::surf_source_bank.reserve(settings::ssw_max_particles);
}
}

void initialize_batch()
{
// Increment current batch
++simulation::current_batch;

if (settings::run_mode == RunMode::FIXED_SOURCE) {
if (settings::solver_type == SolverType::RANDOM_RAY &&
simulation::current_batch < settings::n_inactive + 1) {
Expand Down Expand Up @@ -439,42 +440,49 @@ void finalize_batch()
std::string source_point_filename = fmt::format("{0}source.{1:0{2}}",
settings::path_output, simulation::current_batch, w);
gsl::span<SourceSite> bankspan(simulation::source_bank);
if (settings::source_mcpl_write) {
write_mcpl_source_point(
source_point_filename.c_str(), bankspan, simulation::work_index);
} else {
write_source_point(
source_point_filename.c_str(), bankspan, simulation::work_index);
}
write_source_point(source_point_filename, bankspan,
simulation::work_index, settings::source_mcpl_write);
}

// Write a continously-overwritten source point if requested.
if (settings::source_latest) {

// note: correct file extension appended automatically
auto filename = settings::path_output + "source";
gsl::span<SourceSite> bankspan(simulation::source_bank);
if (settings::source_mcpl_write) {
write_mcpl_source_point(
filename.c_str(), bankspan, simulation::work_index);
} else {
write_source_point(filename.c_str(), bankspan, simulation::work_index);
}
write_source_point(filename.c_str(), bankspan, simulation::work_index,
settings::source_mcpl_write);
}
}

// Write out surface source if requested.
if (settings::surf_source_write &&
simulation::current_batch == settings::n_batches) {
auto filename = settings::path_output + "surface_source";
auto surf_work_index =
mpi::calculate_parallel_index_vector(simulation::surf_source_bank.size());
gsl::span<SourceSite> surfbankspan(simulation::surf_source_bank.begin(),
simulation::surf_source_bank.size());
if (settings::surf_mcpl_write) {
write_mcpl_source_point(filename.c_str(), surfbankspan, surf_work_index);
} else {
write_source_point(filename.c_str(), surfbankspan, surf_work_index);
simulation::ssw_current_file <= settings::ssw_max_files) {
bool last_batch = (simulation::current_batch == settings::n_batches);
if (simulation::surf_source_bank.full() || last_batch) {
// Determine appropriate filename
auto filename = fmt::format("{}surface_source.{}", settings::path_output,
simulation::current_batch);
if (settings::ssw_max_files == 1 ||
(simulation::ssw_current_file == 1 && last_batch)) {
filename = settings::path_output + "surface_source";
}

// Get span of source bank and calculate parallel index vector
auto surf_work_index = mpi::calculate_parallel_index_vector(
simulation::surf_source_bank.size());
gsl::span<SourceSite> surfbankspan(simulation::surf_source_bank.begin(),
simulation::surf_source_bank.size());

// Write surface source file
write_source_point(
filename, surfbankspan, surf_work_index, settings::surf_mcpl_write);

// Reset surface source bank and increment counter
simulation::surf_source_bank.clear();
if (!last_batch && settings::ssw_max_files >= 1) {
simulation::surf_source_bank.reserve(settings::ssw_max_particles);
}
++simulation::ssw_current_file;
}
}
}
Expand Down
20 changes: 18 additions & 2 deletions src/state_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "openmc/error.h"
#include "openmc/file_utils.h"
#include "openmc/hdf5_interface.h"
#include "openmc/mcpl_interface.h"
#include "openmc/mesh.h"
#include "openmc/message_passing.h"
#include "openmc/mgxs_interface.h"
Expand Down Expand Up @@ -568,8 +569,23 @@ hid_t h5banktype()
return banktype;
}

void write_source_point(const char* filename, gsl::span<SourceSite> source_bank,
const vector<int64_t>& bank_index)
void write_source_point(std::string filename, gsl::span<SourceSite> source_bank,
const vector<int64_t>& bank_index, bool use_mcpl)
{
std::string ext = use_mcpl ? "mcpl" : "h5";
write_message("Creating source file {}.{} with {} particles ...", filename,
ext, source_bank.size(), 5);

// Dispatch to appropriate function based on file type
if (use_mcpl) {
write_mcpl_source_point(filename.c_str(), source_bank, bank_index);
} else {
write_h5_source_point(filename.c_str(), source_bank, bank_index);
}
}

void write_h5_source_point(const char* filename,
gsl::span<SourceSite> source_bank, const vector<int64_t>& bank_index)
{
// When using parallel HDF5, the file is written to collectively by all
// processes. With MPI-only, the file is opened and written by the master
Expand Down
3 changes: 1 addition & 2 deletions tests/regression_tests/dagmc/legacy/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,4 @@ def test_surf_source(model):

def test_dagmc(model):
harness = PyAPITestHarness('statepoint.5.h5', model)
harness.main()

harness.main()
2 changes: 1 addition & 1 deletion tests/regression_tests/surface_source/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,4 @@ def test_surface_source_read(model):
harness = SurfaceSourceTestHarness('statepoint.10.h5',
model,
'inputs_true_read.dat')
harness.main()
harness.main()
2 changes: 1 addition & 1 deletion tests/regression_tests/surface_source_write/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1129,4 +1129,4 @@ def test_surface_source_cell_dagmc(
harness = SurfaceSourceWriteTestHarness(
"statepoint.5.h5", model=model, workdir=folder
)
harness.main()
harness.main()
Loading

0 comments on commit 9686851

Please sign in to comment.