Mallard 0.8.0
Unstructured, GPU accelerated, high-order finite volume CFD solver
Mallard website
Loading...
Searching...
No Matches
kinetics.h
Go to the documentation of this file.
1
13#ifndef CHEMISTRY_KINETICS_H
14#define CHEMISTRY_KINETICS_H
15
16#include <cmath>
17#include <cstdint>
18#include <vector>
19
20#include <Kokkos_Core.hpp>
21
22#include "lanes.h"
23#include "mechanism.h"
24
25namespace chemistry {
26
36 double * dq_dT;
37 double * d_forward;
38 double * d_reverse;
39 double * dq_dM;
40 double * dq_uniform;
41
43 static ReactionDerivatives at(double * work, const uint32_t n_reactions, const uint32_t n_forward,
44 const uint32_t n_reverse) {
46 d.dq_dT = work;
47 d.d_forward = d.dq_dT + n_reactions;
50 d.dq_uniform = d.dq_dM + n_reactions;
51 return d;
52 }
53};
54
69template <typename MemorySpace = Kokkos::DefaultExecutionSpace::memory_space>
71 template <typename T>
72 using View1 = Kokkos::View<T *, MemorySpace>;
73
76 View1<uint8_t> type; // ReactionType
77 View1<uint8_t> falloff; // FalloffType
79 Kokkos::View<double *[3], Kokkos::LayoutRight, MemorySpace> rate; // A, b, Ea/R (falloff: high pressure)
80 Kokkos::View<double *[3], Kokkos::LayoutRight, MemorySpace> low; // falloff: low pressure
81 Kokkos::View<double *[5], Kokkos::LayoutRight, MemorySpace> falloff_params;
82 View1<uint32_t> forward_offset; // (n_reactions + 1)
85 View1<uint32_t> reverse_offset; // products and their coefficients (the reverse orders)
88 View1<uint32_t> net_offset; // nonzero net coefficients nu'' - nu'
91 View1<uint32_t> species_offset; // (n_species + 1): the same coefficients by species, by reaction index
94 View1<uint32_t> chunk_offset; // (n_species + 1): chunks of each species' coefficients, for lanes
95 View1<uint32_t> chunk_end; // (chunk): end of its range in the species' coefficients
96 // Jacobian entries d omega_k / d C_j with their terms (per-reaction derivative and coefficient), for lanes
98 View1<uint32_t> entry_offset; // (entries + 1)
99 View1<uint32_t> term_source; // index into the ReactionDerivatives block
101 View1<double> delta_nu; // sum of net coefficients
104 View1<double> efficiency_extra; // efficiency minus the default
106 View1<uint32_t> plog_offset; // (n_reactions + 1): pressure levels of reaction i
108 View1<uint32_t> plog_rate_offset; // (level + 1): rates summed at a level
109 Kokkos::View<double *[3], Kokkos::LayoutRight, MemorySpace> plog_rate; // A, b, Ea/R
110 View1<uint32_t> chebyshev_offset; // (n_reactions + 1): coefficients of reaction i, n_T x n_p row-major
113 Kokkos::View<double *[4], Kokkos::LayoutRight, MemorySpace> chebyshev_range; // 1/T_min, 1/T_max, ln p_min, ln p_max
114
121 static constexpr double C_REG = 1e-12;
122 double C_reg = C_REG; // the concentration used (chemistry.C_reg)
123
125 static constexpr uint32_t MAX_KEPT = 4;
126
129 static double plain_power(const double C, const double order) {
130 const double whole = Kokkos::floor(order);
131 if (whole == order && order >= 0.0 && order <= 4.0) {
132 double p = 1.0;
133 for (int i = 0; i < static_cast<int>(order); i++) p *= C;
134 return p;
135 }
136 return Kokkos::pow(Kokkos::fmax(C, 0.0), order);
137 }
138
141 static double power(const double C, const double order, const double C_reg = C_REG) {
142 if (order > 0.0 && order < 1.0 && C < C_reg) {
143 const double x = C / C_reg;
144 return Kokkos::pow(C_reg, order) * x * ((2.0 - order) + (order - 1.0) * Kokkos::fmax(x, 0.0));
145 }
146 return plain_power(C, order);
147 }
148
151 static double power_derivative(const double C, const double order, const double C_reg = C_REG) {
152 if (order == 0.0) return 0.0;
153 if (order > 0.0 && order < 1.0 && C < C_reg) {
154 const double x = C / C_reg;
155 return Kokkos::pow(C_reg, order - 1.0) * ((2.0 - order) + 2.0 * (order - 1.0) * Kokkos::fmax(x, 0.0));
156 }
157 return order * plain_power(C, order - 1.0);
158 }
159
161 static double arrhenius(const double A, const double b, const double Ea_R, const double log_T, const double inv_T) {
162 return A * Kokkos::exp(b * log_T - Ea_R * inv_T);
163 }
164
167 void plog_level(const uint32_t l, const double log_T, const double inv_T, double & k, double & dlnk_dT) const {
168 k = 0.0;
169 double weighted = 0.0;
170 for (uint32_t e = plog_rate_offset(l); e < plog_rate_offset(l + 1); e++) {
171 const double k_e = arrhenius(plog_rate(e, 0), plog_rate(e, 1), plog_rate(e, 2), log_T, inv_T);
172 k += k_e;
173 weighted += k_e * (plog_rate(e, 1) + plog_rate(e, 2) * inv_T) * inv_T;
174 }
175 dlnk_dT = k != 0.0 ? weighted / k : 0.0;
176 }
177
183 void pressure_rate(const uint32_t i, const double log_T, const double inv_T, const double ln_p, double & k,
184 double & dlnk_dT, double & dlnk_dlnp) const {
185 constexpr double LN10 = 2.302585092994045684;
186 dlnk_dlnp = 0.0;
187 if (type(i) == static_cast<uint8_t>(ReactionType::PLOG)) {
188 const uint32_t first = plog_offset(i), last = plog_offset(i + 1) - 1;
189 if (ln_p <= plog_ln_p(first) || first == last) {
190 plog_level(first, log_T, inv_T, k, dlnk_dT);
191 return;
192 }
193 if (ln_p >= plog_ln_p(last)) {
194 plog_level(last, log_T, inv_T, k, dlnk_dT);
195 return;
196 }
197 uint32_t l = first;
198 while (ln_p >= plog_ln_p(l + 1)) l++;
199 double k1, d1, k2, d2;
200 plog_level(l, log_T, inv_T, k1, d1);
201 plog_level(l + 1, log_T, inv_T, k2, d2);
202 const double span = plog_ln_p(l + 1) - plog_ln_p(l);
203 const double w = (ln_p - plog_ln_p(l)) / span;
204 const double ln_k1 = Kokkos::log(k1), ln_k2 = Kokkos::log(k2);
205 k = Kokkos::exp(ln_k1 + w * (ln_k2 - ln_k1));
206 dlnk_dT = d1 + w * (d2 - d1);
207 dlnk_dlnp = (ln_k2 - ln_k1) / span;
208 return;
209 }
210 // Chebyshev: series in Tr = (2/T - 1/T_min - 1/T_max) / (1/T_max - 1/T_min) and
211 // pr = (2 ln p - ln p_min - ln p_max) / (ln p_max - ln p_min), with the recurrences
212 // phi_{n+1} = 2 x phi_n - phi_{n-1} and phi'_{n+1} = 2 phi_n + 2 x phi'_n - phi'_{n-1}
213 const double a_T = chebyshev_range(i, 0), b_T = chebyshev_range(i, 1);
214 const double a_p = chebyshev_range(i, 2), b_p = chebyshev_range(i, 3);
215 const double Tr = (2.0 * inv_T - a_T - b_T) / (b_T - a_T);
216 const double pr = (2.0 * ln_p - a_p - b_p) / (b_p - a_p);
217 const uint32_t n_p = chebyshev_n_p(i), first = chebyshev_offset(i);
218 const uint32_t n_T = (chebyshev_offset(i + 1) - first) / n_p;
219 double log_k = 0.0, dlogk_dTr = 0.0, dlogk_dpr = 0.0;
220 double phi_T = 1.0, phi_T_prev = 0.0, d_T = 0.0, d_T_prev = 0.0;
221 for (uint32_t t = 0; t < n_T; t++) {
222 double sum = 0.0, dsum = 0.0;
223 double phi_p = 1.0, phi_p_prev = 0.0, d_p = 0.0, d_p_prev = 0.0;
224 for (uint32_t q = 0; q < n_p; q++) {
225 const double alpha = chebyshev(first + t * n_p + q);
226 sum += alpha * phi_p;
227 dsum += alpha * d_p;
228 const double phi_next = q == 0 ? pr : 2.0 * pr * phi_p - phi_p_prev;
229 const double d_next = q == 0 ? 1.0 : 2.0 * phi_p + 2.0 * pr * d_p - d_p_prev;
231 d_p_prev = d_p;
232 phi_p = phi_next;
233 d_p = d_next;
234 }
235 log_k += phi_T * sum;
236 dlogk_dTr += d_T * sum;
237 dlogk_dpr += phi_T * dsum;
238 const double phi_next = t == 0 ? Tr : 2.0 * Tr * phi_T - phi_T_prev;
239 const double d_next = t == 0 ? 1.0 : 2.0 * phi_T + 2.0 * Tr * d_T - d_T_prev;
241 d_T_prev = d_T;
242 phi_T = phi_next;
243 d_T = d_next;
244 }
245 k = Kokkos::pow(10.0, log_k);
246 dlnk_dT = LN10 * dlogk_dTr * (-2.0 * inv_T * inv_T / (b_T - a_T));
247 dlnk_dlnp = LN10 * dlogk_dpr * 2.0 / (b_p - a_p);
248 }
249
252 double third_body(const uint32_t i, const double * C, const double C_total) const {
253 double M = default_efficiency(i) * C_total;
254 for (uint32_t e = efficiency_offset(i); e < efficiency_offset(i + 1); e++) {
256 }
257 return M;
258 }
259
265 void falloff_function(const uint32_t i, const double T, const double Pr, double & F, double & dlnF_dlnPr,
266 double & dlnF_dT) const {
267 constexpr double SMALL = 1e-300;
268 constexpr double LN10 = 2.302585092994045684;
269 F = 1.0;
270 dlnF_dlnPr = 0.0;
271 dlnF_dT = 0.0;
272 const double L = Kokkos::log10(Kokkos::fmax(Pr, SMALL));
273 if (falloff(i) == static_cast<uint8_t>(FalloffType::TROE)) {
274 const double A = falloff_params(i, 0), T3 = falloff_params(i, 1), T1 = falloff_params(i, 2),
275 T2 = falloff_params(i, 3);
276 const double e3 = Kokkos::fabs(T3) > SMALL ? Kokkos::exp(-T / T3) : 0.0;
277 const double e1 = Kokkos::fabs(T1) > SMALL ? Kokkos::exp(-T / T1) : 0.0;
278 const double e2 = T2 != 0.0 ? Kokkos::exp(-T2 / T) : 0.0;
279 const double Fcent = (1.0 - A) * e3 + A * e1 + e2;
280 double dFcent_dT = e2 * T2 / (T * T);
281 if (Kokkos::fabs(T3) > SMALL) dFcent_dT -= (1.0 - A) * e3 / T3;
282 if (Kokkos::fabs(T1) > SMALL) dFcent_dT -= A * e1 / T1;
283 const double Lc = Kokkos::log10(Kokkos::fmax(Fcent, SMALL));
284 const double c = -0.4 - 0.67 * Lc;
285 const double n = 0.75 - 1.27 * Lc;
286 const double x = L + c;
287 const double D = n - 0.14 * x;
288 const double f1 = x / D;
289 const double g = 1.0 / (1.0 + f1 * f1);
290 const double logF = Lc * g;
291 F = Kokkos::pow(10.0, logF);
292 // d f1 / dL at fixed Lc, and d f1 / dLc at fixed L
293 const double df1_dL = n / (D * D);
294 const double df1_dLc = (-0.67 * D - x * (-1.27 + 0.14 * 0.67)) / (D * D);
295 const double dlogF_dL = -Lc * 2.0 * f1 * df1_dL * g * g;
296 const double dlogF_dLc = g - Lc * 2.0 * f1 * df1_dLc * g * g;
297 dlnF_dlnPr = dlogF_dL; // d log10 F / d log10 Pr = d ln F / d ln Pr
298 const double dLc_dT = Fcent > SMALL ? dFcent_dT / (Fcent * LN10) : 0.0;
300 } else if (falloff(i) == static_cast<uint8_t>(FalloffType::SRI)) {
301 const double a = falloff_params(i, 0), b = falloff_params(i, 1), c = falloff_params(i, 2),
302 d = falloff_params(i, 3), e = falloff_params(i, 4);
303 const double X = 1.0 / (1.0 + L * L);
304 const double ea = a * Kokkos::exp(-b / T), ec = Kokkos::exp(-T / c);
305 const double Z = ea + ec;
306 F = d * Kokkos::pow(Z, X) * Kokkos::pow(T, e);
307 dlnF_dlnPr = Kokkos::log(Z) * (-2.0 * L * X * X) / LN10; // dX/dL with L = log10 Pr
308 dlnF_dT = X * (ea * b / (T * T) - ec / c) / Z + e / T;
309 }
310 }
311
314 return 3 * n_reactions + static_cast<uint32_t>(forward_species.extent(0)) +
315 static_cast<uint32_t>(reverse_species.extent(0));
316 }
317
326 template <typename Lanes>
327 KOKKOS_INLINE_FUNCTION void rates_of_progress(const Lanes & lanes, const double T, const double * C,
328 const double C_total, const double * g_RT, const double * h_RT,
329 double * q, const ReactionDerivatives * d) const {
330 const double log_T = Kokkos::log(T), inv_T = 1.0 / T;
331 const double log_c0 = Kokkos::log(ONE_ATM / (GAS_CONSTANT * T));
332 lanes.for_each(n_reactions, [&](const uint32_t i) {
333 double kf = arrhenius(rate(i, 0), rate(i, 1), rate(i, 2), log_T, inv_T);
334 double dlnkf_dT = (rate(i, 1) + rate(i, 2) * inv_T) * inv_T;
335 double dlnk_dlnp = 0.0, dk_dM = 0.0;
336 const uint8_t kind = type(i);
337 if (kind == static_cast<uint8_t>(ReactionType::PLOG) ||
338 kind == static_cast<uint8_t>(ReactionType::CHEBYSHEV)) {
339 // p = C_total R T, so d ln p / dT = 1 / T at fixed concentrations
340 pressure_rate(i, log_T, inv_T, Kokkos::log(C_total * GAS_CONSTANT * T), kf, dlnkf_dT, dlnk_dlnp);
341 dlnkf_dT += dlnk_dlnp * inv_T;
342 } else if (kind == static_cast<uint8_t>(ReactionType::THREE_BODY)) {
343 const double M = third_body(i, C, C_total);
344 dk_dM = kf;
345 kf *= M;
346 } else if (kind == static_cast<uint8_t>(ReactionType::FALLOFF)) {
347 const double k0 = arrhenius(low(i, 0), low(i, 1), low(i, 2), log_T, inv_T);
348 const double dlnk0_dT = (low(i, 1) + low(i, 2) * inv_T) * inv_T;
349 const double M = third_body(i, C, C_total);
350 const double Pr = kf > 0.0 ? k0 * M / kf : 0.0;
351 double F, dlnF_dlnPr, dlnF_dT;
353 // ln k = ln k_inf + ln Pr - ln(1 + Pr) + ln F, with d ln Pr / dT = dlnk0 - dlnkinf
354 const double dlnPr_dT = dlnk0_dT - dlnkf_dT;
355 dlnkf_dT += (1.0 / (1.0 + Pr) + dlnF_dlnPr) * dlnPr_dT + dlnF_dT;
356 // k = k_inf Pr / (1 + Pr) F; dk/dM = k0 F [1 / (1 + Pr)^2 + d ln F / d ln Pr / (1 + Pr)]
357 dk_dM = k0 * F / (1.0 + Pr) * (1.0 / (1.0 + Pr) + dlnF_dlnPr);
358 kf *= Pr / (1.0 + Pr) * F;
359 }
360 // The factors C^order, kept for the derivatives (the first MAX_KEPT of each product)
361 double kept_f[MAX_KEPT] = {}, kept_r[MAX_KEPT] = {};
362 double prod_f = 1.0;
363 for (uint32_t j = forward_offset(i); j < forward_offset(i + 1); j++) {
364 const double factor = power(C[forward_species(j)], forward_order(j), C_reg);
366 prod_f *= factor;
367 }
368 double kr = 0.0, prod_r = 0.0, dlnKc_dT = 0.0;
369 if (reversible(i)) {
370 double sum_g = 0.0, sum_h = 0.0;
371 for (uint32_t j = net_offset(i); j < net_offset(i + 1); j++) {
372 sum_g += net_nu(j) * g_RT[net_species(j)];
373 if (d) sum_h += net_nu(j) * h_RT[net_species(j)];
374 }
375 const double ln_Kc = -sum_g + delta_nu(i) * log_c0;
376 const double inv_Kc = Kokkos::exp(-ln_Kc);
377 kr = kf * inv_Kc;
378 prod_r = inv_Kc;
379 for (uint32_t j = reverse_offset(i); j < reverse_offset(i + 1); j++) {
380 const double factor = power(C[reverse_species(j)], reverse_order(j), C_reg);
382 prod_r *= factor;
383 }
384 // d(g/RT)/dT = -h/(R T^2), d ln(p_atm / RT) / dT = -1/T
385 dlnKc_dT = (sum_h - delta_nu(i)) * inv_T;
386 }
387 const double fwd = kf * prod_f, rev = kf * prod_r;
388 q[i] = fwd - rev;
389 if (!d) return;
390 d->dq_dT[i] = fwd * dlnkf_dT - rev * (dlnkf_dT - dlnKc_dT);
391 // Mass-action terms: kf d(prod C^o)/dC_j and kr d(prod C^nu'')/dC_j
392 for (uint32_t a = forward_offset(i); a < forward_offset(i + 1); a++) {
394 for (uint32_t b = forward_offset(i); b < forward_offset(i + 1); b++) {
395 if (b == a) continue;
398 }
399 d->d_forward[a] = dd;
400 }
401 for (uint32_t a = reverse_offset(i); a < reverse_offset(i + 1); a++) {
402 double dd = 0.0;
403 if (reversible(i)) {
405 for (uint32_t b = reverse_offset(i); b < reverse_offset(i + 1); b++) {
406 if (b == a) continue;
409 }
410 }
411 d->d_reverse[a] = dd;
412 }
413 // Through [M]: d q / d C_j = dq_dM eff_j; through p: d q / d C_j = q (d ln k / d ln p) / C_total
414 d->dq_dM[i] = dk_dM * (prod_f - prod_r);
415 d->dq_uniform[i] = dlnk_dlnp != 0.0 && C_total > 0.0 ? q[i] * dlnk_dlnp / C_total : 0.0;
416 });
417 lanes.sync();
418 }
419
422 void rates_of_progress(const double T, const double * C, const double * g_RT, double * q) const {
423 double C_total = 0.0;
424 for (uint32_t k = 0; k < n_species; k++) C_total += C[k];
425 rates_of_progress(SerialLanes(), T, C, C_total, g_RT, nullptr, q, nullptr);
426 }
427
433 template <typename Lanes>
434 KOKKOS_INLINE_FUNCTION void production_rates(const Lanes & lanes, const double * q, double * omega,
435 double * partial = nullptr) const {
436 if constexpr (Lanes::parallel) {
437 if (partial) {
438 // Chunks of a few reactions per lane, then each species' chunks in order
439 lanes.for_each(static_cast<uint32_t>(chunk_end.extent(0)), [&](const uint32_t c) {
440 const uint32_t begin = c == 0 ? 0 : chunk_end(c - 1);
441 double sum = 0.0;
442 for (uint32_t e = begin; e < chunk_end(c); e++) sum += species_nu(e) * q[species_reaction(e)];
443 partial[c] = sum;
444 });
445 lanes.sync();
446 lanes.for_each(n_species, [&](const uint32_t k) {
447 double sum = 0.0;
448 for (uint32_t c = chunk_offset(k); c < chunk_offset(k + 1); c++) sum += partial[c];
449 omega[k] = sum;
450 });
451 lanes.sync();
452 return;
453 }
454 }
455 lanes.for_each(n_species, [&](const uint32_t k) {
456 double sum = 0.0;
457 for (uint32_t e = species_offset(k); e < species_offset(k + 1); e++) {
458 sum += species_nu(e) * q[species_reaction(e)];
459 }
460 omega[k] = sum;
461 });
462 lanes.sync();
463 }
464
466 void production_rates(const double * q, double * omega) const { production_rates(SerialLanes(), q, omega); }
467
474 template <typename Lanes>
476 double * values, double * all_columns) const {
477 lanes.for_each(n_species, [&](const uint32_t k) {
478 double uniform = 0.0;
479 for (uint32_t e = species_offset(k); e < species_offset(k + 1); e++) {
480 const uint32_t i = species_reaction(e);
481 uniform += species_nu(e) * (d.dq_dM[i] * default_efficiency(i) + d.dq_uniform[i]);
482 }
483 all_columns[k] = uniform;
484 });
485 const double * block = d.dq_dT;
486 lanes.for_each(static_cast<uint32_t>(entry_row.extent(0)), [&](const uint32_t e) {
487 double sum = 0.0;
488 for (uint32_t t = entry_offset(e); t < entry_offset(e + 1); t++) {
489 sum += term_coefficient(t) * block[term_source(t)];
490 }
491 values[e] = sum;
492 });
493 lanes.sync();
494 }
495
506 template <typename Lanes>
507 KOKKOS_INLINE_FUNCTION void production_jacobian(const Lanes & lanes, const ReactionDerivatives & d, double * J,
508 const uint32_t stride, double * all_columns = nullptr) const {
509 const uint32_t n = n_species;
510 constexpr bool CM = Lanes::column_major;
511 if constexpr (Lanes::parallel) {
512 // Rows start from the part shared by all columns; then each lane adds whole entries, so
513 // that the lanes' loads balance (a row's reactions do not)
514 lanes.for_each(n, [&](const uint32_t k) {
515 double uniform = 0.0;
516 for (uint32_t e = species_offset(k); e < species_offset(k + 1); e++) {
517 const uint32_t i = species_reaction(e);
518 uniform += species_nu(e) * (d.dq_dM[i] * default_efficiency(i) + d.dq_uniform[i]);
519 }
520 if (all_columns) all_columns[k] = uniform;
521 const double base = all_columns ? 0.0 : uniform;
522 for (uint32_t j = 0; j < n; j++) J[dense_index<CM>(stride, k, j)] = base;
523 });
524 lanes.sync();
525 const double * block = d.dq_dT;
526 lanes.for_each(static_cast<uint32_t>(entry_row.extent(0)), [&](const uint32_t e) {
527 double sum = 0.0;
528 for (uint32_t t = entry_offset(e); t < entry_offset(e + 1); t++) {
529 sum += term_coefficient(t) * block[term_source(t)];
530 }
531 J[dense_index<CM>(stride, entry_row(e), entry_column(e))] += sum;
532 });
533 lanes.sync();
534 return;
535 }
536 lanes.for_each(n, [&](const uint32_t k) {
537 auto row = [&](const uint32_t j) -> double & { return J[dense_index<CM>(stride, k, j)]; };
538 for (uint32_t j = 0; j < n; j++) row(j) = 0.0;
539 double uniform = 0.0;
540 for (uint32_t e = species_offset(k); e < species_offset(k + 1); e++) {
541 const uint32_t i = species_reaction(e);
542 const double nu = species_nu(e);
543 for (uint32_t a = forward_offset(i); a < forward_offset(i + 1); a++) {
544 row(forward_species(a)) += nu * d.d_forward[a];
545 }
546 for (uint32_t a = reverse_offset(i); a < reverse_offset(i + 1); a++) {
547 row(reverse_species(a)) -= nu * d.d_reverse[a];
548 }
549 const double all = nu * (d.dq_dM[i] * default_efficiency(i) + d.dq_uniform[i]);
550 if (all_columns) {
551 uniform += all;
552 } else if (all != 0.0) {
553 for (uint32_t j = 0; j < n; j++) row(j) += all;
554 }
555 if (d.dq_dM[i] != 0.0) {
556 for (uint32_t x = efficiency_offset(i); x < efficiency_offset(i + 1); x++) {
557 row(efficiency_species(x)) += nu * d.dq_dM[i] * efficiency_extra(x);
558 }
559 }
560 }
561 if (all_columns) all_columns[k] = uniform;
562 });
563 lanes.sync();
564 }
565};
566
571template <typename MemorySpace = Kokkos::DefaultExecutionSpace::memory_space>
573 const double C_reg = KineticsTable<MemorySpace>::C_REG) {
575 Table t;
576 t.C_reg = C_reg;
577 t.n_species = mechanism.n_species();
578 t.n_reactions = static_cast<uint32_t>(mechanism.reactions.size());
579 const uint32_t nr = t.n_reactions;
580 auto u32 = [](size_t n) { return static_cast<uint32_t>(n); };
581 std::vector<uint8_t> type(nr), falloff(nr), reversible(nr);
582 std::vector<double> rate(3 * nr), low(3 * nr), params(5 * nr), delta_nu(nr), default_eff(nr);
583 std::vector<uint32_t> f_off{0}, r_off{0}, n_off{0}, e_off{0}, f_sp, r_sp, n_sp, e_sp;
584 std::vector<double> f_ord, r_ord, n_nu, e_extra;
585 std::vector<uint32_t> plog_off{0}, plog_rate_off{0}, cheb_off{0}, cheb_n_p(nr, 1);
586 std::vector<double> plog_ln_p, plog_rate, cheb, cheb_range(4 * nr, 0.0);
587 for (uint32_t i = 0; i < nr; i++) {
588 const Reaction & r = mechanism.reactions[i];
589 for (size_t e = 0; e < r.plog.size(); e++) {
590 if (e == 0 || r.plog[e].first != r.plog[e - 1].first) {
591 if (e > 0) plog_rate_off.push_back(u32(plog_rate.size() / 3));
592 plog_ln_p.push_back(std::log(r.plog[e].first));
593 }
594 plog_rate.insert(plog_rate.end(), {r.plog[e].second.A, r.plog[e].second.b, r.plog[e].second.Ea_R});
595 }
596 if (!r.plog.empty()) plog_rate_off.push_back(u32(plog_rate.size() / 3));
597 plog_off.push_back(u32(plog_ln_p.size()));
598 if (r.type == ReactionType::CHEBYSHEV) {
599 cheb.insert(cheb.end(), r.chebyshev.begin(), r.chebyshev.end());
600 cheb_n_p[i] = r.chebyshev_n_p;
601 cheb_range[4 * i] = 1.0 / r.chebyshev_range[0];
602 cheb_range[4 * i + 1] = 1.0 / r.chebyshev_range[1];
603 cheb_range[4 * i + 2] = std::log(r.chebyshev_range[2]);
604 cheb_range[4 * i + 3] = std::log(r.chebyshev_range[3]);
605 }
606 cheb_off.push_back(u32(cheb.size()));
607 type[i] = static_cast<uint8_t>(r.type);
608 falloff[i] = static_cast<uint8_t>(r.falloff);
609 reversible[i] = r.reversible;
610 const Arrhenius arr[2] = {r.rate, r.low};
611 for (int a = 0; a < 2; a++) {
612 std::vector<double> & dst = a ? low : rate;
613 dst[3 * i] = arr[a].A;
614 dst[3 * i + 1] = arr[a].b;
615 dst[3 * i + 2] = arr[a].Ea_R;
616 }
617 for (uint32_t p = 0; p < 5; p++) params[5 * i + p] = r.falloff_params[p];
618 for (const auto & [k, order] : r.orders) {
619 f_sp.push_back(k);
620 f_ord.push_back(order);
621 }
622 f_off.push_back(u32(f_sp.size()));
623 for (const auto & [k, nu] : r.products) {
624 r_sp.push_back(k);
625 r_ord.push_back(nu);
626 }
627 r_off.push_back(u32(r_sp.size()));
628 std::vector<double> net(t.n_species, 0.0);
629 for (const auto & [k, nu] : r.products) net[k] += nu;
630 for (const auto & [k, nu] : r.reactants) net[k] -= nu;
631 double sum = 0.0;
632 for (uint32_t k = 0; k < t.n_species; k++) {
633 if (net[k] == 0.0) continue;
634 n_sp.push_back(k);
635 n_nu.push_back(net[k]);
636 sum += net[k];
637 }
638 n_off.push_back(u32(n_sp.size()));
639 delta_nu[i] = sum;
640 default_eff[i] = r.default_efficiency;
641 for (const auto & [k, eff] : r.efficiencies) {
642 e_sp.push_back(k);
643 e_extra.push_back(eff - r.default_efficiency);
644 }
645 e_off.push_back(u32(e_sp.size()));
646 }
647 auto copy = [](const auto & v, const char * label) {
648 using T = typename std::decay_t<decltype(v)>::value_type;
649 Kokkos::View<T *, MemorySpace> d(label, v.size());
650 auto h = Kokkos::create_mirror_view(d);
651 for (size_t a = 0; a < v.size(); a++) h(a) = v[a];
652 Kokkos::deep_copy(d, h);
653 return d;
654 };
655 auto copy2 = [&](const std::vector<double> & v, auto & dst, const char * label) {
656 using V = std::decay_t<decltype(dst)>;
657 constexpr size_t W = V::static_extent(1);
658 dst = V(label, v.size() / W);
659 auto h = Kokkos::create_mirror_view(dst);
660 for (size_t a = 0; a < v.size() / W; a++) {
661 for (size_t b = 0; b < W; b++) h(a, b) = v[a * W + b];
662 }
663 Kokkos::deep_copy(dst, h);
664 };
665 t.type = copy(type, "kinetics_type");
666 t.falloff = copy(falloff, "kinetics_falloff");
667 t.reversible = copy(reversible, "kinetics_reversible");
668 copy2(rate, t.rate, "kinetics_rate");
669 copy2(low, t.low, "kinetics_low");
670 copy2(params, t.falloff_params, "kinetics_falloff_params");
671 t.forward_offset = copy(f_off, "kinetics_forward_offset");
672 t.forward_species = copy(f_sp, "kinetics_forward_species");
673 t.forward_order = copy(f_ord, "kinetics_forward_order");
674 t.reverse_offset = copy(r_off, "kinetics_reverse_offset");
675 t.reverse_species = copy(r_sp, "kinetics_reverse_species");
676 t.reverse_order = copy(r_ord, "kinetics_reverse_order");
677 t.net_offset = copy(n_off, "kinetics_net_offset");
678 t.net_species = copy(n_sp, "kinetics_net_species");
679 t.net_nu = copy(n_nu, "kinetics_net_nu");
680 // Net coefficients by species, in reaction order
681 std::vector<uint32_t> s_off(t.n_species + 1, 0), s_re(n_sp.size());
682 std::vector<double> s_nu(n_sp.size());
683 for (uint32_t k : n_sp) s_off[k + 1]++;
684 for (uint32_t k = 0; k < t.n_species; k++) s_off[k + 1] += s_off[k];
685 std::vector<uint32_t> fill(s_off.begin(), s_off.end() - 1);
686 for (uint32_t i = 0; i < nr; i++) {
687 for (uint32_t j = n_off[i]; j < n_off[i + 1]; j++) {
688 s_re[fill[n_sp[j]]] = i;
689 s_nu[fill[n_sp[j]]++] = n_nu[j];
690 }
691 }
692 // Chunks of at most 4 coefficients of each species
693 {
694 std::vector<uint32_t> offsets{0}, ends;
695 for (uint32_t k = 0; k < t.n_species; k++) {
696 for (uint32_t e = s_off[k]; e < s_off[k + 1]; e += 4) ends.push_back(std::min(e + 4, s_off[k + 1]));
697 offsets.push_back(static_cast<uint32_t>(ends.size()));
698 }
699 t.chunk_offset = copy(offsets, "kinetics_chunk_offset");
700 t.chunk_end = copy(ends, "kinetics_chunk_end");
701 }
702 // Jacobian entries and their terms, in the order of production_jacobian's row loops
703 {
704 const uint32_t nf = static_cast<uint32_t>(f_sp.size()), nrv = static_cast<uint32_t>(r_sp.size());
705 const uint32_t off_forward = nr, off_reverse = nr + nf, off_dM = nr + nf + nrv;
706 std::vector<std::vector<std::pair<uint32_t, double>>> terms(static_cast<size_t>(t.n_species) * t.n_species);
707 for (uint32_t k = 0; k < t.n_species; k++) {
708 for (uint32_t e = s_off[k]; e < s_off[k + 1]; e++) {
709 const uint32_t i = s_re[e];
710 const double nu = s_nu[e];
711 auto & row = terms;
712 for (uint32_t a = f_off[i]; a < f_off[i + 1]; a++) {
713 row[k * t.n_species + f_sp[a]].emplace_back(off_forward + a, nu);
714 }
715 if (reversible[i]) {
716 for (uint32_t a = r_off[i]; a < r_off[i + 1]; a++) {
717 row[k * t.n_species + r_sp[a]].emplace_back(off_reverse + a, -nu);
718 }
719 }
720 for (uint32_t x = e_off[i]; x < e_off[i + 1]; x++) {
721 row[k * t.n_species + e_sp[x]].emplace_back(off_dM + i, nu * e_extra[x]);
722 }
723 }
724 }
725 std::vector<uint32_t> rows, columns, offsets{0}, sources;
726 std::vector<double> coefficients;
727 for (uint32_t k = 0; k < t.n_species; k++) {
728 for (uint32_t j = 0; j < t.n_species; j++) {
729 const auto & list = terms[k * t.n_species + j];
730 if (list.empty()) continue;
731 rows.push_back(k);
732 columns.push_back(j);
733 for (const auto & [source, coefficient] : list) {
734 sources.push_back(source);
735 coefficients.push_back(coefficient);
736 }
737 offsets.push_back(static_cast<uint32_t>(sources.size()));
738 }
739 }
740 t.entry_row = copy(rows, "kinetics_entry_row");
741 t.entry_column = copy(columns, "kinetics_entry_column");
742 t.entry_offset = copy(offsets, "kinetics_entry_offset");
743 t.term_source = copy(sources, "kinetics_term_source");
744 t.term_coefficient = copy(coefficients, "kinetics_term_coefficient");
745 }
746 t.species_offset = copy(s_off, "kinetics_species_offset");
747 t.species_reaction = copy(s_re, "kinetics_species_reaction");
748 t.species_nu = copy(s_nu, "kinetics_species_nu");
749 t.delta_nu = copy(delta_nu, "kinetics_delta_nu");
750 t.efficiency_offset = copy(e_off, "kinetics_efficiency_offset");
751 t.efficiency_species = copy(e_sp, "kinetics_efficiency_species");
752 t.efficiency_extra = copy(e_extra, "kinetics_efficiency_extra");
753 t.default_efficiency = copy(default_eff, "kinetics_default_efficiency");
754 t.plog_offset = copy(plog_off, "kinetics_plog_offset");
755 t.plog_ln_p = copy(plog_ln_p, "kinetics_plog_ln_p");
756 t.plog_rate_offset = copy(plog_rate_off, "kinetics_plog_rate_offset");
757 copy2(plog_rate, t.plog_rate, "kinetics_plog_rate");
758 t.chebyshev_offset = copy(cheb_off, "kinetics_chebyshev_offset");
759 t.chebyshev_n_p = copy(cheb_n_p, "kinetics_chebyshev_n_p");
760 t.chebyshev = copy(cheb, "kinetics_chebyshev");
761 copy2(cheb_range, t.chebyshev_range, "kinetics_chebyshev_range");
762 return t;
763}
764
765} // namespace chemistry
766
767#endif // CHEMISTRY_KINETICS_H
KOKKOS_INLINE_FUNCTION rtype dot(const rtype *a, const rtype *b)
Compute the dot product of two arrays.
Definition common_math.h:32
Execution of one cell's chemistry by one thread or by the vector lanes of a team: loops over species,...
std::string third_body
Definition mechanism.cpp:60
bool falloff
Definition mechanism.cpp:61
Gas-phase mechanisms read from Cantera YAML files.
Definition kinetics.h:25
constexpr double ONE_ATM
Definition mechanism.h:28
constexpr double GAS_CONSTANT
Definition mechanism.h:26
KineticsTable< MemorySpace > make_kinetics_table(const Mechanism &mechanism, const double C_reg=KineticsTable< MemorySpace >::C_REG)
Copy a mechanism's reactions to MemorySpace.
Definition kinetics.h:572
Modified Arrhenius rate k = A T^b exp(-Ea / (R T)), SI units with kmol.
Definition mechanism.h:97
double A
Definition mechanism.h:98
Reactions of a mechanism as flat device tables (compressed rows per reaction), in MemorySpace....
Definition kinetics.h:70
KOKKOS_INLINE_FUNCTION void pressure_rate(const uint32_t i, const double log_T, const double inv_T, const double ln_p, double &k, double &dlnk_dT, double &dlnk_dlnp) const
Rate constant of a PLOG or Chebyshev reaction at T and ln p, with d ln k / dT at fixed p and d ln k /...
Definition kinetics.h:183
KOKKOS_INLINE_FUNCTION void production_jacobian_entries(const Lanes &lanes, const ReactionDerivatives &d, double *values, double *all_columns) const
The structural entries of the Jacobian of the production rates (d omega_k / d C_j at fixed T,...
Definition kinetics.h:475
View1< double > term_coefficient
Definition kinetics.h:100
View1< uint32_t > reverse_species
Definition kinetics.h:86
static constexpr uint32_t MAX_KEPT
Factors C^order of a reaction's products that the derivatives reuse.
Definition kinetics.h:125
View1< uint32_t > chunk_end
Definition kinetics.h:95
View1< uint32_t > chebyshev_offset
Definition kinetics.h:110
View1< double > delta_nu
Definition kinetics.h:101
View1< double > plog_ln_p
Definition kinetics.h:107
KOKKOS_INLINE_FUNCTION void plog_level(const uint32_t l, const double log_T, const double inv_T, double &k, double &dlnk_dT) const
Sum of the PLOG rates at pressure level l and its d ln k / dT.
Definition kinetics.h:167
static KOKKOS_INLINE_FUNCTION double plain_power(const double C, const double order)
C^order: repeated products for integer orders, pow of max(C, 0) otherwise.
Definition kinetics.h:129
View1< uint32_t > chunk_offset
Definition kinetics.h:94
View1< uint32_t > plog_rate_offset
Definition kinetics.h:108
View1< uint8_t > type
Definition kinetics.h:76
KOKKOS_INLINE_FUNCTION void production_rates(const double *q, double *omega) const
Definition kinetics.h:466
static KOKKOS_INLINE_FUNCTION double arrhenius(const double A, const double b, const double Ea_R, const double log_T, const double inv_T)
Definition kinetics.h:161
KOKKOS_INLINE_FUNCTION double third_body(const uint32_t i, const double *C, const double C_total) const
Third-body concentration of reaction i.
Definition kinetics.h:252
Kokkos::View< double *[5], Kokkos::LayoutRight, MemorySpace > falloff_params
Definition kinetics.h:81
Kokkos::View< T *, MemorySpace > View1
Definition kinetics.h:72
static KOKKOS_INLINE_FUNCTION double power(const double C, const double order, const double C_reg=C_REG)
Mass-action factor C^order, regularized below C_reg for 0 < order < 1.
Definition kinetics.h:141
KOKKOS_INLINE_FUNCTION void rates_of_progress(const Lanes &lanes, const double T, const double *C, const double C_total, const double *g_RT, const double *h_RT, double *q, const ReactionDerivatives *d) const
Rates of progress q_i [kmol/(m^3 s)] of every reaction (one per lane) and, with derivatives,...
Definition kinetics.h:327
View1< uint32_t > species_reaction
Definition kinetics.h:92
View1< double > species_nu
Definition kinetics.h:93
View1< double > reverse_order
Definition kinetics.h:87
View1< double > chebyshev
Definition kinetics.h:112
View1< uint32_t > net_offset
Definition kinetics.h:88
View1< uint32_t > efficiency_species
Definition kinetics.h:103
View1< uint32_t > entry_offset
Definition kinetics.h:98
KOKKOS_INLINE_FUNCTION void production_jacobian(const Lanes &lanes, const ReactionDerivatives &d, double *J, const uint32_t stride, double *all_columns=nullptr) const
Jacobian of the production rates with respect to the concentrations at fixed T, one row per lane: J[d...
Definition kinetics.h:507
uint32_t n_species
Definition kinetics.h:74
static constexpr double C_REG
Concentration [kmol/m^3] below which orders 0 < n < 1 follow C_reg^n x ((2 - n) + (n - 1) x),...
Definition kinetics.h:121
View1< uint32_t > plog_offset
Definition kinetics.h:106
View1< uint32_t > chebyshev_n_p
Definition kinetics.h:111
static KOKKOS_INLINE_FUNCTION double power_derivative(const double C, const double order, const double C_reg=C_REG)
d power(C, order) / dC.
Definition kinetics.h:151
View1< uint8_t > falloff
Definition kinetics.h:77
Kokkos::View< double *[3], Kokkos::LayoutRight, MemorySpace > low
Definition kinetics.h:80
KOKKOS_INLINE_FUNCTION void production_rates(const Lanes &lanes, const double *q, double *omega, double *partial=nullptr) const
Net production rates omega_k = sum_i nu_ki q_i [kmol/(m^3 s)], one species per lane,...
Definition kinetics.h:434
KOKKOS_INLINE_FUNCTION void falloff_function(const uint32_t i, const double T, const double Pr, double &F, double &dlnF_dlnPr, double &dlnF_dT) const
Falloff blending F(Pr, T) and its logarithmic derivatives d ln F / d ln Pr and d ln F / dT at fixed P...
Definition kinetics.h:265
View1< double > default_efficiency
Definition kinetics.h:105
View1< uint32_t > entry_column
Definition kinetics.h:97
View1< uint32_t > entry_row
Definition kinetics.h:97
Kokkos::View< double *[3], Kokkos::LayoutRight, MemorySpace > plog_rate
Definition kinetics.h:109
View1< double > efficiency_extra
Definition kinetics.h:104
KOKKOS_INLINE_FUNCTION void rates_of_progress(const double T, const double *C, const double *g_RT, double *q) const
Rates of progress of one thread, without derivatives.
Definition kinetics.h:422
View1< uint32_t > species_offset
Definition kinetics.h:91
View1< uint32_t > reverse_offset
Definition kinetics.h:85
Kokkos::View< double *[4], Kokkos::LayoutRight, MemorySpace > chebyshev_range
Definition kinetics.h:113
View1< double > net_nu
Definition kinetics.h:90
double C_reg
Definition kinetics.h:122
View1< uint32_t > efficiency_offset
Definition kinetics.h:102
View1< uint32_t > term_source
Definition kinetics.h:99
View1< uint32_t > net_species
Definition kinetics.h:89
Kokkos::View< double *[3], Kokkos::LayoutRight, MemorySpace > rate
Definition kinetics.h:79
uint32_t n_reactions
Definition kinetics.h:75
View1< uint8_t > reversible
Definition kinetics.h:78
KOKKOS_INLINE_FUNCTION uint32_t derivatives_size() const
Doubles of ReactionDerivatives storage.
Definition kinetics.h:313
View1< double > forward_order
Definition kinetics.h:84
View1< uint32_t > forward_offset
Definition kinetics.h:82
View1< uint32_t > forward_species
Definition kinetics.h:83
One ideal-gas phase of a Cantera YAML file: its elements, species and reactions.
Definition mechanism.h:145
uint32_t n_species() const
Definition mechanism.h:152
std::vector< Reaction > reactions
Definition mechanism.h:150
Per-reaction derivatives of the rates of progress, in one block of KineticsTable::derivatives_size() ...
Definition kinetics.h:35
double * dq_uniform
Definition kinetics.h:40
double * d_reverse
Definition kinetics.h:38
double * d_forward
Definition kinetics.h:37
static KOKKOS_INLINE_FUNCTION ReactionDerivatives at(double *work, const uint32_t n_reactions, const uint32_t n_forward, const uint32_t n_reverse)
Definition kinetics.h:43
double * dq_dM
Definition kinetics.h:39
double * dq_dT
Definition kinetics.h:36
A gas-phase reaction: elementary, three-body or falloff (Lindemann, Troe, SRI), reversible (reverse r...
Definition mechanism.h:122
One thread does all the work of a cell.
Definition lanes.h:38
uint32_t rows
Definition teno_setup.cpp:455
SlotArray< double, Space > X
Definition teno_setup.cpp:478
CellSetup< ExecSpace > c
Definition teno_setup.cpp:1810
uint8_t r
Definition teno_setup.cpp:687
Geometry< Space > g
Definition teno_setup.cpp:678
SlotArray< double, Space > A
Definition teno_setup.cpp:478
double factor
Definition units.cpp:31