29#ifndef PECLET_FLOW_SCALAR_KRYLOV_HPP
30#define PECLET_FLOW_SCALAR_KRYLOV_HPP
34#include <Kokkos_Core.hpp>
63 "peclet::flow::skr_axpy",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
65 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
72 "peclet::flow::skr_aypx",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
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);
81 "peclet::flow::skr_residual",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
83 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
105 Kokkos::deep_copy(
CCExec(), y.f, x.f);
107 Kokkos::deep_copy(
CCExec(), y.s, x.s);
110 Kokkos::deep_copy(
CCExec(), y.f, 0.0);
112 Kokkos::deep_copy(
CCExec(), y.s, 0.0);
134using Slot = Kokkos::View<double, CCMem>;
135inline Slot slot(
const Kokkos::View<double*, CCMem>& ks,
int k) {
136 return Slot(ks.data() + k);
155 Kokkos::View<double*, CCMem>
ks;
156 Kokkos::View<double*, Kokkos::HostSpace>
pk;
164template <
class State>
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);
180 Kokkos::parallel_for(name, Kokkos::RangePolicy<CCExec>(
CCExec(), 0, 1), f);
197 if (!Kokkos::isfinite(
rhoNew) || Kokkos::fabs(
rhoNew) < 1e-300) {
209 if (ks(
kStop) != 0.0)
212 if (!Kokkos::isfinite(
rhv) || Kokkos::fabs(
rhv) < 1e-300) {
223 const double tt = ks(
kTt);
224 if (!Kokkos::isfinite(
tt) ||
tt < 1e-300) {
228 const double om = ks(
kTr) /
tt;
229 if (!Kokkos::isfinite(
om) || Kokkos::fabs(
om) < 1e-300) {
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;
243 "peclet::flow::skr_p",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
245 if (ks(
kStop) != 0.0)
247 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
250 pf(i) = rf(i) + beta * pf(i);
253 ps(i) =
rs(i) + beta *
ps(i);
259 const Kokkos::View<double*, CCMem>& ks,
C3 e,
int g) {
261 const bool two = r.solid;
263 "peclet::flow::skr_s",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
265 if (ks(
kStop) != 0.0)
267 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
268 const double na = -ks(
kAlpha);
276 const Kokkos::View<double*, CCMem>& ks,
C3 e,
int g) {
278 const bool two = x.solid;
280 "peclet::flow::skr_x_half",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
282 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
283 const double a = ks(
kAlpha);
293 const Kokkos::View<double*, CCMem>& ks,
C3 e,
int g) {
296 const bool two = x.solid;
298 "peclet::flow::skr_x_r",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
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) {
310 xf(i) +=
om *
z2f(i);
311 rf(i) +=
nom *
tf(i);
314 xs(i) +=
om *
z2s(i);
315 rs(i) +=
nom * ts(i);
334 const ScalarVec&
z2,
int maxit,
double rtol,
bool singular) {
339 const double ax0 =
ops.maxabs(t);
340 const double bmax =
ops.maxabs(b);
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;
354 const double r0n =
ops.maxabs(r);
355 if (!std::isfinite(
r0n))
356 throw std::runtime_error(
"scalar cut-cell solve: non-finite residual");
359 double rho = 1.0, alpha = 1.0,
omega = 1.0;
364 for (; it < maxit; ++it) {
366 if (!std::isfinite(
rhoNew) || std::fabs(
rhoNew) < 1e-300)
368 const double beta = (
rhoNew / rho) * (alpha /
omega);
377 if (!std::isfinite(
rhv) || std::fabs(
rhv) < 1e-300)
383 double rn =
ops.maxabs(r);
384 if (!std::isfinite(
rn))
385 throw std::runtime_error(
"scalar cut-cell solve: non-finite residual");
395 double tr = 0.0,
tt = 0.0;
396 ops.dot2(t, r, t, tr,
tt);
397 if (!std::isfinite(
tt) ||
tt < 1e-300) {
403 if (!std::isfinite(
omega) || std::fabs(
omega) < 1e-300) {
414 if (!std::isfinite(
rn))
415 throw std::runtime_error(
"scalar cut-cell solve: non-finite residual");
436 const double r0n =
ops.maxabs(r);
437 if (!std::isfinite(
r0n))
438 throw std::runtime_error(
"scalar cut-cell solve: non-finite residual");
445 const auto& ks =
ops.ks;
450 Kokkos::deep_copy(
ops.pk,
453 return x.solid ? std::fmax(
ops.pk(1),
ops.pk(2)) :
ops.pk(1);
455 for (; it < maxit; ++it) {
468 if (!std::isfinite(
rn))
469 throw std::runtime_error(
"scalar cut-cell solve: non-finite residual");
485 if (!std::isfinite(
rn))
486 throw std::runtime_error(
"scalar cut-cell solve: non-finite residual");
506 const double rn =
ops.maxabs(r);
507 if (!std::isfinite(
rn))
508 throw std::runtime_error(
"scalar cut-cell solve: non-finite residual");
511 const bool resident =
ops.resident && !singular;
515 if (res.trueRes > 10.0 *
thresh && it < maxit) {
516 res.restarted =
true;
522 res.converged = res.trueRes <= 10.0 *
thresh;
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).
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.
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
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