Mallard 0.8.0
Unstructured, GPU accelerated, high-order finite volume CFD solver
Mallard website
Loading...
Searching...
No Matches
sparse_lu.h
Go to the documentation of this file.
1
14#ifndef CHEMISTRY_SPARSE_LU_H
15#define CHEMISTRY_SPARSE_LU_H
16
17#include <algorithm>
18#include <array>
19#include <cstdint>
20#include <type_traits>
21#include <vector>
22
23#include <Kokkos_Core.hpp>
24
25#include "kinetics.h"
26#include "lanes.h"
27#include "mechanism.h"
28
29namespace chemistry {
30
45template <typename MemorySpace = Kokkos::DefaultExecutionSpace::memory_space>
47 template <typename T>
48 using View1 = Kokkos::View<T *, MemorySpace>;
49
50 uint32_t n = 0; // ns + 1
51 uint32_t nnz = 0; // entries of L + U
52 View1<uint32_t> perm; // perm[new] = original index
53 View1<uint32_t> source; // (entry): original row * n + column, for the gather from a dense J
54 View1<uint32_t> diagonal; // (row): entry of the diagonal
55 using Pairs = Kokkos::View<uint32_t *[2], Kokkos::LayoutRight, MemorySpace>;
56 // One thread factors pivot by pivot and solves column by column: per pivot, the L entries below it and
57 // its updates (target, l, u): a_t -= a_l a_u; L's entries below it and U's above it, with their rows.
58 // Teams take the stages, levels and chain below, with the same bits
60 Kokkos::View<uint32_t *[3], Kokkos::LayoutRight, MemorySpace> pivot_update;
63 // Factorization in stages of independent pivots: per stage, the L entries of its pivots with their
64 // diagonal entries, then the entries it updates (targets), each with its updates a_t -= a_l a_u in
65 // pivot order
68 Pairs scale_entry; // (L entry, diagonal entry)
70 View1<uint32_t> target; // (target): entry
72 Pairs update; // (l, u)
73 // The chain: pivots chain..n-1, each alone in its stage, after all the others (the dense core of
74 // radicals, and T). Its block is dense, by columns, from entry chain_offset on; the factorization and the
75 // solves take its pivots one by one, with the rows or entries of a pivot across the lanes, and the
76 // solves the other columns by levels of independent rows, each row by one lane
78 View1<uint32_t> chain_upper_offset; // (pivot - chain + 1)
79 Pairs chain_upper_entry; // (entry, row) of U in the chain's columns and the rows before it
83 Pairs lower_entry; // (entry, column) of each row of L in the columns before the chain, by increasing column
84 Pairs upper_entry; // (entry, column) of each row of U before the chain, above the diagonal and before the
85 // chain, by decreasing column
86 View1<double> v; // (original index): 1 / W_k, 0 for T
87 // The compact Jacobian: the entries of KineticsTable (species block, row-major), then the T column (n - 1)
88 // and the T row (n); its entries by column, and the source of each L + U entry in it (-1: zero)
92
95
97 KOKKOS_INLINE_FUNCTION uint32_t work_size() const { return nnz + 3 * n; }
98};
99
101template <typename MemorySpace = Kokkos::DefaultExecutionSpace::memory_space>
103 const uint32_t ns = mechanism.n_species(), n = ns + 1;
104 const uint32_t words = (n + 63) / 64;
105 using Bits = std::vector<uint64_t>;
106 auto set = [&](Bits & row, const uint32_t c) { row[c / 64] |= uint64_t(1) << (c % 64); };
107 auto test = [&](const Bits & row, const uint32_t c) { return (row[c / 64] >> (c % 64)) & 1; };
108 // Pattern of J_s in the original order: mass-action and extra-efficiency terms (the entries of
109 // KineticsTable), then the diagonal and the T row and column
110 std::vector<Bits> P(n, Bits(words, 0));
111 for (const Reaction & r : mechanism.reactions) {
112 std::vector<double> net(ns, 0.0);
113 for (const auto & [k, nu] : r.products) net[k] += nu;
114 for (const auto & [k, nu] : r.reactants) net[k] -= nu;
115 for (uint32_t k = 0; k < ns; k++) {
116 if (net[k] == 0.0) continue;
117 for (const auto & term : r.orders) set(P[k], static_cast<uint32_t>(term.first));
118 if (r.reversible) {
119 for (const auto & term : r.products) set(P[k], static_cast<uint32_t>(term.first));
120 }
121 for (const auto & term : r.efficiencies) set(P[k], static_cast<uint32_t>(term.first));
122 }
123 }
124 std::vector<uint32_t> e_row, e_col;
125 std::vector<int64_t> compact(static_cast<size_t>(n) * n, -1); // original (r, c) -> compact Jacobian index
126 for (uint32_t k = 0; k < ns; k++) {
127 for (uint32_t j = 0; j < ns; j++) {
128 if (!test(P[k], j)) continue;
129 compact[static_cast<size_t>(k) * n + j] = static_cast<int64_t>(e_row.size());
130 e_row.push_back(k);
131 e_col.push_back(j);
132 }
133 }
134 const uint32_t n_entries = static_cast<uint32_t>(e_row.size());
135 for (uint32_t k = 0; k < ns; k++) compact[static_cast<size_t>(k) * n + ns] = n_entries + k;
136 for (uint32_t j = 0; j < n; j++) compact[static_cast<size_t>(ns) * n + j] = n_entries + ns + j;
137 std::vector<uint32_t> column_offset(ns + 1, 0), column_entry(n_entries);
138 for (uint32_t e = 0; e < n_entries; e++) column_offset[e_col[e] + 1]++;
139 for (uint32_t j = 0; j < ns; j++) column_offset[j + 1] += column_offset[j];
140 {
141 std::vector<uint32_t> fill(column_offset.begin(), column_offset.end() - 1);
142 for (uint32_t e = 0; e < n_entries; e++) column_entry[fill[e_col[e]]++] = e;
143 }
144 for (uint32_t k = 0; k < n; k++) {
145 set(P[k], k);
146 set(P[k], ns);
147 set(P[ns], k);
148 }
149 // Minimum degree on the symmetrized species block, T last
150 std::vector<Bits> G(ns, Bits(words, 0));
151 for (uint32_t a = 0; a < ns; a++) {
152 for (uint32_t b = 0; b < ns; b++) {
153 if (a != b && (test(P[a], b) || test(P[b], a))) {
154 set(G[a], b);
155 set(G[b], a);
156 }
157 }
158 }
159 std::vector<uint32_t> perm;
160 std::vector<char> done(ns, 0);
161 auto degree = [&](const uint32_t a) {
162 uint32_t d = 0;
164 return d;
165 };
166 for (uint32_t step = 0; step < ns; step++) {
167 uint32_t best = ns, best_degree = n + 1;
168 for (uint32_t a = 0; a < ns; a++) {
169 if (done[a]) continue;
170 const uint32_t d = degree(a);
171 if (d < best_degree) {
172 best = a;
173 best_degree = d;
174 }
175 }
176 done[best] = 1;
177 perm.push_back(best);
178 // Eliminate: its neighbors become a clique, without it
179 std::vector<uint32_t> neighbors;
180 for (uint32_t b = 0; b < ns; b++) {
181 if (test(G[best], b)) neighbors.push_back(b);
182 }
183 for (const uint32_t b : neighbors) {
184 for (uint32_t w = 0; w < words; w++) G[b][w] |= G[best][w];
185 G[b][b / 64] &= ~(uint64_t(1) << (b % 64));
186 G[b][best / 64] &= ~(uint64_t(1) << (best % 64));
187 }
188 for (uint32_t w = 0; w < words; w++) G[best][w] = 0;
189 for (uint32_t b = 0; b < ns; b++) {
190 if (done[b]) continue;
191 G[b][best / 64] &= ~(uint64_t(1) << (best % 64));
192 }
193 }
194 perm.push_back(ns);
195 std::vector<uint32_t> position(n);
196 for (uint32_t i = 0; i < n; i++) position[perm[i]] = i;
197 // Permuted pattern and its fill under elimination without pivoting
198 std::vector<Bits> F(n, Bits(words, 0));
199 for (uint32_t r = 0; r < n; r++) {
200 for (uint32_t c = 0; c < n; c++) {
201 if (test(P[r], c)) set(F[position[r]], position[c]);
202 }
203 }
204 for (uint32_t k = 0; k < n; k++) {
205 // Row k's columns beyond k, a word at a time
206 const uint32_t w0 = (k + 1) / 64;
207 const uint64_t first_mask = ~uint64_t(0) << ((k + 1) % 64);
208 for (uint32_t r = k + 1; r < n; r++) {
209 if (!test(F[r], k)) continue;
210 F[r][w0] |= F[k][w0] & first_mask;
211 for (uint32_t w = w0 + 1; w < words; w++) F[r][w] |= F[k][w];
212 }
213 }
214 // Stages of the factorization: pivot k depends on the pivots j < k that change its row or column
215 // (L(k, j) or U(j, k) nonzero), and runs in the stage after the last of them
216 std::vector<uint32_t> stage_of(n, 0);
217 for (uint32_t k = 0; k < n; k++) {
218 for (uint32_t j = 0; j < k; j++) {
219 if (test(F[k], j) || test(F[j], k)) stage_of[k] = std::max(stage_of[k], stage_of[j] + 1);
220 }
221 }
222 const uint32_t n_stages = n > 0 ? *std::max_element(stage_of.begin(), stage_of.end()) + 1 : 0;
223 // The chain: the last pivots, from q on, each alone in its stage after all earlier ones (the dense core
224 // of radicals, and T). Its block is stored dense, by columns, after the other entries
225 uint32_t q = n;
226 while (q > 0 && stage_of[q - 1] + (n - q) + 1 == n_stages) {
227 bool alone = true;
228 for (uint32_t j = 0; j + 1 < q && alone; j++) alone = stage_of[j] < stage_of[q - 1];
229 if (!alone) break;
230 q--;
231 }
232 const uint32_t m = n - q;
233 const std::vector<Bits> fill = F; // without the chain's padding
234 for (uint32_t r = q; r < n; r++) {
235 for (uint32_t c = q; c < n; c++) set(F[r], c);
236 }
237 std::vector<int64_t> entry(static_cast<size_t>(n) * n, -1);
238 std::vector<uint32_t> source, diagonal(n);
239 std::vector<int32_t> jacobian_source;
240 auto add_entry = [&](const uint32_t r, const uint32_t c) {
241 entry[static_cast<size_t>(r) * n + c] = static_cast<int64_t>(source.size());
242 if (r == c) diagonal[r] = static_cast<uint32_t>(source.size());
243 source.push_back(perm[r] * n + perm[c]);
244 jacobian_source.push_back(static_cast<int32_t>(compact[static_cast<size_t>(perm[r]) * n + perm[c]]));
245 };
246 for (uint32_t r = 0; r < n; r++) {
247 for (uint32_t c = 0; c < n; c++) {
248 if (test(F[r], c) && (r < q || c < q)) add_entry(r, c);
249 }
250 }
251 const uint32_t chain_offset = static_cast<uint32_t>(source.size());
252 for (uint32_t c = q; c < n; c++) {
253 for (uint32_t r = q; r < n; r++) add_entry(r, c);
254 }
255 auto at = [&](const uint32_t r, const uint32_t c) { return entry[static_cast<size_t>(r) * n + c]; };
256 auto real = [&](const uint32_t r, const uint32_t c) { return test(fill[r], c) != 0; };
257 // Each entry's updates a_t -= a_l a_u in pivot order, each in the stage of the latest pivot so far: no
258 // earlier than its pivot's (whose column is then scaled) and in the same order as pivot by pivot
259 std::vector<std::vector<uint32_t>> scale(n_stages); // (stage): L entry, its diagonal
260 std::vector<std::vector<std::array<uint32_t, 3>>> updates(source.size()); // (target): (stage, l, u) in order
261 // One thread eliminates pivot by pivot: per pivot, its L entries and its updates (target, l, u)
262 std::vector<uint32_t> pivot_lower_offset{0}, pivot_lower_entry, pivot_update_offset{0}, pivot_update;
263 for (uint32_t k = 0; k < n; k++) {
264 for (uint32_t r = k + 1; r < n; r++) {
265 if (at(r, k) < 0) continue;
266 const uint32_t l = static_cast<uint32_t>(at(r, k));
267 if (k < q) scale[stage_of[k]].insert(scale[stage_of[k]].end(), {l, diagonal[k]});
268 if (real(r, k)) pivot_lower_entry.push_back(l);
269 for (uint32_t c = k + 1; c < n; c++) {
270 if (at(k, c) < 0) continue;
271 const uint32_t t = static_cast<uint32_t>(at(r, c)), u = static_cast<uint32_t>(at(k, c));
272 if (real(r, k) && real(k, c)) pivot_update.insert(pivot_update.end(), {t, l, u});
273 if (k >= q) continue;
274 auto & list = updates[t];
275 const uint32_t s = list.empty() ? stage_of[k] : std::max(stage_of[k], list.back()[0]);
276 list.push_back({s, l, u});
277 }
278 }
279 pivot_lower_offset.push_back(static_cast<uint32_t>(pivot_lower_entry.size()));
280 pivot_update_offset.push_back(static_cast<uint32_t>(pivot_update.size() / 3));
281 }
282 std::vector<uint32_t> scale_offset{0}, scale_entry, target_offset{0}, target, target_update{0}, update;
283 {
284 std::vector<std::vector<uint32_t>> stage_targets(n_stages - m); // (stage): target, first and end update
285 for (uint32_t t = 0; t < updates.size(); t++) {
286 const auto & list = updates[t];
287 for (size_t a = 0; a < list.size();) {
288 size_t b = a;
289 while (b < list.size() && list[b][0] == list[a][0]) b++;
290 stage_targets[list[a][0]].insert(stage_targets[list[a][0]].end(),
291 {t, static_cast<uint32_t>(a), static_cast<uint32_t>(b)});
292 a = b;
293 }
294 }
295 for (uint32_t s = 0; s < n_stages - m; s++) {
296 scale_entry.insert(scale_entry.end(), scale[s].begin(), scale[s].end());
297 scale_offset.push_back(static_cast<uint32_t>(scale_entry.size() / 2));
298 for (size_t g = 0; g < stage_targets[s].size(); g += 3) {
299 const uint32_t t = stage_targets[s][g];
300 target.push_back(t);
301 for (uint32_t a = stage_targets[s][g + 1]; a < stage_targets[s][g + 2]; a++) {
302 update.insert(update.end(), {updates[t][a][1], updates[t][a][2]});
303 }
304 target_update.push_back(static_cast<uint32_t>(update.size() / 2));
305 }
306 target_offset.push_back(static_cast<uint32_t>(target.size()));
307 }
308 }
309 std::vector<uint32_t> lower_level(n, 0), upper_level(n, 0);
310 std::vector<uint32_t> lower_offset{0}, lower_entry, upper_offset{0}, upper_entry;
311 for (uint32_t r = 0; r < n; r++) {
312 for (uint32_t c = 0; c < std::min(r, q); c++) {
313 if (at(r, c) < 0) continue;
314 lower_entry.insert(lower_entry.end(), {static_cast<uint32_t>(at(r, c)), c});
315 lower_level[r] = std::max(lower_level[r], lower_level[c] + 1);
316 }
317 lower_offset.push_back(static_cast<uint32_t>(lower_entry.size() / 2));
318 }
319 for (uint32_t r = q; r-- > 0;) {
320 for (uint32_t c = q; c-- > r + 1;) {
321 if (at(r, c) < 0) continue;
322 upper_level[r] = std::max(upper_level[r], upper_level[c] + 1);
323 }
324 }
325 for (uint32_t r = 0; r < q; r++) {
326 for (uint32_t c = q; c-- > r + 1;) {
327 if (at(r, c) >= 0) upper_entry.insert(upper_entry.end(), {static_cast<uint32_t>(at(r, c)), c});
328 }
329 upper_offset.push_back(static_cast<uint32_t>(upper_entry.size() / 2));
330 }
331 // Solves column by column: per pivot, the entries of L below it and of U above it, with their rows
332 std::vector<uint32_t> column_lower_offset{0}, column_lower_entry, column_upper_offset{0}, column_upper_entry;
333 for (uint32_t k = 0; k < n; k++) {
334 for (uint32_t r = k + 1; r < n; r++) {
335 if (real(r, k)) column_lower_entry.insert(column_lower_entry.end(), {static_cast<uint32_t>(at(r, k)), r});
336 }
337 column_lower_offset.push_back(static_cast<uint32_t>(column_lower_entry.size() / 2));
338 for (uint32_t r = 0; r < k; r++) {
339 if (real(r, k)) column_upper_entry.insert(column_upper_entry.end(), {static_cast<uint32_t>(at(r, k)), r});
340 }
341 column_upper_offset.push_back(static_cast<uint32_t>(column_upper_entry.size() / 2));
342 }
343 // U's entries in the chain's columns, in the rows before it
344 std::vector<uint32_t> chain_upper_offset{0}, chain_upper_entry;
345 for (uint32_t k = q; k < n; k++) {
346 for (uint32_t r = 0; r < q; r++) {
347 if (at(r, k) >= 0) chain_upper_entry.insert(chain_upper_entry.end(), {static_cast<uint32_t>(at(r, k)), r});
348 }
349 chain_upper_offset.push_back(static_cast<uint32_t>(chain_upper_entry.size() / 2));
350 }
351 // Rows by level: of L those with entries in the other columns, of U those before the chain
352 auto by_level = [&](const std::vector<uint32_t> & level, const uint32_t rows_end, const bool skip_empty,
353 std::vector<uint32_t> & offset, std::vector<uint32_t> & rows) {
354 uint32_t levels = 0;
355 for (uint32_t r = 0; r < rows_end; r++) {
356 if (!(skip_empty && lower_offset[r + 1] == lower_offset[r])) levels = std::max(levels, level[r] + 1);
357 }
358 offset.assign(1, 0);
359 for (uint32_t l = 0; l < levels; l++) {
360 for (uint32_t r = 0; r < rows_end; r++) {
361 if (level[r] == l && !(skip_empty && lower_offset[r + 1] == lower_offset[r])) rows.push_back(r);
362 }
363 offset.push_back(static_cast<uint32_t>(rows.size()));
364 }
365 };
366 std::vector<uint32_t> lower_level_offset, lower_level_row, upper_level_offset, upper_level_row;
367 by_level(lower_level, n, true, lower_level_offset, lower_level_row);
368 by_level(upper_level, q, false, upper_level_offset, upper_level_row);
369 std::vector<double> v(n, 0.0);
370 for (uint32_t k = 0; k < ns; k++) v[k] = 1.0 / mechanism.species[k].molecular_weight;
371
373 p.n = n;
374 p.nnz = static_cast<uint32_t>(source.size());
375 auto copy = [](const auto & h, const char * label) {
376 using T = typename std::decay_t<decltype(h)>::value_type;
377 Kokkos::View<T *, MemorySpace> d(label, h.size());
378 auto mirror = Kokkos::create_mirror_view(d);
379 for (size_t a = 0; a < h.size(); a++) mirror(a) = h[a];
380 Kokkos::deep_copy(d, mirror);
381 return d;
382 };
383 p.perm = copy(perm, "lu_perm");
384 p.source = copy(source, "lu_source");
385 p.diagonal = copy(diagonal, "lu_diagonal");
386 auto copy2 = [](const std::vector<uint32_t> & h, const char * label) {
387 Kokkos::View<uint32_t *[2], Kokkos::LayoutRight, MemorySpace> d(label, h.size() / 2);
388 auto mirror = Kokkos::create_mirror_view(d);
389 for (size_t a = 0; a < h.size() / 2; a++) {
390 mirror(a, 0) = h[2 * a];
391 mirror(a, 1) = h[2 * a + 1];
392 }
393 Kokkos::deep_copy(d, mirror);
394 return d;
395 };
396 p.pivot_lower_offset = copy(pivot_lower_offset, "lu_pivot_lower_offset");
397 p.pivot_lower_entry = copy(pivot_lower_entry, "lu_pivot_lower_entry");
398 p.pivot_update_offset = copy(pivot_update_offset, "lu_pivot_update_offset");
399 p.pivot_update = Kokkos::View<uint32_t *[3], Kokkos::LayoutRight, MemorySpace>("lu_pivot_update", pivot_update.size() / 3);
400 {
401 auto mirror = Kokkos::create_mirror_view(p.pivot_update);
402 for (size_t a = 0; a < pivot_update.size() / 3; a++) {
403 for (size_t b = 0; b < 3; b++) mirror(a, b) = pivot_update[3 * a + b];
404 }
405 Kokkos::deep_copy(p.pivot_update, mirror);
406 }
407 p.n_stages = n_stages;
408 p.scale_offset = copy(scale_offset, "lu_scale_offset");
409 p.scale_entry = copy2(scale_entry, "lu_scale_entry");
410 p.target_offset = copy(target_offset, "lu_target_offset");
411 p.target = copy(target, "lu_target");
412 p.target_update = copy(target_update, "lu_target_update");
413 p.update = copy2(update, "lu_update");
414 p.n_lower_levels = static_cast<uint32_t>(lower_level_offset.size() - 1);
415 p.n_upper_levels = static_cast<uint32_t>(upper_level_offset.size() - 1);
416 p.lower_level_offset = copy(lower_level_offset, "lu_lower_level_offset");
417 p.lower_level_row = copy(lower_level_row, "lu_lower_level_row");
418 p.upper_level_offset = copy(upper_level_offset, "lu_upper_level_offset");
419 p.upper_level_row = copy(upper_level_row, "lu_upper_level_row");
420 p.column_lower_offset = copy(column_lower_offset, "lu_column_lower_offset");
421 p.column_lower_entry = copy2(column_lower_entry, "lu_column_lower_entry");
422 p.column_upper_offset = copy(column_upper_offset, "lu_column_upper_offset");
423 p.column_upper_entry = copy2(column_upper_entry, "lu_column_upper_entry");
424 p.chain = q;
425 p.chain_offset = chain_offset;
426 p.chain_upper_offset = copy(chain_upper_offset, "lu_chain_upper_offset");
427 p.chain_upper_entry = copy2(chain_upper_entry, "lu_chain_upper_entry");
428 p.lower_offset = copy(lower_offset, "lu_lower_offset");
429 p.lower_entry = copy2(lower_entry, "lu_lower_entry");
430 p.upper_offset = copy(upper_offset, "lu_upper_offset");
431 p.upper_entry = copy2(upper_entry, "lu_upper_entry");
432 p.v = copy(v, "lu_v");
433 p.n_entries = n_entries;
434 p.entry_row = copy(e_row, "lu_entry_row");
435 p.entry_column = copy(e_col, "lu_entry_column");
436 p.column_offset = copy(column_offset, "lu_column_offset");
437 p.column_entry = copy(column_entry, "lu_column_entry");
438 p.jacobian_source = copy(jacobian_source, "lu_jacobian_source");
439 return p;
440}
441
448template <typename MemorySpace = Kokkos::DefaultExecutionSpace::memory_space>
449struct SparseLU {
451 double * values; // (nnz)
452 double * z; // (n)
453 const double * u; // (n), the rank-one column, set by the reactor
454 double * x; // (n) scratch
455 double * beta; // one double
456
461 template <typename Pairs>
462 KOKKOS_INLINE_FUNCTION static double subtract(double a, const Pairs & entry, const uint32_t begin,
463 const uint32_t end, const double * values, const double * x) {
464 constexpr uint32_t B = 8;
465 uint32_t e = begin;
466 for (; e + B <= end; e += B) {
467 double l[B], r[B];
468 for (uint32_t i = 0; i < B; i++) {
469 l[i] = values[entry(e + i, 0)];
470 r[i] = x[entry(e + i, 1)];
471 }
472 for (uint32_t i = 0; i < B; i++) a -= l[i] * r[i];
473 }
474 for (; e < end; e++) a -= values[entry(e, 0)] * x[entry(e, 1)];
475 return a;
476 }
477
485 template <typename Lanes>
486 KOKKOS_INLINE_FUNCTION void solve_s(const Lanes & lanes, double * b) const {
487 const uint32_t n = p.n;
488 // Into the elimination order
489 lanes.for_each(n, [&](const uint32_t i) { x[i] = b[p.perm(i)]; });
490 lanes.sync();
491 if constexpr (!Lanes::parallel) {
492 for (uint32_t k = 0; k < n; k++) {
493 const double x_k = x[k];
494 const uint32_t first = p.column_lower_offset(k);
495 lanes.for_each(p.column_lower_offset(k + 1) - first, [&](const uint32_t a) {
496 x[p.column_lower_entry(first + a, 1)] -= values[p.column_lower_entry(first + a, 0)] * x_k;
497 });
498 lanes.sync();
499 }
500 for (uint32_t k = n; k-- > 0;) {
501 const double x_k = x[k] / values[p.diagonal(k)];
502 lanes.sync();
503 lanes.single([&]() { x[k] = x_k; });
504 const uint32_t first = p.column_upper_offset(k);
505 lanes.for_each(p.column_upper_offset(k + 1) - first, [&](const uint32_t a) {
506 x[p.column_upper_entry(first + a, 1)] -= values[p.column_upper_entry(first + a, 0)] * x_k;
507 });
508 lanes.sync();
509 }
510 } else {
511 for (uint32_t l = 0; l < p.n_lower_levels; l++) {
512 const uint32_t first = p.lower_level_offset(l);
513 lanes.for_each(p.lower_level_offset(l + 1) - first, [&](const uint32_t m) {
514 const uint32_t r = p.lower_level_row(first + m);
515 x[r] = subtract(x[r], p.lower_entry, p.lower_offset(r), p.lower_offset(r + 1), values, x);
516 });
517 lanes.sync();
518 }
519 // The chain's block, by columns: entry (chain + i, chain + j) at block[j * m + i]
520 const uint32_t m = n - p.chain;
521 const double * block = values + p.chain_offset;
522 double * x_chain = x + p.chain;
523 for (uint32_t j = 0; j + 1 < m; j++) {
524 const double x_j = x_chain[j];
525 const double * column = block + j * m;
526 lanes.for_each(m - 1 - j, [&](const uint32_t i) { x_chain[j + 1 + i] -= column[j + 1 + i] * x_j; });
527 lanes.sync();
528 }
529 for (uint32_t j = m; j-- > 0;) {
530 const double * column = block + j * m;
531 const double x_j = x_chain[j] / column[j];
532 lanes.sync();
533 lanes.single([&]() { x_chain[j] = x_j; });
534 const uint32_t first = p.chain_upper_offset(j), before = p.chain_upper_offset(j + 1) - first;
535 lanes.for_each(j + before, [&](const uint32_t i) {
536 if (i < j) {
537 x_chain[i] -= column[i] * x_j;
538 } else {
539 x[p.chain_upper_entry(first + i - j, 1)] -= values[p.chain_upper_entry(first + i - j, 0)] * x_j;
540 }
541 });
542 lanes.sync();
543 }
544 for (uint32_t l = 0; l < p.n_upper_levels; l++) {
545 const uint32_t first = p.upper_level_offset(l);
546 lanes.for_each(p.upper_level_offset(l + 1) - first, [&](const uint32_t i) {
547 const uint32_t r = p.upper_level_row(first + i);
548 x[r] = subtract(x[r], p.upper_entry, p.upper_offset(r), p.upper_offset(r + 1), values, x) /
549 values[p.diagonal(r)];
550 });
551 lanes.sync();
552 }
553 }
554 lanes.for_each(n, [&](const uint32_t i) { b[p.perm(i)] = x[i]; });
555 lanes.sync();
556 }
557
564 template <typename Lanes>
565 KOKKOS_INLINE_FUNCTION bool factor(const Lanes & lanes, const double * J, const double diagonal) const {
566 const uint32_t n = p.n;
567 lanes.for_each(p.nnz, [&](const uint32_t e) {
568 const int32_t src = p.jacobian_source(e);
569 const uint32_t r = p.source(e) / n, c = p.source(e) % n;
570 values[e] = (src >= 0 ? -J[src] : 0.0) + (r == c ? diagonal : 0.0);
571 });
572 lanes.sync();
573 constexpr bool staged = Lanes::parallel;
574 for (uint32_t k = 0; !staged && k < n; k++) {
575 const double inv = 1.0 / values[p.diagonal(k)];
576 const uint32_t first_lower = p.pivot_lower_offset(k);
577 lanes.for_each(p.pivot_lower_offset(k + 1) - first_lower,
578 [&](const uint32_t a) { values[p.pivot_lower_entry(first_lower + a)] *= inv; });
579 lanes.sync();
580 const uint32_t first_update = p.pivot_update_offset(k);
581 lanes.for_each(p.pivot_update_offset(k + 1) - first_update, [&](const uint32_t a) {
582 const uint32_t e = first_update + a;
583 values[p.pivot_update(e, 0)] -= values[p.pivot_update(e, 1)] * values[p.pivot_update(e, 2)];
584 });
585 lanes.sync();
586 }
587 const uint32_t m = n - p.chain;
588 for (uint32_t s = 0; staged && s + m < p.n_stages; s++) {
589 const uint32_t first_scale = p.scale_offset(s);
590 lanes.for_each(p.scale_offset(s + 1) - first_scale, [&](const uint32_t i) {
591 const uint32_t a = first_scale + i;
592 const double inv = 1.0 / values[p.scale_entry(a, 1)];
593 values[p.scale_entry(a, 0)] *= inv;
594 });
595 lanes.sync();
596 const uint32_t first_target = p.target_offset(s);
597 lanes.for_each(p.target_offset(s + 1) - first_target, [&](const uint32_t i) {
598 const uint32_t t = first_target + i;
599 values[p.target(t)] =
600 subtract(values[p.target(t)], p.update, p.target_update(t), p.target_update(t + 1), values, values);
601 });
602 lanes.sync();
603 }
604 // The chain's dense block (see solve_s), a pivot at a time: its column scaled, then the trailing
605 // block's entries across the lanes, a few at a time each
606 double * block = values + p.chain_offset;
607 const uint32_t L = lanes.lanes;
608 for (uint32_t j = 0; staged && j < m; j++) {
609 double * column = block + j * m;
610 const uint32_t w = m - 1 - j;
611 const double inv = 1.0 / column[j];
612 lanes.for_each(w, [&](const uint32_t i) { column[j + 1 + i] *= inv; });
613 lanes.sync();
614 constexpr uint32_t B = 4;
615 lanes.for_each(L, [&](const uint32_t lane) {
616 for (uint32_t first = lane; first < w * w; first += B * L) {
617 double a[B], l[B], upper[B];
618 uint32_t at[B];
619 for (uint32_t b = 0; b < B; b++) {
620 const uint32_t i = Kokkos::min(first + b * L, w * w - 1);
621 const uint32_t r = j + 1 + i % w, c = j + 1 + i / w;
622 at[b] = c * m + r;
623 a[b] = block[at[b]];
624 l[b] = column[r];
625 upper[b] = block[c * m + j];
626 }
627 for (uint32_t b = 0; b < B; b++) {
628 if (first + b * L < w * w) block[at[b]] = a[b] - l[b] * upper[b];
629 }
630 }
631 });
632 lanes.sync();
633 }
634 // A pivot is final from its stage on: one that vanished then fails the factorization
635 const double bad = lanes.sum(n, [&](const uint32_t k) {
636 const double pivot = values[p.diagonal(k)];
637 return !(Kokkos::fabs(pivot) > 0.0) || !Kokkos::isfinite(pivot) ? 1.0 : 0.0;
638 });
639 if (bad > 0.0) return false;
640 // Sherman-Morrison: A = A_s - u v^T
641 lanes.for_each(n, [&](const uint32_t i) { z[i] = u[i]; });
642 lanes.sync();
643 solve_s(lanes, z);
644 const double b = 1.0 - lanes.sum(n, [&](const uint32_t i) { return p.v(i) * z[i]; });
645 if (!(Kokkos::fabs(b) > 1e-12) || !Kokkos::isfinite(b)) return false;
646 lanes.single([&]() { *beta = b; });
647 lanes.sync();
648 return true;
649 }
650
651 template <typename Lanes>
652 KOKKOS_INLINE_FUNCTION void solve(const Lanes & lanes, double * b) const {
653 solve_s(lanes, b);
654 const double s = lanes.sum(p.n, [&](const uint32_t i) { return p.v(i) * b[i]; }) / *beta;
655 lanes.for_each(p.n, [&](const uint32_t i) { b[i] += z[i] * s; });
656 lanes.sync();
657 }
658};
659
660} // namespace chemistry
661
662#endif // CHEMISTRY_SPARSE_LU_H
KOKKOS_INLINE_FUNCTION rtype dot(const rtype *a, const rtype *b)
Compute the dot product of two arrays.
Definition common_math.h:32
Device tables of a mechanism's reactions: rates of progress, production rates and their analytical de...
Execution of one cell's chemistry by one thread or by the vector lanes of a team: loops over species,...
Gas-phase mechanisms read from Cantera YAML files.
Definition kinetics.h:25
SparseLUPattern< MemorySpace > make_sparse_lu_pattern(const Mechanism &mechanism)
Build the pattern for a mechanism's reactor Jacobian (see SparseLUPattern).
Definition sparse_lu.h:102
One ideal-gas phase of a Cantera YAML file: its elements, species and reactions.
Definition mechanism.h:145
std::vector< Species > species
Definition mechanism.h:149
uint32_t n_species() const
Definition mechanism.h:152
std::vector< Reaction > reactions
Definition mechanism.h:150
A gas-phase reaction: elementary, three-body or falloff (Lindemann, Troe, SRI), reversible (reverse r...
Definition mechanism.h:122
The pattern of a reactor Jacobian in (Y_1 .. Y_Ns, T) without its rank-one part, an elimination order...
Definition sparse_lu.h:46
Pairs upper_entry
Definition sparse_lu.h:84
Kokkos::View< uint32_t *[3], Kokkos::LayoutRight, MemorySpace > pivot_update
Definition sparse_lu.h:60
View1< uint32_t > entry_row
Definition sparse_lu.h:90
Kokkos::View< uint32_t *[2], Kokkos::LayoutRight, MemorySpace > Pairs
Definition sparse_lu.h:55
View1< int32_t > jacobian_source
Definition sparse_lu.h:91
View1< uint32_t > entry_column
Definition sparse_lu.h:90
View1< uint32_t > upper_level_offset
Definition sparse_lu.h:81
View1< double > v
Definition sparse_lu.h:86
View1< uint32_t > diagonal
Definition sparse_lu.h:54
KOKKOS_INLINE_FUNCTION uint32_t jacobian_size() const
Doubles of a cell's compact Jacobian.
Definition sparse_lu.h:94
uint32_t n_entries
Definition sparse_lu.h:89
View1< uint32_t > source
Definition sparse_lu.h:53
Kokkos::View< T *, MemorySpace > View1
Definition sparse_lu.h:48
View1< uint32_t > pivot_lower_offset
Definition sparse_lu.h:59
uint32_t nnz
Definition sparse_lu.h:51
uint32_t chain
Definition sparse_lu.h:77
View1< uint32_t > scale_offset
Definition sparse_lu.h:67
Pairs lower_entry
Definition sparse_lu.h:83
uint32_t n_lower_levels
Definition sparse_lu.h:80
Pairs scale_entry
Definition sparse_lu.h:68
Pairs column_lower_entry
Definition sparse_lu.h:62
Pairs update
Definition sparse_lu.h:72
View1< uint32_t > column_lower_offset
Definition sparse_lu.h:61
uint32_t n
Definition sparse_lu.h:50
View1< uint32_t > lower_level_offset
Definition sparse_lu.h:81
View1< uint32_t > column_offset
Definition sparse_lu.h:90
Pairs column_upper_entry
Definition sparse_lu.h:62
View1< uint32_t > column_upper_offset
Definition sparse_lu.h:61
View1< uint32_t > target_offset
Definition sparse_lu.h:69
View1< uint32_t > chain_upper_offset
Definition sparse_lu.h:78
View1< uint32_t > perm
Definition sparse_lu.h:52
View1< uint32_t > lower_level_row
Definition sparse_lu.h:81
View1< uint32_t > target_update
Definition sparse_lu.h:71
View1< uint32_t > target
Definition sparse_lu.h:70
uint32_t chain_offset
Definition sparse_lu.h:77
View1< uint32_t > pivot_lower_entry
Definition sparse_lu.h:59
View1< uint32_t > column_entry
Definition sparse_lu.h:90
uint32_t n_upper_levels
Definition sparse_lu.h:80
Pairs chain_upper_entry
Definition sparse_lu.h:79
View1< uint32_t > upper_offset
Definition sparse_lu.h:82
View1< uint32_t > pivot_update_offset
Definition sparse_lu.h:59
KOKKOS_INLINE_FUNCTION uint32_t work_size() const
Doubles of a cell's work memory: values, z = A_s^-1 u, u and one vector.
Definition sparse_lu.h:97
View1< uint32_t > lower_offset
Definition sparse_lu.h:82
View1< uint32_t > upper_level_row
Definition sparse_lu.h:81
uint32_t n_stages
Definition sparse_lu.h:66
The linear solver of integrate() for SparseLUPattern: factor() gathers diagonal I - J_s from the comp...
Definition sparse_lu.h:449
KOKKOS_INLINE_FUNCTION bool factor(const Lanes &lanes, const double *J, const double diagonal) const
Factor diagonal I - J_s (see SparseLU): one thread pivot by pivot; lanes stage by stage,...
Definition sparse_lu.h:565
double * x
Definition sparse_lu.h:454
KOKKOS_INLINE_FUNCTION void solve_s(const Lanes &lanes, double *b) const
A_s^-1 b in place, with every entry's terms in the order of the elimination column by column (so the ...
Definition sparse_lu.h:486
const double * u
Definition sparse_lu.h:453
static KOKKOS_INLINE_FUNCTION double subtract(double a, const Pairs &entry, const uint32_t begin, const uint32_t end, const double *values, const double *x)
a - sum_e values[entry(e, 0)] x[entry(e, 1)] for e in [begin, end), in order; the loads of a few term...
Definition sparse_lu.h:462
double * beta
Definition sparse_lu.h:455
const SparseLUPattern< MemorySpace > & p
Definition sparse_lu.h:450
KOKKOS_INLINE_FUNCTION void solve(const Lanes &lanes, double *b) const
Definition sparse_lu.h:652
double * values
Definition sparse_lu.h:451
double * z
Definition sparse_lu.h:452
uint32_t rows
Definition teno_setup.cpp:455
CellSetup< ExecSpace > c
Definition teno_setup.cpp:1810
uint8_t r
Definition teno_setup.cpp:687
Geometry< Space > g
Definition teno_setup.cpp:678
uint16_t ns
Definition teno_setup.cpp:688
Lane< rtype > scale
Definition teno_setup.cpp:531