From f8a6b08ce8fb1807b68548f09abce09f411d98cf Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 14 Jan 2026 19:08:19 +0200 Subject: [PATCH 01/11] add delay to activation of ifp tallies --- docs/source/usersguide/kinetics.rst | 4 +--- include/openmc/tallies/tally.h | 3 +++ src/settings.cpp | 8 +------- src/simulation.cpp | 17 ++++++++++++++++- src/tallies/tally.cpp | 5 +---- 5 files changed, 22 insertions(+), 15 deletions(-) diff --git a/docs/source/usersguide/kinetics.rst b/docs/source/usersguide/kinetics.rst index 9024ff8227a..3edd772f082 100644 --- a/docs/source/usersguide/kinetics.rst +++ b/docs/source/usersguide/kinetics.rst @@ -32,9 +32,7 @@ the ``ifp_n_generation`` settings in the Python API:: settings.ifp_n_generation = 5 -``ifp_n_generation`` should be greater than 0, but should also be lower than -or equal to the number of inactive batches declared for the calculation. -The respect of these constraints is verified by OpenMC before any calculation. +``ifp_n_generation`` should be greater than 0. OpenMC will automatically detect the type of data that needs to be stored based on the tally scores selected by the user. This guarantees that only information diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 374daff92a0..e877fdca9e0 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -148,6 +148,9 @@ class Tally { //! Whether this tally is currently being updated bool active_ {false}; + //! Offset batch to activate this tally after. + int offset_ {0}; + //! Number of realizations int n_realizations_ {0}; diff --git a/src/settings.cpp b/src/settings.cpp index 5b472468fc4..03d97ed7cda 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -552,14 +552,8 @@ void read_settings_xml(pugi::xml_node root) // Probability (IFP) method if (check_for_node(root, "ifp_n_generation")) { ifp_n_generation = std::stoi(get_node_value(root, "ifp_n_generation")); - if (ifp_n_generation <= 0) { + if (ifp_n_generation <= 0) fatal_error("'ifp_n_generation' must be greater than 0."); - } - // Avoid tallying 0 if IFP logs are not complete when active cycles start - if (ifp_n_generation > n_inactive) { - fatal_error("'ifp_n_generation' must be lower than or equal to the " - "number of inactive cycles."); - } } } diff --git a/src/simulation.cpp b/src/simulation.cpp index b536ae5881f..4628adaace9 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -376,6 +376,7 @@ void initialize_batch() // Determine if this batch is the first inactive or active batch. bool first_inactive = false; bool first_active = false; + bool first_ifp_active = false; if (!settings::restart_run) { first_inactive = settings::n_inactive > 0 && simulation::current_batch == 1; first_active = simulation::current_batch == settings::n_inactive + 1; @@ -391,7 +392,21 @@ void initialize_batch() simulation::time_inactive.stop(); simulation::time_active.start(); for (auto& t : model::tallies) { - t->active_ = true; + if (t->offset_ == 0) + t->active_ = true; + } + } + + // Activate tallies which have activation offset. + for (auto& t : model::tallies) { + if (t->offset_ > 0) { + if (!settings::restart_run) { + if (simulation::current_batch == settings::n_inactive + 1 + t->offset_) + t->active_ = true; + } else if (simulation::current_batch == simulation::restart_batch + 1) { + if (simulation::restart_batch >= settings::n_inactive + t->offset_) + t->active_ = true; + } } } diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 6eef1da9cfd..f59b32a3f48 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -210,10 +210,6 @@ Tally::Tally(pugi::xml_node node) "changed using the 'ifp_n_generation' settings.", settings::ifp_n_generation)); } - if (settings::ifp_n_generation > settings::n_inactive) { - fatal_error("'ifp_n_generation' must be lower than or equal to the " - "number of inactive cycles."); - } settings::ifp_on = true; } else if (settings::run_mode == RunMode::FIXED_SOURCE) { fatal_error( @@ -656,6 +652,7 @@ void Tally::set_scores(const vector& scores) case SCORE_IFP_BETA_NUM: case SCORE_IFP_DENOM: estimator_ = TallyEstimator::COLLISION; + offset_ = settings::ifp_n_generation; break; } From b39675226978ac0d62c46fb9e02a2e488a469c20 Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 14 Jan 2026 19:45:35 +0200 Subject: [PATCH 02/11] add simplification --- src/simulation.cpp | 1 - src/tallies/tally_scoring.cpp | 56 +++++++++++++---------------------- 2 files changed, 20 insertions(+), 37 deletions(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index 4628adaace9..cc736f2ce6b 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -376,7 +376,6 @@ void initialize_batch() // Determine if this batch is the first inactive or active batch. bool first_inactive = false; bool first_active = false; - bool first_ifp_active = false; if (!settings::restart_run) { first_inactive = settings::n_inactive > 0 && simulation::current_batch == 1; first_active = simulation::current_batch == settings::n_inactive + 1; diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index 67e851644a9..e7236a4a476 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -945,11 +945,9 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, if (settings::ifp_on) { if ((p.type() == Type::neutron) && (p.fission())) { if (is_generation_time_or_both()) { - const auto& lifetimes = - simulation::ifp_source_lifetime_bank[p.current_work() - 1]; - if (lifetimes.size() == settings::ifp_n_generation) { - score = lifetimes[0] * p.wgt_last(); - } + const auto& lifetime = + simulation::ifp_source_lifetime_bank[p.current_work() - 1][0]; + score = lifetimes[0] * p.wgt_last(); } } } @@ -959,20 +957,19 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, if (settings::ifp_on) { if ((p.type() == Type::neutron) && (p.fission())) { if (is_beta_effective_or_both()) { - const auto& delayed_groups = - simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; - if (delayed_groups.size() == settings::ifp_n_generation) { - if (delayed_groups[0] > 0) { - score = p.wgt_last(); - if (tally.delayedgroup_filter_ != C_NONE) { - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - const DelayedGroupFilter& filt { - *dynamic_cast( - model::tally_filters[i_dg_filt].get())}; - score_fission_delayed_dg(i_tally, delayed_groups[0] - 1, - score, score_index, p.filter_matches()); - continue; - } + const auto& delayed_group = + simulation::ifp_source_delayed_group_bank[p.current_work() - 1] + [0]; + if (delayed_group > 0) { + score = p.wgt_last(); + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt { + *dynamic_cast( + model::tally_filters[i_dg_filt].get())}; + score_fission_delayed_dg(i_tally, delayed_groups[0] - 1, score, + score_index, p.filter_matches()); + continue; } } } @@ -982,21 +979,8 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, case SCORE_IFP_DENOM: if (settings::ifp_on) { - if ((p.type() == Type::neutron) && (p.fission())) { - int ifp_data_size; - if (is_beta_effective_or_both()) { - ifp_data_size = static_cast( - simulation::ifp_source_delayed_group_bank[p.current_work() - 1] - .size()); - } else { - ifp_data_size = static_cast( - simulation::ifp_source_lifetime_bank[p.current_work() - 1] - .size()); - } - if (ifp_data_size == settings::ifp_n_generation) { - score = p.wgt_last(); - } - } + if ((p.type() == Type::neutron) && (p.fission())) + score = p.wgt_last(); } break; @@ -2644,7 +2628,7 @@ void score_surface_tally(Particle& p, const vector& tallies) // for a further scoring function. double score = current * filter_weight; for (auto score_index = 0; score_index < tally.scores_.size(); - ++score_index) { + ++score_index) { #pragma omp atomic tally.results_(filter_index, score_index, TallyResult::VALUE) += score; } @@ -2719,7 +2703,7 @@ void score_pulse_height_tally(Particle& p, const vector& tallies) // Loop over scores. for (auto score_index = 0; score_index < tally.scores_.size(); - ++score_index) { + ++score_index) { #pragma omp atomic tally.results_(filter_index, score_index, TallyResult::VALUE) += filter_weight; From 0746b240eba7f6e34d46baa2f83111fd2ff30d9b Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 14 Jan 2026 19:47:08 +0200 Subject: [PATCH 03/11] fix typo --- src/tallies/tally_scoring.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index e7236a4a476..0eb41cfd434 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -947,7 +947,7 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, if (is_generation_time_or_both()) { const auto& lifetime = simulation::ifp_source_lifetime_bank[p.current_work() - 1][0]; - score = lifetimes[0] * p.wgt_last(); + score = lifetime * p.wgt_last(); } } } @@ -967,7 +967,7 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, const DelayedGroupFilter& filt { *dynamic_cast( model::tally_filters[i_dg_filt].get())}; - score_fission_delayed_dg(i_tally, delayed_groups[0] - 1, score, + score_fission_delayed_dg(i_tally, delayed_group - 1, score, score_index, p.filter_matches()); continue; } From e23cbf091d10a0f2c76b98108454e1df168c2ca0 Mon Sep 17 00:00:00 2001 From: GuySten <62616591+GuySten@users.noreply.github.com> Date: Wed, 14 Jan 2026 19:52:03 +0200 Subject: [PATCH 04/11] Apply suggestions from code review --- src/tallies/tally_scoring.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index 0eb41cfd434..3028b78a1cf 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -2628,7 +2628,7 @@ void score_surface_tally(Particle& p, const vector& tallies) // for a further scoring function. double score = current * filter_weight; for (auto score_index = 0; score_index < tally.scores_.size(); - ++score_index) { + ++score_index) { #pragma omp atomic tally.results_(filter_index, score_index, TallyResult::VALUE) += score; } @@ -2703,7 +2703,7 @@ void score_pulse_height_tally(Particle& p, const vector& tallies) // Loop over scores. for (auto score_index = 0; score_index < tally.scores_.size(); - ++score_index) { + ++score_index) { #pragma omp atomic tally.results_(filter_index, score_index, TallyResult::VALUE) += filter_weight; From d529debab01bc910a25038cc04b1f1265068967b Mon Sep 17 00:00:00 2001 From: GuySten <62616591+GuySten@users.noreply.github.com> Date: Wed, 14 Jan 2026 19:53:25 +0200 Subject: [PATCH 05/11] Apply suggestions from code review --- src/tallies/tally_scoring.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index 3028b78a1cf..7026590251b 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -2628,7 +2628,7 @@ void score_surface_tally(Particle& p, const vector& tallies) // for a further scoring function. double score = current * filter_weight; for (auto score_index = 0; score_index < tally.scores_.size(); - ++score_index) { + ++score_index) { #pragma omp atomic tally.results_(filter_index, score_index, TallyResult::VALUE) += score; } @@ -2703,7 +2703,7 @@ void score_pulse_height_tally(Particle& p, const vector& tallies) // Loop over scores. for (auto score_index = 0; score_index < tally.scores_.size(); - ++score_index) { + ++score_index) { #pragma omp atomic tally.results_(filter_index, score_index, TallyResult::VALUE) += filter_weight; From 2c4d51f0cfabd1e1f32606ffa00becf3723cc292 Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 14 Jan 2026 21:59:07 +0200 Subject: [PATCH 06/11] further simplifications --- include/openmc/ifp.h | 15 ++----- include/openmc/settings.h | 13 +----- include/openmc/simulation.h | 2 + src/bank.cpp | 18 +++------ src/eigenvalue.cpp | 76 +++++++++++++---------------------- src/ifp.cpp | 61 +++++++++------------------- src/physics.cpp | 4 +- src/settings.cpp | 2 - src/simulation.cpp | 6 +-- src/tallies/tally.cpp | 67 ++++++------------------------ src/tallies/tally_scoring.cpp | 48 +++++++++------------- 11 files changed, 95 insertions(+), 217 deletions(-) diff --git a/include/openmc/ifp.h b/include/openmc/ifp.h index 2b751d66f0e..28ae2c8b2db 100644 --- a/include/openmc/ifp.h +++ b/include/openmc/ifp.h @@ -5,21 +5,12 @@ #include "openmc/particle.h" #include "openmc/particle_data.h" #include "openmc/settings.h" +#include "openmc/simulation.h" #include // for copy namespace openmc { -//! Check the value of the IFP parameter for beta effective or both. -//! -//! \return true if "BetaEffective" or "Both", false otherwise. -bool is_beta_effective_or_both(); - -//! Check the value of the IFP parameter for generation time or both. -//! -//! \return true if "GenerationTime" or "Both", false otherwise. -bool is_generation_time_or_both(); - //! Resize IFP vectors //! //! \param[in,out] delayed_groups List of delayed group numbers @@ -28,10 +19,10 @@ bool is_generation_time_or_both(); template void resize_ifp_data(vector& delayed_groups, vector& lifetimes, int64_t n) { - if (is_beta_effective_or_both()) { + if (simulation::ifp_delayed_on) { delayed_groups.resize(n); } - if (is_generation_time_or_both()) { + if (simulation::ifp_lifetime_on) { lifetimes.resize(n); } } diff --git a/include/openmc/settings.h b/include/openmc/settings.h index b369c99fef8..259ba6dd1a4 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -24,14 +24,6 @@ enum class SSWCellType { To, }; -// Type of IFP parameters -enum class IFPParameter { - None, - Both, - BetaEffective, - GenerationTime, -}; - struct CollisionTrackConfig { bool mcpl_write {false}; //!< Write collision tracks using MCPL? std::unordered_set @@ -69,8 +61,7 @@ extern bool delayed_photon_scaling; //!< Scale fission photon yield to include delayed extern "C" bool entropy_on; //!< calculate Shannon entropy? extern "C" bool - event_based; //!< use event-based mode (instead of history-based) -extern bool ifp_on; //!< Use IFP for kinetics parameters? + event_based; //!< use event-based mode (instead of history-based) extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular? extern bool material_cell_offsets; //!< create material cells offsets? extern "C" bool output_summary; //!< write summary.h5? @@ -142,8 +133,6 @@ extern array time_cutoff; //!< Time cutoff in [s] for each particle type extern int ifp_n_generation; //!< Number of generation for Iterated Fission Probability -extern IFPParameter - ifp_parameter; //!< Parameter to calculate for Iterated Fission Probability extern int legendre_to_tabular_points; //!< number of points to convert Legendres extern int max_order; //!< Maximum Legendre order for multigroup data diff --git a/include/openmc/simulation.h b/include/openmc/simulation.h index 9a6cf1b2131..ebda84c1b0e 100644 --- a/include/openmc/simulation.h +++ b/include/openmc/simulation.h @@ -25,6 +25,8 @@ namespace simulation { extern int ct_current_file; //!< current collision track file index extern "C" int current_batch; //!< current batch extern "C" int current_gen; //!< current fission generation +extern bool ifp_delayed_on; //!< Store delayed group IFP data? +extern bool ifp_lifetime_on; //!< Store lifetime IFP data? extern "C" bool initialized; //!< has simulation been initialized? extern "C" double keff; //!< average k over batches extern "C" double keff_std; //!< standard deviation of average k diff --git a/src/bank.cpp b/src/bank.cpp index 33790379b85..2c5d1808302 100644 --- a/src/bank.cpp +++ b/src/bank.cpp @@ -103,10 +103,8 @@ void sort_fission_bank() sorted_bank = &simulation::fission_bank[simulation::fission_bank.size()]; } - if (settings::ifp_on) { - allocate_temporary_vector_ifp( - sorted_ifp_delayed_group_bank, sorted_ifp_lifetime_bank); - } + allocate_temporary_vector_ifp( + sorted_ifp_delayed_group_bank, sorted_ifp_lifetime_bank); // Use parent and progeny indices to sort fission bank for (int64_t i = 0; i < simulation::fission_bank.size(); i++) { @@ -118,19 +116,15 @@ void sort_fission_bank() "shared fission bank size."); } sorted_bank[idx] = site; - if (settings::ifp_on) { - copy_ifp_data_from_fission_banks( - i, sorted_ifp_delayed_group_bank[idx], sorted_ifp_lifetime_bank[idx]); - } + copy_ifp_data_from_fission_banks( + i, sorted_ifp_delayed_group_bank[idx], sorted_ifp_lifetime_bank[idx]); } // Copy sorted bank into the fission bank std::copy(sorted_bank, sorted_bank + simulation::fission_bank.size(), simulation::fission_bank.data()); - if (settings::ifp_on) { - copy_ifp_data_to_fission_banks( - sorted_ifp_delayed_group_bank.data(), sorted_ifp_lifetime_bank.data()); - } + copy_ifp_data_to_fission_banks( + sorted_ifp_delayed_group_bank.data(), sorted_ifp_lifetime_bank.data()); } //============================================================================== diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index 2fb9dabf16d..0d8300d6974 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -138,10 +138,8 @@ void synchronize_bank() // Temporary banks for IFP vector> temp_delayed_groups; vector> temp_lifetimes; - if (settings::ifp_on) { - resize_ifp_data( - temp_delayed_groups, temp_lifetimes, 3 * simulation::work_per_rank); - } + resize_ifp_data( + temp_delayed_groups, temp_lifetimes, 3 * simulation::work_per_rank); // ========================================================================== // SAMPLE N_PARTICLES FROM FISSION BANK AND PLACE IN TEMP_SITES @@ -167,10 +165,8 @@ void synchronize_bank() for (int64_t i = tooth_start; i < tooth_end; i++) { int64_t idx = std::floor(tooth) - start; temp_sites[index_temp] = simulation::fission_bank[idx]; - if (settings::ifp_on) { - copy_ifp_data_from_fission_banks( - idx, temp_delayed_groups[index_temp], temp_lifetimes[index_temp]); - } + copy_ifp_data_from_fission_banks( + idx, temp_delayed_groups[index_temp], temp_lifetimes[index_temp]); ++index_temp; // Next tooth @@ -209,10 +205,8 @@ void synchronize_bank() // IFP number of generation int ifp_n_generation; - if (settings::ifp_on) { - broadcast_ifp_n_generation( - ifp_n_generation, temp_delayed_groups, temp_lifetimes); - } + broadcast_ifp_n_generation( + ifp_n_generation, temp_delayed_groups, temp_lifetimes); int64_t index_local = 0; vector requests; @@ -228,7 +222,7 @@ void synchronize_bank() simulation::work_index.begin(), simulation::work_index.end(), start); // Resize IFP send buffers - if (settings::ifp_on && mpi::n_procs > 1) { + if (mpi::n_procs > 1) { resize_ifp_data(send_delayed_groups, send_lifetimes, ifp_n_generation * 3 * simulation::work_per_rank); } @@ -246,15 +240,12 @@ void synchronize_bank() mpi::source_site, neighbor, mpi::rank, mpi::intracomm, &requests.back()); - if (settings::ifp_on) { - // Send IFP data - if (is_beta_effective_or_both()) - send_ifp_info(index_local, n, ifp_n_generation, neighbor, requests, - temp_delayed_groups, send_delayed_groups); - if (is_generation_time_or_both()) - send_ifp_info(index_local, n, ifp_n_generation, neighbor, requests, - temp_lifetimes, send_lifetimes); - } + if (simulation::ifp_delayed_on) + send_ifp_info(index_local, n, ifp_n_generation, neighbor, requests, + temp_delayed_groups, send_delayed_groups); + if (simulation::ifp_lifetime_on) + send_ifp_info(index_local, n, ifp_n_generation, neighbor, requests, + temp_lifetimes, send_lifetimes); } // Increment all indices @@ -293,7 +284,7 @@ void synchronize_bank() } // Resize IFP receive buffers - if (settings::ifp_on && mpi::n_procs > 1) { + if (mpi::n_procs > 1) { resize_ifp_data(recv_delayed_groups, recv_lifetimes, ifp_n_generation * simulation::work_per_rank); } @@ -317,15 +308,12 @@ void synchronize_bank() MPI_Irecv(&simulation::source_bank[index_local], static_cast(n), mpi::source_site, neighbor, neighbor, mpi::intracomm, &requests.back()); - if (settings::ifp_on) { - // Receive IFP data - if (is_beta_effective_or_both()) - receive_ifp_data(index_local, n, ifp_n_generation, neighbor, requests, - recv_delayed_groups, deserialization_info); - if (is_generation_time_or_both()) - receive_ifp_data(index_local, n, ifp_n_generation, neighbor, requests, - recv_lifetimes, deserialization_info); - } + if (simulation::ifp_delayed_on) + receive_ifp_data(index_local, n, ifp_n_generation, neighbor, requests, + recv_delayed_groups, deserialization_info); + if (simulation::ifp_lifetime_on) + receive_ifp_data(index_local, n, ifp_n_generation, neighbor, requests, + recv_lifetimes, deserialization_info); } else { // If the source sites are on this processor, we can simply copy them @@ -335,10 +323,8 @@ void synchronize_bank() std::copy(&temp_sites[index_temp], &temp_sites[index_temp + n], &simulation::source_bank[index_local]); - if (settings::ifp_on) { - copy_partial_ifp_data_to_source_banks( - index_temp, n, index_local, temp_delayed_groups, temp_lifetimes); - } + copy_partial_ifp_data_to_source_banks( + index_temp, n, index_local, temp_delayed_groups, temp_lifetimes); } // Increment all indices @@ -354,21 +340,17 @@ void synchronize_bank() int n_request = requests.size(); MPI_Waitall(n_request, requests.data(), MPI_STATUSES_IGNORE); - if (settings::ifp_on) { - if (is_beta_effective_or_both()) - deserialize_ifp_info(ifp_n_generation, recv_delayed_groups, - simulation::ifp_source_delayed_group_bank, deserialization_info); - if (is_generation_time_or_both()) - deserialize_ifp_info(ifp_n_generation, recv_lifetimes, - simulation::ifp_source_lifetime_bank, deserialization_info); - } + if (simulation::ifp_delayed_on) + deserialize_ifp_info(ifp_n_generation, recv_delayed_groups, + simulation::ifp_source_delayed_group_bank, deserialization_info); + if (simulation::ifp_lifetime_on) + deserialize_ifp_info(ifp_n_generation, recv_lifetimes, + simulation::ifp_source_lifetime_bank, deserialization_info); #else std::copy(temp_sites.data(), temp_sites.data() + settings::n_particles, simulation::source_bank.begin()); - if (settings::ifp_on) { - copy_complete_ifp_data_to_source_banks(temp_delayed_groups, temp_lifetimes); - } + copy_complete_ifp_data_to_source_banks(temp_delayed_groups, temp_lifetimes); #endif simulation::time_bank_sendrecv.stop(); diff --git a/src/ifp.cpp b/src/ifp.cpp index a5157440df0..4fce2c577c0 100644 --- a/src/ifp.cpp +++ b/src/ifp.cpp @@ -10,33 +10,15 @@ namespace openmc { -bool is_beta_effective_or_both() -{ - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - return true; - } - return false; -} - -bool is_generation_time_or_both() -{ - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - return true; - } - return false; -} - void ifp(const Particle& p, int64_t idx) { - if (is_beta_effective_or_both()) { + if (simulation::ifp_delayed_on) { const auto& delayed_groups = simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; simulation::ifp_fission_delayed_group_bank[idx] = _ifp(p.delayed_group(), delayed_groups); } - if (is_generation_time_or_both()) { + if (simulation::ifp_lifetime_on) { const auto& lifetimes = simulation::ifp_source_lifetime_bank[p.current_work() - 1]; simulation::ifp_fission_lifetime_bank[idx] = _ifp(p.lifetime(), lifetimes); @@ -54,12 +36,11 @@ void resize_simulation_ifp_banks() void copy_ifp_data_from_fission_banks( int i_bank, vector& delayed_groups, vector& lifetimes) { - if (is_beta_effective_or_both()) { + if (simulation::ifp_delayed_on) delayed_groups = simulation::ifp_fission_delayed_group_bank[i_bank]; - } - if (is_generation_time_or_both()) { + + if (simulation::ifp_lifetime_on) lifetimes = simulation::ifp_fission_lifetime_bank[i_bank]; - } } #ifdef OPENMC_MPI @@ -68,10 +49,12 @@ void broadcast_ifp_n_generation(int& n_generation, const vector>& lifetimes) { if (mpi::rank == 0) { - if (is_beta_effective_or_both()) { + if (simulation::ifp_delayed_on) { n_generation = static_cast(delayed_groups[0].size()); - } else { + } else if (simulation::ifp_lifetime_on) { n_generation = static_cast(lifetimes[0].size()); + } else { + return; } } MPI_Bcast(&n_generation, 1, MPI_INT, 0, mpi::intracomm); @@ -81,14 +64,12 @@ void copy_partial_ifp_data_to_source_banks(int64_t idx, int n, int64_t i_bank, const vector>& delayed_groups, const vector>& lifetimes) { - if (is_beta_effective_or_both()) { + if (simulation::ifp_delayed_on) std::copy(&delayed_groups[idx], &delayed_groups[idx + n], &simulation::ifp_source_delayed_group_bank[i_bank]); - } - if (is_generation_time_or_both()) { + if (simulation::ifp_lifetime_on) std::copy(&lifetimes[idx], &lifetimes[idx + n], &simulation::ifp_source_lifetime_bank[i_bank]); - } } #endif @@ -96,40 +77,36 @@ void copy_complete_ifp_data_to_source_banks( const vector>& delayed_groups, const vector>& lifetimes) { - if (is_beta_effective_or_both()) { + if (simulation::ifp_delayed_on) std::copy(delayed_groups.data(), delayed_groups.data() + settings::n_particles, simulation::ifp_source_delayed_group_bank.begin()); - } - if (is_generation_time_or_both()) { + if (simulation::ifp_lifetime_on) std::copy(lifetimes.data(), lifetimes.data() + settings::n_particles, simulation::ifp_source_lifetime_bank.begin()); - } } void allocate_temporary_vector_ifp( vector>& delayed_groups, vector>& lifetimes) { - if (is_beta_effective_or_both()) { + if (simulation::ifp_delayed_on) delayed_groups.resize(simulation::fission_bank.size()); - } - if (is_generation_time_or_both()) { + + if (simulation::ifp_lifetime_on) lifetimes.resize(simulation::fission_bank.size()); - } } void copy_ifp_data_to_fission_banks(const vector* const delayed_groups_ptr, const vector* lifetimes_ptr) { - if (is_beta_effective_or_both()) { + if (simulation::ifp_delayed_on) std::copy(delayed_groups_ptr, delayed_groups_ptr + simulation::fission_bank.size(), simulation::ifp_fission_delayed_group_bank.data()); - } - if (is_generation_time_or_both()) { + + if (simulation::ifp_lifetime_on) std::copy(lifetimes_ptr, lifetimes_ptr + simulation::fission_bank.size(), simulation::ifp_fission_lifetime_bank.data()); - } } } // namespace openmc diff --git a/src/physics.cpp b/src/physics.cpp index 41509af97be..d03c85195e8 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -246,9 +246,7 @@ void create_fission_sites(Particle& p, int i_nuclide, const Reaction& rx) break; } // Iterated Fission Probability (IFP) method - if (settings::ifp_on) { - ifp(p, idx); - } + ifp(p, idx); } else { p.secondary_bank().push_back(site); } diff --git a/src/settings.cpp b/src/settings.cpp index 03d97ed7cda..3157541e914 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -55,7 +55,6 @@ bool create_fission_neutrons {true}; bool delayed_photon_scaling {true}; bool entropy_on {false}; bool event_based {false}; -bool ifp_on {false}; bool legendre_to_tabular {true}; bool material_cell_offsets {true}; bool output_summary {true}; @@ -111,7 +110,6 @@ ElectronTreatment electron_treatment {ElectronTreatment::TTB}; array energy_cutoff {0.0, 1000.0, 0.0, 0.0}; array time_cutoff {INFTY, INFTY, INFTY, INFTY}; int ifp_n_generation {-1}; -IFPParameter ifp_parameter {IFPParameter::None}; int legendre_to_tabular_points {C_NONE}; int max_order {0}; int n_log_bins {8000}; diff --git a/src/simulation.cpp b/src/simulation.cpp index cc736f2ce6b..e0ff12864f4 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -301,6 +301,8 @@ namespace simulation { int ct_current_file; int current_batch; int current_gen; +bool ifp_delayed_on {false}; +bool ifp_lifetime_on {false}; bool initialized {false}; double keff {1.0}; double keff_std; @@ -340,9 +342,7 @@ void allocate_banks() init_fission_bank(3 * simulation::work_per_rank); // Allocate IFP bank - if (settings::ifp_on) { - resize_simulation_ifp_banks(); - } + resize_simulation_ifp_banks(); } if (settings::surf_source_write) { diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index f59b32a3f48..4c3cd5f0d61 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -188,60 +188,6 @@ Tally::Tally(pugi::xml_node node) fatal_error(fmt::format("No scores specified on tally {}.", id_)); } - // Set IFP if needed - if (!settings::ifp_on) { - // Determine if this tally has an IFP score - bool has_ifp_score = false; - for (int score : scores_) { - if (score == SCORE_IFP_TIME_NUM || score == SCORE_IFP_BETA_NUM || - score == SCORE_IFP_DENOM) { - has_ifp_score = true; - break; - } - } - - // Check for errors - if (has_ifp_score) { - if (settings::run_mode == RunMode::EIGENVALUE) { - if (settings::ifp_n_generation < 0) { - settings::ifp_n_generation = DEFAULT_IFP_N_GENERATION; - warning(fmt::format( - "{} generations will be used for IFP (default value). It can be " - "changed using the 'ifp_n_generation' settings.", - settings::ifp_n_generation)); - } - settings::ifp_on = true; - } else if (settings::run_mode == RunMode::FIXED_SOURCE) { - fatal_error( - "Iterated Fission Probability can only be used in an eigenvalue " - "calculation."); - } - } - } - - // Set IFP parameters if needed - if (settings::ifp_on) { - for (int score : scores_) { - switch (score) { - case SCORE_IFP_TIME_NUM: - if (settings::ifp_parameter == IFPParameter::None) { - settings::ifp_parameter = IFPParameter::GenerationTime; - } else if (settings::ifp_parameter == IFPParameter::BetaEffective) { - settings::ifp_parameter = IFPParameter::Both; - } - break; - case SCORE_IFP_BETA_NUM: - case SCORE_IFP_DENOM: - if (settings::ifp_parameter == IFPParameter::None) { - settings::ifp_parameter = IFPParameter::BetaEffective; - } else if (settings::ifp_parameter == IFPParameter::GenerationTime) { - settings::ifp_parameter = IFPParameter::Both; - } - break; - } - } - } - // Check if tally is compatible with particle type if (!settings::photon_transport) { for (int score : scores_) { @@ -649,9 +595,22 @@ void Tally::set_scores(const vector& scores) break; case SCORE_IFP_TIME_NUM: + simulation::ifp_lifetime_on = true; case SCORE_IFP_BETA_NUM: + simulation::ifp_delayed_on = true; case SCORE_IFP_DENOM: + if (settings::run_mode == RunMode::FIXED_SOURCE) + fatal_error( + "Iterated Fission Probability can only be used in an eigenvalue " + "calculation."); estimator_ = TallyEstimator::COLLISION; + if (settings::ifp_n_generation < 0) { + settings::ifp_n_generation = DEFAULT_IFP_N_GENERATION; + warning(fmt::format( + "{} generations will be used for IFP (default value). It can be " + "changed using the 'ifp_n_generation' settings.", + settings::ifp_n_generation)); + } offset_ = settings::ifp_n_generation; break; } diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index 7026590251b..0cefb43e0e2 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -942,46 +942,34 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, break; case SCORE_IFP_TIME_NUM: - if (settings::ifp_on) { - if ((p.type() == Type::neutron) && (p.fission())) { - if (is_generation_time_or_both()) { - const auto& lifetime = - simulation::ifp_source_lifetime_bank[p.current_work() - 1][0]; - score = lifetime * p.wgt_last(); - } - } + if ((p.type() == Type::neutron) && (p.fission())) { + const auto& lifetime = + simulation::ifp_source_lifetime_bank[p.current_work() - 1][0]; + score = lifetime * p.wgt_last(); } break; case SCORE_IFP_BETA_NUM: - if (settings::ifp_on) { - if ((p.type() == Type::neutron) && (p.fission())) { - if (is_beta_effective_or_both()) { - const auto& delayed_group = - simulation::ifp_source_delayed_group_bank[p.current_work() - 1] - [0]; - if (delayed_group > 0) { - score = p.wgt_last(); - if (tally.delayedgroup_filter_ != C_NONE) { - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - const DelayedGroupFilter& filt { - *dynamic_cast( - model::tally_filters[i_dg_filt].get())}; - score_fission_delayed_dg(i_tally, delayed_group - 1, score, - score_index, p.filter_matches()); - continue; - } - } + if ((p.type() == Type::neutron) && (p.fission())) { + const auto& delayed_group = + simulation::ifp_source_delayed_group_bank[p.current_work() - 1][0]; + if (delayed_group > 0) { + score = p.wgt_last(); + if (tally.delayedgroup_filter_ != C_NONE) { + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& filt {*dynamic_cast( + model::tally_filters[i_dg_filt].get())}; + score_fission_delayed_dg(i_tally, delayed_group - 1, score, + score_index, p.filter_matches()); + continue; } } } break; case SCORE_IFP_DENOM: - if (settings::ifp_on) { - if ((p.type() == Type::neutron) && (p.fission())) - score = p.wgt_last(); - } + if ((p.type() == Type::neutron) && (p.fission())) + score = p.wgt_last(); break; case N_2N: From af6d0a06cacd56fc7164f6c61d25d0d07228d58a Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 14 Jan 2026 22:09:09 +0200 Subject: [PATCH 07/11] update test results --- .../ifp/groupwise/results_true.dat | 20 +++++++++---------- .../ifp/total/results_true.dat | 12 +++++------ tests/unit_tests/test_ifp.py | 2 -- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/tests/regression_tests/ifp/groupwise/results_true.dat b/tests/regression_tests/ifp/groupwise/results_true.dat index ea66a8de3c9..f35e2050201 100644 --- a/tests/regression_tests/ifp/groupwise/results_true.dat +++ b/tests/regression_tests/ifp/groupwise/results_true.dat @@ -1,21 +1,21 @@ k-combined: 1.006559E+00 5.389391E-03 tally 1: -9.109384E-08 -5.667165E-16 +6.064971E-08 +3.807785E-16 tally 2: -3.000000E-03 -9.000000E-06 0.000000E+00 0.000000E+00 -2.100000E-02 -1.370000E-04 -2.800000E-02 -2.220000E-04 +0.000000E+00 +0.000000E+00 +1.500000E-02 +1.010000E-04 +1.800000E-02 +1.400000E-04 0.000000E+00 0.000000E+00 0.000000E+00 0.000000E+00 tally 3: -1.489000E+01 -1.480036E+01 +9.876000E+00 +9.761494E+00 diff --git a/tests/regression_tests/ifp/total/results_true.dat b/tests/regression_tests/ifp/total/results_true.dat index 466ca1f015e..5eb02226b34 100644 --- a/tests/regression_tests/ifp/total/results_true.dat +++ b/tests/regression_tests/ifp/total/results_true.dat @@ -1,9 +1,9 @@ k-combined: 1.006559E+00 5.389391E-03 tally 1: -9.109384E-08 -5.667165E-16 -5.200000E-02 -5.420000E-04 -1.489000E+01 -1.480036E+01 +6.064971E-08 +3.807785E-16 +3.300000E-02 +3.610000E-04 +9.876000E+00 +9.761494E+00 diff --git a/tests/unit_tests/test_ifp.py b/tests/unit_tests/test_ifp.py index e527f162460..68ebce1df16 100644 --- a/tests/unit_tests/test_ifp.py +++ b/tests/unit_tests/test_ifp.py @@ -32,8 +32,6 @@ def geometry(): ({"ifp_n_generation": 0}, ValueError), ({"ifp_n_generation": -1}, ValueError), ({"run_mode": "fixed source"}, RuntimeError), - ({"inactive": 5, "ifp_n_generation": 6}, RuntimeError), - ({"inactive": 9}, RuntimeError) ], ) def test_exceptions(options, error, run_in_tmpdir, geometry): From 7dc3e87d90eccff22d488adc0e63c8d74c01e559 Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 14 Jan 2026 22:19:23 +0200 Subject: [PATCH 08/11] increase statistics of ifp groupwise --- .../ifp/groupwise/inputs_true.dat | 2 +- .../ifp/groupwise/results_true.dat | 34 +++++++++---------- tests/regression_tests/ifp/groupwise/test.py | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/regression_tests/ifp/groupwise/inputs_true.dat b/tests/regression_tests/ifp/groupwise/inputs_true.dat index 6d7e20717b9..5b1da86b6c4 100644 --- a/tests/regression_tests/ifp/groupwise/inputs_true.dat +++ b/tests/regression_tests/ifp/groupwise/inputs_true.dat @@ -12,7 +12,7 @@ eigenvalue - 1000 + 10000 20 5 diff --git a/tests/regression_tests/ifp/groupwise/results_true.dat b/tests/regression_tests/ifp/groupwise/results_true.dat index f35e2050201..1839a5b0c01 100644 --- a/tests/regression_tests/ifp/groupwise/results_true.dat +++ b/tests/regression_tests/ifp/groupwise/results_true.dat @@ -1,21 +1,21 @@ k-combined: -1.006559E+00 5.389391E-03 +1.011403E+00 2.852123E-03 tally 1: -6.064971E-08 -3.807785E-16 +6.322790E-08 +4.014613E-16 tally 2: -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 -1.500000E-02 -1.010000E-04 -1.800000E-02 -1.400000E-04 -0.000000E+00 -0.000000E+00 -0.000000E+00 -0.000000E+00 +1.700000E-03 +1.490000E-06 +9.100000E-03 +1.351000E-05 +1.630000E-02 +5.251000E-05 +2.090000E-02 +5.605000E-05 +1.060000E-02 +2.056000E-05 +9.000000E-04 +2.300000E-07 tally 3: -9.876000E+00 -9.761494E+00 +9.945300E+00 +9.892586E+00 diff --git a/tests/regression_tests/ifp/groupwise/test.py b/tests/regression_tests/ifp/groupwise/test.py index a1a0ebefb8d..d850fe07cf4 100644 --- a/tests/regression_tests/ifp/groupwise/test.py +++ b/tests/regression_tests/ifp/groupwise/test.py @@ -21,7 +21,7 @@ def ifp_model(): # Settings settings = openmc.Settings() - settings.particles = 1000 + settings.particles = 10000 settings.batches = 20 settings.inactive = 5 settings.ifp_n_generation = 5 From 41256785590ea30fcb2ae31327969c588bc50004 Mon Sep 17 00:00:00 2001 From: GuySten Date: Wed, 14 Jan 2026 23:58:14 +0200 Subject: [PATCH 09/11] more fixes --- src/tallies/tally.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 4c3cd5f0d61..a134cd7929b 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -595,15 +595,16 @@ void Tally::set_scores(const vector& scores) break; case SCORE_IFP_TIME_NUM: - simulation::ifp_lifetime_on = true; case SCORE_IFP_BETA_NUM: - simulation::ifp_delayed_on = true; case SCORE_IFP_DENOM: + if (score == SCORE_IFP_TIME_NUM) + simulation::ifp_lifetime_on = true; + if (score == SCORE_IFP_BETA_NUM) + simulation::ifp_delayed_on = true; if (settings::run_mode == RunMode::FIXED_SOURCE) fatal_error( "Iterated Fission Probability can only be used in an eigenvalue " "calculation."); - estimator_ = TallyEstimator::COLLISION; if (settings::ifp_n_generation < 0) { settings::ifp_n_generation = DEFAULT_IFP_N_GENERATION; warning(fmt::format( @@ -611,6 +612,7 @@ void Tally::set_scores(const vector& scores) "changed using the 'ifp_n_generation' settings.", settings::ifp_n_generation)); } + estimator_ = TallyEstimator::COLLISION; offset_ = settings::ifp_n_generation; break; } From e889bc600a3da8bbd6a50b133edee5fe743d00b2 Mon Sep 17 00:00:00 2001 From: GuySten Date: Thu, 15 Jan 2026 18:15:59 +0200 Subject: [PATCH 10/11] further simplification --- include/openmc/ifp.h | 10 ++++++---- src/bank.cpp | 2 +- src/eigenvalue.cpp | 2 +- src/ifp.cpp | 10 ++++++---- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/include/openmc/ifp.h b/include/openmc/ifp.h index 28ae2c8b2db..60bcd9d6bf3 100644 --- a/include/openmc/ifp.h +++ b/include/openmc/ifp.h @@ -76,10 +76,12 @@ void resize_simulation_ifp_banks(); //! Retrieve IFP data from the IFP fission banks. //! //! \param[in] i_bank Index in the fission banks -//! \param[in,out] delayed_groups Delayed group numbers -//! \param[in,out] lifetimes Lifetimes lists -void copy_ifp_data_from_fission_banks( - int i_bank, vector& delayed_groups, vector& lifetimes); +//! \param[in] j_bank Index in the ifp banks +//! \param[in,out] delayed_groups_bank Delayed group numbers +//! \param[in,out] lifetimes_bank Lifetimes lists +void copy_ifp_data_from_fission_banks(int i_bank, int j_bank, + vector>& delayed_groups_bank, + vector>& lifetimes_bank); #ifdef OPENMC_MPI diff --git a/src/bank.cpp b/src/bank.cpp index 2c5d1808302..6d830e153bf 100644 --- a/src/bank.cpp +++ b/src/bank.cpp @@ -117,7 +117,7 @@ void sort_fission_bank() } sorted_bank[idx] = site; copy_ifp_data_from_fission_banks( - i, sorted_ifp_delayed_group_bank[idx], sorted_ifp_lifetime_bank[idx]); + i, idx, sorted_ifp_delayed_group_bank, sorted_ifp_lifetime_bank); } // Copy sorted bank into the fission bank diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index 0d8300d6974..e7265d13544 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -166,7 +166,7 @@ void synchronize_bank() int64_t idx = std::floor(tooth) - start; temp_sites[index_temp] = simulation::fission_bank[idx]; copy_ifp_data_from_fission_banks( - idx, temp_delayed_groups[index_temp], temp_lifetimes[index_temp]); + idx, index_temp, temp_delayed_groups, temp_lifetimes); ++index_temp; // Next tooth diff --git a/src/ifp.cpp b/src/ifp.cpp index 4fce2c577c0..4d1a4097844 100644 --- a/src/ifp.cpp +++ b/src/ifp.cpp @@ -33,14 +33,16 @@ void resize_simulation_ifp_banks() simulation::ifp_fission_lifetime_bank, 3 * simulation::work_per_rank); } -void copy_ifp_data_from_fission_banks( - int i_bank, vector& delayed_groups, vector& lifetimes) +void copy_ifp_data_from_fission_banks(int i_bank, int j_bank, + vector>& delayed_groups_bank, + vector>& lifetimes_bank) { if (simulation::ifp_delayed_on) - delayed_groups = simulation::ifp_fission_delayed_group_bank[i_bank]; + delayed_groups_bank[j_bank] = + simulation::ifp_fission_delayed_group_bank[i_bank]; if (simulation::ifp_lifetime_on) - lifetimes = simulation::ifp_fission_lifetime_bank[i_bank]; + lifetimes_bank[j_bank] = simulation::ifp_fission_lifetime_bank[i_bank]; } #ifdef OPENMC_MPI From 7298a3323c015ea2b061560c119a753ddd6914fd Mon Sep 17 00:00:00 2001 From: GuySten Date: Fri, 16 Jan 2026 00:09:04 +0200 Subject: [PATCH 11/11] try fix for mpi --- src/ifp.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ifp.cpp b/src/ifp.cpp index 4d1a4097844..68d9b34b739 100644 --- a/src/ifp.cpp +++ b/src/ifp.cpp @@ -50,16 +50,15 @@ void broadcast_ifp_n_generation(int& n_generation, const vector>& delayed_groups, const vector>& lifetimes) { - if (mpi::rank == 0) { - if (simulation::ifp_delayed_on) { + if (simulation::ifp_delayed_on) { + if (mpi::rank == 0) n_generation = static_cast(delayed_groups[0].size()); - } else if (simulation::ifp_lifetime_on) { + MPI_Bcast(&n_generation, 1, MPI_INT, 0, mpi::intracomm); + } else if (simulation::ifp_lifetime_on) { + if (mpi::rank == 0) n_generation = static_cast(lifetimes[0].size()); - } else { - return; - } + MPI_Bcast(&n_generation, 1, MPI_INT, 0, mpi::intracomm); } - MPI_Bcast(&n_generation, 1, MPI_INT, 0, mpi::intracomm); } void copy_partial_ifp_data_to_source_banks(int64_t idx, int n, int64_t i_bank,