From c246913ddf38abb17086db0d289add3aa94170b3 Mon Sep 17 00:00:00 2001 From: Joffrey Dorville Date: Mon, 16 Sep 2024 11:43:04 -0500 Subject: [PATCH 01/17] Kinetics parameters using Iterated Fission Probability --- include/openmc/bank.h | 8 + include/openmc/constants.h | 5 +- include/openmc/eigenvalue.h | 10 + include/openmc/particle_data.h | 7 + include/openmc/settings.h | 15 +- openmc/settings.py | 53 +++++ src/bank.cpp | 54 +++++ src/eigenvalue.cpp | 246 +++++++++++++++++++- src/finalize.cpp | 3 +- src/output.cpp | 3 + src/particle.cpp | 3 + src/physics.cpp | 68 ++++++ src/reaction.cpp | 3 + src/settings.cpp | 52 +++++ src/simulation.cpp | 17 ++ src/source.cpp | 14 ++ src/tallies/tally.cpp | 5 + src/tallies/tally_scoring.cpp | 55 +++++ tests/regression_tests/ifp/__init__.py | 0 tests/regression_tests/ifp/inputs_true.dat | 34 +++ tests/regression_tests/ifp/results_true.dat | 9 + tests/regression_tests/ifp/test.py | 56 +++++ tests/unit_tests/test_ifp.py | 43 ++++ 23 files changed, 759 insertions(+), 4 deletions(-) create mode 100644 tests/regression_tests/ifp/__init__.py create mode 100644 tests/regression_tests/ifp/inputs_true.dat create mode 100644 tests/regression_tests/ifp/results_true.dat create mode 100644 tests/regression_tests/ifp/test.py create mode 100644 tests/unit_tests/test_ifp.py diff --git a/include/openmc/bank.h b/include/openmc/bank.h index 95386514d7b..fd8fbd73ee5 100644 --- a/include/openmc/bank.h +++ b/include/openmc/bank.h @@ -22,6 +22,14 @@ extern SharedArray surf_source_bank; extern SharedArray fission_bank; +extern vector> ifp_source_delayed_group_bank; + +extern vector> ifp_source_lifetime_bank; + +extern vector> ifp_fission_delayed_group_bank; + +extern vector> ifp_fission_lifetime_bank; + extern vector progeny_per_particle; } // namespace simulation diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 605ae1839d8..97d0218d1a0 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -308,7 +308,10 @@ enum TallyScore { SCORE_FISS_Q_PROMPT = -14, // prompt fission Q-value SCORE_FISS_Q_RECOV = -15, // recoverable fission Q-value SCORE_DECAY_RATE = -16, // delayed neutron precursor decay rate - SCORE_PULSE_HEIGHT = -17 // pulse-height + SCORE_PULSE_HEIGHT = -17, // pulse-height + SCORE_IFP_TIME_NUM = -18, // IFP lifetime numerator + SCORE_IFP_BETA_NUM = -19, // IFP delayed fraction numerator + SCORE_IFP_DENOM = -20 // IFP common denominator }; // Global tally parameters diff --git a/include/openmc/eigenvalue.h b/include/openmc/eigenvalue.h index b456fee21eb..7d818ac16f7 100644 --- a/include/openmc/eigenvalue.h +++ b/include/openmc/eigenvalue.h @@ -79,6 +79,16 @@ void write_eigenvalue_hdf5(hid_t group); //! \param[in] group HDF5 group void read_eigenvalue_hdf5(hid_t group); +//============================================================================== +// Type definitions +//============================================================================== + +//! Deserialization info for IFP +struct DeserializationInfo { + int64_t index_local; + int64_t n; +}; + } // namespace openmc #endif // OPENMC_EIGENVALUE_H diff --git a/include/openmc/particle_data.h b/include/openmc/particle_data.h index 164148cce10..fbe75f6d57c 100644 --- a/include/openmc/particle_data.h +++ b/include/openmc/particle_data.h @@ -424,6 +424,9 @@ class ParticleData : public GeometryState { int cell_born_ {-1}; + // Iterated Fission Probability + double lifetime_ {0.0}; //!< neutron lifetime [s] + int n_collision_ {0}; bool write_track_ {false}; @@ -522,6 +525,10 @@ class ParticleData : public GeometryState { double& time_last() { return time_last_; } const double& time_last() const { return time_last_; } + // Particle lifetime + double& lifetime() { return lifetime_; } + const double& lifetime() const { return lifetime_; } + // What event took place, described in greater detail below TallyEvent& event() { return event_; } const TallyEvent& event() const { return event_; } diff --git a/include/openmc/settings.h b/include/openmc/settings.h index a95f1ced9f1..92600d81c04 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -24,6 +24,14 @@ enum class SSWCellType { To, }; +// Type of IFP parameters +enum class IFPParameter { + None, + Both, + BetaEffective, + GenerationTime, +}; + //============================================================================== // Global variable declarations //============================================================================== @@ -42,7 +50,8 @@ 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) + event_based; //!< use event-based mode (instead of history-based) +extern bool ifp; //!< Use IFP for kinetics parameters? extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular? extern bool material_cell_offsets; //!< create material cells offsets? extern "C" bool output_summary; //!< write summary.h5? @@ -109,6 +118,10 @@ extern array energy_cutoff; //!< Energy cutoff in [eV] for each particle type 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/openmc/settings.py b/openmc/settings.py index ddaac040191..6bf04654ea2 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -79,6 +79,13 @@ class Settings: .. versionadded:: 0.12 generations_per_batch : int Number of generations per batch + iterated_fission_probability: dict + Dictionary indicating the Iterated Fission Probability parameters. + Acceptable keys are: + + :parameter: Kinetics parameter to calculate between 'beta_effective' and + 'generation_time', or 'both' (str) + :n_generation: Number of generation (int) max_lost_particles : int Maximum number of lost particles @@ -335,6 +342,9 @@ def __init__(self, **kwargs): self._output = None + # Iterated Fission Probability + self._iterated_fission_probability = {} + # Output options self._statepoint = {} self._sourcepoint = {} @@ -760,6 +770,27 @@ def verbosity(self, verbosity: int): cv.check_less_than('verbosity', verbosity, 10, True) self._verbosity = verbosity + @property + def iterated_fission_probability(self) -> dict: + return self._iterated_fission_probability + + @iterated_fission_probability.setter + def iterated_fission_probability(self, iterated_fission_probability: dict): + cv.check_type( + "Iterated Fission Probability options", + iterated_fission_probability, + Mapping, + ) + for key, value in iterated_fission_probability.items(): + cv.check_value("Iterated Fission Probability key", key, ["parameter", "n_generation"]) + if key == "parameter": + cv.check_value("parameter", value, ["beta_effective", "generation_time", "both"]) + elif key == "n_generation": + cv.check_type("number of generation", value, Integral) + cv.check_greater_than("number of generation", value, 0) + + self._iterated_fission_probability = iterated_fission_probability + @property def tabular_legendre(self) -> dict: return self._tabular_legendre @@ -1347,6 +1378,16 @@ def _create_no_reduce_subelement(self, root): element = ET.SubElement(root, "no_reduce") element.text = str(self._no_reduce).lower() + def _create_iterated_fission_probability_subelements(self, root): + if self.iterated_fission_probability: + element = ET.SubElement(root, "iterated_fission_probability") + if 'parameter' in self._iterated_fission_probability: + subelement = ET.SubElement(element, "parameter") + subelement.text = str(self._iterated_fission_probability['parameter']) + if 'n_generation' in self._iterated_fission_probability: + subelement = ET.SubElement(element, "n_generation") + subelement.text = str(self._iterated_fission_probability['n_generation']) + def _create_tabular_legendre_subelements(self, root): if self.tabular_legendre: element = ET.SubElement(root, "tabular_legendre") @@ -1747,6 +1788,16 @@ def _verbosity_from_xml_element(self, root): if text is not None: self.verbosity = int(text) + def _iterated_fission_probability_from_xml_element(self, root): + elem = root.find('iterated_fission_probability') + if elem is not None: + text = get_text(elem, 'parameter') + if text is not None: + self.iterated_fission_probability['parameter'] = text + text = get_text(elem, 'n_generation') + if text is not None: + self.iterated_fission_probability['n_generation'] = int(text) + def _tabular_legendre_from_xml_element(self, root): elem = root.find('tabular_legendre') if elem is not None: @@ -1946,6 +1997,7 @@ def to_xml_element(self, mesh_memo=None): self._create_trigger_subelement(element) self._create_no_reduce_subelement(element) self._create_verbosity_subelement(element) + self._create_iterated_fission_probability_subelements(element) self._create_tabular_legendre_subelements(element) self._create_temperature_subelements(element) self._create_trace_subelement(element) @@ -2052,6 +2104,7 @@ def from_xml_element(cls, elem, meshes=None): settings._trigger_from_xml_element(elem) settings._no_reduce_from_xml_element(elem) settings._verbosity_from_xml_element(elem) + settings._iterated_fission_probability_from_xml_element(elem) settings._tabular_legendre_from_xml_element(elem) settings._temperature_from_xml_element(elem) settings._trace_from_xml_element(elem) diff --git a/src/bank.cpp b/src/bank.cpp index 8d00d544090..5fff1e3458f 100644 --- a/src/bank.cpp +++ b/src/bank.cpp @@ -26,6 +26,14 @@ SharedArray surf_source_bank; // function. SharedArray fission_bank; +vector> ifp_source_delayed_group_bank; + +vector> ifp_source_lifetime_bank; + +vector> ifp_fission_delayed_group_bank; + +vector> ifp_fission_lifetime_bank; + // Each entry in this vector corresponds to the number of progeny produced // this generation for the particle located at that index. This vector is // used to efficiently sort the fission bank after each iteration. @@ -82,6 +90,10 @@ void sort_fission_bank() // over provisioned, so we can use that as scratch space. SourceSite* sorted_bank; vector sorted_bank_holder; + vector* sorted_ifp_delayed_group_bank; + vector> sorted_ifp_delayed_group_bank_holder; + vector* sorted_ifp_lifetime_bank; + vector> sorted_ifp_lifetime_bank_holder; // If there is not enough space, allocate a temporary vector and point to it if (simulation::fission_bank.size() > @@ -92,6 +104,21 @@ void sort_fission_bank() sorted_bank = &simulation::fission_bank[simulation::fission_bank.size()]; } + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + sorted_ifp_delayed_group_bank_holder.resize( + simulation::fission_bank.size()); + sorted_ifp_delayed_group_bank = + sorted_ifp_delayed_group_bank_holder.data(); + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + sorted_ifp_lifetime_bank_holder.resize(simulation::fission_bank.size()); + sorted_ifp_lifetime_bank = sorted_ifp_lifetime_bank_holder.data(); + } + } + // Use parent and progeny indices to sort fission bank for (int64_t i = 0; i < simulation::fission_bank.size(); i++) { const auto& site = simulation::fission_bank[i]; @@ -102,11 +129,38 @@ void sort_fission_bank() "shared fission bank size."); } sorted_bank[idx] = site; + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + const auto& ifp_delayed_groups = + simulation::ifp_fission_delayed_group_bank[i]; + sorted_ifp_delayed_group_bank[idx] = ifp_delayed_groups; + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + const auto& ifp_lifetimes = simulation::ifp_fission_lifetime_bank[i]; + sorted_ifp_lifetime_bank[idx] = ifp_lifetimes; + } + } } // Copy sorted bank into the fission bank std::copy(sorted_bank, sorted_bank + simulation::fission_bank.size(), simulation::fission_bank.data()); + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + std::copy(sorted_ifp_delayed_group_bank, + sorted_ifp_delayed_group_bank + simulation::fission_bank.size(), + simulation::ifp_fission_delayed_group_bank.data()); + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + std::copy(sorted_ifp_lifetime_bank, + sorted_ifp_lifetime_bank + simulation::fission_bank.size(), + simulation::ifp_fission_lifetime_bank.data()); + } + } } //============================================================================== diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index 8669d76f947..69a05cf4a0a 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -153,11 +153,41 @@ void synchronize_bank() // Allocate temporary source bank -- we don't really know how many fission // sites were created, so overallocate by a factor of 3 int64_t index_temp = 0; + vector temp_sites(3 * simulation::work_per_rank); + vector> temp_delayed_groups; + vector> temp_lifetimes; + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + temp_delayed_groups.resize(3 * simulation::work_per_rank); + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + temp_lifetimes.resize(3 * simulation::work_per_rank); + } + } for (int64_t i = 0; i < simulation::fission_bank.size(); i++) { const auto& site = simulation::fission_bank[i]; + // Declare pointer to constant IFP data that will be initialized if + // ifp is requested by the user. + const vector* delayed_groups_ptr; + const vector* lifetimes_ptr; + + // Initialize IFP data pointer + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + delayed_groups_ptr = &simulation::ifp_fission_delayed_group_bank[i]; + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + lifetimes_ptr = &simulation::ifp_fission_lifetime_bank[i]; + } + } + // If there are less than n_particles particles banked, automatically add // int(n_particles/total) sites to temp_sites. For example, if you need // 1000 and 300 were banked, this would add 3 source sites per banked site @@ -165,6 +195,16 @@ void synchronize_bank() if (total < settings::n_particles) { for (int64_t j = 1; j <= settings::n_particles / total; ++j) { temp_sites[index_temp] = site; + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + temp_delayed_groups[index_temp] = *delayed_groups_ptr; + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + temp_lifetimes[index_temp] = *lifetimes_ptr; + } + } ++index_temp; } } @@ -172,6 +212,16 @@ void synchronize_bank() // Randomly sample sites needed if (prn(&seed) < p_sample) { temp_sites[index_temp] = site; + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + temp_delayed_groups[index_temp] = *delayed_groups_ptr; + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + temp_lifetimes[index_temp] = *lifetimes_ptr; + } + } ++index_temp; } } @@ -188,6 +238,8 @@ void synchronize_bank() MPI_Exscan(&index_temp, &start, 1, MPI_INT64_T, MPI_SUM, mpi::intracomm); finish = start + index_temp; + // TODO: protect for MPI_Exscan at rank 0 + // Allocate space for bank_position if this hasn't been done yet int64_t bank_position[mpi::n_procs]; MPI_Allgather( @@ -211,9 +263,23 @@ void synchronize_bank() // If we have too few sites, repeat sites from the very end of the // fission bank sites_needed = settings::n_particles - finish; + // TODO: sites_needed > simulation::fission_bank.size() or other test to + // make sure we don't need info from other proc for (int i = 0; i < sites_needed; ++i) { int i_bank = simulation::fission_bank.size() - sites_needed + i; temp_sites[index_temp] = simulation::fission_bank[i_bank]; + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + temp_delayed_groups[index_temp] = + simulation::ifp_fission_delayed_group_bank[i_bank]; + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + temp_lifetimes[index_temp] = + simulation::ifp_fission_lifetime_bank[i_bank]; + } + } ++index_temp; } } @@ -229,15 +295,47 @@ void synchronize_bank() // ========================================================================== // SEND BANK SITES TO NEIGHBORS + // If IFP, broadcast the number of generation from the size of the first + // element + int ifp_n_generation; + if (settings::ifp) { + // TODO: + if (mpi::rank == 0) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + ifp_n_generation = static_cast(temp_delayed_groups[0].size()); + } else { + ifp_n_generation = static_cast(temp_lifetimes[0].size()); + } + } + MPI_Bcast(&ifp_n_generation, 1, MPI_INT, 0, mpi::intracomm); + } + int64_t index_local = 0; vector requests; + vector send_delayed_groups; + vector send_lifetimes; + if (start < settings::n_particles) { // Determine the index of the processor which has the first part of the // source_bank for the local processor int neighbor = upper_bound_index( simulation::work_index.begin(), simulation::work_index.end(), start); + // Resize IFP send buffers + if (settings::ifp && mpi::n_procs > 1) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + send_delayed_groups.resize( + ifp_n_generation * 3 * simulation::work_per_rank); + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + send_lifetimes.resize(ifp_n_generation * 3 * simulation::work_per_rank); + } + } + while (start < finish) { // Determine the number of sites to send int64_t n = @@ -250,6 +348,41 @@ void synchronize_bank() MPI_Isend(&temp_sites[index_local], static_cast(n), mpi::source_site, neighbor, mpi::rank, mpi::intracomm, &requests.back()); + + if (settings::ifp) { + + for (int i = index_local; i < index_local + n; i++) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + std::copy(temp_delayed_groups[i].begin(), + temp_delayed_groups[i].end(), + send_delayed_groups.begin() + i * ifp_n_generation); + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + std::copy(temp_lifetimes[i].begin(), temp_lifetimes[i].end(), + send_lifetimes.begin() + i * ifp_n_generation); + } + } + + // Send delayed groups + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + requests.emplace_back(); + MPI_Isend(&send_delayed_groups[ifp_n_generation * index_local], + ifp_n_generation * static_cast(n), MPI_INT, neighbor, + mpi::rank, mpi::intracomm, &requests.back()); + } + + // Send lifetimes + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + requests.emplace_back(); + MPI_Isend(&send_lifetimes[ifp_n_generation * index_local], + ifp_n_generation * static_cast(n), MPI_DOUBLE, neighbor, + mpi::rank, mpi::intracomm, &requests.back()); + } + } } // Increment all indices @@ -271,6 +404,10 @@ void synchronize_bank() start = simulation::work_index[mpi::rank]; index_local = 0; + vector recv_delayed_groups; + vector recv_lifetimes; + vector deserialization_info; + // Determine what process has the source sites that will need to be stored at // the beginning of this processor's source bank. @@ -282,6 +419,18 @@ void synchronize_bank() upper_bound_index(bank_position, bank_position + mpi::n_procs, start); } + // Resize IFP receive buffers + if (settings::ifp && mpi::n_procs > 1) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + recv_delayed_groups.resize(ifp_n_generation * simulation::work_per_rank); + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + recv_lifetimes.resize(ifp_n_generation * simulation::work_per_rank); + } + } + while (start < simulation::work_index[mpi::rank + 1]) { // Determine how many sites need to be received int64_t n; @@ -301,13 +450,53 @@ 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) { + + // Receive delayed groups + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + requests.emplace_back(); + MPI_Irecv(&recv_delayed_groups[ifp_n_generation * index_local], + ifp_n_generation * static_cast(n), MPI_INT, neighbor, neighbor, + mpi::intracomm, &requests.back()); + } + + // Receive lifetimes + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + requests.emplace_back(); + MPI_Irecv(&recv_lifetimes[ifp_n_generation * index_local], + ifp_n_generation * static_cast(n), MPI_DOUBLE, neighbor, + neighbor, mpi::intracomm, &requests.back()); + } + + // Deserialization info to reconstruct data later + DeserializationInfo info = {index_local, n}; + deserialization_info.push_back(info); + } + } else { - // If the source sites are on this procesor, we can simply copy them + // If the source sites are on this processor, we can simply copy them // from the temp_sites bank index_temp = start - bank_position[mpi::rank]; std::copy(&temp_sites[index_temp], &temp_sites[index_temp + n], &simulation::source_bank[index_local]); + + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + std::copy(&temp_delayed_groups[index_temp], + &temp_delayed_groups[index_temp + n], + &simulation::ifp_source_delayed_group_bank[index_local]); + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + std::copy(&temp_lifetimes[index_temp], + &temp_lifetimes[index_temp + n], + &simulation::ifp_source_lifetime_bank[index_local]); + } + } } // Increment all indices @@ -323,10 +512,65 @@ void synchronize_bank() int n_request = requests.size(); MPI_Waitall(n_request, requests.data(), MPI_STATUSES_IGNORE); + if (settings::ifp) { + + // Deserialize + int64_t n; + for (auto info : deserialization_info) { + index_local = info.index_local; + n = info.n; + + // Store deserialized data in banks + for (int i = index_local; i < index_local + n; i++) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + vector delayed_groups_received( + recv_delayed_groups.begin() + ifp_n_generation * i, + recv_delayed_groups.begin() + ifp_n_generation * (i + 1)); + simulation::ifp_source_delayed_group_bank[i] = + delayed_groups_received; + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + vector lifetimes_received( + recv_lifetimes.begin() + ifp_n_generation * i, + recv_lifetimes.begin() + ifp_n_generation * (i + 1)); + simulation::ifp_source_lifetime_bank[i] = lifetimes_received; + } + } + } + + // Clear IFP buffers + send_delayed_groups.clear(); + send_lifetimes.clear(); + recv_delayed_groups.clear(); + recv_lifetimes.clear(); + + // Clear deserialization info + deserialization_info.clear(); + } + #else std::copy(temp_sites.data(), temp_sites.data() + settings::n_particles, simulation::source_bank.begin()); + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + std::copy(temp_delayed_groups.data(), + temp_delayed_groups.data() + settings::n_particles, + simulation::ifp_source_delayed_group_bank.begin()); + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + std::copy(temp_lifetimes.data(), + temp_lifetimes.data() + settings::n_particles, + simulation::ifp_source_lifetime_bank.begin()); + } + } #endif + temp_sites.clear(); + temp_delayed_groups.clear(); + temp_lifetimes.clear(); simulation::time_bank_sendrecv.stop(); simulation::time_bank.stop(); diff --git a/src/finalize.cpp b/src/finalize.cpp index 2bde0e3387c..a574cc5b840 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -163,8 +163,9 @@ int openmc_finalize() // Free all MPI types #ifdef OPENMC_MPI - if (mpi::source_site != MPI_DATATYPE_NULL) + if (mpi::source_site != MPI_DATATYPE_NULL) { MPI_Type_free(&mpi::source_site); + } #endif return 0; diff --git a/src/output.cpp b/src/output.cpp index 5fdbea1304e..ae5d3658aeb 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -601,6 +601,9 @@ const std::unordered_map score_names = { {SCORE_FISS_Q_RECOV, "Recoverable fission power"}, {SCORE_CURRENT, "Current"}, {SCORE_PULSE_HEIGHT, "pulse-height"}, + {SCORE_IFP_TIME_NUM, "IFP lifetime numerator"}, + {SCORE_IFP_BETA_NUM, "IFP delayed fraction numerator"}, + {SCORE_IFP_DENOM, "IFP common denominator"}, }; //! Create an ASCII output file showing all tally results. diff --git a/src/particle.cpp b/src/particle.cpp index 64c50c9438f..ad9a10c1ca3 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -114,6 +114,7 @@ void Particle::from_source(const SourceSite* src) n_collision() = 0; fission() = false; zero_flux_derivs(); + lifetime() = 0.0; // Copy attributes from source bank site type() = src->particle; @@ -233,6 +234,7 @@ void Particle::event_advance() coord(j).r += distance * coord(j).u; } this->time() += distance / this->speed(); + this->lifetime() += distance / this->speed(); // Kill particle if its time exceeds the cutoff bool hit_time_boundary = false; @@ -240,6 +242,7 @@ void Particle::event_advance() if (time() > time_cutoff) { double dt = time() - time_cutoff; time() = time_cutoff; + lifetime() = time_cutoff; double push_back_distance = speed() * dt; this->move_distance(-push_back_distance); diff --git a/src/physics.cpp b/src/physics.cpp index 69e74f2eafd..5909d8bda56 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -209,6 +209,59 @@ void create_fission_sites(Particle& p, int i_nuclide, const Reaction& rx) // Sample delayed group and angle/energy for fission reaction sample_fission_neutron(i_nuclide, rx, &site, p); + // Iterated Fission Probability (IFP) method + // Needs to be done after the delayed group is found + + vector updated_ifp_delayed_groups; + vector updated_ifp_lifetimes; + + if (settings::ifp) { + + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + + const auto& ifp_delayed_groups = + simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; + size_t idx = ifp_delayed_groups.size(); + + if (idx < settings::ifp_n_generation) { + updated_ifp_delayed_groups.resize(idx + 1); + for (size_t i = 0; i < idx; i++) { + updated_ifp_delayed_groups[i] = ifp_delayed_groups[i]; + } + updated_ifp_delayed_groups[idx] = site.delayed_group; + } else if (idx == settings::ifp_n_generation) { + updated_ifp_delayed_groups.resize(idx); + for (size_t i = 0; i < idx - 1; i++) { + updated_ifp_delayed_groups[i] = ifp_delayed_groups[i + 1]; + } + updated_ifp_delayed_groups[idx - 1] = site.delayed_group; + } + } + + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + + const auto& ifp_lifetimes = + simulation::ifp_source_lifetime_bank[p.current_work() - 1]; + size_t idx = ifp_lifetimes.size(); + + if (idx < settings::ifp_n_generation) { + updated_ifp_lifetimes.resize(idx + 1); + for (size_t i = 0; i < idx; i++) { + updated_ifp_lifetimes[i] = ifp_lifetimes[i]; + } + updated_ifp_lifetimes[idx] = p.lifetime(); + } else if (idx == settings::ifp_n_generation) { + updated_ifp_lifetimes.resize(idx); + for (size_t i = 0; i < idx - 1; i++) { + updated_ifp_lifetimes[i] = ifp_lifetimes[i + 1]; + } + updated_ifp_lifetimes[idx - 1] = p.lifetime(); + } + } + } + // Store fission site in bank if (use_fission_bank) { int64_t idx = simulation::fission_bank.thread_safe_append(site); @@ -226,6 +279,21 @@ void create_fission_sites(Particle& p, int i_nuclide, const Reaction& rx) // Break out of loop as no more sites can be added to fission bank break; } + // If IFP, add the IFP information in the IFP banks using the same index + // as the one used to append the fission site to the fission bank. + // Multithreading protection is guaranteed by the index returned by the + // previous thread_safe_append call. + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + simulation::ifp_fission_delayed_group_bank[idx] = + updated_ifp_delayed_groups; + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + simulation::ifp_fission_lifetime_bank[idx] = updated_ifp_lifetimes; + } + } } else { p.secondary_bank().push_back(site); } diff --git a/src/reaction.cpp b/src/reaction.cpp index 0643cb08bdd..2ac170dd5b7 100644 --- a/src/reaction.cpp +++ b/src/reaction.cpp @@ -172,6 +172,9 @@ std::unordered_map REACTION_NAME_MAP { {SCORE_FISS_Q_PROMPT, "fission-q-prompt"}, {SCORE_FISS_Q_RECOV, "fission-q-recoverable"}, {SCORE_PULSE_HEIGHT, "pulse-height"}, + {SCORE_IFP_TIME_NUM, "ifp-time-numerator"}, + {SCORE_IFP_BETA_NUM, "ifp-beta-numerator"}, + {SCORE_IFP_DENOM, "ifp-denominator"}, // Normal ENDF-based reactions {TOTAL_XS, "(n,total)"}, {ELASTIC, "(n,elastic)"}, diff --git a/src/settings.cpp b/src/settings.cpp index ba451e219c5..90c11459df5 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -51,6 +51,7 @@ bool create_fission_neutrons {true}; bool delayed_photon_scaling {true}; bool entropy_on {false}; bool event_based {false}; +bool ifp {false}; bool legendre_to_tabular {true}; bool material_cell_offsets {true}; bool output_summary {true}; @@ -102,6 +103,8 @@ int max_particle_events {1000000}; 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 {0}; +IFPParameter ifp_parameter {IFPParameter::None}; int legendre_to_tabular_points {C_NONE}; int max_order {0}; int n_log_bins {8000}; @@ -976,6 +979,55 @@ void read_settings_xml(pugi::xml_node root) temperature_range[1] = range.at(1); } + // Check for Iterated Fission Probability (IFP) + if (check_for_node(root, "iterated_fission_probability")) { + + // IFP only works with eigenvalue calculations + if (run_mode == RunMode::EIGENVALUE) { + ifp = true; + // Get iterated_fission_probability write node + xml_node node_ifp = root.child("iterated_fission_probability"); + + // Parameter to compute + if (check_for_node(node_ifp, "parameter")) { + std::string temp_str = + get_node_value(node_ifp, "parameter", true, true); + if (temp_str == "beta_effective") { + ifp_parameter = IFPParameter::BetaEffective; + } else if (temp_str == "generation_time") { + ifp_parameter = IFPParameter::GenerationTime; + } else if (temp_str == "both") { + ifp_parameter = IFPParameter::Both; + } else { + fatal_error("Unrecognized parameter for IFP: " + temp_str); + } + } else { + fatal_error(" should be declared for ifp."); + } + + // Number of generation + if (check_for_node(node_ifp, "n_generation")) { + ifp_n_generation = std::stoi(get_node_value(node_ifp, "n_generation")); + if (ifp_n_generation <= 0) { + fatal_error(" 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(" must be lower than or equal to the " + "number of inactive cycles."); + } + } else { + fatal_error(" must be specified as a subelement of " + "."); + } + } else { + fatal_error( + "Iterated Fission Probability can only be used in an eigenvalue " + "calculation."); + } + } + // Check for tabular_legendre options if (check_for_node(root, "tabular_legendre")) { // Get pointer to tabular_legendre node diff --git a/src/simulation.cpp b/src/simulation.cpp index be03e48553c..81c714993ff 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -333,6 +333,23 @@ void allocate_banks() // Allocate fission bank init_fission_bank(3 * simulation::work_per_rank); + + // Allocate IFP bank + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + simulation::ifp_source_delayed_group_bank.resize( + simulation::work_per_rank); + simulation::ifp_fission_delayed_group_bank.resize( + 3 * simulation::work_per_rank); + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + simulation::ifp_source_lifetime_bank.resize(simulation::work_per_rank); + simulation::ifp_fission_lifetime_bank.resize( + 3 * simulation::work_per_rank); + } + } } if (settings::surf_source_write) { diff --git a/src/source.cpp b/src/source.cpp index 15fe8433ba5..4a60a9df422 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -594,6 +594,20 @@ void initialize_source() // sample external source distribution simulation::source_bank[i] = sample_external_source(&seed); + + // Initialize IFP data + if (settings::ifp) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + vector ifp_delayed_groups; + simulation::ifp_source_delayed_group_bank[i] = ifp_delayed_groups; + } + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + vector ifp_lifetimes; + simulation::ifp_source_lifetime_bank[i] = ifp_lifetimes; + } + } } // Write out initial source diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 674987b8f13..73dadfbc6f9 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -584,7 +584,12 @@ void Tally::set_scores(const vector& scores) } } } + break; + case SCORE_IFP_TIME_NUM: + case SCORE_IFP_BETA_NUM: + case SCORE_IFP_DENOM: + estimator_ = TallyEstimator::COLLISION; break; } diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index e798161ec2f..a96cafb66a8 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -890,6 +890,61 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, score_fission_q(p, score_bin, tally, flux, i_nuclide, atom_density); break; + case SCORE_IFP_TIME_NUM: + if (settings::ifp) { + if ((p.type() == Type::neutron) && (p.fission())) { + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + const auto& lifetimes = + simulation::ifp_source_lifetime_bank[p.current_work() - 1]; + int n_generation = static_cast(lifetimes.size()); + if (n_generation == settings::ifp_n_generation) { + score = lifetimes[0] * p.wgt_last(); + } + } + } + } + break; + + case SCORE_IFP_BETA_NUM: + if (settings::ifp) { + if ((p.type() == Type::neutron) && (p.fission())) { + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + const auto& delayed_groups = + simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; + int n_generation = static_cast(delayed_groups.size()); + if (n_generation == settings::ifp_n_generation) { + if (delayed_groups[0] > 0) { + score = p.wgt_last(); + } + } + } + } + } + break; + + case SCORE_IFP_DENOM: + if (settings::ifp) { + if ((p.type() == Type::neutron) && (p.fission())) { + int n_generation; + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + n_generation = static_cast( + simulation::ifp_source_delayed_group_bank[p.current_work() - 1] + .size()); + } else { + n_generation = static_cast( + simulation::ifp_source_lifetime_bank[p.current_work() - 1] + .size()); + } + if (n_generation == settings::ifp_n_generation) { + score = p.wgt_last(); + } + } + } + break; + case N_2N: case N_3N: case N_4N: diff --git a/tests/regression_tests/ifp/__init__.py b/tests/regression_tests/ifp/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/regression_tests/ifp/inputs_true.dat b/tests/regression_tests/ifp/inputs_true.dat new file mode 100644 index 00000000000..84f5e91acce --- /dev/null +++ b/tests/regression_tests/ifp/inputs_true.dat @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + eigenvalue + 1000 + 20 + 5 + + + -10.0 -10.0 -10.0 10.0 10.0 10.0 + + + false + + both + 5 + + + + + ifp-time-numerator ifp-beta-numerator ifp-denominator + + + diff --git a/tests/regression_tests/ifp/results_true.dat b/tests/regression_tests/ifp/results_true.dat new file mode 100644 index 00000000000..a74e2bd78bc --- /dev/null +++ b/tests/regression_tests/ifp/results_true.dat @@ -0,0 +1,9 @@ +k-combined: +1.007452E+00 5.705278E-03 +tally 1: +8.996235E-08 +5.461421E-16 +4.800000E-02 +4.680000E-04 +1.512000E+01 +1.526063E+01 diff --git a/tests/regression_tests/ifp/test.py b/tests/regression_tests/ifp/test.py new file mode 100644 index 00000000000..7567f69b3a5 --- /dev/null +++ b/tests/regression_tests/ifp/test.py @@ -0,0 +1,56 @@ +"""Test the Iterated Fission Probability (IFP) method to compute adjoint-weighted +kinetics parameters using dedicated tallies.""" + +import openmc +import pytest + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture() +def ifp_model(): + openmc.reset_auto_ids() + model = openmc.model.Model() + + # Material + material = openmc.Material(name="core") + material.add_nuclide("U235", 1.0) + material.set_density('g/cm3', 16.0) + + # Geometry + radius = 10.0 + sphere = openmc.Sphere(r=radius, boundary_type="vacuum") + cell = openmc.Cell(region=-sphere, fill=material) + model.geometry = openmc.Geometry([cell]) + + # Settings + settings = openmc.Settings() + settings.run_mode = 'eigenvalue' + settings.particles = 1000 + settings.batches = 20 + settings.inactive = 5 + settings.photon_transport = False + settings.iterated_fission_probability = {"parameter": "both", "n_generation": 5} + + bounds = [ + -radius, -radius, -radius, + radius, radius, radius + ] + distribution = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) + settings.source = openmc.source.IndependentSource(space=distribution) + + model.settings = settings + + # Tally IFP scores + tally = openmc.Tally(name="ifp-scores") + tally.scores = ["ifp-time-numerator", "ifp-beta-numerator", "ifp-denominator"] + + tallies = openmc.Tallies([tally]) + model.tallies = tallies + + return model + + +def test_iterated_fission_probability(ifp_model): + harness = PyAPITestHarness("statepoint.20.h5", model=ifp_model) + harness.main() diff --git a/tests/unit_tests/test_ifp.py b/tests/unit_tests/test_ifp.py new file mode 100644 index 00000000000..34199704c8c --- /dev/null +++ b/tests/unit_tests/test_ifp.py @@ -0,0 +1,43 @@ +"""Test the Iterated Fission Probability (IFP) method to compute adjoint-weighted +kinetics parameters using dedicated tallies.""" + +import pytest +import openmc + + +def test_xml_serialization(run_in_tmpdir): + """Check that a simple use case can be written and read in XML.""" + parameter = {"n_generation": 5} + settings = openmc.Settings() + settings.iterated_fission_probability = parameter + settings.export_to_xml() + + read_settings = openmc.Settings.from_xml() + assert read_settings.iterated_fission_probability == parameter + + +@pytest.fixture(scope="module") +def geometry(): + openmc.reset_auto_ids() + material = openmc.Material() + sphere = openmc.Sphere(r=1.0, boundary_type="vacuum") + cell = openmc.Cell(region=-sphere, fill=material) + return openmc.Geometry([cell]) + + +@pytest.mark.parametrize( + "settings, error", + [ + ({"iterated_fission_probability": {"n_generation": 0}}, ValueError), + ({"run_mode": "fixed source", "iterated_fission_probability": {"n_generation": 5}}, RuntimeError), + ({"n_inactive": 6, "iterated_fission_probability": {"n_generation": 5}}, RuntimeError) + ], +) +def test_exceptions(settings, error, run_in_tmpdir, geometry): + """Test settings configuration that should return an error.""" + with pytest.raises(error): + settings = openmc.Settings(**settings) + settings.particles = 100 + settings.batches = 10 + model = openmc.Model(geometry=geometry, settings=settings) + model.run() From b069abb37e6840ebf04cc8af4ad25d8baa802592 Mon Sep 17 00:00:00 2001 From: realmisch <79525520+realmisch@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:17:51 -0500 Subject: [PATCH 02/17] Quick Multigroup IFP Adapting of the tally scores to give multigroup beta-effective values when passed a delayed group filter. --- src/tallies/tally.cpp | 2 +- src/tallies/tally_scoring.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 73dadfbc6f9..806d46b6722 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -498,7 +498,7 @@ void Tally::set_scores(const vector& scores) for (auto score_str : scores) { // Make sure a delayed group filter wasn't used with an incompatible score. if (delayedgroup_filter_ != C_NONE) { - if (score_str != "delayed-nu-fission" && score_str != "decay-rate") + if (score_str != "delayed-nu-fission" && score_str != "decay-rate" && score_str != "ifp-beta-numerator") fatal_error("Cannot tally " + score_str + "with a delayedgroup filter"); } diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index a96cafb66a8..26ce715ccf6 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -915,7 +915,7 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; int n_generation = static_cast(delayed_groups.size()); if (n_generation == settings::ifp_n_generation) { - if (delayed_groups[0] > 0) { + if (delayed_groups[0] == filter_index + 1) { score = p.wgt_last(); } } From 189b929b1033303692b2040ae7aaf5461d4bb923 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 14 Jan 2025 12:09:39 -0600 Subject: [PATCH 03/17] Small changes in settings.py --- openmc/settings.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 6bf04654ea2..7e1ddf145e2 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -79,7 +79,7 @@ class Settings: .. versionadded:: 0.12 generations_per_batch : int Number of generations per batch - iterated_fission_probability: dict + iterated_fission_probability : dict Dictionary indicating the Iterated Fission Probability parameters. Acceptable keys are: @@ -782,12 +782,12 @@ def iterated_fission_probability(self, iterated_fission_probability: dict): Mapping, ) for key, value in iterated_fission_probability.items(): - cv.check_value("Iterated Fission Probability key", key, ["parameter", "n_generation"]) + cv.check_value("Iterated Fission Probability key", key, {"parameter", "n_generation"}) if key == "parameter": - cv.check_value("parameter", value, ["beta_effective", "generation_time", "both"]) + cv.check_value("parameter", value, {"beta_effective", "generation_time", "both"}) elif key == "n_generation": - cv.check_type("number of generation", value, Integral) - cv.check_greater_than("number of generation", value, 0) + cv.check_type("number of generations", value, Integral) + cv.check_greater_than("number of generations", value, 0) self._iterated_fission_probability = iterated_fission_probability @@ -1792,11 +1792,13 @@ def _iterated_fission_probability_from_xml_element(self, root): elem = root.find('iterated_fission_probability') if elem is not None: text = get_text(elem, 'parameter') + ifp = {} if text is not None: - self.iterated_fission_probability['parameter'] = text + ifp['parameter'] = text text = get_text(elem, 'n_generation') if text is not None: - self.iterated_fission_probability['n_generation'] = int(text) + ifp['n_generation'] = int(text) + self.iterated_fission_probability = ifp def _tabular_legendre_from_xml_element(self, root): elem = root.find('tabular_legendre') From 0495246d97a1f033a9a1ec3b2eaab6dc61ad4db7 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 14 Jan 2025 12:15:09 -0600 Subject: [PATCH 04/17] Add new scores in openmc/lib/tally.py --- openmc/lib/tally.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openmc/lib/tally.py b/openmc/lib/tally.py index d0b34aedc26..c17b16597f9 100644 --- a/openmc/lib/tally.py +++ b/openmc/lib/tally.py @@ -104,7 +104,9 @@ -5: 'absorption', -6: 'fission', -7: 'nu-fission', -8: 'kappa-fission', -9: 'current', -10: 'events', -11: 'delayed-nu-fission', -12: 'prompt-nu-fission', -13: 'inverse-velocity', -14: 'fission-q-prompt', - -15: 'fission-q-recoverable', -16: 'decay-rate', -17: 'pulse-height' + -15: 'fission-q-recoverable', -16: 'decay-rate', -17: 'pulse-height', + -18: 'ifp-time-numerator', -19: 'ifp-beta-numerator', + -20: 'ifp-denominator', } _ESTIMATORS = { 0: 'analog', 1: 'tracklength', 2: 'collision' From d50f83a3f8dd686e8879f1b6ad9b8336b4a0bcf0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 14 Jan 2025 16:21:21 -0600 Subject: [PATCH 05/17] Only call Particle::speed() once --- src/particle.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/particle.cpp b/src/particle.cpp index ad9a10c1ca3..397be56045c 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -233,8 +233,9 @@ void Particle::event_advance() for (int j = 0; j < n_coord(); ++j) { coord(j).r += distance * coord(j).u; } - this->time() += distance / this->speed(); - this->lifetime() += distance / this->speed(); + double dt = distance / this->speed(); + this->time() += dt; + this->lifetime() += dt; // Kill particle if its time exceeds the cutoff bool hit_time_boundary = false; From 94154a1e7953c2129e7ed3eef58c6cc9c890f950 Mon Sep 17 00:00:00 2001 From: realmisch <79525520+realmisch@users.noreply.github.com> Date: Thu, 16 Jan 2025 10:05:38 -0500 Subject: [PATCH 06/17] Fixed Tally Index Fixed setting the Tally Index for the DelayedGroup Filter. --- src/tallies/tally_scoring.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index 26ce715ccf6..46c1940f812 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -915,7 +915,8 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; int n_generation = static_cast(delayed_groups.size()); if (n_generation == settings::ifp_n_generation) { - if (delayed_groups[0] == filter_index + 1) { + if (delayed_groups[0] > 0) { + filter_index = delayed_groups[0] - 1; score = p.wgt_last(); } } From 825bcd787ac07c7255cbbf12f826b8797a732f70 Mon Sep 17 00:00:00 2001 From: realmisch <79525520+realmisch@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:03:21 -0500 Subject: [PATCH 07/17] Cache optimization and ifp convenience methods changed @lru_cache to the lighter @cache from Python 3.9 added _init_ifp() to automatically create IFP tallies for the enabled setting and ifp_results() for quick data retrieval with automatic arithmetic. --- openmc/model/model.py | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 2ea579ab7df..3379309d445 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1,6 +1,6 @@ from __future__ import annotations from collections.abc import Iterable -from functools import lru_cache +from functools import cache import os from pathlib import Path from numbers import Integral @@ -81,6 +81,9 @@ def __init__(self, geometry=None, materials=None, settings=None, if plots is not None: self.plots = plots + if settings.iterated_fission_probability: + self._init_ifp() + @property def geometry(self) -> openmc.Geometry | None: return self._geometry @@ -150,7 +153,7 @@ def is_initialized(self) -> bool: return False @property - @lru_cache(maxsize=None) + @cache def _materials_by_id(self) -> dict: """Dictionary mapping material ID --> material""" if self.materials: @@ -160,14 +163,14 @@ def _materials_by_id(self) -> dict: return {mat.id: mat for mat in mats} @property - @lru_cache(maxsize=None) + @cache def _cells_by_id(self) -> dict: """Dictionary mapping cell ID --> cell""" cells = self.geometry.get_all_cells() return {cell.id: cell for cell in cells.values()} @property - @lru_cache(maxsize=None) + @cache def _cells_by_name(self) -> dict[int, openmc.Cell]: # Get the names maps, but since names are not unique, store a set for # each name key. In this way when the user requests a change by a name, @@ -180,7 +183,7 @@ def _cells_by_name(self) -> dict[int, openmc.Cell]: return result @property - @lru_cache(maxsize=None) + @cache def _materials_by_name(self) -> dict[int, openmc.Material]: if self.materials is None: mats = self.geometry.get_all_materials().values() @@ -193,6 +196,31 @@ def _materials_by_name(self) -> dict[int, openmc.Material]: result[mat.name].add(mat) return result + def _init_ifp(self) -> None: + """Automate tally creation for calculating Iterated Fission Probability kinetics parameters""" + param = self.settings.iterated_fission_probability['parameter'] + if param == 'both' or param == 'generation_time': + gen_time_tally = openmc.Tally() + gen_time_tally.scores = ['ifp-time-numerator'] + self._tallies.append(gen_time_tally) + + if param == 'both' or param == 'beta_effective': + beta_tally = openmc.Tally() + beta_tally.scores = ['ifp-beta-numerator'] + + has_dg_filter = False + for tally in self._tallies: + if tally.contains_filter(openmc.DelayedGroupFilter): + has_dg_filter = True + beta_tally.filters = [tally.find_filter(openmc.DelayedGroupFilter)] + if not has_dg_filter: + beta_tally.filters = [openmc.DelayedGroupFilter(list(range(1,7)))] + self._tallies.append(beta_tally) + + denom_tally = openmc.Tally() + denom_tally.scores = ['ifp-denominator'] + self._tallies.append(denom_tally) + @classmethod def from_xml(cls, geometry='geometry.xml', materials='materials.xml', settings='settings.xml', tallies='tallies.xml', From c096f317f09b3d9da105efc98ec4fa721ca02715 Mon Sep 17 00:00:00 2001 From: realmisch <79525520+realmisch@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:03:37 -0500 Subject: [PATCH 08/17] Recommit --- openmc/statepoint.py | 48 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 13aac0caf7e..a1ca8bc19b3 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -7,6 +7,7 @@ import h5py import numpy as np from uncertainties import ufloat +from uncertainties.unumpy import uarray import openmc import openmc.checkvalue as cv @@ -119,7 +120,7 @@ class StatePoint: Version of OpenMC summary : None or openmc.Summary A summary object if the statepoint has been linked with a summary file - + """ def __init__(self, filepath, autolink=True): @@ -691,3 +692,48 @@ def link_with_summary(self, summary): tally_filter.paths = cell.paths self._summary = summary + + def ifp_results(self,param): + """ + Calculates kinetics parameters from Iterated Fission Probablity tallies. + + Parameters + ---------- + param : string + A string specifying which parameter to return. Selection of + 'both', 'beta_effective', or 'generation_time'. + + Returns + ------- + result : dict of np.ndarray + A dictionary with 'Generation Time' and 'Beta Effective' keys holding arrays + with their respective values. + + Raises + ------ + LookupError + An error for when the IFP tallies are not in the StatePoint file. + """ + result = {} + try: + denom_tally = self.get_tally(scores = ['ifp-denominator'],exact_scores = True) + except LookupError: + raise LookupError('Iterated Fission Probability tally not found.') from None + denom_values = uarray(denom_tally.get_reshaped_data(), + denom_tally.get_reshaped_data(value = 'rel_err')) + + if param == 'both' or param == 'generation_time': + gen_time_tally = self.get_tally(scores = ['ifp-time-numerator'],exact_scores = True) + gen_time_values = uarray(gen_time_tally.get_reshaped_data(), + gen_time_tally.get_reshaped_data(value = 'rel_err')) + gen_time_values /= denom_values + result['Generation Time'] = gen_time_values.flatten() + + if param == 'both' or param == 'beta_effective': + beta_tally = self.get_tally(scores = ['ifp-beta-numerator'],exact_scores = True) + beta_values = uarray(beta_tally.get_reshaped_data(), + beta_tally.get_reshaped_data(value = 'rel_err')) + beta_values /= denom_values + result['Beta Effective'] = beta_values.flatten() + + return result From b12933aae7113ed968bbd25e1ac16f860746f037 Mon Sep 17 00:00:00 2001 From: realmisch <79525520+realmisch@users.noreply.github.com> Date: Fri, 24 Jan 2025 16:08:28 -0500 Subject: [PATCH 09/17] Cache optimization and ifp convenience methods changed @lru_cache to the lighter @cache from Python 3.9 added _init_ifp() to automatically create IFP tallies for the enabled setting and ifp_results() for quick data retrieval with automatic arithmetic. --- openmc/model/model.py | 38 +++++++++++++++++++++++++++++----- openmc/statepoint.py | 48 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 6 deletions(-) diff --git a/openmc/model/model.py b/openmc/model/model.py index 2ea579ab7df..3379309d445 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1,6 +1,6 @@ from __future__ import annotations from collections.abc import Iterable -from functools import lru_cache +from functools import cache import os from pathlib import Path from numbers import Integral @@ -81,6 +81,9 @@ def __init__(self, geometry=None, materials=None, settings=None, if plots is not None: self.plots = plots + if settings.iterated_fission_probability: + self._init_ifp() + @property def geometry(self) -> openmc.Geometry | None: return self._geometry @@ -150,7 +153,7 @@ def is_initialized(self) -> bool: return False @property - @lru_cache(maxsize=None) + @cache def _materials_by_id(self) -> dict: """Dictionary mapping material ID --> material""" if self.materials: @@ -160,14 +163,14 @@ def _materials_by_id(self) -> dict: return {mat.id: mat for mat in mats} @property - @lru_cache(maxsize=None) + @cache def _cells_by_id(self) -> dict: """Dictionary mapping cell ID --> cell""" cells = self.geometry.get_all_cells() return {cell.id: cell for cell in cells.values()} @property - @lru_cache(maxsize=None) + @cache def _cells_by_name(self) -> dict[int, openmc.Cell]: # Get the names maps, but since names are not unique, store a set for # each name key. In this way when the user requests a change by a name, @@ -180,7 +183,7 @@ def _cells_by_name(self) -> dict[int, openmc.Cell]: return result @property - @lru_cache(maxsize=None) + @cache def _materials_by_name(self) -> dict[int, openmc.Material]: if self.materials is None: mats = self.geometry.get_all_materials().values() @@ -193,6 +196,31 @@ def _materials_by_name(self) -> dict[int, openmc.Material]: result[mat.name].add(mat) return result + def _init_ifp(self) -> None: + """Automate tally creation for calculating Iterated Fission Probability kinetics parameters""" + param = self.settings.iterated_fission_probability['parameter'] + if param == 'both' or param == 'generation_time': + gen_time_tally = openmc.Tally() + gen_time_tally.scores = ['ifp-time-numerator'] + self._tallies.append(gen_time_tally) + + if param == 'both' or param == 'beta_effective': + beta_tally = openmc.Tally() + beta_tally.scores = ['ifp-beta-numerator'] + + has_dg_filter = False + for tally in self._tallies: + if tally.contains_filter(openmc.DelayedGroupFilter): + has_dg_filter = True + beta_tally.filters = [tally.find_filter(openmc.DelayedGroupFilter)] + if not has_dg_filter: + beta_tally.filters = [openmc.DelayedGroupFilter(list(range(1,7)))] + self._tallies.append(beta_tally) + + denom_tally = openmc.Tally() + denom_tally.scores = ['ifp-denominator'] + self._tallies.append(denom_tally) + @classmethod def from_xml(cls, geometry='geometry.xml', materials='materials.xml', settings='settings.xml', tallies='tallies.xml', diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 13aac0caf7e..a1ca8bc19b3 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -7,6 +7,7 @@ import h5py import numpy as np from uncertainties import ufloat +from uncertainties.unumpy import uarray import openmc import openmc.checkvalue as cv @@ -119,7 +120,7 @@ class StatePoint: Version of OpenMC summary : None or openmc.Summary A summary object if the statepoint has been linked with a summary file - + """ def __init__(self, filepath, autolink=True): @@ -691,3 +692,48 @@ def link_with_summary(self, summary): tally_filter.paths = cell.paths self._summary = summary + + def ifp_results(self,param): + """ + Calculates kinetics parameters from Iterated Fission Probablity tallies. + + Parameters + ---------- + param : string + A string specifying which parameter to return. Selection of + 'both', 'beta_effective', or 'generation_time'. + + Returns + ------- + result : dict of np.ndarray + A dictionary with 'Generation Time' and 'Beta Effective' keys holding arrays + with their respective values. + + Raises + ------ + LookupError + An error for when the IFP tallies are not in the StatePoint file. + """ + result = {} + try: + denom_tally = self.get_tally(scores = ['ifp-denominator'],exact_scores = True) + except LookupError: + raise LookupError('Iterated Fission Probability tally not found.') from None + denom_values = uarray(denom_tally.get_reshaped_data(), + denom_tally.get_reshaped_data(value = 'rel_err')) + + if param == 'both' or param == 'generation_time': + gen_time_tally = self.get_tally(scores = ['ifp-time-numerator'],exact_scores = True) + gen_time_values = uarray(gen_time_tally.get_reshaped_data(), + gen_time_tally.get_reshaped_data(value = 'rel_err')) + gen_time_values /= denom_values + result['Generation Time'] = gen_time_values.flatten() + + if param == 'both' or param == 'beta_effective': + beta_tally = self.get_tally(scores = ['ifp-beta-numerator'],exact_scores = True) + beta_values = uarray(beta_tally.get_reshaped_data(), + beta_tally.get_reshaped_data(value = 'rel_err')) + beta_values /= denom_values + result['Beta Effective'] = beta_values.flatten() + + return result From a9b1b4dacab90ccc0b2f89319319cb346492e1de Mon Sep 17 00:00:00 2001 From: Joffrey Dorville Date: Tue, 4 Feb 2025 16:27:56 -0600 Subject: [PATCH 10/17] Refactoring --- CMakeLists.txt | 1 + include/openmc/ifp.h | 14 ++++++++ src/ifp.cpp | 79 ++++++++++++++++++++++++++++++++++++++++++++ src/physics.cpp | 69 ++------------------------------------ 4 files changed, 97 insertions(+), 66 deletions(-) create mode 100644 include/openmc/ifp.h create mode 100644 src/ifp.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f4cc1b527b..b82c53c5c3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -353,6 +353,7 @@ list(APPEND libopenmc_SOURCES src/geometry.cpp src/geometry_aux.cpp src/hdf5_interface.cpp + src/ifp.cpp src/initialize.cpp src/lattice.cpp src/material.cpp diff --git a/include/openmc/ifp.h b/include/openmc/ifp.h new file mode 100644 index 00000000000..8162c5d1800 --- /dev/null +++ b/include/openmc/ifp.h @@ -0,0 +1,14 @@ +#ifndef OPENMC_IFP_H +#define OPENMC_IFP_H + +#include "openmc/particle.h" +#include "openmc/particle_data.h" + +namespace openmc { + +//! Iterated Fission Probability (IFP) method +void ifp(Particle& p, SourceSite& site, int64_t idx); + +} // namespace openmc + +#endif // OPENMC_PHYSICS_H \ No newline at end of file diff --git a/src/ifp.cpp b/src/ifp.cpp new file mode 100644 index 00000000000..f795ba29674 --- /dev/null +++ b/src/ifp.cpp @@ -0,0 +1,79 @@ +#include "openmc/ifp.h" + +#include "openmc/bank.h" +#include "openmc/particle.h" +#include "openmc/particle_data.h" +#include "openmc/settings.h" +#include "openmc/simulation.h" +#include "openmc/vector.h" + +namespace openmc { + +void ifp(Particle& p, SourceSite& site, int64_t idx) +{ + // Iterated Fission Probability (IFP) method + + // Needs to be done after the delayed group is found. + + // Add the IFP information in the IFP banks using the same index + // as the one used to append the fission site to the fission bank. + // Multithreading protection is guaranteed by the index returned by the + // thread_safe_append call in physics.cpp. + + // Beta effective + if (settings::ifp_parameter == IFPParameter::BetaEffective || + settings::ifp_parameter == IFPParameter::Both) { + + vector updated_ifp_delayed_groups; + + const auto& ifp_delayed_groups = + simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; + size_t ifp_idx = ifp_delayed_groups.size(); + + if (ifp_idx < settings::ifp_n_generation) { + updated_ifp_delayed_groups.resize(ifp_idx + 1); + for (size_t i = 0; i < ifp_idx; i++) { + updated_ifp_delayed_groups[i] = ifp_delayed_groups[i]; + } + updated_ifp_delayed_groups[ifp_idx] = site.delayed_group; + } else if (ifp_idx == settings::ifp_n_generation) { + updated_ifp_delayed_groups.resize(ifp_idx); + for (size_t i = 0; i < ifp_idx - 1; i++) { + updated_ifp_delayed_groups[i] = ifp_delayed_groups[i + 1]; + } + updated_ifp_delayed_groups[ifp_idx - 1] = site.delayed_group; + } + + simulation::ifp_fission_delayed_group_bank[idx] = + updated_ifp_delayed_groups; + } + + // Generation time + if (settings::ifp_parameter == IFPParameter::GenerationTime || + settings::ifp_parameter == IFPParameter::Both) { + + vector updated_ifp_lifetimes; + + const auto& ifp_lifetimes = + simulation::ifp_source_lifetime_bank[p.current_work() - 1]; + size_t ifp_idx = ifp_lifetimes.size(); + + if (ifp_idx < settings::ifp_n_generation) { + updated_ifp_lifetimes.resize(ifp_idx + 1); + for (size_t i = 0; i < ifp_idx; i++) { + updated_ifp_lifetimes[i] = ifp_lifetimes[i]; + } + updated_ifp_lifetimes[ifp_idx] = p.lifetime(); + } else if (ifp_idx == settings::ifp_n_generation) { + updated_ifp_lifetimes.resize(ifp_idx); + for (size_t i = 0; i < ifp_idx - 1; i++) { + updated_ifp_lifetimes[i] = ifp_lifetimes[i + 1]; + } + updated_ifp_lifetimes[ifp_idx - 1] = p.lifetime(); + } + + simulation::ifp_fission_lifetime_bank[idx] = updated_ifp_lifetimes; + } +} + +} // namespace openmc diff --git a/src/physics.cpp b/src/physics.cpp index 5909d8bda56..93c6a6b0b71 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -7,6 +7,7 @@ #include "openmc/eigenvalue.h" #include "openmc/endf.h" #include "openmc/error.h" +#include "openmc/ifp.h" #include "openmc/material.h" #include "openmc/math_functions.h" #include "openmc/message_passing.h" @@ -209,59 +210,6 @@ void create_fission_sites(Particle& p, int i_nuclide, const Reaction& rx) // Sample delayed group and angle/energy for fission reaction sample_fission_neutron(i_nuclide, rx, &site, p); - // Iterated Fission Probability (IFP) method - // Needs to be done after the delayed group is found - - vector updated_ifp_delayed_groups; - vector updated_ifp_lifetimes; - - if (settings::ifp) { - - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - - const auto& ifp_delayed_groups = - simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; - size_t idx = ifp_delayed_groups.size(); - - if (idx < settings::ifp_n_generation) { - updated_ifp_delayed_groups.resize(idx + 1); - for (size_t i = 0; i < idx; i++) { - updated_ifp_delayed_groups[i] = ifp_delayed_groups[i]; - } - updated_ifp_delayed_groups[idx] = site.delayed_group; - } else if (idx == settings::ifp_n_generation) { - updated_ifp_delayed_groups.resize(idx); - for (size_t i = 0; i < idx - 1; i++) { - updated_ifp_delayed_groups[i] = ifp_delayed_groups[i + 1]; - } - updated_ifp_delayed_groups[idx - 1] = site.delayed_group; - } - } - - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - - const auto& ifp_lifetimes = - simulation::ifp_source_lifetime_bank[p.current_work() - 1]; - size_t idx = ifp_lifetimes.size(); - - if (idx < settings::ifp_n_generation) { - updated_ifp_lifetimes.resize(idx + 1); - for (size_t i = 0; i < idx; i++) { - updated_ifp_lifetimes[i] = ifp_lifetimes[i]; - } - updated_ifp_lifetimes[idx] = p.lifetime(); - } else if (idx == settings::ifp_n_generation) { - updated_ifp_lifetimes.resize(idx); - for (size_t i = 0; i < idx - 1; i++) { - updated_ifp_lifetimes[i] = ifp_lifetimes[i + 1]; - } - updated_ifp_lifetimes[idx - 1] = p.lifetime(); - } - } - } - // Store fission site in bank if (use_fission_bank) { int64_t idx = simulation::fission_bank.thread_safe_append(site); @@ -279,20 +227,9 @@ void create_fission_sites(Particle& p, int i_nuclide, const Reaction& rx) // Break out of loop as no more sites can be added to fission bank break; } - // If IFP, add the IFP information in the IFP banks using the same index - // as the one used to append the fission site to the fission bank. - // Multithreading protection is guaranteed by the index returned by the - // previous thread_safe_append call. + // Iterated Fission Probability (IFP) method if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - simulation::ifp_fission_delayed_group_bank[idx] = - updated_ifp_delayed_groups; - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - simulation::ifp_fission_lifetime_bank[idx] = updated_ifp_lifetimes; - } + ifp(p, site, idx); } } else { p.secondary_bank().push_back(site); From 78c3ae8d0a8562f98a3faa127825e1e985f82fb3 Mon Sep 17 00:00:00 2001 From: Joffrey Dorville Date: Wed, 5 Feb 2025 13:02:49 -0600 Subject: [PATCH 11/17] Detect the correct value for ifp_parameter based on tally scores --- openmc/settings.py | 14 ++----------- src/settings.cpp | 18 +---------------- src/tallies/tally.cpp | 23 ++++++++++++++++++++++ tests/regression_tests/ifp/inputs_true.dat | 1 - tests/regression_tests/ifp/test.py | 2 +- 5 files changed, 27 insertions(+), 31 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 7e1ddf145e2..4a95e671c1c 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -83,8 +83,6 @@ class Settings: Dictionary indicating the Iterated Fission Probability parameters. Acceptable keys are: - :parameter: Kinetics parameter to calculate between 'beta_effective' and - 'generation_time', or 'both' (str) :n_generation: Number of generation (int) max_lost_particles : int Maximum number of lost particles @@ -782,10 +780,8 @@ def iterated_fission_probability(self, iterated_fission_probability: dict): Mapping, ) for key, value in iterated_fission_probability.items(): - cv.check_value("Iterated Fission Probability key", key, {"parameter", "n_generation"}) - if key == "parameter": - cv.check_value("parameter", value, {"beta_effective", "generation_time", "both"}) - elif key == "n_generation": + cv.check_value("Iterated Fission Probability key", key, {"n_generation"}) + if key == "n_generation": cv.check_type("number of generations", value, Integral) cv.check_greater_than("number of generations", value, 0) @@ -1381,9 +1377,6 @@ def _create_no_reduce_subelement(self, root): def _create_iterated_fission_probability_subelements(self, root): if self.iterated_fission_probability: element = ET.SubElement(root, "iterated_fission_probability") - if 'parameter' in self._iterated_fission_probability: - subelement = ET.SubElement(element, "parameter") - subelement.text = str(self._iterated_fission_probability['parameter']) if 'n_generation' in self._iterated_fission_probability: subelement = ET.SubElement(element, "n_generation") subelement.text = str(self._iterated_fission_probability['n_generation']) @@ -1791,10 +1784,7 @@ def _verbosity_from_xml_element(self, root): def _iterated_fission_probability_from_xml_element(self, root): elem = root.find('iterated_fission_probability') if elem is not None: - text = get_text(elem, 'parameter') ifp = {} - if text is not None: - ifp['parameter'] = text text = get_text(elem, 'n_generation') if text is not None: ifp['n_generation'] = int(text) diff --git a/src/settings.cpp b/src/settings.cpp index 90c11459df5..025c7e3ef67 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -985,26 +985,10 @@ void read_settings_xml(pugi::xml_node root) // IFP only works with eigenvalue calculations if (run_mode == RunMode::EIGENVALUE) { ifp = true; + // Get iterated_fission_probability write node xml_node node_ifp = root.child("iterated_fission_probability"); - // Parameter to compute - if (check_for_node(node_ifp, "parameter")) { - std::string temp_str = - get_node_value(node_ifp, "parameter", true, true); - if (temp_str == "beta_effective") { - ifp_parameter = IFPParameter::BetaEffective; - } else if (temp_str == "generation_time") { - ifp_parameter = IFPParameter::GenerationTime; - } else if (temp_str == "both") { - ifp_parameter = IFPParameter::Both; - } else { - fatal_error("Unrecognized parameter for IFP: " + temp_str); - } - } else { - fatal_error(" should be declared for ifp."); - } - // Number of generation if (check_for_node(node_ifp, "n_generation")) { ifp_n_generation = std::stoi(get_node_value(node_ifp, "n_generation")); diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 73dadfbc6f9..3b94ff41735 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -179,6 +179,29 @@ Tally::Tally(pugi::xml_node node) fatal_error(fmt::format("No scores specified on tally {}.", id_)); } + // Set the IFP parameters if needed + if (settings::ifp) { + 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_) { diff --git a/tests/regression_tests/ifp/inputs_true.dat b/tests/regression_tests/ifp/inputs_true.dat index 84f5e91acce..5c409d49fdd 100644 --- a/tests/regression_tests/ifp/inputs_true.dat +++ b/tests/regression_tests/ifp/inputs_true.dat @@ -22,7 +22,6 @@ false - both 5 diff --git a/tests/regression_tests/ifp/test.py b/tests/regression_tests/ifp/test.py index 7567f69b3a5..efed9132608 100644 --- a/tests/regression_tests/ifp/test.py +++ b/tests/regression_tests/ifp/test.py @@ -30,7 +30,7 @@ def ifp_model(): settings.batches = 20 settings.inactive = 5 settings.photon_transport = False - settings.iterated_fission_probability = {"parameter": "both", "n_generation": 5} + settings.iterated_fission_probability = {"n_generation": 5} bounds = [ -radius, -radius, -radius, From e18eff1aa6796cce5eb29b6a6473989ef33d45e4 Mon Sep 17 00:00:00 2001 From: Joffrey Dorville Date: Wed, 5 Feb 2025 13:49:36 -0600 Subject: [PATCH 12/17] Select IFP automatically with the default value of n_generation if an IFP tally is used --- include/openmc/constants.h | 3 ++ openmc/settings.py | 62 ++++++++++++++------------------------ src/settings.cpp | 45 +++++++++------------------ src/tallies/tally.cpp | 34 ++++++++++++++++++++- 4 files changed, 73 insertions(+), 71 deletions(-) diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 97d0218d1a0..62a5bba1c3c 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -321,6 +321,9 @@ enum class GlobalTally { K_COLLISION, K_ABSORPTION, K_TRACKLENGTH, LEAKAGE }; // Miscellaneous constexpr int C_NONE {-1}; +// Default value of generation for IFP +constexpr int DEFAULT_IFP_N_GENERATION {10}; + // Interpolation rules enum class Interpolation { histogram = 1, diff --git a/openmc/settings.py b/openmc/settings.py index 4a95e671c1c..1f3ccd5a8b1 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -79,11 +79,9 @@ class Settings: .. versionadded:: 0.12 generations_per_batch : int Number of generations per batch - iterated_fission_probability : dict - Dictionary indicating the Iterated Fission Probability parameters. - Acceptable keys are: - - :n_generation: Number of generation (int) + ifp_n_generation : int + Number of generations to consider for the Iterated Fission Probability + method. max_lost_particles : int Maximum number of lost particles @@ -341,7 +339,7 @@ def __init__(self, **kwargs): self._output = None # Iterated Fission Probability - self._iterated_fission_probability = {} + self._ifp_n_generation = None # Output options self._statepoint = {} @@ -769,23 +767,15 @@ def verbosity(self, verbosity: int): self._verbosity = verbosity @property - def iterated_fission_probability(self) -> dict: - return self._iterated_fission_probability - - @iterated_fission_probability.setter - def iterated_fission_probability(self, iterated_fission_probability: dict): - cv.check_type( - "Iterated Fission Probability options", - iterated_fission_probability, - Mapping, - ) - for key, value in iterated_fission_probability.items(): - cv.check_value("Iterated Fission Probability key", key, {"n_generation"}) - if key == "n_generation": - cv.check_type("number of generations", value, Integral) - cv.check_greater_than("number of generations", value, 0) - - self._iterated_fission_probability = iterated_fission_probability + def ifp_n_generation(self) -> int: + return self._ifp_n_generation + + @ifp_n_generation.setter + def ifp_n_generation(self, ifp_n_generation: int): + if ifp_n_generation is not None: + cv.check_type("number of generations", ifp_n_generation, Integral) + cv.check_greater_than("number of generations", ifp_n_generation, 0) + self._ifp_n_generation = ifp_n_generation @property def tabular_legendre(self) -> dict: @@ -1374,12 +1364,10 @@ def _create_no_reduce_subelement(self, root): element = ET.SubElement(root, "no_reduce") element.text = str(self._no_reduce).lower() - def _create_iterated_fission_probability_subelements(self, root): - if self.iterated_fission_probability: - element = ET.SubElement(root, "iterated_fission_probability") - if 'n_generation' in self._iterated_fission_probability: - subelement = ET.SubElement(element, "n_generation") - subelement.text = str(self._iterated_fission_probability['n_generation']) + def _create_ifp_n_generation_subelement(self, root): + if self._ifp_n_generation is not None: + element = ET.SubElement(root, "ifp_n_generation") + element.text = str(self._ifp_n_generation) def _create_tabular_legendre_subelements(self, root): if self.tabular_legendre: @@ -1781,14 +1769,10 @@ def _verbosity_from_xml_element(self, root): if text is not None: self.verbosity = int(text) - def _iterated_fission_probability_from_xml_element(self, root): - elem = root.find('iterated_fission_probability') - if elem is not None: - ifp = {} - text = get_text(elem, 'n_generation') - if text is not None: - ifp['n_generation'] = int(text) - self.iterated_fission_probability = ifp + def _ifp_n_generation_from_xml_element(self, root): + text = get_text(root, 'ifp_n_generation') + if text is not None: + self.ifp_n_generation = int(text) def _tabular_legendre_from_xml_element(self, root): elem = root.find('tabular_legendre') @@ -1989,7 +1973,7 @@ def to_xml_element(self, mesh_memo=None): self._create_trigger_subelement(element) self._create_no_reduce_subelement(element) self._create_verbosity_subelement(element) - self._create_iterated_fission_probability_subelements(element) + self._create_ifp_n_generation_subelement(element) self._create_tabular_legendre_subelements(element) self._create_temperature_subelements(element) self._create_trace_subelement(element) @@ -2096,7 +2080,7 @@ def from_xml_element(cls, elem, meshes=None): settings._trigger_from_xml_element(elem) settings._no_reduce_from_xml_element(elem) settings._verbosity_from_xml_element(elem) - settings._iterated_fission_probability_from_xml_element(elem) + settings._ifp_n_generation_from_xml_element(elem) settings._tabular_legendre_from_xml_element(elem) settings._temperature_from_xml_element(elem) settings._trace_from_xml_element(elem) diff --git a/src/settings.cpp b/src/settings.cpp index 025c7e3ef67..49fac7df446 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -103,7 +103,7 @@ int max_particle_events {1000000}; 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 {0}; +int ifp_n_generation {-1}; IFPParameter ifp_parameter {IFPParameter::None}; int legendre_to_tabular_points {C_NONE}; int max_order {0}; @@ -979,36 +979,19 @@ void read_settings_xml(pugi::xml_node root) temperature_range[1] = range.at(1); } - // Check for Iterated Fission Probability (IFP) - if (check_for_node(root, "iterated_fission_probability")) { - - // IFP only works with eigenvalue calculations - if (run_mode == RunMode::EIGENVALUE) { - ifp = true; - - // Get iterated_fission_probability write node - xml_node node_ifp = root.child("iterated_fission_probability"); - - // Number of generation - if (check_for_node(node_ifp, "n_generation")) { - ifp_n_generation = std::stoi(get_node_value(node_ifp, "n_generation")); - if (ifp_n_generation <= 0) { - fatal_error(" 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(" must be lower than or equal to the " - "number of inactive cycles."); - } - } else { - fatal_error(" must be specified as a subelement of " - "."); - } - } else { - fatal_error( - "Iterated Fission Probability can only be used in an eigenvalue " - "calculation."); + // Check for user value for the number of generation of the Iterated Fission + // Probability (IFP) method + if (check_for_node(root, "ifp_n_generation")) { + ifp_n_generation = std::stoi(get_node_value(root, "ifp_n_generation")); + warning(fmt::format("{}", ifp_n_generation)); + warning(fmt::format("{}", n_inactive)); + 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/tallies/tally.cpp b/src/tallies/tally.cpp index 3b94ff41735..2a784f9e601 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -179,7 +179,39 @@ Tally::Tally(pugi::xml_node node) fatal_error(fmt::format("No scores specified on tally {}.", id_)); } - // Set the IFP parameters if needed + // Set IFP if needed + if (!settings::ifp) { + for (int score : scores_) { + switch (score) { + case SCORE_IFP_TIME_NUM: + case SCORE_IFP_BETA_NUM: + case SCORE_IFP_DENOM: + 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)); + } + 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 = true; + } else { + fatal_error( + "Iterated Fission Probability can only be used in an eigenvalue " + "calculation."); + } + goto exit_for_loop; + break; + } + } + } +exit_for_loop:; + + // Set IFP parameters if needed if (settings::ifp) { for (int score : scores_) { switch (score) { From 715eed13d88112f363e43febf8c0f58a68006e40 Mon Sep 17 00:00:00 2001 From: Joffrey Dorville Date: Wed, 5 Feb 2025 15:56:40 -0600 Subject: [PATCH 13/17] Update tests --- tests/regression_tests/ifp/inputs_true.dat | 9 +++---- tests/regression_tests/ifp/test.py | 6 ++--- tests/unit_tests/test_ifp.py | 28 +++++++++++++--------- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/tests/regression_tests/ifp/inputs_true.dat b/tests/regression_tests/ifp/inputs_true.dat index 5c409d49fdd..d4a20985f13 100644 --- a/tests/regression_tests/ifp/inputs_true.dat +++ b/tests/regression_tests/ifp/inputs_true.dat @@ -16,14 +16,15 @@ 20 5 - + -10.0 -10.0 -10.0 10.0 10.0 10.0 + + true + false - - 5 - + 5 diff --git a/tests/regression_tests/ifp/test.py b/tests/regression_tests/ifp/test.py index efed9132608..1745f789752 100644 --- a/tests/regression_tests/ifp/test.py +++ b/tests/regression_tests/ifp/test.py @@ -30,14 +30,14 @@ def ifp_model(): settings.batches = 20 settings.inactive = 5 settings.photon_transport = False - settings.iterated_fission_probability = {"n_generation": 5} + settings.ifp_n_generation = 5 bounds = [ -radius, -radius, -radius, radius, radius, radius ] - distribution = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True) - settings.source = openmc.source.IndependentSource(space=distribution) + space = openmc.stats.Box(bounds[:3], bounds[3:]) + settings.source = openmc.IndependentSource(space=space, constraints={'fissionable': True}) model.settings = settings diff --git a/tests/unit_tests/test_ifp.py b/tests/unit_tests/test_ifp.py index 34199704c8c..8d0fd98010d 100644 --- a/tests/unit_tests/test_ifp.py +++ b/tests/unit_tests/test_ifp.py @@ -7,37 +7,43 @@ def test_xml_serialization(run_in_tmpdir): """Check that a simple use case can be written and read in XML.""" - parameter = {"n_generation": 5} + parameter = 5 settings = openmc.Settings() - settings.iterated_fission_probability = parameter + settings.ifp_n_generation = parameter settings.export_to_xml() read_settings = openmc.Settings.from_xml() - assert read_settings.iterated_fission_probability == parameter + assert read_settings.ifp_n_generation == parameter @pytest.fixture(scope="module") def geometry(): openmc.reset_auto_ids() material = openmc.Material() + material.add_nuclide("U235", 1.0) sphere = openmc.Sphere(r=1.0, boundary_type="vacuum") cell = openmc.Cell(region=-sphere, fill=material) return openmc.Geometry([cell]) @pytest.mark.parametrize( - "settings, error", + "options, error", [ - ({"iterated_fission_probability": {"n_generation": 0}}, ValueError), - ({"run_mode": "fixed source", "iterated_fission_probability": {"n_generation": 5}}, RuntimeError), - ({"n_inactive": 6, "iterated_fission_probability": {"n_generation": 5}}, RuntimeError) + ({"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(settings, error, run_in_tmpdir, geometry): +def test_exceptions(options, error, run_in_tmpdir, geometry): """Test settings configuration that should return an error.""" with pytest.raises(error): - settings = openmc.Settings(**settings) + settings = openmc.Settings(**options) settings.particles = 100 - settings.batches = 10 - model = openmc.Model(geometry=geometry, settings=settings) + settings.batches = 15 + tally = openmc.Tally(name="ifp-scores") + tally.scores = ["ifp-time-numerator", "ifp-beta-numerator", "ifp-denominator"] + tallies = openmc.Tallies([tally]) + model = openmc.Model(geometry=geometry, settings=settings, tallies=tallies) model.run() From 87b569377eba5d20596720c8491f7c32806a20e4 Mon Sep 17 00:00:00 2001 From: realmisch <79525520+realmisch@users.noreply.github.com> Date: Fri, 7 Feb 2025 12:03:11 -0500 Subject: [PATCH 14/17] Added Keff division, DelayedFilter check in score --- openmc/statepoint.py | 2 +- src/tallies/tally_scoring.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index a1ca8bc19b3..782d5a2eb18 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -726,7 +726,7 @@ def ifp_results(self,param): gen_time_tally = self.get_tally(scores = ['ifp-time-numerator'],exact_scores = True) gen_time_values = uarray(gen_time_tally.get_reshaped_data(), gen_time_tally.get_reshaped_data(value = 'rel_err')) - gen_time_values /= denom_values + gen_time_values /= denom_values*self.keff result['Generation Time'] = gen_time_values.flatten() if param == 'both' or param == 'beta_effective': diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index 46c1940f812..f84835b46a4 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -916,7 +916,9 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, int n_generation = static_cast(delayed_groups.size()); if (n_generation == settings::ifp_n_generation) { if (delayed_groups[0] > 0) { - filter_index = delayed_groups[0] - 1; + if (tally.delayedgroup_filter_ != C_NONE) { + filter_index = delayed_groups[0] - 1; + } score = p.wgt_last(); } } From de964e2e1beff9d8855fd1ad432a81e03f752bae Mon Sep 17 00:00:00 2001 From: Joffrey Dorville Date: Mon, 10 Feb 2025 14:40:34 -0600 Subject: [PATCH 15/17] Refactoring in progress --- include/openmc/eigenvalue.h | 10 -- include/openmc/ifp.h | 134 ++++++++++++++- src/bank.cpp | 43 +---- src/eigenvalue.cpp | 214 ++++------------------- src/ifp.cpp | 308 +++++++++++++++++++++++++++++----- src/simulation.cpp | 15 +- src/tallies/tally_scoring.cpp | 10 +- 7 files changed, 447 insertions(+), 287 deletions(-) diff --git a/include/openmc/eigenvalue.h b/include/openmc/eigenvalue.h index 7d818ac16f7..b456fee21eb 100644 --- a/include/openmc/eigenvalue.h +++ b/include/openmc/eigenvalue.h @@ -79,16 +79,6 @@ void write_eigenvalue_hdf5(hid_t group); //! \param[in] group HDF5 group void read_eigenvalue_hdf5(hid_t group); -//============================================================================== -// Type definitions -//============================================================================== - -//! Deserialization info for IFP -struct DeserializationInfo { - int64_t index_local; - int64_t n; -}; - } // namespace openmc #endif // OPENMC_EIGENVALUE_H diff --git a/include/openmc/ifp.h b/include/openmc/ifp.h index 8162c5d1800..02583ecb306 100644 --- a/include/openmc/ifp.h +++ b/include/openmc/ifp.h @@ -1,14 +1,146 @@ #ifndef OPENMC_IFP_H #define OPENMC_IFP_H +#include "openmc/message_passing.h" #include "openmc/particle.h" #include "openmc/particle_data.h" +#include "openmc/settings.h" namespace openmc { +// --------------------------------------------------------- +// Common helpers +// --------------------------------------------------------- + +bool is_beta_effective_or_both(); + +bool is_generation_time_or_both(); + +// --------------------------------------------------------- +// physics.cpp functions +// --------------------------------------------------------- + +template +vector _ifp(const T& site_value, const vector& data) { + vector updated_data; + size_t ifp_idx = data.size(); + if (ifp_idx < settings::ifp_n_generation) { + updated_data.resize(ifp_idx + 1); + for (size_t i = 0; i < ifp_idx; i++) { + updated_data[i] = data[i]; + } + updated_data[ifp_idx] = site_value; + } else if (ifp_idx == settings::ifp_n_generation) { + updated_data.resize(ifp_idx); + for (size_t i = 0; i < ifp_idx - 1; i++) { + updated_data[i] = data[i + 1]; + } + updated_data[ifp_idx - 1] = site_value; + } + return updated_data; +} + //! Iterated Fission Probability (IFP) method +//! +//! Needs to be done after the delayed group is found. +//! +//! Add the IFP information in the IFP banks using the same index +//! as the one used to append the fission site to the fission bank. +//! Multithreading protection is guaranteed by the index returned by the +//! thread_safe_append call in physics.cpp. void ifp(Particle& p, SourceSite& site, int64_t idx); +// --------------------------------------------------------- +// simulation.cpp functions +// --------------------------------------------------------- + +//! Resize the IFP banks used in the simulation +void resize_simulation_ifp_banks(); + +// --------------------------------------------------------- +// eigenvalue.cpp functions +// --------------------------------------------------------- + +//! Resize IFP vectors +template +void resize_ifp_data( + vector& delayed_groups, vector& lifetimes, int64_t n_dg, int64_t n_l) +{ + if (is_beta_effective_or_both()) { + delayed_groups.resize(n_dg); + } + if (is_generation_time_or_both()) { + lifetimes.resize(n_l); + } +} + +void initialize_ifp_pointers(int64_t i, const vector*& delayed_groups_ptr, + const vector*& lifetimes_ptr); + +void add_ifp_data_to_temp(int64_t index_temp, + vector>& temp_delayed_groups, + const vector* delayed_groups_ptr, vector>& temp_lifetimes, + const vector* lifetimes_ptr); + +void copy_ifp_data_to_temp_from_fission_banks(int64_t index_temp, int i_bank, + vector>& temp_delayed_groups, + vector>& temp_lifetimes); + +#ifdef OPENMC_MPI + +//! Deserialization info for IFP transfer via MPI +struct DeserializationInfo { + int64_t index_local; + int64_t n; +}; + +//! Broadcast the number of generation from the size of the first element +void broadcast_ifp_n_generation(int& ifp_n_generation, + vector>& temp_delayed_groups, + vector>& temp_lifetimes); + +//! Send IFP info via MPI +void send_ifp_info(int64_t index_local, int64_t n, int ifp_n_generation, + int neighbor, vector& requests, + vector> temp_delayed_groups, vector send_delayed_groups, + vector> temp_lifetimes, vector send_lifetimes); + +//! Receive IFP info through MPI +void receive_ifp_data(int64_t index_local, int64_t n, int ifp_n_generation, + int neighbor, vector& requests, vector& recv_delayed_groups, + vector& recv_lifetimes, + vector& deserialization_info); + +void copy_ifp_temp_to_source_banks_partial(int64_t index_temp, int n, + int64_t index_local, const vector>& temp_delayed_groups, + const vector>& temp_lifetimes); + +//! Deserialize IFP information +void deserialize_ifp_info(int ifp_n_generation, + vector& deserialization_info, + vector& recv_delayed_groups, vector& recv_lifetimes); + +#endif + +//! Copy IFP temporary vector to source banks +void copy_ifp_temp_to_source_banks(vector>& temp_delayed_groups, + vector>& temp_lifetimes); + +// --------------------------------------------------------- +// bank.cpp functions +// --------------------------------------------------------- + +void allocate_temporary_vector_ifp( + vector>& delayed_group_bank_holder, + vector*& delayed_group_bank, + vector>& lifetime_bank_holder, vector*& lifetime_bank); + +void sort_ifp_banks(int64_t i, int64_t idx, vector*& delayed_group_bank, + vector*& lifetime_bank); + +void copy_ifp_banks_to_fission_banks( + vector*& delayed_group_bank, vector*& lifetime_bank); + } // namespace openmc -#endif // OPENMC_PHYSICS_H \ No newline at end of file +#endif // OPENMC_IFP_H diff --git a/src/bank.cpp b/src/bank.cpp index 5fff1e3458f..dc1ed26e945 100644 --- a/src/bank.cpp +++ b/src/bank.cpp @@ -1,6 +1,7 @@ #include "openmc/bank.h" #include "openmc/capi.h" #include "openmc/error.h" +#include "openmc/ifp.h" #include "openmc/message_passing.h" #include "openmc/simulation.h" #include "openmc/vector.h" @@ -105,18 +106,9 @@ void sort_fission_bank() } if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - sorted_ifp_delayed_group_bank_holder.resize( - simulation::fission_bank.size()); - sorted_ifp_delayed_group_bank = - sorted_ifp_delayed_group_bank_holder.data(); - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - sorted_ifp_lifetime_bank_holder.resize(simulation::fission_bank.size()); - sorted_ifp_lifetime_bank = sorted_ifp_lifetime_bank_holder.data(); - } + allocate_temporary_vector_ifp(sorted_ifp_delayed_group_bank_holder, + sorted_ifp_delayed_group_bank, sorted_ifp_lifetime_bank_holder, + sorted_ifp_lifetime_bank); } // Use parent and progeny indices to sort fission bank @@ -130,17 +122,8 @@ void sort_fission_bank() } sorted_bank[idx] = site; if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - const auto& ifp_delayed_groups = - simulation::ifp_fission_delayed_group_bank[i]; - sorted_ifp_delayed_group_bank[idx] = ifp_delayed_groups; - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - const auto& ifp_lifetimes = simulation::ifp_fission_lifetime_bank[i]; - sorted_ifp_lifetime_bank[idx] = ifp_lifetimes; - } + sort_ifp_banks( + i, idx, sorted_ifp_delayed_group_bank, sorted_ifp_lifetime_bank); } } @@ -148,18 +131,8 @@ void sort_fission_bank() std::copy(sorted_bank, sorted_bank + simulation::fission_bank.size(), simulation::fission_bank.data()); if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - std::copy(sorted_ifp_delayed_group_bank, - sorted_ifp_delayed_group_bank + simulation::fission_bank.size(), - simulation::ifp_fission_delayed_group_bank.data()); - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - std::copy(sorted_ifp_lifetime_bank, - sorted_ifp_lifetime_bank + simulation::fission_bank.size(), - simulation::ifp_fission_lifetime_bank.data()); - } + copy_ifp_banks_to_fission_banks( + sorted_ifp_delayed_group_bank, sorted_ifp_lifetime_bank); } } diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index 69a05cf4a0a..f819d42e9c4 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -11,6 +11,7 @@ #include "openmc/constants.h" #include "openmc/error.h" #include "openmc/hdf5_interface.h" +#include "openmc/ifp.h" #include "openmc/math_functions.h" #include "openmc/mesh.h" #include "openmc/message_passing.h" @@ -155,17 +156,13 @@ void synchronize_bank() int64_t index_temp = 0; vector temp_sites(3 * simulation::work_per_rank); + + // Temporary banks for IFP vector> temp_delayed_groups; vector> temp_lifetimes; if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - temp_delayed_groups.resize(3 * simulation::work_per_rank); - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - temp_lifetimes.resize(3 * simulation::work_per_rank); - } + resize_ifp_data(temp_delayed_groups, temp_lifetimes, + 3 * simulation::work_per_rank, 3 * simulation::work_per_rank); } for (int64_t i = 0; i < simulation::fission_bank.size(); i++) { @@ -178,14 +175,7 @@ void synchronize_bank() // Initialize IFP data pointer if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - delayed_groups_ptr = &simulation::ifp_fission_delayed_group_bank[i]; - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - lifetimes_ptr = &simulation::ifp_fission_lifetime_bank[i]; - } + initialize_ifp_pointers(i, delayed_groups_ptr, lifetimes_ptr); } // If there are less than n_particles particles banked, automatically add @@ -196,14 +186,8 @@ void synchronize_bank() for (int64_t j = 1; j <= settings::n_particles / total; ++j) { temp_sites[index_temp] = site; if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - temp_delayed_groups[index_temp] = *delayed_groups_ptr; - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - temp_lifetimes[index_temp] = *lifetimes_ptr; - } + add_ifp_data_to_temp(index_temp, temp_delayed_groups, + delayed_groups_ptr, temp_lifetimes, lifetimes_ptr); } ++index_temp; } @@ -213,14 +197,8 @@ void synchronize_bank() if (prn(&seed) < p_sample) { temp_sites[index_temp] = site; if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - temp_delayed_groups[index_temp] = *delayed_groups_ptr; - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - temp_lifetimes[index_temp] = *lifetimes_ptr; - } + add_ifp_data_to_temp(index_temp, temp_delayed_groups, + delayed_groups_ptr, temp_lifetimes, lifetimes_ptr); } ++index_temp; } @@ -269,16 +247,8 @@ void synchronize_bank() int i_bank = simulation::fission_bank.size() - sites_needed + i; temp_sites[index_temp] = simulation::fission_bank[i_bank]; if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - temp_delayed_groups[index_temp] = - simulation::ifp_fission_delayed_group_bank[i_bank]; - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - temp_lifetimes[index_temp] = - simulation::ifp_fission_lifetime_bank[i_bank]; - } + copy_ifp_data_to_temp_from_fission_banks( + index_temp, i_bank, temp_delayed_groups, temp_lifetimes); } ++index_temp; } @@ -295,25 +265,17 @@ void synchronize_bank() // ========================================================================== // SEND BANK SITES TO NEIGHBORS - // If IFP, broadcast the number of generation from the size of the first - // element + // IFP number of generation int ifp_n_generation; if (settings::ifp) { - // TODO: - if (mpi::rank == 0) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - ifp_n_generation = static_cast(temp_delayed_groups[0].size()); - } else { - ifp_n_generation = static_cast(temp_lifetimes[0].size()); - } - } - MPI_Bcast(&ifp_n_generation, 1, MPI_INT, 0, mpi::intracomm); + broadcast_ifp_n_generation( + ifp_n_generation, temp_delayed_groups, temp_lifetimes); } int64_t index_local = 0; vector requests; + // IFP send buffers vector send_delayed_groups; vector send_lifetimes; @@ -325,15 +287,9 @@ void synchronize_bank() // Resize IFP send buffers if (settings::ifp && mpi::n_procs > 1) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - send_delayed_groups.resize( - ifp_n_generation * 3 * simulation::work_per_rank); - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - send_lifetimes.resize(ifp_n_generation * 3 * simulation::work_per_rank); - } + resize_ifp_data(send_delayed_groups, send_lifetimes, + ifp_n_generation * 3 * simulation::work_per_rank, + ifp_n_generation * 3 * simulation::work_per_rank); } while (start < finish) { @@ -350,38 +306,10 @@ void synchronize_bank() &requests.back()); if (settings::ifp) { - - for (int i = index_local; i < index_local + n; i++) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - std::copy(temp_delayed_groups[i].begin(), - temp_delayed_groups[i].end(), - send_delayed_groups.begin() + i * ifp_n_generation); - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - std::copy(temp_lifetimes[i].begin(), temp_lifetimes[i].end(), - send_lifetimes.begin() + i * ifp_n_generation); - } - } - - // Send delayed groups - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - requests.emplace_back(); - MPI_Isend(&send_delayed_groups[ifp_n_generation * index_local], - ifp_n_generation * static_cast(n), MPI_INT, neighbor, - mpi::rank, mpi::intracomm, &requests.back()); - } - - // Send lifetimes - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - requests.emplace_back(); - MPI_Isend(&send_lifetimes[ifp_n_generation * index_local], - ifp_n_generation * static_cast(n), MPI_DOUBLE, neighbor, - mpi::rank, mpi::intracomm, &requests.back()); - } + // Send IFP data + send_ifp_info(index_local, n, ifp_n_generation, neighbor, requests, + temp_delayed_groups, send_delayed_groups, temp_lifetimes, + send_lifetimes); } } @@ -404,6 +332,7 @@ void synchronize_bank() start = simulation::work_index[mpi::rank]; index_local = 0; + // IFP receive buffers vector recv_delayed_groups; vector recv_lifetimes; vector deserialization_info; @@ -421,14 +350,9 @@ void synchronize_bank() // Resize IFP receive buffers if (settings::ifp && mpi::n_procs > 1) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - recv_delayed_groups.resize(ifp_n_generation * simulation::work_per_rank); - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - recv_lifetimes.resize(ifp_n_generation * simulation::work_per_rank); - } + resize_ifp_data(recv_delayed_groups, recv_lifetimes, + ifp_n_generation * simulation::work_per_rank, + ifp_n_generation * simulation::work_per_rank); } while (start < simulation::work_index[mpi::rank + 1]) { @@ -451,28 +375,9 @@ void synchronize_bank() mpi::source_site, neighbor, neighbor, mpi::intracomm, &requests.back()); if (settings::ifp) { - - // Receive delayed groups - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - requests.emplace_back(); - MPI_Irecv(&recv_delayed_groups[ifp_n_generation * index_local], - ifp_n_generation * static_cast(n), MPI_INT, neighbor, neighbor, - mpi::intracomm, &requests.back()); - } - - // Receive lifetimes - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - requests.emplace_back(); - MPI_Irecv(&recv_lifetimes[ifp_n_generation * index_local], - ifp_n_generation * static_cast(n), MPI_DOUBLE, neighbor, - neighbor, mpi::intracomm, &requests.back()); - } - - // Deserialization info to reconstruct data later - DeserializationInfo info = {index_local, n}; - deserialization_info.push_back(info); + // Receive IFP data + receive_ifp_data(index_local, n, ifp_n_generation, neighbor, requests, + recv_delayed_groups, recv_lifetimes, deserialization_info); } } else { @@ -484,18 +389,8 @@ void synchronize_bank() &simulation::source_bank[index_local]); if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - std::copy(&temp_delayed_groups[index_temp], - &temp_delayed_groups[index_temp + n], - &simulation::ifp_source_delayed_group_bank[index_local]); - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - std::copy(&temp_lifetimes[index_temp], - &temp_lifetimes[index_temp + n], - &simulation::ifp_source_lifetime_bank[index_local]); - } + copy_ifp_temp_to_source_banks_partial( + index_temp, n, index_local, temp_delayed_groups, temp_lifetimes); } } @@ -513,40 +408,14 @@ void synchronize_bank() MPI_Waitall(n_request, requests.data(), MPI_STATUSES_IGNORE); if (settings::ifp) { - - // Deserialize - int64_t n; - for (auto info : deserialization_info) { - index_local = info.index_local; - n = info.n; - - // Store deserialized data in banks - for (int i = index_local; i < index_local + n; i++) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - vector delayed_groups_received( - recv_delayed_groups.begin() + ifp_n_generation * i, - recv_delayed_groups.begin() + ifp_n_generation * (i + 1)); - simulation::ifp_source_delayed_group_bank[i] = - delayed_groups_received; - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - vector lifetimes_received( - recv_lifetimes.begin() + ifp_n_generation * i, - recv_lifetimes.begin() + ifp_n_generation * (i + 1)); - simulation::ifp_source_lifetime_bank[i] = lifetimes_received; - } - } - } + deserialize_ifp_info(ifp_n_generation, deserialization_info, + recv_delayed_groups, recv_lifetimes); // Clear IFP buffers send_delayed_groups.clear(); send_lifetimes.clear(); recv_delayed_groups.clear(); recv_lifetimes.clear(); - - // Clear deserialization info deserialization_info.clear(); } @@ -554,21 +423,12 @@ void synchronize_bank() std::copy(temp_sites.data(), temp_sites.data() + settings::n_particles, simulation::source_bank.begin()); if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - std::copy(temp_delayed_groups.data(), - temp_delayed_groups.data() + settings::n_particles, - simulation::ifp_source_delayed_group_bank.begin()); - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - std::copy(temp_lifetimes.data(), - temp_lifetimes.data() + settings::n_particles, - simulation::ifp_source_lifetime_bank.begin()); - } + copy_ifp_temp_to_source_banks(temp_delayed_groups, temp_lifetimes); } #endif temp_sites.clear(); + + // Clear IFP buffers temp_delayed_groups.clear(); temp_lifetimes.clear(); diff --git a/src/ifp.cpp b/src/ifp.cpp index f795ba29674..39dd498fb1d 100644 --- a/src/ifp.cpp +++ b/src/ifp.cpp @@ -1,6 +1,7 @@ #include "openmc/ifp.h" #include "openmc/bank.h" +#include "openmc/message_passing.h" #include "openmc/particle.h" #include "openmc/particle_data.h" #include "openmc/settings.h" @@ -9,70 +10,287 @@ namespace openmc { -void ifp(Particle& p, SourceSite& site, int64_t idx) -{ - // Iterated Fission Probability (IFP) method - - // Needs to be done after the delayed group is found. - - // Add the IFP information in the IFP banks using the same index - // as the one used to append the fission site to the fission bank. - // Multithreading protection is guaranteed by the index returned by the - // thread_safe_append call in physics.cpp. +// --------------------------------------------------------- +// Common helpers +// --------------------------------------------------------- - // Beta effective +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; +} - vector updated_ifp_delayed_groups; +// --------------------------------------------------------- +// physics.cpp functions +// --------------------------------------------------------- +void ifp(Particle& p, SourceSite& site, int64_t idx) +{ + // Beta effective + if (is_beta_effective_or_both()) { const auto& ifp_delayed_groups = simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; - size_t ifp_idx = ifp_delayed_groups.size(); + vector updated_ifp_delayed_groups = + _ifp(site.delayed_group, ifp_delayed_groups); + simulation::ifp_fission_delayed_group_bank[idx] = + updated_ifp_delayed_groups; + } + // Generation time + if (is_generation_time_or_both()) { + const auto& ifp_lifetimes = + simulation::ifp_source_lifetime_bank[p.current_work() - 1]; + vector updated_ifp_lifetimes = _ifp(p.lifetime(), ifp_lifetimes); + simulation::ifp_fission_lifetime_bank[idx] = updated_ifp_lifetimes; + } +} - if (ifp_idx < settings::ifp_n_generation) { - updated_ifp_delayed_groups.resize(ifp_idx + 1); - for (size_t i = 0; i < ifp_idx; i++) { - updated_ifp_delayed_groups[i] = ifp_delayed_groups[i]; - } - updated_ifp_delayed_groups[ifp_idx] = site.delayed_group; - } else if (ifp_idx == settings::ifp_n_generation) { - updated_ifp_delayed_groups.resize(ifp_idx); - for (size_t i = 0; i < ifp_idx - 1; i++) { - updated_ifp_delayed_groups[i] = ifp_delayed_groups[i + 1]; - } - updated_ifp_delayed_groups[ifp_idx - 1] = site.delayed_group; +// --------------------------------------------------------- +// simulation.cpp functions +// --------------------------------------------------------- + +void resize_simulation_ifp_banks() { + if (is_beta_effective_or_both()) { + simulation::ifp_source_delayed_group_bank.resize( + simulation::work_per_rank); + simulation::ifp_fission_delayed_group_bank.resize( + 3 * simulation::work_per_rank); + } + if (is_generation_time_or_both()) { + simulation::ifp_source_lifetime_bank.resize(simulation::work_per_rank); + simulation::ifp_fission_lifetime_bank.resize( + 3 * simulation::work_per_rank); + } +} + +// --------------------------------------------------------- +// eigenvalue.cpp functions +// --------------------------------------------------------- + +void initialize_ifp_pointers(int64_t i, const vector*& delayed_groups_ptr, + const vector*& lifetimes_ptr) +{ + if (is_beta_effective_or_both()) { + delayed_groups_ptr = &simulation::ifp_fission_delayed_group_bank[i]; + } + if (is_generation_time_or_both()) { + lifetimes_ptr = &simulation::ifp_fission_lifetime_bank[i]; + } +} + +void add_ifp_data_to_temp(int64_t index_temp, + vector>& temp_delayed_groups, + const vector* delayed_groups_ptr, vector>& temp_lifetimes, + const vector* lifetimes_ptr) +{ + if (is_beta_effective_or_both()) { + temp_delayed_groups[index_temp] = *delayed_groups_ptr; + } + if (is_generation_time_or_both()) { + temp_lifetimes[index_temp] = *lifetimes_ptr; + } +} + +void copy_ifp_data_to_temp_from_fission_banks(int64_t index_temp, int i_bank, + vector>& temp_delayed_groups, + vector>& temp_lifetimes) +{ + if (is_beta_effective_or_both()) { + temp_delayed_groups[index_temp] = + simulation::ifp_fission_delayed_group_bank[i_bank]; + } + if (is_generation_time_or_both()) { + temp_lifetimes[index_temp] = simulation::ifp_fission_lifetime_bank[i_bank]; + } +} + +#ifdef OPENMC_MPI + +void broadcast_ifp_n_generation(int& ifp_n_generation, + vector>& temp_delayed_groups, + vector>& temp_lifetimes) +{ + if (mpi::rank == 0) { + if (is_beta_effective_or_both()) { + ifp_n_generation = static_cast(temp_delayed_groups[0].size()); + } else { + ifp_n_generation = static_cast(temp_lifetimes[0].size()); } + } + MPI_Bcast(&ifp_n_generation, 1, MPI_INT, 0, mpi::intracomm); +} - simulation::ifp_fission_delayed_group_bank[idx] = - updated_ifp_delayed_groups; +void send_ifp_info(int64_t index_local, int64_t n, int ifp_n_generation, + int neighbor, vector& requests, + vector> temp_delayed_groups, vector send_delayed_groups, + vector> temp_lifetimes, vector send_lifetimes) +{ + for (int i = index_local; i < index_local + n; i++) { + if (is_beta_effective_or_both()) { + std::copy(temp_delayed_groups[i].begin(), temp_delayed_groups[i].end(), + send_delayed_groups.begin() + i * ifp_n_generation); + } + if (is_generation_time_or_both()) { + std::copy(temp_lifetimes[i].begin(), temp_lifetimes[i].end(), + send_lifetimes.begin() + i * ifp_n_generation); + } } - // Generation time - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { + // Send delayed groups + if (is_beta_effective_or_both()) { + requests.emplace_back(); + MPI_Isend(&send_delayed_groups[ifp_n_generation * index_local], + ifp_n_generation * static_cast(n), MPI_INT, neighbor, mpi::rank, + mpi::intracomm, &requests.back()); + } - vector updated_ifp_lifetimes; + // Send lifetimes + if (is_generation_time_or_both()) { + requests.emplace_back(); + MPI_Isend(&send_lifetimes[ifp_n_generation * index_local], + ifp_n_generation * static_cast(n), MPI_DOUBLE, neighbor, mpi::rank, + mpi::intracomm, &requests.back()); + } +} - const auto& ifp_lifetimes = - simulation::ifp_source_lifetime_bank[p.current_work() - 1]; - size_t ifp_idx = ifp_lifetimes.size(); +void receive_ifp_data(int64_t index_local, int64_t n, int ifp_n_generation, + int neighbor, vector& requests, vector& recv_delayed_groups, + vector& recv_lifetimes, + vector& deserialization_info) +{ + // Receive delayed groups + if (is_beta_effective_or_both()) { + requests.emplace_back(); + MPI_Irecv(&recv_delayed_groups[ifp_n_generation * index_local], + ifp_n_generation * static_cast(n), MPI_INT, neighbor, neighbor, + mpi::intracomm, &requests.back()); + } + + // Receive lifetimes + if (is_generation_time_or_both()) { + requests.emplace_back(); + MPI_Irecv(&recv_lifetimes[ifp_n_generation * index_local], + ifp_n_generation * static_cast(n), MPI_DOUBLE, neighbor, neighbor, + mpi::intracomm, &requests.back()); + } + + // Deserialization info to reconstruct data later + DeserializationInfo info = {index_local, n}; + deserialization_info.push_back(info); +} + +void copy_ifp_temp_to_source_banks_partial(int64_t index_temp, int n, + int64_t index_local, const vector>& temp_delayed_groups, + const vector>& temp_lifetimes) +{ + if (is_beta_effective_or_both()) { + std::copy(&temp_delayed_groups[index_temp], + &temp_delayed_groups[index_temp + n], + &simulation::ifp_source_delayed_group_bank[index_local]); + } + if (is_generation_time_or_both()) { + std::copy(&temp_lifetimes[index_temp], &temp_lifetimes[index_temp + n], + &simulation::ifp_source_lifetime_bank[index_local]); + } +} + +void deserialize_ifp_info(int ifp_n_generation, + vector& deserialization_info, + vector& recv_delayed_groups, vector& recv_lifetimes) +{ + int64_t n; + for (auto info : deserialization_info) { + int64_t index_local = info.index_local; + n = info.n; - if (ifp_idx < settings::ifp_n_generation) { - updated_ifp_lifetimes.resize(ifp_idx + 1); - for (size_t i = 0; i < ifp_idx; i++) { - updated_ifp_lifetimes[i] = ifp_lifetimes[i]; + for (int i = index_local; i < index_local + n; i++) { + if (is_beta_effective_or_both()) { + vector delayed_groups_received( + recv_delayed_groups.begin() + ifp_n_generation * i, + recv_delayed_groups.begin() + ifp_n_generation * (i + 1)); + simulation::ifp_source_delayed_group_bank[i] = delayed_groups_received; } - updated_ifp_lifetimes[ifp_idx] = p.lifetime(); - } else if (ifp_idx == settings::ifp_n_generation) { - updated_ifp_lifetimes.resize(ifp_idx); - for (size_t i = 0; i < ifp_idx - 1; i++) { - updated_ifp_lifetimes[i] = ifp_lifetimes[i + 1]; + if (is_generation_time_or_both()) { + vector lifetimes_received( + recv_lifetimes.begin() + ifp_n_generation * i, + recv_lifetimes.begin() + ifp_n_generation * (i + 1)); + simulation::ifp_source_lifetime_bank[i] = lifetimes_received; } - updated_ifp_lifetimes[ifp_idx - 1] = p.lifetime(); } + } +} - simulation::ifp_fission_lifetime_bank[idx] = updated_ifp_lifetimes; +#endif + +void copy_ifp_temp_to_source_banks(vector>& temp_delayed_groups, + vector>& temp_lifetimes) +{ + if (is_beta_effective_or_both()) { + std::copy(temp_delayed_groups.data(), + temp_delayed_groups.data() + settings::n_particles, + simulation::ifp_source_delayed_group_bank.begin()); + } + if (is_generation_time_or_both()) { + std::copy(temp_lifetimes.data(), + temp_lifetimes.data() + settings::n_particles, + simulation::ifp_source_lifetime_bank.begin()); + } +} + +// --------------------------------------------------------- +// bank.cpp functions +// --------------------------------------------------------- + +void allocate_temporary_vector_ifp( + vector>& delayed_group_bank_holder, + vector*& delayed_group_bank, + vector>& lifetime_bank_holder, vector*& lifetime_bank) +{ + if (is_beta_effective_or_both()) { + delayed_group_bank_holder.resize(simulation::fission_bank.size()); + delayed_group_bank = delayed_group_bank_holder.data(); + } + if (is_generation_time_or_both()) { + lifetime_bank_holder.resize(simulation::fission_bank.size()); + lifetime_bank = lifetime_bank_holder.data(); + } +} + +void sort_ifp_banks(int64_t i, int64_t idx, vector*& delayed_group_bank, + vector*& lifetime_bank) +{ + if (is_beta_effective_or_both()) { + const auto& delayed_groups = simulation::ifp_fission_delayed_group_bank[i]; + delayed_group_bank[idx] = delayed_groups; + } + if (is_generation_time_or_both()) { + const auto& lifetimes = simulation::ifp_fission_lifetime_bank[i]; + lifetime_bank[idx] = lifetimes; + } +} + +void copy_ifp_banks_to_fission_banks( + vector*& delayed_group_bank, vector*& lifetime_bank) +{ + if (is_beta_effective_or_both()) { + std::copy(delayed_group_bank, + delayed_group_bank + simulation::fission_bank.size(), + simulation::ifp_fission_delayed_group_bank.data()); + } + if (is_generation_time_or_both()) { + std::copy(lifetime_bank, lifetime_bank + simulation::fission_bank.size(), + simulation::ifp_fission_lifetime_bank.data()); } } diff --git a/src/simulation.cpp b/src/simulation.cpp index 81c714993ff..5eb70badbf1 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -7,6 +7,7 @@ #include "openmc/error.h" #include "openmc/event.h" #include "openmc/geometry_aux.h" +#include "openmc/ifp.h" #include "openmc/material.h" #include "openmc/mcpl_interface.h" #include "openmc/message_passing.h" @@ -336,19 +337,7 @@ void allocate_banks() // Allocate IFP bank if (settings::ifp) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { - simulation::ifp_source_delayed_group_bank.resize( - simulation::work_per_rank); - simulation::ifp_fission_delayed_group_bank.resize( - 3 * simulation::work_per_rank); - } - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { - simulation::ifp_source_lifetime_bank.resize(simulation::work_per_rank); - simulation::ifp_fission_lifetime_bank.resize( - 3 * simulation::work_per_rank); - } + resize_simulation_ifp_banks(); } } diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index a96cafb66a8..13511216870 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -5,6 +5,7 @@ #include "openmc/constants.h" #include "openmc/error.h" #include "openmc/material.h" +#include "openmc/ifp.h" #include "openmc/mgxs_interface.h" #include "openmc/nuclide.h" #include "openmc/photon.h" @@ -893,8 +894,7 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, case SCORE_IFP_TIME_NUM: if (settings::ifp) { if ((p.type() == Type::neutron) && (p.fission())) { - if (settings::ifp_parameter == IFPParameter::GenerationTime || - settings::ifp_parameter == IFPParameter::Both) { + if (is_generation_time_or_both()) { const auto& lifetimes = simulation::ifp_source_lifetime_bank[p.current_work() - 1]; int n_generation = static_cast(lifetimes.size()); @@ -909,8 +909,7 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, case SCORE_IFP_BETA_NUM: if (settings::ifp) { if ((p.type() == Type::neutron) && (p.fission())) { - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { + if (is_beta_effective_or_both()) { const auto& delayed_groups = simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; int n_generation = static_cast(delayed_groups.size()); @@ -928,8 +927,7 @@ void score_general_ce_nonanalog(Particle& p, int i_tally, int start_index, if (settings::ifp) { if ((p.type() == Type::neutron) && (p.fission())) { int n_generation; - if (settings::ifp_parameter == IFPParameter::BetaEffective || - settings::ifp_parameter == IFPParameter::Both) { + if (is_beta_effective_or_both()) { n_generation = static_cast( simulation::ifp_source_delayed_group_bank[p.current_work() - 1] .size()); From 7be644fb8f7dd4f9abc855041c3adb6b3470a2fa Mon Sep 17 00:00:00 2001 From: Joffrey Dorville Date: Mon, 10 Feb 2025 15:49:45 -0600 Subject: [PATCH 16/17] Refactoring in progress --- include/openmc/ifp.h | 69 +++++++++-------- src/bank.cpp | 4 +- src/eigenvalue.cpp | 14 ++-- src/ifp.cpp | 175 ++++++++++++++++++++----------------------- 4 files changed, 125 insertions(+), 137 deletions(-) diff --git a/include/openmc/ifp.h b/include/openmc/ifp.h index 02583ecb306..9c1806fdb26 100644 --- a/include/openmc/ifp.h +++ b/include/openmc/ifp.h @@ -21,7 +21,8 @@ bool is_generation_time_or_both(); // --------------------------------------------------------- template -vector _ifp(const T& site_value, const vector& data) { +vector _ifp(const T& value, const vector& data) +{ vector updated_data; size_t ifp_idx = data.size(); if (ifp_idx < settings::ifp_n_generation) { @@ -29,13 +30,13 @@ vector _ifp(const T& site_value, const vector& data) { for (size_t i = 0; i < ifp_idx; i++) { updated_data[i] = data[i]; } - updated_data[ifp_idx] = site_value; + updated_data[ifp_idx] = value; } else if (ifp_idx == settings::ifp_n_generation) { updated_data.resize(ifp_idx); for (size_t i = 0; i < ifp_idx - 1; i++) { updated_data[i] = data[i + 1]; } - updated_data[ifp_idx - 1] = site_value; + updated_data[ifp_idx - 1] = value; } return updated_data; } @@ -48,7 +49,7 @@ vector _ifp(const T& site_value, const vector& data) { //! as the one used to append the fission site to the fission bank. //! Multithreading protection is guaranteed by the index returned by the //! thread_safe_append call in physics.cpp. -void ifp(Particle& p, SourceSite& site, int64_t idx); +void ifp(const Particle& p, const SourceSite& site, int64_t idx); // --------------------------------------------------------- // simulation.cpp functions @@ -74,17 +75,15 @@ void resize_ifp_data( } } -void initialize_ifp_pointers(int64_t i, const vector*& delayed_groups_ptr, - const vector*& lifetimes_ptr); +void initialize_ifp_pointers(int64_t i_bank, + const vector*& delayed_groups_ptr, const vector*& lifetimes_ptr); -void add_ifp_data_to_temp(int64_t index_temp, - vector>& temp_delayed_groups, - const vector* delayed_groups_ptr, vector>& temp_lifetimes, +void add_ifp_data(int64_t idx, vector>& delayed_groups, + const vector* delayed_groups_ptr, vector>& lifetimes, const vector* lifetimes_ptr); -void copy_ifp_data_to_temp_from_fission_banks(int64_t index_temp, int i_bank, - vector>& temp_delayed_groups, - vector>& temp_lifetimes); +void retrieve_ifp_data_from_fission_banks(int64_t idx, int i_bank, + vector>& delayed_groups, vector>& lifetimes); #ifdef OPENMC_MPI @@ -95,36 +94,36 @@ struct DeserializationInfo { }; //! Broadcast the number of generation from the size of the first element -void broadcast_ifp_n_generation(int& ifp_n_generation, - vector>& temp_delayed_groups, - vector>& temp_lifetimes); +void broadcast_ifp_n_generation(int& n_generation, + const vector>& delayed_groups, + const vector>& lifetimes); //! Send IFP info via MPI -void send_ifp_info(int64_t index_local, int64_t n, int ifp_n_generation, - int neighbor, vector& requests, - vector> temp_delayed_groups, vector send_delayed_groups, - vector> temp_lifetimes, vector send_lifetimes); +void send_ifp_info(int64_t idx, int64_t n, int n_generation, int neighbor, + vector& requests, const vector> delayed_groups, + vector send_delayed_groups, const vector> lifetimes, + vector send_lifetimes); //! Receive IFP info through MPI -void receive_ifp_data(int64_t index_local, int64_t n, int ifp_n_generation, - int neighbor, vector& requests, vector& recv_delayed_groups, - vector& recv_lifetimes, - vector& deserialization_info); +void receive_ifp_data(int64_t idx, int64_t n, int n_generation, int neighbor, + vector& requests, vector& delayed_groups, + vector& lifetimes, vector& deserialization); -void copy_ifp_temp_to_source_banks_partial(int64_t index_temp, int n, - int64_t index_local, const vector>& temp_delayed_groups, - const vector>& temp_lifetimes); +void copy_partial_ifp_data_to_source_banks(int64_t idx, int n, int64_t i_bank, + const vector>& delayed_groups, + const vector>& lifetimes); //! Deserialize IFP information -void deserialize_ifp_info(int ifp_n_generation, - vector& deserialization_info, - vector& recv_delayed_groups, vector& recv_lifetimes); +void deserialize_ifp_info(int n_generation, + const vector& deserialization, + const vector& delayed_groups, const vector& lifetimes); #endif //! Copy IFP temporary vector to source banks -void copy_ifp_temp_to_source_banks(vector>& temp_delayed_groups, - vector>& temp_lifetimes); +void copy_complete_ifp_data_to_source_banks( + const vector>& delayed_groups, + const vector>& lifetimes); // --------------------------------------------------------- // bank.cpp functions @@ -135,12 +134,12 @@ void allocate_temporary_vector_ifp( vector*& delayed_group_bank, vector>& lifetime_bank_holder, vector*& lifetime_bank); -void sort_ifp_banks(int64_t i, int64_t idx, vector*& delayed_group_bank, - vector*& lifetime_bank); - -void copy_ifp_banks_to_fission_banks( +void sort_ifp_data_from_fission_banks(int64_t i_bank, int64_t idx, vector*& delayed_group_bank, vector*& lifetime_bank); +void copy_ifp_data_to_fission_banks( + vector* const& delayed_group_bank, vector* const& lifetime_bank); + } // namespace openmc #endif // OPENMC_IFP_H diff --git a/src/bank.cpp b/src/bank.cpp index dc1ed26e945..4c6bb065d55 100644 --- a/src/bank.cpp +++ b/src/bank.cpp @@ -122,7 +122,7 @@ void sort_fission_bank() } sorted_bank[idx] = site; if (settings::ifp) { - sort_ifp_banks( + sort_ifp_data_from_fission_banks( i, idx, sorted_ifp_delayed_group_bank, sorted_ifp_lifetime_bank); } } @@ -131,7 +131,7 @@ void sort_fission_bank() std::copy(sorted_bank, sorted_bank + simulation::fission_bank.size(), simulation::fission_bank.data()); if (settings::ifp) { - copy_ifp_banks_to_fission_banks( + copy_ifp_data_to_fission_banks( sorted_ifp_delayed_group_bank, sorted_ifp_lifetime_bank); } } diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index f819d42e9c4..e41ee8f1b46 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -186,8 +186,8 @@ void synchronize_bank() for (int64_t j = 1; j <= settings::n_particles / total; ++j) { temp_sites[index_temp] = site; if (settings::ifp) { - add_ifp_data_to_temp(index_temp, temp_delayed_groups, - delayed_groups_ptr, temp_lifetimes, lifetimes_ptr); + add_ifp_data(index_temp, temp_delayed_groups, delayed_groups_ptr, + temp_lifetimes, lifetimes_ptr); } ++index_temp; } @@ -197,8 +197,8 @@ void synchronize_bank() if (prn(&seed) < p_sample) { temp_sites[index_temp] = site; if (settings::ifp) { - add_ifp_data_to_temp(index_temp, temp_delayed_groups, - delayed_groups_ptr, temp_lifetimes, lifetimes_ptr); + add_ifp_data(index_temp, temp_delayed_groups, delayed_groups_ptr, + temp_lifetimes, lifetimes_ptr); } ++index_temp; } @@ -247,7 +247,7 @@ void synchronize_bank() int i_bank = simulation::fission_bank.size() - sites_needed + i; temp_sites[index_temp] = simulation::fission_bank[i_bank]; if (settings::ifp) { - copy_ifp_data_to_temp_from_fission_banks( + retrieve_ifp_data_from_fission_banks( index_temp, i_bank, temp_delayed_groups, temp_lifetimes); } ++index_temp; @@ -389,7 +389,7 @@ void synchronize_bank() &simulation::source_bank[index_local]); if (settings::ifp) { - copy_ifp_temp_to_source_banks_partial( + copy_partial_ifp_data_to_source_banks( index_temp, n, index_local, temp_delayed_groups, temp_lifetimes); } } @@ -423,7 +423,7 @@ void synchronize_bank() std::copy(temp_sites.data(), temp_sites.data() + settings::n_particles, simulation::source_bank.begin()); if (settings::ifp) { - copy_ifp_temp_to_source_banks(temp_delayed_groups, temp_lifetimes); + copy_complete_ifp_data_to_source_banks(temp_delayed_groups, temp_lifetimes); } #endif temp_sites.clear(); diff --git a/src/ifp.cpp b/src/ifp.cpp index 39dd498fb1d..0cfa2e83daa 100644 --- a/src/ifp.cpp +++ b/src/ifp.cpp @@ -36,23 +36,22 @@ bool is_generation_time_or_both() // physics.cpp functions // --------------------------------------------------------- -void ifp(Particle& p, SourceSite& site, int64_t idx) +void ifp(const Particle& p, const SourceSite& site, int64_t idx) { // Beta effective if (is_beta_effective_or_both()) { - const auto& ifp_delayed_groups = + const auto& delayed_groups = simulation::ifp_source_delayed_group_bank[p.current_work() - 1]; - vector updated_ifp_delayed_groups = - _ifp(site.delayed_group, ifp_delayed_groups); - simulation::ifp_fission_delayed_group_bank[idx] = - updated_ifp_delayed_groups; + vector updated_delayed_groups = + _ifp(site.delayed_group, delayed_groups); + simulation::ifp_fission_delayed_group_bank[idx] = updated_delayed_groups; } // Generation time if (is_generation_time_or_both()) { - const auto& ifp_lifetimes = + const auto& lifetimes = simulation::ifp_source_lifetime_bank[p.current_work() - 1]; - vector updated_ifp_lifetimes = _ifp(p.lifetime(), ifp_lifetimes); - simulation::ifp_fission_lifetime_bank[idx] = updated_ifp_lifetimes; + vector updated_lifetimes = _ifp(p.lifetime(), lifetimes); + simulation::ifp_fission_lifetime_bank[idx] = updated_lifetimes; } } @@ -60,17 +59,16 @@ void ifp(Particle& p, SourceSite& site, int64_t idx) // simulation.cpp functions // --------------------------------------------------------- -void resize_simulation_ifp_banks() { +void resize_simulation_ifp_banks() +{ if (is_beta_effective_or_both()) { - simulation::ifp_source_delayed_group_bank.resize( - simulation::work_per_rank); + simulation::ifp_source_delayed_group_bank.resize(simulation::work_per_rank); simulation::ifp_fission_delayed_group_bank.resize( 3 * simulation::work_per_rank); } if (is_generation_time_or_both()) { simulation::ifp_source_lifetime_bank.resize(simulation::work_per_rank); - simulation::ifp_fission_lifetime_bank.resize( - 3 * simulation::work_per_rank); + simulation::ifp_fission_lifetime_bank.resize(3 * simulation::work_per_rank); } } @@ -78,153 +76,143 @@ void resize_simulation_ifp_banks() { // eigenvalue.cpp functions // --------------------------------------------------------- -void initialize_ifp_pointers(int64_t i, const vector*& delayed_groups_ptr, - const vector*& lifetimes_ptr) +void initialize_ifp_pointers(int64_t i_bank, + const vector*& delayed_groups_ptr, const vector*& lifetimes_ptr) { if (is_beta_effective_or_both()) { - delayed_groups_ptr = &simulation::ifp_fission_delayed_group_bank[i]; + delayed_groups_ptr = &simulation::ifp_fission_delayed_group_bank[i_bank]; } if (is_generation_time_or_both()) { - lifetimes_ptr = &simulation::ifp_fission_lifetime_bank[i]; + lifetimes_ptr = &simulation::ifp_fission_lifetime_bank[i_bank]; } } -void add_ifp_data_to_temp(int64_t index_temp, - vector>& temp_delayed_groups, - const vector* delayed_groups_ptr, vector>& temp_lifetimes, +void add_ifp_data(int64_t idx, vector>& delayed_groups, + const vector* delayed_groups_ptr, vector>& lifetimes, const vector* lifetimes_ptr) { if (is_beta_effective_or_both()) { - temp_delayed_groups[index_temp] = *delayed_groups_ptr; + delayed_groups[idx] = *delayed_groups_ptr; } if (is_generation_time_or_both()) { - temp_lifetimes[index_temp] = *lifetimes_ptr; + lifetimes[idx] = *lifetimes_ptr; } } -void copy_ifp_data_to_temp_from_fission_banks(int64_t index_temp, int i_bank, - vector>& temp_delayed_groups, - vector>& temp_lifetimes) +void retrieve_ifp_data_from_fission_banks(int64_t idx, int i_bank, + vector>& delayed_groups, vector>& lifetimes) { if (is_beta_effective_or_both()) { - temp_delayed_groups[index_temp] = - simulation::ifp_fission_delayed_group_bank[i_bank]; + delayed_groups[idx] = simulation::ifp_fission_delayed_group_bank[i_bank]; } if (is_generation_time_or_both()) { - temp_lifetimes[index_temp] = simulation::ifp_fission_lifetime_bank[i_bank]; + lifetimes[idx] = simulation::ifp_fission_lifetime_bank[i_bank]; } } #ifdef OPENMC_MPI -void broadcast_ifp_n_generation(int& ifp_n_generation, - vector>& temp_delayed_groups, - vector>& temp_lifetimes) +void broadcast_ifp_n_generation(int& n_generation, + const vector>& delayed_groups, + const vector>& lifetimes) { if (mpi::rank == 0) { if (is_beta_effective_or_both()) { - ifp_n_generation = static_cast(temp_delayed_groups[0].size()); + n_generation = static_cast(delayed_groups[0].size()); } else { - ifp_n_generation = static_cast(temp_lifetimes[0].size()); + n_generation = static_cast(lifetimes[0].size()); } } - MPI_Bcast(&ifp_n_generation, 1, MPI_INT, 0, mpi::intracomm); + MPI_Bcast(&n_generation, 1, MPI_INT, 0, mpi::intracomm); } -void send_ifp_info(int64_t index_local, int64_t n, int ifp_n_generation, - int neighbor, vector& requests, - vector> temp_delayed_groups, vector send_delayed_groups, - vector> temp_lifetimes, vector send_lifetimes) +void send_ifp_info(int64_t idx, int64_t n, int n_generation, int neighbor, + vector& requests, const vector> delayed_groups, + vector send_delayed_groups, const vector> lifetimes, + vector send_lifetimes) { - for (int i = index_local; i < index_local + n; i++) { + // Copy data in send buffers + for (int i = idx; i < idx + n; i++) { if (is_beta_effective_or_both()) { - std::copy(temp_delayed_groups[i].begin(), temp_delayed_groups[i].end(), - send_delayed_groups.begin() + i * ifp_n_generation); + std::copy(delayed_groups[i].begin(), delayed_groups[i].end(), + send_delayed_groups.begin() + i * n_generation); } if (is_generation_time_or_both()) { - std::copy(temp_lifetimes[i].begin(), temp_lifetimes[i].end(), - send_lifetimes.begin() + i * ifp_n_generation); + std::copy(lifetimes[i].begin(), lifetimes[i].end(), + send_lifetimes.begin() + i * n_generation); } } - // Send delayed groups if (is_beta_effective_or_both()) { requests.emplace_back(); - MPI_Isend(&send_delayed_groups[ifp_n_generation * index_local], - ifp_n_generation * static_cast(n), MPI_INT, neighbor, mpi::rank, + MPI_Isend(&send_delayed_groups[n_generation * idx], + n_generation * static_cast(n), MPI_INT, neighbor, mpi::rank, mpi::intracomm, &requests.back()); } - // Send lifetimes if (is_generation_time_or_both()) { requests.emplace_back(); - MPI_Isend(&send_lifetimes[ifp_n_generation * index_local], - ifp_n_generation * static_cast(n), MPI_DOUBLE, neighbor, mpi::rank, + MPI_Isend(&send_lifetimes[n_generation * idx], + n_generation * static_cast(n), MPI_DOUBLE, neighbor, mpi::rank, mpi::intracomm, &requests.back()); } } -void receive_ifp_data(int64_t index_local, int64_t n, int ifp_n_generation, - int neighbor, vector& requests, vector& recv_delayed_groups, - vector& recv_lifetimes, - vector& deserialization_info) +void receive_ifp_data(int64_t idx, int64_t n, int n_generation, int neighbor, + vector& requests, vector& delayed_groups, + vector& lifetimes, vector& deserialization) { // Receive delayed groups if (is_beta_effective_or_both()) { requests.emplace_back(); - MPI_Irecv(&recv_delayed_groups[ifp_n_generation * index_local], - ifp_n_generation * static_cast(n), MPI_INT, neighbor, neighbor, + MPI_Irecv(&delayed_groups[n_generation * idx], + n_generation * static_cast(n), MPI_INT, neighbor, neighbor, mpi::intracomm, &requests.back()); } - // Receive lifetimes if (is_generation_time_or_both()) { requests.emplace_back(); - MPI_Irecv(&recv_lifetimes[ifp_n_generation * index_local], - ifp_n_generation * static_cast(n), MPI_DOUBLE, neighbor, neighbor, + MPI_Irecv(&lifetimes[n_generation * idx], + n_generation * static_cast(n), MPI_DOUBLE, neighbor, neighbor, mpi::intracomm, &requests.back()); } - // Deserialization info to reconstruct data later - DeserializationInfo info = {index_local, n}; - deserialization_info.push_back(info); + DeserializationInfo info = {idx, n}; + deserialization.push_back(info); } -void copy_ifp_temp_to_source_banks_partial(int64_t index_temp, int n, - int64_t index_local, const vector>& temp_delayed_groups, - const vector>& temp_lifetimes) +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()) { - std::copy(&temp_delayed_groups[index_temp], - &temp_delayed_groups[index_temp + n], - &simulation::ifp_source_delayed_group_bank[index_local]); + std::copy(&delayed_groups[idx], &delayed_groups[idx + n], + &simulation::ifp_source_delayed_group_bank[i_bank]); } if (is_generation_time_or_both()) { - std::copy(&temp_lifetimes[index_temp], &temp_lifetimes[index_temp + n], - &simulation::ifp_source_lifetime_bank[index_local]); + std::copy(&lifetimes[idx], &lifetimes[idx + n], + &simulation::ifp_source_lifetime_bank[i_bank]); } } -void deserialize_ifp_info(int ifp_n_generation, - vector& deserialization_info, - vector& recv_delayed_groups, vector& recv_lifetimes) +void deserialize_ifp_info(int n_generation, + const vector& deserialization, + const vector& delayed_groups, const vector& lifetimes) { - int64_t n; - for (auto info : deserialization_info) { + for (auto info : deserialization) { int64_t index_local = info.index_local; - n = info.n; + int64_t n = info.n; for (int i = index_local; i < index_local + n; i++) { if (is_beta_effective_or_both()) { vector delayed_groups_received( - recv_delayed_groups.begin() + ifp_n_generation * i, - recv_delayed_groups.begin() + ifp_n_generation * (i + 1)); + delayed_groups.begin() + n_generation * i, + delayed_groups.begin() + n_generation * (i + 1)); simulation::ifp_source_delayed_group_bank[i] = delayed_groups_received; } if (is_generation_time_or_both()) { - vector lifetimes_received( - recv_lifetimes.begin() + ifp_n_generation * i, - recv_lifetimes.begin() + ifp_n_generation * (i + 1)); + vector lifetimes_received(lifetimes.begin() + n_generation * i, + lifetimes.begin() + n_generation * (i + 1)); simulation::ifp_source_lifetime_bank[i] = lifetimes_received; } } @@ -233,17 +221,17 @@ void deserialize_ifp_info(int ifp_n_generation, #endif -void copy_ifp_temp_to_source_banks(vector>& temp_delayed_groups, - vector>& temp_lifetimes) +void copy_complete_ifp_data_to_source_banks( + const vector>& delayed_groups, + const vector>& lifetimes) { if (is_beta_effective_or_both()) { - std::copy(temp_delayed_groups.data(), - temp_delayed_groups.data() + settings::n_particles, + std::copy(delayed_groups.data(), + delayed_groups.data() + settings::n_particles, simulation::ifp_source_delayed_group_bank.begin()); } if (is_generation_time_or_both()) { - std::copy(temp_lifetimes.data(), - temp_lifetimes.data() + settings::n_particles, + std::copy(lifetimes.data(), lifetimes.data() + settings::n_particles, simulation::ifp_source_lifetime_bank.begin()); } } @@ -267,21 +255,22 @@ void allocate_temporary_vector_ifp( } } -void sort_ifp_banks(int64_t i, int64_t idx, vector*& delayed_group_bank, - vector*& lifetime_bank) +void sort_ifp_data_from_fission_banks(int64_t i_bank, int64_t idx, + vector*& delayed_group_bank, vector*& lifetime_bank) { if (is_beta_effective_or_both()) { - const auto& delayed_groups = simulation::ifp_fission_delayed_group_bank[i]; + const auto& delayed_groups = + simulation::ifp_fission_delayed_group_bank[i_bank]; delayed_group_bank[idx] = delayed_groups; } if (is_generation_time_or_both()) { - const auto& lifetimes = simulation::ifp_fission_lifetime_bank[i]; + const auto& lifetimes = simulation::ifp_fission_lifetime_bank[i_bank]; lifetime_bank[idx] = lifetimes; } } -void copy_ifp_banks_to_fission_banks( - vector*& delayed_group_bank, vector*& lifetime_bank) +void copy_ifp_data_to_fission_banks( + vector* const& delayed_group_bank, vector* const& lifetime_bank) { if (is_beta_effective_or_both()) { std::copy(delayed_group_bank, From e3b4a2284ad22200da60b352b229c39064044131 Mon Sep 17 00:00:00 2001 From: Joffrey Dorville Date: Mon, 10 Feb 2025 17:05:15 -0600 Subject: [PATCH 17/17] Format --- src/tallies/tally_scoring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index 13511216870..c32d17d7b3f 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -4,8 +4,8 @@ #include "openmc/capi.h" #include "openmc/constants.h" #include "openmc/error.h" -#include "openmc/material.h" #include "openmc/ifp.h" +#include "openmc/material.h" #include "openmc/mgxs_interface.h" #include "openmc/nuclide.h" #include "openmc/photon.h"