flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
scalar_krylov.hpp
Go to the documentation of this file.
1// BiCGStab for the cut-cell scalar operator (doc/scalar_ibm_design.md §5.1, D9/D11).
2//
3// Modelled statement for statement on `CutcellMG::solveBiCGStab` (mac_cutcell_mg.hpp): the same
4// recurrence, the same breakdown guards, the same stagnation guard, and the mean projection of
5// r / v / t in the singular case. Three pieces are injected by the caller through `Ops`: the matvec
6// of the TRUE (probe) operator, the preconditioner z = M^-1 r (one ScalarMG V-cycle on the SPD
7// surrogate, scalar_mg.hpp; level 0 alone = 2 + 2 red-black sweeps under the transient level
8// rule), and the reductions (`dot`, `dot2`,
9// `maxabs`, `removeMean`), which carry the MPI_Allreduce. Vectors are block fields on flow's G = 2
10// block; vector operations run over inner cells, and identity rows keep every Krylov vector exactly
11// 0 at the cells that are not unknowns, so the dots need no mask.
12//
13// Stopping (§5.1): ref = max(max|b|, max|A x0|) (A x0 = b - r0 comes free); stop when the
14// RECURRENCE residual max|r_k| <= rtol ref, checked at each half-step. At exit the TRUE residual is
15// recomputed once; if it exceeds 10 rtol ref the iteration restarts once from it (within the same
16// iteration budget). The max-norm stop decision is reduction-order independent. A non-finite
17// residual raises.
18//
19// Device-resident scalars (WO-9b; flow's A6, CutcellMG::solvePCGResident): on a single rank and
20// for a non-singular system the pass keeps rho, alpha, omega and beta on the device. The
21// reductions land in slots of a small device array (the SAME policies and functors as the host
22// dots, so the same values); one-thread kernels form the coefficients with the same IEEE
23// divisions and run the breakdown tests; the vector updates read their coefficient from the slot
24// and skip once a stop code is set. The host reads ONE packet {stop, max|r|} per half-step — what
25// the max-norm stop test needs — instead of five scalars per iteration. Every exit returns the
26// same iterate and iteration count as the host-scalar pass, bitwise. The distributed and the
27// singular solves keep the host-scalar pass (their reductions are an MPI_Allreduce or feed the
28// mean projection on the host).
29#ifndef PECLET_FLOW_SCALAR_KRYLOV_HPP
30#define PECLET_FLOW_SCALAR_KRYLOV_HPP
31
32#include <cmath>
33#include <functional>
34#include <Kokkos_Core.hpp>
35#include <stdexcept>
36#include <utility>
37
38#include "mac_cutcell.hpp"
39
40namespace peclet::flow {
41
44 int iterations = 0;
45 double ref = 0.0;
46 double trueRes = 0.0;
47 bool restarted = false;
48 bool converged = true;
49};
50
53struct ScalarVec {
55 bool solid = false;
56};
57
58namespace skr {
59
61inline void axpy(CCField y, double a, CCConst x, C3 e, int g) {
62 ccFor3(
63 "peclet::flow::skr_axpy", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
64 KOKKOS_LAMBDA(int lx, int ly, int lz) {
65 const long i = (long)lx + (long)ly * e.x + (long)lz * (long)e.x * e.y;
66 y(i) += a * x(i);
67 });
68}
70inline void aypx(CCField y, double a, CCConst x, C3 e, int g) {
71 ccFor3(
72 "peclet::flow::skr_aypx", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
73 KOKKOS_LAMBDA(int lx, int ly, int lz) {
74 const long i = (long)lx + (long)ly * e.x + (long)lz * (long)e.x * e.y;
75 y(i) = x(i) + a * y(i);
76 });
77}
79inline void residualFrom(CCField y, CCConst b, C3 e, int g) {
80 ccFor3(
81 "peclet::flow::skr_residual", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
82 KOKKOS_LAMBDA(int lx, int ly, int lz) {
83 const long i = (long)lx + (long)ly * e.x + (long)lz * (long)e.x * e.y;
84 y(i) = b(i) - y(i);
85 });
86}
87
89inline void axpy(const ScalarVec& y, double a, const ScalarVec& x, C3 e, int g) {
90 axpy(y.f, a, CCConst(x.f), e, g);
91 if (y.solid)
92 axpy(y.s, a, CCConst(x.s), e, g);
93}
94inline void aypx(const ScalarVec& y, double a, const ScalarVec& x, C3 e, int g) {
95 aypx(y.f, a, CCConst(x.f), e, g);
96 if (y.solid)
97 aypx(y.s, a, CCConst(x.s), e, g);
98}
99inline void residualFrom(const ScalarVec& y, const ScalarVec& b, C3 e, int g) {
100 residualFrom(y.f, CCConst(b.f), e, g);
101 if (y.solid)
102 residualFrom(y.s, CCConst(b.s), e, g);
103}
104inline void copy(const ScalarVec& y, const ScalarVec& x) {
105 Kokkos::deep_copy(CCExec(), y.f, x.f);
106 if (y.solid)
107 Kokkos::deep_copy(CCExec(), y.s, x.s);
108}
109inline void zero(const ScalarVec& y) {
110 Kokkos::deep_copy(CCExec(), y.f, 0.0);
111 if (y.solid)
112 Kokkos::deep_copy(CCExec(), y.s, 0.0);
113}
114
115} // namespace skr
116
118namespace skr {
119enum : int {
120 kRho = 0,
123 kBeta = 3,
125 kRhv = 5,
126 kTr = 6,
127 kTt = 7,
128 kStop = 8,
129 kRn = 9,
130 kRnS = 10,
131 kNSlots = 11
133constexpr double kBrkRho = 1.0, kBrkRhv = 2.0, kBrkTt = 3.0, kBrkOmega = 4.0;
134using Slot = Kokkos::View<double, CCMem>;
135inline Slot slot(const Kokkos::View<double*, CCMem>& ks, int k) {
136 return Slot(ks.data() + k);
137}
138} // namespace skr
139
143 C3 e{0, 0, 0};
144 int g = 0;
145 std::function<void(const ScalarVec&, const ScalarVec&)> matvec, precond;
146 std::function<double(const ScalarVec&, const ScalarVec&)> dot;
147 std::function<void(const ScalarVec&, const ScalarVec&, const ScalarVec&, double&, double&)> dot2;
148 std::function<double(const ScalarVec&)> maxabs;
149 std::function<void(const ScalarVec&)> removeMean;
154 bool resident = false;
155 Kokkos::View<double*, CCMem> ks;
156 Kokkos::View<double*, Kokkos::HostSpace> pk;
157 std::function<void(const ScalarVec&, const ScalarVec&, skr::Slot)> dotTo;
158 std::function<void(const ScalarVec&, const ScalarVec&, const ScalarVec&, skr::Slot, skr::Slot)>
160 std::function<void(const ScalarVec&, skr::Slot, skr::Slot)> maxabsTo;
161};
162
164template <class State>
165inline void residentKrylov(State& st, ScalarKrylovOps& ops) {
166 if (st.ks.extent(0) < (std::size_t)skr::kNSlots)
167 st.ks = Kokkos::View<double*, CCMem>("peclet::flow::skr_slots", skr::kNSlots);
168 if (st.pk.extent(0) < 3)
169 st.pk = Kokkos::View<double*, Kokkos::HostSpace>("peclet::flow::skr_packet", 3);
170 ops.resident = true;
171 ops.ks = st.ks;
172 ops.pk = st.pk;
173}
174
175namespace skr {
176
178template <class F>
179inline void scalarKernel(const char* name, F f) {
180 Kokkos::parallel_for(name, Kokkos::RangePolicy<CCExec>(CCExec(), 0, 1), f);
181}
182
183inline void residentInit(const Kokkos::View<double*, CCMem>& ks) {
185 "peclet::flow::skr_init", KOKKOS_LAMBDA(int) {
186 ks(kRho) = 1.0;
187 ks(kAlpha) = 1.0;
188 ks(kOmega) = 1.0;
189 ks(kStop) = 0.0;
190 });
191}
193inline void residentBeta(const Kokkos::View<double*, CCMem>& ks) {
195 "peclet::flow::skr_beta", KOKKOS_LAMBDA(int) {
196 const double rhoNew = ks(kRhoNew);
197 if (!Kokkos::isfinite(rhoNew) || Kokkos::fabs(rhoNew) < 1e-300) {
198 ks(kStop) = kBrkRho;
199 return;
200 }
201 ks(kBeta) = (rhoNew / ks(kRho)) * (ks(kAlpha) / ks(kOmega));
202 ks(kRho) = rhoNew;
203 });
204}
206inline void residentAlpha(const Kokkos::View<double*, CCMem>& ks) {
208 "peclet::flow::skr_alpha", KOKKOS_LAMBDA(int) {
209 if (ks(kStop) != 0.0)
210 return;
211 const double rhv = ks(kRhv);
212 if (!Kokkos::isfinite(rhv) || Kokkos::fabs(rhv) < 1e-300) {
213 ks(kStop) = kBrkRhv;
214 return;
215 }
216 ks(kAlpha) = ks(kRho) / rhv;
217 });
218}
220inline void residentOmega(const Kokkos::View<double*, CCMem>& ks) {
222 "peclet::flow::skr_omega", KOKKOS_LAMBDA(int) {
223 const double tt = ks(kTt);
224 if (!Kokkos::isfinite(tt) || tt < 1e-300) {
225 ks(kStop) = kBrkTt;
226 return;
227 }
228 const double om = ks(kTr) / tt;
229 if (!Kokkos::isfinite(om) || Kokkos::fabs(om) < 1e-300) {
230 ks(kStop) = kBrkOmega;
231 return;
232 }
233 ks(kOmega) = om;
234 });
235}
238inline void residentP(const ScalarVec& p, const ScalarVec& v, const ScalarVec& r,
239 const Kokkos::View<double*, CCMem>& ks, C3 e, int g) {
240 CCField pf = p.f, ps = p.s, vf = v.f, vs = v.s, rf = r.f, rs = r.s;
241 const bool two = p.solid;
242 ccFor3(
243 "peclet::flow::skr_p", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
244 KOKKOS_LAMBDA(int lx, int ly, int lz) {
245 if (ks(kStop) != 0.0)
246 return;
247 const long i = (long)lx + (long)ly * e.x + (long)lz * (long)e.x * e.y;
248 const double na = -ks(kOmega), beta = ks(kBeta);
249 pf(i) += na * vf(i);
250 pf(i) = rf(i) + beta * pf(i);
251 if (two) {
252 ps(i) += na * vs(i);
253 ps(i) = rs(i) + beta * ps(i);
254 }
255 });
256}
258inline void residentS(const ScalarVec& r, const ScalarVec& v,
259 const Kokkos::View<double*, CCMem>& ks, C3 e, int g) {
260 CCField rf = r.f, rs = r.s, vf = v.f, vs = v.s;
261 const bool two = r.solid;
262 ccFor3(
263 "peclet::flow::skr_s", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
264 KOKKOS_LAMBDA(int lx, int ly, int lz) {
265 if (ks(kStop) != 0.0)
266 return;
267 const long i = (long)lx + (long)ly * e.x + (long)lz * (long)e.x * e.y;
268 const double na = -ks(kAlpha);
269 rf(i) += na * vf(i);
270 if (two)
271 rs(i) += na * vs(i);
272 });
273}
275inline void residentXHalf(const ScalarVec& x, const ScalarVec& z,
276 const Kokkos::View<double*, CCMem>& ks, C3 e, int g) {
277 CCField xf = x.f, xs = x.s, zf = z.f, zs = z.s;
278 const bool two = x.solid;
279 ccFor3(
280 "peclet::flow::skr_x_half", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
281 KOKKOS_LAMBDA(int lx, int ly, int lz) {
282 const long i = (long)lx + (long)ly * e.x + (long)lz * (long)e.x * e.y;
283 const double a = ks(kAlpha);
284 xf(i) += a * zf(i);
285 if (two)
286 xs(i) += a * zs(i);
287 });
288}
291inline void residentXR(const ScalarVec& x, const ScalarVec& r, const ScalarVec& z,
292 const ScalarVec& z2, const ScalarVec& t,
293 const Kokkos::View<double*, CCMem>& ks, C3 e, int g) {
294 CCField xf = x.f, xs = x.s, rf = r.f, rs = r.s, zf = z.f, zs = z.s, z2f = z2.f, z2s = z2.s,
295 tf = t.f, ts = t.s;
296 const bool two = x.solid;
297 ccFor3(
298 "peclet::flow::skr_x_r", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
299 KOKKOS_LAMBDA(int lx, int ly, int lz) {
300 const long i = (long)lx + (long)ly * e.x + (long)lz * (long)e.x * e.y;
301 const double a = ks(kAlpha);
302 if (ks(kStop) != 0.0) {
303 xf(i) += a * zf(i);
304 if (two)
305 xs(i) += a * zs(i);
306 return;
307 }
308 const double om = ks(kOmega), nom = -om;
309 xf(i) += a * zf(i);
310 xf(i) += om * z2f(i);
311 rf(i) += nom * tf(i);
312 if (two) {
313 xs(i) += a * zs(i);
314 xs(i) += om * z2s(i);
315 rs(i) += nom * ts(i);
316 }
317 });
318}
319} // namespace skr
320
330template <class Ops>
332 const ScalarVec& r, const ScalarVec& rh, const ScalarVec& p,
333 const ScalarVec& v, const ScalarVec& t, const ScalarVec& z,
334 const ScalarVec& z2, int maxit, double rtol, bool singular) {
335 const C3 e = ops.e;
336 const int g = ops.g;
338 ops.matvec(t, x); // t = A x0
339 const double ax0 = ops.maxabs(t);
340 const double bmax = ops.maxabs(b);
341 res.ref = bmax > ax0 ? bmax : ax0;
342 if (!std::isfinite(res.ref))
343 throw std::runtime_error("scalar cut-cell solve: non-finite right-hand side or initial guess");
344 const double thresh = rtol * res.ref;
345 int it = 0;
346 // One BiCGStab pass from the current x (CutcellMG::solveBiCGStab's loop, with the §5.1 stop).
347 auto pass = [&]() {
348 ops.matvec(t, x); // r = b - A x (t as scratch)
349 skr::copy(r, t);
350 skr::residualFrom(r, b, e, g);
351 if (singular)
352 ops.removeMean(r);
353 skr::copy(rh, r); // shadow residual r^ = r_0
354 const double r0n = ops.maxabs(r);
355 if (!std::isfinite(r0n))
356 throw std::runtime_error("scalar cut-cell solve: non-finite residual");
357 if (!(r0n > thresh))
358 return;
359 double rho = 1.0, alpha = 1.0, omega = 1.0;
360 double best = r0n;
361 int lastImprove = it;
362 skr::zero(p);
363 skr::zero(v);
364 for (; it < maxit; ++it) {
365 const double rhoNew = ops.dot(rh, r);
366 if (!std::isfinite(rhoNew) || std::fabs(rhoNew) < 1e-300)
367 break; // (rh, r) breakdown: keep the last finite iterate
368 const double beta = (rhoNew / rho) * (alpha / omega);
369 rho = rhoNew;
370 skr::axpy(p, -omega, v, e, g); // p = r + beta (p - omega v)
371 skr::aypx(p, beta, r, e, g);
372 ops.precond(z, p);
373 ops.matvec(v, z);
374 if (singular)
375 ops.removeMean(v);
376 const double rhv = ops.dot(rh, v);
377 if (!std::isfinite(rhv) || std::fabs(rhv) < 1e-300)
378 break;
379 alpha = rho / rhv;
380 skr::axpy(r, -alpha, v, e, g); // r <- s = r - alpha v
381 if (singular)
382 ops.removeMean(r);
383 double rn = ops.maxabs(r);
384 if (!std::isfinite(rn))
385 throw std::runtime_error("scalar cut-cell solve: non-finite residual");
386 if (rn <= thresh) {
387 skr::axpy(x, alpha, z, e, g);
388 ++it;
389 break;
390 }
391 ops.precond(z2, r);
392 ops.matvec(t, z2);
393 if (singular)
394 ops.removeMean(t);
395 double tr = 0.0, tt = 0.0;
396 ops.dot2(t, r, t, tr, tt);
397 if (!std::isfinite(tt) || tt < 1e-300) {
398 skr::axpy(x, alpha, z, e, g); // omega breakdown: take the alpha half-step, stop
399 ++it;
400 break;
401 }
402 omega = tr / tt;
403 if (!std::isfinite(omega) || std::fabs(omega) < 1e-300) {
404 skr::axpy(x, alpha, z, e, g);
405 ++it;
406 break;
407 }
408 skr::axpy(x, alpha, z, e, g);
409 skr::axpy(x, omega, z2, e, g);
410 skr::axpy(r, -omega, t, e, g);
411 if (singular)
412 ops.removeMean(r);
413 rn = ops.maxabs(r);
414 if (!std::isfinite(rn))
415 throw std::runtime_error("scalar cut-cell solve: non-finite residual");
416 if (rn <= thresh) {
417 ++it;
418 break;
419 }
420 if (rn < 0.999 * best) {
421 best = rn;
422 lastImprove = it;
423 } else if (it - lastImprove > 30) {
424 ++it; // stagnation: accept the best-so-far level
425 break;
426 }
427 }
428 };
429 // The same pass with the scalars on the device (WO-9b; see the file comment). Statement for
430 // statement `pass` above, through the skr::resident* kernels.
431 auto passResident = [&]() {
432 ops.matvec(t, x);
433 skr::copy(r, t);
434 skr::residualFrom(r, b, e, g);
435 skr::copy(rh, r);
436 const double r0n = ops.maxabs(r);
437 if (!std::isfinite(r0n))
438 throw std::runtime_error("scalar cut-cell solve: non-finite residual");
439 if (!(r0n > thresh))
440 return;
441 double best = r0n;
442 int lastImprove = it;
443 skr::zero(p);
444 skr::zero(v);
445 const auto& ks = ops.ks;
447 // the packet {stop, max|r|} (both phases combined with std::fmax, as ops.maxabs)
448 auto readPacket = [&](double& stop) {
449 ops.maxabsTo(r, skr::slot(ks, skr::kRn), skr::slot(ks, skr::kRnS));
450 Kokkos::deep_copy(ops.pk,
451 Kokkos::subview(ks, std::make_pair((int)skr::kStop, (int)skr::kStop + 3)));
452 stop = ops.pk(0);
453 return x.solid ? std::fmax(ops.pk(1), ops.pk(2)) : ops.pk(1);
454 };
455 for (; it < maxit; ++it) {
456 ops.dotTo(rh, r, skr::slot(ks, skr::kRhoNew));
458 skr::residentP(p, v, r, ks, e, g); // p = r + beta (p - omega v)
459 ops.precond(z, p);
460 ops.matvec(v, z);
461 ops.dotTo(rh, v, skr::slot(ks, skr::kRhv));
463 skr::residentS(r, v, ks, e, g); // r <- s = r - alpha v
464 double stop = 0.0;
465 double rn = readPacket(stop); // THE read of the half-step
466 if (stop != 0.0)
467 break; // (rh, r) or (rh, v) breakdown: keep the last finite iterate
468 if (!std::isfinite(rn))
469 throw std::runtime_error("scalar cut-cell solve: non-finite residual");
470 if (rn <= thresh) {
471 skr::residentXHalf(x, z, ks, e, g);
472 ++it;
473 break;
474 }
475 ops.precond(z2, r);
476 ops.matvec(t, z2);
477 ops.dot2To(t, r, t, skr::slot(ks, skr::kTr), skr::slot(ks, skr::kTt));
479 skr::residentXR(x, r, z, z2, t, ks, e, g);
480 rn = readPacket(stop); // THE read of the full step
481 if (stop != 0.0) {
482 ++it; // tt / omega breakdown: the alpha half-step was taken, stop
483 break;
484 }
485 if (!std::isfinite(rn))
486 throw std::runtime_error("scalar cut-cell solve: non-finite residual");
487 if (rn <= thresh) {
488 ++it;
489 break;
490 }
491 if (rn < 0.999 * best) {
492 best = rn;
493 lastImprove = it;
494 } else if (it - lastImprove > 30) {
495 ++it; // stagnation: accept the best-so-far level
496 break;
497 }
498 }
499 };
500 auto trueResidual = [&]() {
501 ops.matvec(t, x);
502 skr::copy(r, t);
503 skr::residualFrom(r, b, e, g);
504 if (singular)
505 ops.removeMean(r);
506 const double rn = ops.maxabs(r);
507 if (!std::isfinite(rn))
508 throw std::runtime_error("scalar cut-cell solve: non-finite residual");
509 return rn;
510 };
511 const bool resident = ops.resident && !singular;
512 if (res.ref > 0.0) {
513 resident ? passResident() : pass();
514 res.trueRes = trueResidual();
515 if (res.trueRes > 10.0 * thresh && it < maxit) {
516 res.restarted = true;
517 resident ? passResident() : pass();
518 res.trueRes = trueResidual();
519 }
520 }
521 res.iterations = it;
522 res.converged = res.trueRes <= 10.0 * thresh;
523 return res;
524}
525
526} // namespace peclet::flow
527
528#endif // PECLET_FLOW_SCALAR_KRYLOV_HPP
flow — portable (Kokkos) cut-cell pressure-operator face openness from an SDF.
@ kStop
the packet the host reads: {stop, max|r_f|, max|r_s|}
void residentAlpha(const Kokkos::View< double *, CCMem > &ks)
(rh, v) breakdown, else alpha = rho / (rh, v).
Slot slot(const Kokkos::View< double *, CCMem > &ks, int k)
void residentS(const ScalarVec &r, const ScalarVec &v, const Kokkos::View< double *, CCMem > &ks, C3 e, int g)
r <- s = r - alpha v (axpy(r, -alpha, v)); skipped on a stop.
constexpr double kBrkOmega
void aypx(CCField y, double a, CCConst x, C3 e, int g)
y = x + a y over inner cells.
void zero(const ScalarVec &y)
void axpy(CCField y, double a, CCConst x, C3 e, int g)
y += a x over inner cells.
void residentP(const ScalarVec &p, const ScalarVec &v, const ScalarVec &r, const Kokkos::View< double *, CCMem > &ks, C3 e, int g)
p = r + beta (p - omega v): axpy(p, -omega, v) then aypx(p, beta, r), per phase; skipped on a stop.
void residualFrom(CCField y, CCConst b, C3 e, int g)
y = b - y over inner cells (the residual from a matvec result held in y).
void residentXHalf(const ScalarVec &x, const ScalarVec &z, const Kokkos::View< double *, CCMem > &ks, C3 e, int g)
x += alpha z (the half-step exit).
constexpr double kBrkRho
void residentInit(const Kokkos::View< double *, CCMem > &ks)
void residentXR(const ScalarVec &x, const ScalarVec &r, const ScalarVec &z, const ScalarVec &z2, const ScalarVec &t, const Kokkos::View< double *, CCMem > &ks, C3 e, int g)
x += alpha z; x += omega z2; r -= omega t (the three axpys) — or, after a tt / omega breakdown,...
void residentOmega(const Kokkos::View< double *, CCMem > &ks)
The tt breakdown, else omega = (t, r) / (t, t) and its breakdown.
constexpr double kBrkRhv
void scalarKernel(const char *name, F f)
One-thread kernel on the slots (the coefficient arithmetic of the host pass, on the device).
Kokkos::View< double, CCMem > Slot
constexpr double kBrkTt
void copy(const ScalarVec &y, const ScalarVec &x)
void residentBeta(const Kokkos::View< double *, CCMem > &ks)
(rh, r) breakdown, else beta = (rhoNew / rho) (alpha / omega) and rho = rhoNew.
ScalarKrylovResult scalarBiCGStab(Ops &ops, const ScalarVec &b, const ScalarVec &x, const ScalarVec &r, const ScalarVec &rh, const ScalarVec &p, const ScalarVec &v, const ScalarVec &t, const ScalarVec &z, const ScalarVec &z2, int maxit, double rtol, bool singular)
BiCGStab on A x = b.
void residentKrylov(State &st, ScalarKrylovOps &ops)
Arm ops for the resident pass with the persistent slots of st (ks, pk, allocated once).
void ccFor3(const char *name, C3 lo, C3 hi, F f)
Kokkos::View< double *, CCMem > CCField
Kokkos::DefaultExecutionSpace CCExec
Kokkos::View< const double *, CCMem > CCConst
The injected pieces as callables (host-side calls that launch kernels; the overhead of the indirectio...
std::function< void(const ScalarVec &, const ScalarVec &, skr::Slot)> dotTo
std::function< void(const ScalarVec &, const ScalarVec &)> matvec
std::function< double(const ScalarVec &)> maxabs
std::function< void(const ScalarVec &, const ScalarVec &, const ScalarVec &, double &, double &)> dot2
std::function< void(const ScalarVec &, const ScalarVec &)> precond
std::function< void(const ScalarVec &, skr::Slot, skr::Slot)> maxabsTo
std::function< double(const ScalarVec &, const ScalarVec &)> dot
std::function< void(const ScalarVec &, const ScalarVec &, const ScalarVec &, skr::Slot, skr::Slot)> dot2To
Kokkos::View< double *, CCMem > ks
Kokkos::View< double *, Kokkos::HostSpace > pk
std::function< void(const ScalarVec &)> removeMean
bool resident
The resident pass (single rank): ks (>= skr::kNSlots device doubles) and pk (3 host doubles) persiste...
Outcome of one scalarBiCGStab call.
int iterations
BiCGStab iterations (both passes when it restarted)
double trueRes
max|b - A x| at exit (after the restart, if any)
double ref
max(max|b|, max|A x0|)
bool converged
trueRes <= 10 rtol ref
A Krylov vector (§5.1): the fluid field and, for a conjugate scalar, the solid field (WO-7).
static constexpr double F