chombo-discharge
Loading...
Searching...
No Matches
CD_KMCDualStateReactionImplem.H
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021-2026 SINTEF Energy Research
3 *
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
13#ifndef CD_KMCDUALSTATEREACTIONIMPLEM_H
14#define CD_KMCDUALSTATEREACTIONIMPLEM_H
15
16// Std includes
17#include <limits>
18
19// Chombo includes
20#include <CH_assert.H>
21
22// Our includes
24#include <CD_NamespaceHeader.H>
25
26template <typename State, typename T>
27inline KMCDualStateReaction<State, T>::KMCDualStateReaction(const std::list<size_t>& a_lhsReactives,
28 const std::list<size_t>& a_rhsReactives,
29 const std::list<size_t>& a_rhsNonReactives) noexcept
30{
31 m_lhsReactives = a_lhsReactives;
32 m_rhsReactives = a_rhsReactives;
33 m_rhsNonReactives = a_rhsNonReactives;
34
35 CH_assert(a_lhsReactives.size() > 0);
36
37 this->computeStateChanges();
38}
39
40template <typename State, typename T>
43
44template <typename State, typename T>
45inline void
47{
48 // Consumed species.
49 for (const auto& r : m_lhsReactives) {
50 if (m_reactiveStateChange.find(r) == m_reactiveStateChange.end()) {
51 m_reactiveStateChange.emplace(r, -1);
52 }
53 else {
54 m_reactiveStateChange[r]--;
55 }
56 }
57
58 // Produced species.
59 for (const auto& p : m_rhsReactives) {
60 if (m_reactiveStateChange.find(p) == m_reactiveStateChange.end()) {
61 m_reactiveStateChange.emplace(p, +1);
62 }
63 else {
64 m_reactiveStateChange[p]++;
65 }
66 }
67
68 // Produced photons.
69 for (const auto& p : m_rhsNonReactives) {
70 if (m_nonReactiveStateChange.find(p) == m_nonReactiveStateChange.end()) {
71 m_nonReactiveStateChange.emplace(p, +1);
72 }
73 else {
74 m_nonReactiveStateChange[p]++;
75 }
76 }
77
78 // Compute the propensity factor that we need when there are bi- or tri-particle reactions involving the same species.
79 // We need to do this because for biparticle reactions of N particles of the same type, there are 0.5 * N * (N-1)
80 // unique pairs of particles. Or in general when we have a k-order reaction we have 'N choose k' = N!/(k! * (N-k)!)
81 // combinations. In the propensity function we compute the propensity as
82 //
83 // a = rate * factor * N * (N-1) * (N-2) ...
84 //
85 // For this to make sense we have
86 //
87 // a = rate * N * (N-1) * (N-2) ... (N-k+1) * (N-k)!/(k! * (N-k)!)
88 // = rate * N * (N-1) * (N-2) ... * 1/k!
89 //
90 // so the factor is just 1/k! (for each species).
91
92 m_propensityFactor = 1.0;
93
94 std::map<size_t, size_t> reactantNumbers;
95 for (const auto& r : m_lhsReactives) {
96 if (reactantNumbers.find(r) == reactantNumbers.end()) {
97 reactantNumbers.emplace(r, 1);
98 }
99 else {
100 reactantNumbers[r]++;
101 }
102 }
103
104 // Factorial function.
105 auto factorial = [](const T& N) -> T {
106 T fac = (T)1;
107 for (T i = 2; i <= N; i++) {
108 fac *= i;
109 }
110
111 return fac;
112 };
113
114 for (const auto& rn : reactantNumbers) {
115 m_propensityFactor *= 1.0 / factorial(rn.second);
116 }
117
118 // Compact the reactant tally into the form propensity() consumes, so that the falling factorial
119 // can be formed straight off the incoming state rather than off a private copy of it.
120 m_lhsOrders.clear();
121 m_lhsOrders.reserve(reactantNumbers.size());
122
123 for (const auto& rn : reactantNumbers) {
124 m_lhsOrders.emplace_back(rn.first, rn.second);
125 }
126
127 // Dense mirror of m_reactiveStateChange for the per-cell lookups in KMCSolver. The map is keyed by
128 // species index, so the highest key fixes the length; species the reaction does not touch keep a
129 // zero state change.
130 m_reactiveStateChangeDense.clear();
131
132 if (!m_reactiveStateChange.empty()) {
133 const size_t highestSpecies = m_reactiveStateChange.rbegin()->first;
134
135 m_reactiveStateChangeDense.assign(highestSpecies + 1, (T)0);
136
137 for (const auto& s : m_reactiveStateChange) {
138 m_reactiveStateChangeDense[s.first] = s.second;
139 }
140 }
141}
142
143template <typename State, typename T>
144inline Real&
146{
147 return m_rate;
148}
149
150template <typename State, typename T>
151inline T
152KMCDualStateReaction<State, T>::population(const size_t& a_reactant, const State& a_state) noexcept
153{
154 const auto& reactiveState = a_state.getReactiveState();
155
156 CH_assert(reactiveState.size() > a_reactant);
157
158 return reactiveState[a_reactant];
159}
160
161template <typename State, typename T>
162inline Real
163KMCDualStateReaction<State, T>::propensity(const State& a_state) const noexcept
164{
165#ifndef NDEBUG
166 this->sanityCheck(a_state);
167#endif
168
169 Real A = m_rate * m_propensityFactor;
170
171 const auto& reactiveState = a_state.getReactiveState();
172
173 // For a species entering the reaction k times the contribution is the falling factorial
174 // X * (X-1) * ... * (X-k+1) -- the same product the previous implementation formed by walking
175 // m_lhsReactives and decrementing a private copy of the state after each factor. Taking the order
176 // from m_lhsOrders gives the identical product without copying the state.
177 for (const auto& lhs : m_lhsOrders) {
178 T X = reactiveState[lhs.first];
179
180 for (size_t k = 0; k < lhs.second; k++) {
181 A *= X;
182
183 X -= (T)1;
184 }
185 }
186
187 return A;
188}
189
190template <typename State, typename T>
191inline T
193{
194#ifndef NDEBUG
195 this->sanityCheck(a_state);
196#endif
197
198 T Lj = std::numeric_limits<T>::max();
199
200 const auto& reactiveState = a_state.getReactiveState();
201
202 for (const auto& s : m_reactiveStateChange) {
203
204 const size_t rI = s.first;
205 const T nuIJ = s.second;
206
207 if (nuIJ < 0) {
208 Lj = std::min(Lj, reactiveState[rI] / std::abs(nuIJ));
209 }
210 }
211
212 return Lj;
213}
214
215template <typename State, typename T>
216inline const std::list<size_t>&
218{
219 return m_lhsReactives;
220}
221
222template <typename State, typename T>
223inline std::list<size_t>
225{
226 return m_rhsReactives;
227}
228
229template <typename State, typename T>
230inline std::list<size_t>
232{
233 return m_rhsNonReactives;
234}
235
236template <typename State, typename T>
237inline T
238KMCDualStateReaction<State, T>::getStateChange(const size_t a_particleReactant) const noexcept
239{
240 // Species beyond the dense vector are not part of this reaction, so their state change is zero --
241 // the same answer the map lookup gave for an absent key.
242 T nuIJ = 0;
243
244 if (a_particleReactant < m_reactiveStateChangeDense.size()) {
245 nuIJ = m_reactiveStateChangeDense[a_particleReactant];
246 }
247
248 return nuIJ;
249}
250
251template <typename State, typename T>
252inline void
253KMCDualStateReaction<State, T>::advanceState(State& a_state, const T& a_numReactions) const noexcept
254{
255#ifndef NDEBUG
256 this->sanityCheck(a_state);
257#endif
258 CH_assert(a_state.isValidState());
259
260 auto& reactiveState = a_state.getReactiveState();
261 auto& photonState = a_state.getNonReactiveState();
262
263 for (const auto& s : m_reactiveStateChange) {
264 reactiveState[s.first] += a_numReactions * s.second;
265 }
266
267 for (const auto& s : m_nonReactiveStateChange) {
268 photonState[s.first] += a_numReactions * s.second;
269 }
270
271 CH_assert(a_state.isValidState());
272}
273
274template <typename State, typename T>
275inline void
276KMCDualStateReaction<State, T>::sanityCheck(const State& a_state) const noexcept
277{
278 const auto& reactiveState = a_state.getReactiveState();
279 const auto& photonState = a_state.getNonReactiveState();
280
281 for (const auto& idx : m_lhsReactives) {
282 CH_assert(reactiveState.size() > idx);
283 }
284
285 for (const auto& idx : m_rhsReactives) {
286 CH_assert(reactiveState.size() > idx);
287 }
288
289 for (const auto& idx : m_rhsNonReactives) {
290 CH_assert(photonState.size() > idx);
291 }
292}
293
294#include <CD_NamespaceFooter.H>
295
296#endif
Declaration of a simple plasma reaction type for Kinetic Monte Carlo.
T getStateChange(const size_t a_reactant) const noexcept
Get the state change due to a change in the input reactant.
Definition CD_KMCDualStateReactionImplem.H:238
void sanityCheck(const State &a_state) const noexcept
Debugging function which ensures that the class data holders do not reach out of the incoming state.
Definition CD_KMCDualStateReactionImplem.H:276
void computeStateChanges() noexcept
Compute state change vectors from the reactant/product lists.
Definition CD_KMCDualStateReactionImplem.H:46
std::list< size_t > getNonReactiveProducts() const noexcept
Get the non-reactive products from the reaction.
Definition CD_KMCDualStateReactionImplem.H:231
KMCDualStateReaction()=default
Default constructor.
virtual ~KMCDualStateReaction()
Destructor.
Definition CD_KMCDualStateReactionImplem.H:41
std::list< size_t > getReactiveProducts() const noexcept
Get the products from the reaction.
Definition CD_KMCDualStateReactionImplem.H:224
Real propensity(const State &a_state) const noexcept
Compute the propensity function for this reaction type.
Definition CD_KMCDualStateReactionImplem.H:163
T computeCriticalNumberOfReactions(const State &a_state) const noexcept
Compute the number of times the reaction can fire before exhausting one of the reactants.
Definition CD_KMCDualStateReactionImplem.H:192
Real & rate() const noexcept
Get modifiable reaction rate.
Definition CD_KMCDualStateReactionImplem.H:145
void advanceState(State &a_state, const T &a_numReactions) const noexcept
Advance the incoming state with the number of reactions.
Definition CD_KMCDualStateReactionImplem.H:253
const std::list< size_t > & getReactants() const noexcept
Get the reactants in the reaction.
Definition CD_KMCDualStateReactionImplem.H:217
static T population(const size_t &a_reactant, const State &a_state) noexcept
Get the population of the reactant in the input state.
Definition CD_KMCDualStateReactionImplem.H:152