Skip to content
Open
Show file tree
Hide file tree
Changes from 68 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
af27f73
Initial commit on progress towards clean shared secondary bank
jtramm Dec 4, 2025
c9e8b0c
working now for non-shared case again at least
jtramm Dec 4, 2025
0d6d737
seems to work just initializing the history again, though will need t…
jtramm Dec 4, 2025
ad06cbd
added single node sorting
jtramm Dec 4, 2025
bd2bee4
working towards reproducibility, but not yet there.
jtramm Dec 4, 2025
a48e618
working on consistent initialization
jtramm Dec 4, 2025
bb4fab6
cleanup of the synchronize ranks function
jtramm Dec 4, 2025
b30ac0f
appears to be working fairly well
jtramm Dec 4, 2025
b5a89fa
code cleanup
jtramm Dec 5, 2025
3ca4cf2
code comments and output cleanup
jtramm Dec 5, 2025
2f3201c
Shared sorting routine now working (finally!)
jtramm Dec 5, 2025
5ad63be
code cleanup
jtramm Dec 5, 2025
09b05e1
inter-rank order validation code
jtramm Dec 5, 2025
e65de25
fixed bug with normal eigenvalue mode (issue was counting secondary p…
jtramm Dec 5, 2025
01ccfdc
updated python SourceSite type
jtramm Dec 5, 2025
c3519a3
Merge develop into feature branch
Feb 14, 2026
76bafa4
Clean up shared secondary bank feature post-merge
Feb 14, 2026
b9d74eb
fixing a few indexing bugs
Feb 16, 2026
3fb00cb
Restore accidentally deleted AGENTS.md
Feb 16, 2026
b7492b2
simplified MPI ray exchange. Also added event-based shared secondary …
Feb 16, 2026
2b25bdc
added clear command
Feb 16, 2026
04a1114
add shared secondary toggle setting, and updated weight windows test.
Feb 19, 2026
40bd63d
correct splitting counter
Feb 19, 2026
4ebff5e
updated particle production filter to new refactored name
Feb 19, 2026
4fa0d40
fixed non void function warning
Feb 19, 2026
7df5baa
Added track rate reporting
Feb 20, 2026
a8de194
Merge branch 'develop' into another_shared_secondary_branch
Feb 26, 2026
c1225d7
updated survival biasing test results as shared secondary is used
Feb 26, 2026
1fd298e
adjusting tracks vs. particles terminology.
Feb 26, 2026
3f89fc4
added back in index check
Feb 26, 2026
1e29061
DRY for event queue processing
Feb 26, 2026
c257c8a
Merge remote-tracking branch 'upstream/develop' into another_shared_s…
Feb 26, 2026
96e65a9
bug fix with fission particles ant the particle production filter
Feb 27, 2026
7c8ca90
Add regression test for ParticleProductionFilter with fission
Feb 27, 2026
d1ee5d1
zeroing progeny per particle to prevent rare bug where particle isnt …
Feb 27, 2026
d9107c5
added clarifying comment
Feb 27, 2026
f3e4d32
changed function ordering
Feb 27, 2026
2c700c8
moved process transport function to event file
Feb 27, 2026
4236a79
adding thread bank to reduce contention
Feb 27, 2026
9079e33
ran clang format
Feb 27, 2026
6ce6cb8
Merge branch 'develop' into post_fix_shared_secondary
Mar 4, 2026
124f08c
Add regression tests for shared secondary bank feature
Mar 4, 2026
fb8fb5b
Parametrize pulse_height test for shared secondary bank
Mar 4, 2026
a3fb4ac
Parametrize weightwindows test for shared/local secondary bank
Mar 4, 2026
a97076d
Add MPI int overflow bounds check in synchronize_global_secondary_bank
Mar 4, 2026
540cc6d
Add eigenvalue guard for explicit shared_secondary_bank setting
Mar 4, 2026
2dd05f6
Fix write_restart and particle restart seed formula for shared second…
Mar 4, 2026
a57d45c
Fix n_split type mismatch between SourceSite and ParticleData
Mar 4, 2026
08a2747
Add missing wgt_born/wgt_ww_born in MG create_fission_sites
Mar 4, 2026
3c14c39
Guard track write for freshly constructed secondary particles
Mar 4, 2026
7fc2dc8
Remove dead SourceSite::current_work field
Mar 4, 2026
3d87016
Fix pulse-height tallies in shared secondary mode
Mar 4, 2026
b367041
Always call event_death for secondary particles in history-based shar…
Mar 4, 2026
6abc8b7
Propagate n_split to fission sites in create_fission_sites
Mar 4, 2026
26217b6
Fix variable shadowing of 'site' in Phase 2 transport loop
Mar 4, 2026
37b10a3
Remove redundant n_split assignment in event_revive_from_secondary
Mar 4, 2026
f815364
Add weightwindows_pulse_height regression test
Mar 4, 2026
874cb01
Add default initializers and guard unnecessary n_tracks increment
Mar 4, 2026
adfd4ca
adding non-shared ww test, and doing DRY for particle seeding
Mar 10, 2026
8dab7c7
Parametrize survival_biasing weight windows test for shared/local sec…
Mar 10, 2026
c2343b0
Merge branch 'develop' into post_fix_shared_secondary
Mar 10, 2026
038b82e
fixing write_restart issue
Mar 10, 2026
dbdda48
ran git clang format
Mar 10, 2026
e821469
final claude review comments
Mar 10, 2026
791a29b
removed spurious files
Mar 10, 2026
a94ae15
expanded mesh on test to get non-zero results
Mar 10, 2026
be226a3
disabling shared secondary mode when pulse height tallies are on
Mar 10, 2026
83a38e4
ran clang format again
Mar 11, 2026
e672783
fix track output and photon heating test for shared secondary
Mar 11, 2026
954a870
changed particle count in photon heating test
Mar 12, 2026
00a6e11
incorporation of copilot comment
Mar 12, 2026
0400083
incorporation of copilot comment
Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/source/io_formats/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1711,6 +1711,17 @@ following sub-elements/attributes:
The ``weight_windows_file`` element has no attributes and contains the path to
a weight windows HDF5 file to load during simulation initialization.

----------------------------------------
``<shared_secondary_bank>`` Element
----------------------------------------

The ``shared_secondary_bank`` element indicates whether to use a shared
secondary particle bank. When enabled, secondary particles are collected into
a global bank, sorted for reproducibility, and load-balanced across MPI ranks
between generations. If not specified, the shared secondary bank is enabled
automatically for fixed-source simulations with weight windows active, and
disabled otherwise.

-------------------------------
``<weight_windows_on>`` Element
-------------------------------
Expand Down
8 changes: 7 additions & 1 deletion include/openmc/bank.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,24 @@ extern vector<vector<double>> ifp_fission_lifetime_bank;

extern vector<int64_t> progeny_per_particle;

extern SharedArray<SourceSite> shared_secondary_bank_read;
extern SharedArray<SourceSite> shared_secondary_bank_write;

} // namespace simulation

//==============================================================================
// Non-member functions
//==============================================================================

void sort_fission_bank();
void sort_bank(SharedArray<SourceSite>& bank, bool is_fission_bank);

void free_memory_bank();

void init_fission_bank(int64_t max);

int64_t synchronize_global_secondary_bank(
SharedArray<SourceSite>& shared_secondary_bank);

} // namespace openmc

#endif // OPENMC_BANK_H
13 changes: 13 additions & 0 deletions include/openmc/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ void process_collision_events();
//! \param n_particles The number of particles in the particle buffer
void process_death_events(int64_t n_particles);

//! Process event queues until all are empty. Each iteration processes the
//! longest queue first to maximize vectorization efficiency.
void process_transport_events();

//! Initialize secondary particles from a shared secondary bank for
//! event-based transport
//
//! \param n_particles The number of particles to initialize
//! \param offset The offset index in the shared secondary bank
//! \param shared_secondary_bank The shared secondary bank to read from
void process_init_secondary_events(int64_t n_particles, int64_t offset,
SharedArray<SourceSite>& shared_secondary_bank);

} // namespace openmc

#endif // OPENMC_EVENT_H
3 changes: 2 additions & 1 deletion include/openmc/particle.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ class Particle : public ParticleData {
void event_advance();
void event_cross_surface();
void event_collide();
void event_revive_from_secondary();
void event_revive_from_secondary(SourceSite& site);
void event_check_limit_and_revive();
void event_death();

//! pulse-height recording
Expand Down
34 changes: 22 additions & 12 deletions include/openmc/particle_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,11 @@ struct SourceSite {

// Extra attributes that don't show up in source written to file
int parent_nuclide {-1};
int64_t parent_id;
int64_t progeny_id;
int64_t parent_id {0};
int64_t progeny_id {0};
double wgt_born {1.0};
double wgt_ww_born {-1.0};
int64_t n_split {0};
};

struct CollisionTrackSite {
Expand Down Expand Up @@ -533,14 +536,14 @@ class ParticleData : public GeometryState {
uint64_t seeds_[N_STREAMS];
int stream_;

vector<SourceSite> secondary_bank_;
vector<SourceSite> local_secondary_bank_;

// Keep track of how many secondary particles were created in the collision
// and what the starting index is in the secondary bank for this particle
int n_secondaries_ {0};
int secondary_bank_index_ {0};

int64_t current_work_;
int64_t current_work_ {0};

vector<double> flux_derivs_;

Expand All @@ -563,7 +566,9 @@ class ParticleData : public GeometryState {

int n_event_ {0};

int n_split_ {0};
int64_t n_tracks_ {0}; //!< number of tracks in this particle history

int64_t n_split_ {0};
double ww_factor_ {0.0};

int64_t n_progeny_ {0};
Expand Down Expand Up @@ -693,12 +698,14 @@ class ParticleData : public GeometryState {
int& stream() { return stream_; }

// secondary particle bank
SourceSite& secondary_bank(int i) { return secondary_bank_[i]; }
const SourceSite& secondary_bank(int i) const { return secondary_bank_[i]; }
decltype(secondary_bank_)& secondary_bank() { return secondary_bank_; }
decltype(secondary_bank_) const& secondary_bank() const
SourceSite& local_secondary_bank(int i) { return local_secondary_bank_[i]; }
const SourceSite& local_secondary_bank(int i) const
{
return secondary_bank_;
return local_secondary_bank_[i];
}
decltype(local_secondary_bank_)& local_secondary_bank()
{
return local_secondary_bank_;
}

// Number of secondaries created in a collision
Expand Down Expand Up @@ -747,13 +754,16 @@ class ParticleData : public GeometryState {
int& n_event() { return n_event_; }

// Number of times variance reduction has caused a particle split
int n_split() const { return n_split_; }
int& n_split() { return n_split_; }
int64_t n_split() const { return n_split_; }
int64_t& n_split() { return n_split_; }

// Particle-specific factor for on-the-fly weight window adjustment
double ww_factor() const { return ww_factor_; }
double& ww_factor() { return ww_factor_; }

// Number of tracks in this particle history
int64_t& n_tracks() { return n_tracks_; }

// Number of progeny produced by this particle
int64_t& n_progeny() { return n_progeny_; }

Expand Down
2 changes: 2 additions & 0 deletions include/openmc/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ extern int trigger_batch_interval; //!< Batch interval for triggers
extern "C" int verbosity; //!< How verbose to make output
extern double weight_cutoff; //!< Weight cutoff for Russian roulette
extern double weight_survive; //!< Survival weight after Russian roulette
extern bool
use_shared_secondary_bank; //!< Use shared bank for secondary particles

} // namespace settings

Expand Down
31 changes: 25 additions & 6 deletions include/openmc/shared_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,13 @@ class SharedArray {
//! Default constructor.
SharedArray() = default;

//! Construct a zero size container with space to hold capacity number of
//! elements.
//! Construct a container with size elements, with space to hold size number
//! of elements.
//
//! \param capacity The number of elements for the container to allocate
//! space for
SharedArray(int64_t capacity) : capacity_(capacity)
//! \param size The number of elements to allocate and initialize
SharedArray(int64_t size) : size_(size), capacity_(size)
{
data_ = make_unique<T[]>(capacity);
data_ = make_unique<T[]>(size);
}

//==========================================================================
Expand Down Expand Up @@ -97,8 +96,28 @@ class SharedArray {
capacity_ = 0;
}

//! Push back an element to the array, with capacity and reallocation behavior
//! as if this were a vector. This does not perform any thread safety checks.
//! If the size exceeds the capacity, then the capacity will double just as
//! with a vector. Data will be reallocated and moved to a new pointer and
//! copied in before the new item is appended. Old data will be freed.
void thread_unsafe_append(const T& value)
{
if (size_ == capacity_) {
int64_t new_capacity = capacity_ == 0 ? 1 : 2 * capacity_;
unique_ptr<T[]> new_data = make_unique<T[]>(new_capacity);
for (int64_t i = 0; i < size_; i++) {
new_data[i] = std::move(data_[i]);
}
data_ = std::move(new_data);
capacity_ = new_capacity;
}
data_[size_++] = value;
}

//! Return the number of elements in the container
int64_t size() { return size_; }
int64_t size() const { return size_; }

//! Resize the container to contain a specified number of elements. This is
//! useful in cases where the container is written to in a non-thread safe
Expand Down
30 changes: 26 additions & 4 deletions include/openmc/simulation.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ extern const RegularMesh* ufs_mesh;
extern vector<double> k_generation;
extern vector<int64_t> work_index;

extern int64_t
simulation_tracks_completed; //!< Number of tracks completed on this rank

} // namespace simulation

//==============================================================================
Expand All @@ -59,7 +62,7 @@ extern vector<int64_t> work_index;
void allocate_banks();

//! Determine number of particles to transport per process
void calculate_work();
void calculate_work(int64_t n_particles);

//! Initialize nuclear data before a simulation
void initialize_data();
Expand All @@ -71,7 +74,7 @@ void initialize_batch();
void initialize_generation();

//! Full initialization of a particle history
void initialize_history(Particle& p, int64_t index_source);
void initialize_history(Particle& p, int64_t index_source, bool is_secondary);

//! Finalize a batch
//!
Expand All @@ -92,16 +95,35 @@ void broadcast_results();

void free_memory_simulation();

//! Simulate a single particle history (and all generated secondary particles,
//! if enabled), from birth to death
//! Compute unique particle ID from a 1-based source index
//! \param index_source 1-based source index within this rank's work
//! \return globally unique particle ID
int64_t compute_particle_id(int64_t index_source);

//! Compute the transport RNG seed from a particle ID
//! \param particle_id the particle's globally unique ID
//! \return seed value passed to init_particle_seeds()
int64_t compute_transport_seed(int64_t particle_id);

//! Simulate a single particle history from birth to death, inclusive of any
//! secondary particles. In shared secondary mode, only a single track is
//! transported and secondaries are deposited into a shared bank instead.
void transport_history_based_single_particle(Particle& p);

//! Simulate all particle histories using history-based parallelism
void transport_history_based();

//! Simulate all particles using history-based parallelism, with a shared
//! secondary bank
void transport_history_based_shared_secondary();

//! Simulate all particle histories using event-based parallelism
void transport_event_based();

//! Simulate all particles using event-based parallelism, with a shared
//! secondary bank
void transport_event_based_shared_secondary();

} // namespace openmc

#endif // OPENMC_SIMULATION_H
5 changes: 4 additions & 1 deletion openmc/lib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ class _SourceSite(Structure):
('particle', c_int32),
('parent_nuclide', c_int),
('parent_id', c_int64),
('progeny_id', c_int64)]
('progeny_id', c_int64),
('wgt_born', c_double),
('wgt_ww_born', c_double),
('n_split', c_int64)]

# Define input type for numpy arrays that will be passed into C++ functions
# Must be an int or double array, with single dimension that is contiguous
Expand Down
31 changes: 31 additions & 0 deletions openmc/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,15 @@ class Settings:
Whether delayed neutrons are created in fission.

.. versionadded:: 0.13.3
shared_secondary_bank : bool
Whether to use a shared secondary particle bank. When enabled,
secondary particles are collected into a global bank, sorted for
reproducibility, and load-balanced across MPI ranks between
generations. If not specified, the shared secondary bank is
enabled automatically for fixed-source simulations with weight
windows active, and disabled otherwise.

.. versionadded:: 0.15.1
weight_windows_on : bool
Whether weight windows are enabled

Expand Down Expand Up @@ -475,6 +484,7 @@ def __init__(self, **kwargs):
self._weight_window_generators = cv.CheckedList(
WeightWindowGenerator, 'weight window generators')
self._weight_windows_on = None
self._shared_secondary_bank = None
self._weight_windows_file = None
self._weight_window_checkpoints = {}
self._max_history_splits = None
Expand Down Expand Up @@ -1287,6 +1297,15 @@ def weight_windows_on(self, value: bool):
cv.check_type('weight windows on', value, bool)
self._weight_windows_on = value

@property
def shared_secondary_bank(self) -> bool:
return self._shared_secondary_bank

@shared_secondary_bank.setter
def shared_secondary_bank(self, value: bool):
cv.check_type('shared secondary bank', value, bool)
self._shared_secondary_bank = value

@property
def weight_window_checkpoints(self) -> dict:
return self._weight_window_checkpoints
Expand Down Expand Up @@ -1891,6 +1910,11 @@ def _create_weight_windows_on_subelement(self, root):
elem = ET.SubElement(root, "weight_windows_on")
elem.text = str(self._weight_windows_on).lower()

def _create_shared_secondary_bank_subelement(self, root):
if self._shared_secondary_bank is not None:
elem = ET.SubElement(root, "shared_secondary_bank")
elem.text = str(self._shared_secondary_bank).lower()

def _create_weight_window_generators_subelement(self, root, mesh_memo=None):
if not self.weight_window_generators:
return
Expand Down Expand Up @@ -2379,6 +2403,11 @@ def _weight_windows_on_from_xml_element(self, root):
if text is not None:
self.weight_windows_on = text in ('true', '1')

def _shared_secondary_bank_from_xml_element(self, root):
text = get_text(root, 'shared_secondary_bank')
if text is not None:
self.shared_secondary_bank = text in ('true', '1')

def _weight_windows_file_from_xml_element(self, root):
text = get_text(root, 'weight_windows_file')
if text is not None:
Expand Down Expand Up @@ -2538,6 +2567,7 @@ def to_xml_element(self, mesh_memo=None):
self._create_write_initial_source_subelement(element)
self._create_weight_windows_subelement(element, mesh_memo)
self._create_weight_windows_on_subelement(element)
self._create_shared_secondary_bank_subelement(element)
self._create_weight_window_generators_subelement(element, mesh_memo)
self._create_weight_windows_file_element(element)
self._create_weight_window_checkpoints_subelement(element)
Expand Down Expand Up @@ -2654,6 +2684,7 @@ def from_xml_element(cls, elem, meshes=None):
settings._write_initial_source_from_xml_element(elem)
settings._weight_windows_from_xml_element(elem, meshes)
settings._weight_windows_on_from_xml_element(elem)
settings._shared_secondary_bank_from_xml_element(elem)
settings._weight_windows_file_from_xml_element(elem)
settings._weight_window_generators_from_xml_element(elem, meshes)
settings._weight_window_checkpoints_from_xml_element(elem)
Expand Down
Loading
Loading