Skip to content

Commit 47ca291

Browse files
Kinetics parameters using Iterated Fission Probability (#3133)
Co-authored-by: Paul Romano <paul.k.romano@gmail.com>
1 parent c1a4d43 commit 47ca291

28 files changed

Lines changed: 1002 additions & 6 deletions

‎CMakeLists.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ list(APPEND libopenmc_SOURCES
343343
src/geometry.cpp
344344
src/geometry_aux.cpp
345345
src/hdf5_interface.cpp
346+
src/ifp.cpp
346347
src/initialize.cpp
347348
src/lattice.cpp
348349
src/material.cpp

‎docs/source/usersguide/index.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ essential aspects of using OpenMC to perform simulations.
2222
plots
2323
depletion
2424
decay_sources
25+
kinetics
2526
scripts
2627
processing
2728
parallel
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
.. _kinetics:
2+
3+
===================
4+
Kinetics parameters
5+
===================
6+
7+
OpenMC has the capability to estimate the following adjoint-weighted effective
8+
generation time :math:`\Lambda_{\text{eff}}` and the effective delayed neutron
9+
fraction :math:`\beta_{\text{eff}}`. These parameters are calculated using the
10+
iterated fission probability (IFP) method [Hurwitz_1964]_ based on a similar
11+
approach as in `Serpent 2 <https://doi.org/10.1016/j.anucene.2013.10.032>`_. The
12+
implementation in OpenMC is limited to eigenvalue calculations and is described
13+
in more details in [Dorville_2025]_.
14+
15+
----------------------------------
16+
Iterated Fission Probability (IFP)
17+
----------------------------------
18+
19+
With IFP, additional information needs to be recorded during the simulation
20+
compared to a typical eigenvalue calculation. OpenMC stores an additional
21+
set of values (neutron lifetime or delayed neutron group number for
22+
:math:`\Lambda_{\text{eff}}` or :math:`\beta_{\text{eff}}`, respectively)
23+
for every fission neutron simulated. Each set of values corresponds to
24+
the values that are associated to the :math:`N_{\text{gen}}` direct ancestors
25+
of any given fission neutron.
26+
27+
:math:`N_{\text{gen}}` is referred to as the number of generations in the
28+
IFP method and corresponds to the number of generations between the birth of
29+
a fission neutron and the time its score is added to the IFP tally. By default,
30+
OpenMC considers 10 generations but this value can be modified by the user via
31+
the ``ifp_n_generation`` settings in the Python API::
32+
33+
settings.ifp_n_generation = 5
34+
35+
``ifp_n_generation`` should be greater than 0, but should also be lower than
36+
or equal to the number of inactive batches declared for the calculation.
37+
The respect of these constraints is verified by OpenMC before any calculation.
38+
39+
OpenMC will automatically detect the type of data that needs to be stored based
40+
on the tally scores selected by the user. This guarantees that only information
41+
of interest are stored during a simulation and avoids using extra memory when
42+
only one parameter is needed. The following table shows the tally scores that
43+
are needed to compute kinetics parameters in OpenMC:
44+
45+
.. table:: **OpenMC tally scores needed to calculate adjoint-weighted kinetics parameters**
46+
:align: center
47+
48+
=============================== ============================ ========================== ========
49+
OpenMC tally score \\ Parameter :math:`\Lambda_{\text{eff}}` :math:`\beta_{\text{eff}}` Both
50+
=============================== ============================ ========================== ========
51+
``ifp-time-numerator`` X X
52+
``ifp-beta-numerator`` X X
53+
``ifp-denominator`` X X X
54+
=============================== ============================ ========================== ========
55+
56+
|
57+
58+
.. note:: Because the memory footprint of additional data is generally non-negligible
59+
with IFP, it is recommended to choose the value for ``ifp_n_generation`` carefully.
60+
For example, using one generation for both kinetics parameters corresponds to store
61+
one additional integer (for the delayed neutron group number used with
62+
:math:`\beta_{\text{eff}}`) and one floating point value (for the neutron lifetime
63+
used with :math:`\Lambda_{\text{eff}}`) for every fission neutron simulated once the
64+
asymptotic regime is reached.
65+
66+
-----------------------------
67+
Obtaining kinetics parameters
68+
-----------------------------
69+
70+
Here is an example showing how to declare the three available IFP scores in a
71+
single tally::
72+
73+
tally = openmc.Tally(name="ifp-scores")
74+
tally.scores = [
75+
"ifp-time-numerator",
76+
"ifp-beta-numerator",
77+
"ifp-denominator"
78+
]
79+
80+
The effective generation time :math:`\Lambda_{\text{eff}}` is calculated
81+
by dividing the result of the ``ifp-time-numerator`` score by the one obtained
82+
for ``ifp-denominator`` and by the :math:`k_{\text{eff}}` of the simulation:
83+
84+
.. math::
85+
:label: lambda_eff
86+
87+
\Lambda_{\text{eff}} = \frac{S_{\text{ifp-time-numerator}}}{S_{\text{ifp-denominator}} \times k_{\text{eff}}}
88+
89+
The effective delayed neutron fraction :math:`\beta_{\text{eff}}` is calculated
90+
by dividing the result of the ``ifp-beta-numerator`` score by the one obtained
91+
for ``ifp-denominator``:
92+
93+
.. math::
94+
:label: beta_eff
95+
96+
\beta_{\text{eff}} = \frac{S_{\text{ifp-beta-numerator}}}{S_{\text{ifp-denominator}}}
97+
98+
.. only:: html
99+
100+
.. rubric:: References
101+
102+
.. [Hurwitz_1964] H. Hurwitz Jr., "Naval Reactors Physics Handbook", volume 1, p. 864.
103+
Radkowsky, A. (Ed.), Naval Reactors, Division of Reactor Development, U.S.
104+
Atomic Energy Commission (1964).
105+
106+
.. [Dorville_2025] J. Dorville, L. Labrie-Cleary, and P. K. Romano, "Implementation
107+
of the Iterated Fission Probability Method in OpenMC to Compute Adjoint-Weighted
108+
Kinetics Parameters", International Conference on Mathematics and Computational
109+
Methods Applied to Nuclear Science and Engineering (M&C 2025), Denver, April 27-30,
110+
2025 (to be presented).

‎docs/source/usersguide/tallies.rst‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,24 @@ The following tables show all valid scores:
322322
| |particle. Note that this score can only be combined|
323323
| |with a cell filter and an energy filter. |
324324
+----------------------+---------------------------------------------------+
325+
|ifp-time-numerator |Adjoint-weighted lifetime of neutron produced by |
326+
| |fission in units of seconds per source particle. |
327+
| |This score is used to compute kinetics parameters |
328+
| |using the iterated fission probability (IFP) |
329+
| |method. |
330+
+----------------------+---------------------------------------------------+
331+
|ifp-beta-numerator |Adjoint-weighted number of delayed fission events |
332+
| |in units of number of delayed fission event per |
333+
| |source particle. This score is used to compute |
334+
| |kinetics parameters using the iterated fission |
335+
| |probability (IFP) method. |
336+
+----------------------+---------------------------------------------------+
337+
|ifp-denominator |Weights corresponding to the number of fission |
338+
| |events in units of number of fission event per |
339+
| |source particle. This score is used to compute |
340+
| |kinetics parameters using the iterated fission |
341+
| |probability (IFP) method. |
342+
+----------------------+---------------------------------------------------+
325343

326344
.. _usersguide_tally_normalization:
327345

‎include/openmc/bank.h‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ extern SharedArray<SourceSite> surf_source_bank;
2222

2323
extern SharedArray<SourceSite> fission_bank;
2424

25+
extern vector<vector<int>> ifp_source_delayed_group_bank;
26+
27+
extern vector<vector<double>> ifp_source_lifetime_bank;
28+
29+
extern vector<vector<int>> ifp_fission_delayed_group_bank;
30+
31+
extern vector<vector<double>> ifp_fission_lifetime_bank;
32+
2533
extern vector<int64_t> progeny_per_particle;
2634

2735
} // namespace simulation

‎include/openmc/constants.h‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,10 @@ enum TallyScore {
312312
SCORE_FISS_Q_PROMPT = -14, // prompt fission Q-value
313313
SCORE_FISS_Q_RECOV = -15, // recoverable fission Q-value
314314
SCORE_DECAY_RATE = -16, // delayed neutron precursor decay rate
315-
SCORE_PULSE_HEIGHT = -17 // pulse-height
315+
SCORE_PULSE_HEIGHT = -17, // pulse-height
316+
SCORE_IFP_TIME_NUM = -18, // IFP lifetime numerator
317+
SCORE_IFP_BETA_NUM = -19, // IFP delayed fraction numerator
318+
SCORE_IFP_DENOM = -20 // IFP common denominator
316319
};
317320

318321
// Global tally parameters
@@ -322,6 +325,9 @@ enum class GlobalTally { K_COLLISION, K_ABSORPTION, K_TRACKLENGTH, LEAKAGE };
322325
// Miscellaneous
323326
constexpr int C_NONE {-1};
324327

328+
// Default value of generation for IFP
329+
constexpr int DEFAULT_IFP_N_GENERATION {10};
330+
325331
// Interpolation rules
326332
enum class Interpolation {
327333
histogram = 1,

‎include/openmc/ifp.h‎

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#ifndef OPENMC_IFP_H
2+
#define OPENMC_IFP_H
3+
4+
#include "openmc/message_passing.h"
5+
#include "openmc/particle.h"
6+
#include "openmc/particle_data.h"
7+
#include "openmc/settings.h"
8+
9+
namespace openmc {
10+
11+
//! Check the value of the IFP parameter for beta effective or both.
12+
//!
13+
//! \return true if "BetaEffective" or "Both", false otherwise.
14+
bool is_beta_effective_or_both();
15+
16+
//! Check the value of the IFP parameter for generation time or both.
17+
//!
18+
//! \return true if "GenerationTime" or "Both", false otherwise.
19+
bool is_generation_time_or_both();
20+
21+
//! Resize IFP vectors
22+
//!
23+
//! \param[in,out] delayed_groups List of delayed group numbers
24+
//! \param[in,out] lifetimes List of lifetimes
25+
//! \param[in] n Dimension to resize vectors
26+
template<typename T, typename U>
27+
void resize_ifp_data(vector<T>& delayed_groups, vector<U>& lifetimes, int64_t n)
28+
{
29+
if (is_beta_effective_or_both()) {
30+
delayed_groups.resize(n);
31+
}
32+
if (is_generation_time_or_both()) {
33+
lifetimes.resize(n);
34+
}
35+
}
36+
37+
//! Update a list of values by adding a new value if the size
38+
//! of the list can accomodate the new value or by shifting all
39+
//! values to the left (removing the first value of the list
40+
//! and adding the new value at the end of the list).
41+
//!
42+
//! \param[in] value Value to add to the list
43+
//! \param[in] data Initial version of the list
44+
//! \return Updated list
45+
template<typename T>
46+
vector<T> _ifp(const T& value, const vector<T>& data)
47+
{
48+
vector<T> updated;
49+
size_t source_idx = data.size();
50+
51+
if (source_idx < settings::ifp_n_generation) {
52+
updated.resize(source_idx + 1);
53+
for (size_t i = 0; i < source_idx; i++) {
54+
updated[i] = data[i];
55+
}
56+
updated[source_idx] = value;
57+
} else if (source_idx == settings::ifp_n_generation) {
58+
updated.resize(source_idx);
59+
for (size_t i = 0; i < source_idx - 1; i++) {
60+
updated[i] = data[i + 1];
61+
}
62+
updated[source_idx - 1] = value;
63+
}
64+
return updated;
65+
}
66+
67+
//! \brief Iterated Fission Probability (IFP) method.
68+
//!
69+
//! Add the IFP information in the IFP banks using the same index
70+
//! as the one used to append the fission site to the fission bank.
71+
//! Multithreading protection is guaranteed by the index returned by the
72+
//! thread_safe_append call in physics.cpp.
73+
//!
74+
//! Needs to be done after the delayed group is found.
75+
//!
76+
//! \param[in] p Particle
77+
//! \param[in] site Fission site
78+
//! \param[in] idx Bank index from the thread_safe_append call in physics.cpp
79+
void ifp(const Particle& p, const SourceSite& site, int64_t idx);
80+
81+
//! Resize the IFP banks used in the simulation
82+
void resize_simulation_ifp_banks();
83+
84+
//! Retrieve IFP data from the IFP fission banks.
85+
//!
86+
//! \param[in] i_bank Index in the fission banks
87+
//! \param[in,out] delayed_groups Delayed group numbers
88+
//! \param[in,out] lifetimes Lifetimes lists
89+
void copy_ifp_data_from_fission_banks(
90+
int i_bank, vector<int>& delayed_groups, vector<double>& lifetimes);
91+
92+
#ifdef OPENMC_MPI
93+
94+
//! Deserialization information for transfer of IFP data using MPI
95+
struct DeserializationInfo {
96+
int64_t index_local; //!< local index
97+
int64_t n; //!< number of sites sent
98+
};
99+
100+
//! Broadcast the number of generation determined by the size of the first
101+
//! element on the first processor.
102+
//!
103+
//! \param[in] n_generation Number of generations
104+
//! \param[in] delayed_groups List of delayed group numbers lists
105+
//! \param[in] lifetimes List of lifetimes lists
106+
void broadcast_ifp_n_generation(int& n_generation,
107+
const vector<vector<int>>& delayed_groups,
108+
const vector<vector<double>>& lifetimes);
109+
110+
//! Send IFP data using MPI.
111+
//!
112+
//! \param[in] idx Index of the first site
113+
//! \param[in] n Number of sites to send
114+
//! \param[in] n_generation Number of generations
115+
//! \param[in] neighbor Index of the neighboring processor
116+
//! \param[in] requests MPI requests
117+
//! \param[in] delayed_groups List of delayed group numbers lists
118+
//! \param[out] send_delayed_groups Delayed group numbers buffer
119+
//! \param[in] lifetimes List of lifetimes lists
120+
//! \param[out] send_lifetimes Lifetimes buffer
121+
void send_ifp_info(int64_t idx, int64_t n, int n_generation, int neighbor,
122+
vector<MPI_Request>& requests, const vector<vector<int>>& delayed_groups,
123+
vector<int>& send_delayed_groups, const vector<vector<double>>& lifetimes,
124+
vector<double>& send_lifetimes);
125+
126+
//! Receive IFP data using MPI.
127+
//!
128+
//! \param[in] idx Index of the first site
129+
//! \param[in] n Number of sites to receive
130+
//! \param[in] n_generation Number of generations
131+
//! \param[in] neighbor Index of the neighboring processor
132+
//! \param[in] requests MPI requests
133+
//! \param[in] delayed_groups List of delayed group numbers
134+
//! \param[in] lifetimes List of lifetimes
135+
//! \param[out] deserialization Information to deserialize the received data
136+
void receive_ifp_data(int64_t idx, int64_t n, int n_generation, int neighbor,
137+
vector<MPI_Request>& requests, vector<int>& delayed_groups,
138+
vector<double>& lifetimes, vector<DeserializationInfo>& deserialization);
139+
140+
//! Copy partial IFP data from local lists to source banks.
141+
//!
142+
//! \param[in] idx Index of the first site
143+
//! \param[in] n Number of sites to copy
144+
//! \param[in] i_bank Index in the IFP source banks
145+
//! \param[in] delayed_groups List of delayed group numbers lists
146+
//! \param[in] lifetimes List of lifetimes lists
147+
void copy_partial_ifp_data_to_source_banks(int64_t idx, int n, int64_t i_bank,
148+
const vector<vector<int>>& delayed_groups,
149+
const vector<vector<double>>& lifetimes);
150+
151+
//! Deserialize IFP information received using MPI and store it in
152+
//! the IFP source banks.
153+
//!
154+
//! \param[in] n_generation Number of generations
155+
//! \param[out] deserialization Information to deserialize the received data
156+
//! \param[in] delayed_groups List of delayed group numbers
157+
//! \param[in] lifetimes List of lifetimes
158+
void deserialize_ifp_info(int n_generation,
159+
const vector<DeserializationInfo>& deserialization,
160+
const vector<int>& delayed_groups, const vector<double>& lifetimes);
161+
162+
#endif
163+
164+
//! Copy IFP temporary vectors to source banks.
165+
//!
166+
//! \param[in] delayed_groups List of delayed group numbers lists
167+
//! \param[in] lifetimes List of lifetimes lists
168+
void copy_complete_ifp_data_to_source_banks(
169+
const vector<vector<int>>& delayed_groups,
170+
const vector<vector<double>>& lifetimes);
171+
172+
//! Allocate temporary vectors for IFP data.
173+
//!
174+
//! \param[in,out] delayed_groups List of delayed group numbers lists
175+
//! \param[in,out] lifetimes List of delayed group numbers lists
176+
void allocate_temporary_vector_ifp(
177+
vector<vector<int>>& delayed_groups, vector<vector<double>>& lifetimes);
178+
179+
//! Copy local IFP data to IFP fission banks.
180+
//!
181+
//! \param[in] delayed_groups_ptr Pointer to delayed group numbers
182+
//! \param[in] lifetimes_ptr Pointer to lifetimes
183+
void copy_ifp_data_to_fission_banks(
184+
const vector<int>* delayed_groups_ptr, const vector<double>* lifetimes_ptr);
185+
186+
} // namespace openmc
187+
188+
#endif // OPENMC_IFP_H

‎include/openmc/particle_data.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,9 @@ class ParticleData : public GeometryState {
454454

455455
int cell_born_ {-1};
456456

457+
// Iterated Fission Probability
458+
double lifetime_ {0.0}; //!< neutron lifetime [s]
459+
457460
int n_collision_ {0};
458461

459462
bool write_track_ {false};
@@ -560,6 +563,10 @@ class ParticleData : public GeometryState {
560563
double& time_last() { return time_last_; }
561564
const double& time_last() const { return time_last_; }
562565

566+
// Particle lifetime
567+
double& lifetime() { return lifetime_; }
568+
const double& lifetime() const { return lifetime_; }
569+
563570
// What event took place, described in greater detail below
564571
TallyEvent& event() { return event_; }
565572
const TallyEvent& event() const { return event_; }

0 commit comments

Comments
 (0)