include/pops/numerics/time/integrators/implicit_stepper.hpp Source FileΒΆ

adc_cpp: include/pops/numerics/time/integrators/implicit_stepper.hpp Source File
adc_cpp 0.3.0
Model-free C++23 core for coupled hyperbolic-elliptic systems on adaptive (AMR) meshes, with MPI and GPU (Kokkos) backends
implicit_stepper.hpp
Go to the documentation of this file.
1#pragma once
2
7#include <pops/numerics/spatial_operator.hpp> // load_state, load_aux
8
9#include <algorithm> // std::max (Newton report aggregation, host)
10#include <concepts>
11#include <cstdio> // std::snprintf / fprintf (fail_policy: host message, never in kernel)
12#include <stdexcept> // std::runtime_error (fail_policy = throw, host after reductions)
13#include <string> // std::string (validate_newton_options message prefix)
14
39
40namespace pops {
41
42// Contract of an implicit/IMEX block stepper. Any object (or lambda) that knows how to
43// advance a block over dt by reading the coupler (for up-to-date aux / phi) and the model.
44template <class Stepper, class Coupler, class Block>
46 requires(const Stepper st, Coupler& c, Block& b, Real dt, int s, int n) { st(c, b, dt, s, n); };
47
48// OPTIONAL trait: a model can declare which conserved variables are treated
49// implicitly (the stiff ones). is_implicit(c) -> bool. A model WITHOUT this trait is treated
50// fully implicitly (historical default).
51template <class M>
52concept PartiallyImplicitModel = requires(int c) {
53 { M::is_implicit(c) } -> std::convertible_to<bool>;
54};
55
56// Is component c of the model implicit? Default (no trait): all of them are.
57template <class Model>
58POPS_HD inline bool model_is_implicit(int c) {
60 return Model::is_implicit(c);
61 else
62 return true;
63}
64
65// OPTIONAL trait: ANALYTIC JACOBIAN of the source (review wave 3, JacobianPolicy). When the
66// model (or its source brick, forwarded by CompositeModel) declares
67// source_jacobian(U, aux, J) with J[r][c] = dS_r/dU_c (FULL n_vars x n_vars matrix),
68// the implicit-source Newton uses it INSTEAD of finite differences: exactness
69// (no more fd_eps noise) and n_impl source evaluations saved per iteration. A model
70// WITHOUT the trait keeps the historical finite differences, bit-identical. POPS_HD required.
71template <class M>
73 requires(const M m, const typename M::State u, const Aux a, Real (&J)[M::n_vars][M::n_vars]) {
74 m.source_jacobian(u, a, J);
75 };
76
77// Implicit mask CARRIED BY THE BLOCK / time policy (and NOT by the model): device-clean POD carrier
78// (fixed array N, passed BY VALUE into the kernel, no host pointer dereference on device).
79// When active (active == true), it OVERRIDES the model default (model_is_implicit): only the
80// components with flag[c] == true are advanced implicitly, the others explicitly (forward Euler). This is what
81// lets you REUSE the SAME model with different implicit treatments depending on the block. Inactive (default:
82// active == false) -> falls back to model_is_implicit -> behavior bit-identical to the historical one.
83template <int N>
85 bool active = false;
86 bool flag[N] = {};
87};
88
89// Is component c implicit, with the block mask TAKING PRIORITY over the model default? The inactive mask
90// (default) delegates to model_is_implicit<Model> -> strictly identical to before this change.
91template <class Model, int N>
92POPS_HD inline bool is_implicit_component(const ImplicitMask<N>& mask, int c) {
93 if (mask.active)
94 return mask.flag[c];
95 return model_is_implicit<Model>(c);
96}
97
115 static constexpr int kFailNone = 0;
116 static constexpr int kFailWarn = 1;
117 static constexpr int kFailThrow = 2;
118 int max_iters = 2;
121 Real fd_eps = Real(1e-7);
124};
125
132inline void validate_newton_options(const NewtonOptions& newton, const char* where) {
133 const std::string ctx = std::string(where) + " : ";
134 if (newton.max_iters < 1)
135 throw std::runtime_error(ctx + "newton_max_iters >= 1");
136 if (newton.rel_tol < 0.0 || newton.abs_tol < 0.0 || newton.fd_eps <= 0.0)
137 throw std::runtime_error(ctx + "newton_rel_tol/abs_tol >= 0 and newton_fd_eps > 0");
138 if (!(newton.damping > 0.0 && newton.damping <= 1.0))
139 throw std::runtime_error(ctx + "newton_damping in (0, 1]");
143 throw std::runtime_error(ctx +
144 "newton_fail_policy invalid "
145 "(NewtonOptions::kFailNone|kFailWarn|kFailThrow)");
146}
147
158
166 bool enabled = false;
167 bool converged = true;
170 double n_failed =
171 0;
172 double failed_i = -1, failed_j = -1, failed_comp = -1;
173 void reset() { *this = NewtonReport{}; }
174};
175
178POPS_HD inline bool newton_finite(Real x) {
179 return x == x && x < Real(1e300) && x > Real(-1e300);
180}
181
182namespace detail {
183// Dense solve J x = b on the leading n x n block (n <= N), partial pivoting. J and b
184// destroyed. N is constexpr (= Model::n_vars) -> fixed array, no allocation,
185// device-callable; n (<= N) is the number of implicit variables (partial IMEX).
186// @return true if all pivots are finite and non-zero; false otherwise (degenerate pivot: the
187// numerics stays STRICTLY the original one -- the division produces inf/NaN as before, only the
188// flag is new; historical callers ignore the return, bit-identical).
189template <int N>
190POPS_HD inline bool solve_dense(Real J[N][N], Real b[N], Real x[N], int n) {
191 bool ok = true;
192 for (int p = 0; p < n; ++p) {
193 int piv = p;
194 Real best = J[p][p] < 0 ? -J[p][p] : J[p][p];
195 for (int r = p + 1; r < n; ++r) {
196 const Real v = J[r][p] < 0 ? -J[r][p] : J[r][p];
197 if (v > best) {
198 best = v;
199 piv = r;
200 }
201 }
202 if (piv != p) {
203 for (int c = 0; c < n; ++c) {
204 const Real t = J[p][c];
205 J[p][c] = J[piv][c];
206 J[piv][c] = t;
207 }
208 const Real t = b[p];
209 b[p] = b[piv];
210 b[piv] = t;
211 }
212 const Real d = J[p][p];
213 if (d == Real(0) || !newton_finite(d))
214 ok = false;
215 for (int r = 0; r < n; ++r) {
216 if (r == p)
217 continue;
218 const Real f = J[r][p] / d;
219 for (int c = p; c < n; ++c)
220 J[r][c] -= f * J[p][c];
221 b[r] -= f * b[p];
222 }
223 }
224 for (int p = 0; p < n; ++p)
225 x[p] = b[p] / J[p][p];
226 return ok;
227}
228
229// Assemble the Newton Jacobian of the subsystem reduced to the implicit components:
230// J[rr][cc] = (row == col ? 1: 0) - dt * dS_row/dW_col, row = impl[rr], col = impl[cc].
231// HasSourceJacobian trait present => ANALYTIC Jacobian (m.source_jacobian); otherwise finite
232// differences (step h = fd_eps*|W| + fd_eps, source perturbed column by column around S0 = S(W)).
233// Body EXTRACTED word-for-word from the two paths (2a defaults, 2b instrumented) -> bit-identical;
234// POPS_HD because called from newton_source_solve (device-callable). S0 = m.source(W, a) already computed
235// by the caller (reused as is by the finite differences).
236template <class Model, int N>
237POPS_HD inline void assemble_newton_jacobian(const Model& m, const typename Model::State& W,
238 const Aux& a, Real dt, const NewtonOptions& opts,
239 const int impl[N], int m_impl,
240 const typename Model::State& S0, Real J[N][N]) {
241 if constexpr (HasSourceJacobian<Model>) {
242 // ANALYTIC JACOBIAN (trait, wave 3): J = I - dt * dS/dU restricted to the implicit ones.
243 Real dS[N][N];
244 m.source_jacobian(W, a, dS);
245 for (int cc = 0; cc < m_impl; ++cc)
246 for (int rr = 0; rr < m_impl; ++rr) {
247 const int row = impl[rr], col = impl[cc];
248 J[rr][cc] = (row == col ? Real(1) : Real(0)) - dt * dS[row][col];
249 }
250 } else {
251 for (int cc = 0; cc < m_impl; ++cc) {
252 const int col = impl[cc];
253 const Real wc = W[col] < 0 ? -W[col] : W[col];
254 const Real h = opts.fd_eps * wc + opts.fd_eps;
255 typename Model::State Wp = W;
256 Wp[col] += h;
257 const typename Model::State Sp = m.source(Wp, a);
258 for (int rr = 0; rr < m_impl; ++rr) {
259 const int row = impl[rr];
260 const Real dSdW = (Sp[row] - S0[row]) / h;
261 J[rr][cc] = (row == col ? Real(1) : Real(0)) - dt * dSdW;
262 }
263 }
264 }
265}
266
267// Solve W such that W = Un + dt*S(W,a) in forward-backward Euler (partial IMEX):
268// - EXPLICIT components: forward Euler at the input state, W_e = Un_e + dt*S_e(Un);
269// - IMPLICIT components: Newton on the reduced subsystem, F_i = W_i - Un_i -
270// dt*S_i(W), Jacobian I - dt*(dS/dW) restricted to the implicit ones (columns by
271// finite differences), the explicit ones frozen at their advanced value (known data).
272// WHO is implicit: a mask CARRIED BY THE BLOCK (@p mask) taking priority over the model default
273// (is_implicit_component). Inactive mask (default) + model without is_implicit trait: all
274// components are implicit -> full backward-Euler, strictly identical to the original behavior.
275template <class Model>
276POPS_HD inline typename Model::State newton_source_solve(
277 const Model& m, const typename Model::State& Un, const Aux& a, Real dt,
278 const NewtonOptions& opts, const ImplicitMask<Model::n_vars>& mask = {},
279 NewtonCellStat* stat = nullptr) {
280 constexpr int N = Model::n_vars;
281 int impl[N]; // indices of the implicit components (the first m_impl useful slots)
282 int m_impl = 0;
283 for (int c = 0; c < N; ++c)
284 if (is_implicit_component<Model>(mask, c))
285 impl[m_impl++] = c;
286
287 typename Model::State W = Un;
288 // (1) explicit: forward Euler on the non-implicit components (source at the input).
289 if (m_impl < N) {
290 const typename Model::State S_in = m.source(Un, a);
291 for (int c = 0; c < N; ++c)
292 if (!is_implicit_component<Model>(mask, c))
293 W[c] = Un[c] + dt * S_in[c];
294 }
295 const bool tol_active = opts.rel_tol > Real(0) || opts.abs_tol > Real(0);
296 if (!tol_active && stat == nullptr) {
297 // (2a) HISTORICAL PATH (default): FIXED iterations, no test, no extra residual evaluation
298 // -> BIT-IDENTICAL to the historical one for the defaults (max_iters=2, fd_eps=1e-7).
299 for (int it = 0; it < opts.max_iters; ++it) {
300 const typename Model::State S0 = m.source(W, a);
301 Real F[N];
302 for (int r = 0; r < m_impl; ++r) {
303 const int c = impl[r];
304 F[r] = W[c] - Un[c] - dt * S0[c];
305 }
306 Real J[N][N];
307 assemble_newton_jacobian<Model, N>(m, W, a, dt, opts, impl, m_impl, S0, J);
308 Real delta[N];
309 solve_dense<N>(J, F, delta, m_impl);
310 for (int r = 0; r < m_impl; ++r)
311 W[impl[r]] -= opts.damping * delta[r];
312 }
313 return W;
314 }
315 // (2b) INSTRUMENTED PATH (active tolerances and/or stat requested): same Newton, plus the stop
316 // criterion ||F||_inf <= abs_tol + rel_tol*||F0||_inf at the start of an iteration, the detection of
317 // non-finite residual / degenerate pivot, and the exit statistic. One ADDITIONAL source evaluation
318 // may happen at exit (honest residual after the last update).
319 //
320 // PURE-OBSERVER INVARIANT (adversarial review): tolerances INACTIVE + stat requested (the
321 // newton_diagnostics mode) -> the STATE W is STRICTLY identical to path (2a), INCLUDING on a
322 // degenerate cell: the detection (non-finite residual, degenerate pivot) MARKS failed for the
323 // report but does NOT change the control flow (no break, update applied as in
324 // (2a), identical inf/NaN propagation). A diagnostic that would alter the trajectory would not be
325 // representative of the real run. The ONLY early exit is CONVERGENCE under tolerance
326 // (tol_active), explicitly non-historical opt-in behavior.
327 Real res = Real(0), res0 = Real(0);
328 int used = 0;
329 int worst_comp = -1; // conserved component carrying the max residual at exit (diagnostic)
330 bool failed = false;
331 bool converged = (m_impl == 0); // nothing implicit: trivially converged
332 for (int it = 0; it < opts.max_iters; ++it) {
333 const typename Model::State S0 = m.source(W, a);
334 Real F[N];
335 res = Real(0);
336 for (int r = 0; r < m_impl; ++r) {
337 const int c = impl[r];
338 F[r] = W[c] - Un[c] - dt * S0[c];
339 const Real av = F[r] < 0 ? -F[r] : F[r];
340 if (av > res) {
341 res = av;
342 worst_comp = c;
343 }
344 }
345 if (!newton_finite(res))
346 failed = true; // marks WITHOUT break: trajectory (2a) preserved
347 if (tol_active) {
348 if (it == 0)
349 res0 = res;
350 if (res <= opts.abs_tol + opts.rel_tol * res0) {
351 converged = true;
352 break;
353 }
354 }
355 Real J[N][N];
356 assemble_newton_jacobian<Model, N>(m, W, a, dt, opts, impl, m_impl, S0, J);
357 Real delta[N];
358 const bool ok = solve_dense<N>(J, F, delta, m_impl);
359 if (!ok)
360 failed = true; // degenerate pivot: marks WITHOUT break, inf/NaN division as in (2a)
361 for (int r = 0; r < m_impl; ++r)
362 W[impl[r]] -= opts.damping * delta[r];
363 used = it + 1;
364 }
365 // Exit by budget exhaustion: recompute the residual AFTER the last update (honest
366 // report; the loop residual precedes the update). One extra source evaluation,
367 // only on this instrumented path.
368 if (!failed && used == opts.max_iters && m_impl > 0) {
369 const typename Model::State S0 = m.source(W, a);
370 res = Real(0);
371 for (int r = 0; r < m_impl; ++r) {
372 const int c = impl[r];
373 const Real fr = W[c] - Un[c] - dt * S0[c];
374 const Real av = fr < 0 ? -fr : fr;
375 if (av > res) {
376 res = av;
377 worst_comp = c;
378 }
379 }
380 if (!newton_finite(res))
381 failed = true;
382 else if (tol_active)
383 converged = res <= opts.abs_tol + opts.rel_tol * res0;
384 }
385 if (stat) {
386 stat->res = res;
387 stat->iters = Real(used);
388 stat->failed = (failed || (tol_active && !converged)) ? Real(1) : Real(0);
389 stat->comp = Real(worst_comp);
390 }
391 return W;
392}
393
396template <class Model>
397POPS_HD inline typename Model::State newton_source_solve(
398 const Model& m, const typename Model::State& Un, const Aux& a, Real dt, int iters,
399 const ImplicitMask<Model::n_vars>& mask = {}) {
400 NewtonOptions opts;
401 opts.max_iters = iters;
402 return newton_source_solve(m, Un, a, dt, opts, mask, nullptr);
403}
404} // namespace detail
405
406namespace detail {
407// Device kernel of the implicit step on the source (local Newton in place). NAMED FUNCTOR (and not an
408// extended lambda): ROBUST device emission when the Model-template kernel is instantiated from an EXTERNAL TU
409// (IMEX path of an add_compiled_model block, via the advance std::function of block_builder). Body
410// identical to the old lambda -> bit-identical result on CPU.
411template <class Model>
413 Model m;
417 NewtonOptions opts; // Newton options (POD, by value); defaults = historical bit-identical
418 ImplicitMask<Model::n_vars> mask; // block mask (POD, by value); inactive = model default
419 POPS_HD void operator()(int i, int j) const {
420 const typename Model::State Un = load_state<Model>(uc, i, j);
421 const Aux a = load_aux<aux_comps<Model>()>(ax, i, j);
422 const typename Model::State W = newton_source_solve<Model>(m, Un, a, dt, opts, mask, nullptr);
423 for (int c = 0; c < Model::n_vars; ++c)
424 u(i, j, c) = W[c];
425 }
426};
427
428// INSTRUMENTED variant: same Newton, but writes the exit statistic of EACH cell into
429// the scratch st (comp 0 = ||F||_inf, 1 = iterations, 2 = failure 0/1, 3 = ENCODED OFFENDING CELL).
430// Encoding comp 3: -1 if the cell did not fail; otherwise (j*2^20 + i)*16 + (offending_comp + 1) --
431// exact integer in double up to ~2^44 (i, j < 2^20), so that a MAX reduction yields ONE offending
432// cell (the largest in index) decodable host side without a dedicated arg-max reduction. NAMED
433// FUNCTOR (same cross-TU device contract as BackwardEulerSourceKernel). Used only when a
434// report or a fail_policy is requested.
435template <class Model>
437 Model m;
443 POPS_HD void operator()(int i, int j) const {
444 const typename Model::State Un = load_state<Model>(uc, i, j);
445 const Aux a = load_aux<aux_comps<Model>()>(ax, i, j);
446 NewtonCellStat s{};
447 const typename Model::State W = newton_source_solve<Model>(m, Un, a, dt, opts, mask, &s);
448 for (int c = 0; c < Model::n_vars; ++c)
449 u(i, j, c) = W[c];
450 st(i, j, 0) = s.res;
451 st(i, j, 1) = s.iters;
452 st(i, j, 2) = s.failed;
453 st(i, j, 3) = s.failed > Real(0)
454 ? (Real(j) * Real(1048576) + Real(i)) * Real(16) + (s.comp + Real(1))
455 : Real(-1);
456 }
457};
458
463 int comp;
464 POPS_HD void operator()(int i, int j, Real& acc) const {
465 const Real v = st(i, j, comp);
466 if (v > acc)
467 acc = v;
468 }
469};
472 int comp;
473 POPS_HD void operator()(int i, int j, Real& acc) const { acc += st(i, j, comp); }
474};
475} // namespace detail
476
477// W = U + dt * model.source(W, aux), solved IN PLACE by local Newton (finite-difference
478// Jacobian), driven by a NewtonOptions policy (tolerances / fd_eps / iteration budget).
479// @p mask: implicit mask CARRIED BY THE BLOCK (override of the model default); inactive (default) ->
480// bit-identical behavior. @p report: OPT-IN diagnostics -- if non-null, the Newton goes
481// through the instrumented path (per-cell statistic in a scratch, max/sum reductions +
482// MPI all_reduce) and AGGREGATES into *report (max residual, max iterations, number of failures; reset() is
483// the caller's responsibility at the start of the advance). report == nullptr AND tolerances inactive -> historical
484// path strictly bit-identical, zero allocation, zero extra evaluation.
485template <class Model>
486void backward_euler_source(const Model& model, const MultiFab& aux, MultiFab& U, Real dt,
487 const NewtonOptions& opts, const ImplicitMask<Model::n_vars>& mask = {},
488 NewtonReport* report = nullptr) {
489 // FAST path (historical): neither a report requested nor an active fail_policy -> no scratch, no
490 // reduction, historical kernel bit-identical. A fail_policy != kFailNone REQUIRES the detection,
491 // hence the instrumented path (which remains a PURE OBSERVER of W).
492 if (report == nullptr && opts.fail_policy == NewtonOptions::kFailNone) {
493 for (int li = 0; li < U.local_size(); ++li) {
494 Array4 u = U.fab(li).array();
495 const ConstArray4 uc = U.fab(li).const_array();
496 const ConstArray4 ax = aux.fab(li).const_array();
497 const Box2D b = U.box(li);
498 for_each_cell(b, detail::BackwardEulerSourceKernel<Model>{model, uc, ax, u, dt, opts, mask});
499 }
500 return;
501 }
502 // DIAGNOSTICS path: per-cell scratch (res, iters, failed, encoded cell) then reductions.
503 // The scratch allocation is local to the call (opt-in diagnostics/fail_policy).
504 MultiFab stats(U.box_array(), U.dmap(), 4, 0);
505 for (int li = 0; li < U.local_size(); ++li) {
506 Array4 u = U.fab(li).array();
507 Array4 st = stats.fab(li).array();
508 const ConstArray4 uc = U.fab(li).const_array();
509 const ConstArray4 ax = aux.fab(li).const_array();
510 const Box2D b = U.box(li);
512 b, detail::BackwardEulerSourceStatKernel<Model>{model, uc, ax, u, st, dt, opts, mask});
513 }
514 Real rmax = Real(0), imax = Real(0), nfail = Real(0), enc = Real(-1);
515 for (int li = 0; li < stats.local_size(); ++li) {
516 const ConstArray4 st = stats.fab(li).const_array();
517 const Box2D b = stats.box(li);
518 rmax = std::max(rmax, reduce_max_cell(b, detail::NewtonStatMaxKernel{st, 0}));
519 imax = std::max(imax, reduce_max_cell(b, detail::NewtonStatMaxKernel{st, 1}));
520 nfail += reduce_sum_cell(b, detail::NewtonStatSumKernel{st, 2});
521 enc = std::max(enc, reduce_max_cell(b, detail::NewtonStatMaxKernel{st, 3}));
522 }
523 rmax = static_cast<Real>(all_reduce_max(static_cast<double>(rmax)));
524 imax = static_cast<Real>(all_reduce_max(static_cast<double>(imax)));
525 const double nfail_g = all_reduce_sum(static_cast<double>(nfail));
526 const double enc_g = all_reduce_max(static_cast<double>(enc));
527 double fi = -1, fj = -1, fc = -1;
528 if (nfail_g > 0 && enc_g >= 0) { // decode the offending cell with maximal index (cf. StatKernel)
529 const long long k = static_cast<long long>(enc_g);
530 fc = static_cast<double>(k % 16) - 1.0; // -1 = unknown component (nothing implicit)
531 const long long cell = k / 16;
532 fi = static_cast<double>(cell % 1048576);
533 fj = static_cast<double>(cell / 1048576);
534 }
535 if (report) {
536 report->enabled = true;
537 report->max_residual = std::max(report->max_residual, rmax);
538 report->max_iters_used = std::max(report->max_iters_used, imax);
539 report->n_failed += nfail_g;
540 if (nfail_g > 0) {
541 report->failed_i = fi;
542 report->failed_j = fj;
543 report->failed_comp = fc;
544 }
545 if (nfail_g > 0 || !newton_finite(rmax))
546 report->converged = false;
547 }
548 // FAIL_POLICY (host, after reductions -- the device kernels never throw): reaction to the
549 // failed cells. kFailWarn: one stderr warning (rank 0). kFailThrow: hard error with
550 // the offending cell (the step is ABANDONED as is; up to the caller to decide what to do with it).
551 if (nfail_g > 0 && opts.fail_policy != NewtonOptions::kFailNone) {
552 char msg[256];
553 std::snprintf(msg, sizeof(msg),
554 "Implicit source Newton: %.0f cell(s) failed (max residual %.3e; cell "
555 "(%g, %g), component %g)",
556 nfail_g, static_cast<double>(rmax), fi, fj, fc);
558 throw std::runtime_error(msg);
559 if (my_rank() == 0)
560 std::fprintf(stderr, "[pops] WARNING %s\n", msg);
561 }
562}
563
567template <class Model>
568void backward_euler_source(const Model& model, const MultiFab& aux, MultiFab& U, Real dt,
569 int iters = 2, const ImplicitMask<Model::n_vars>& mask = {}) {
570 NewtonOptions opts;
571 opts.max_iters = iters;
572 backward_euler_source(model, aux, U, dt, opts, mask, nullptr);
573}
574
575// Default implicit stepper: backward-Euler (Newton) on the model source.
576// Models ImplicitBlockStepper; passed as is to SystemCoupler::step as the implicit
577// advance callback. The user writes no solver.
579 int iters = 2;
580
581 template <class Coupler, class Block>
582 void operator()(Coupler& coupler, Block& block, Real dt, int /*substep*/, int /*nsub*/) const {
583 backward_euler_source(block.model, coupler.aux(), block.U(), dt, iters);
584 }
585};
586
587} // namespace pops
Single-block hyperbolic-elliptic coupler.
Definition coupler.hpp:74
const MultiFab & aux() const
Definition coupler.hpp:172
ConstArray4 const_array() const
READ handle (POD device-copyable) over this Fab. Valid as long as the Fab lives.
Definition fab2d.hpp:96
Array4 array()
WRITE handle (POD device-copyable) over this Fab. Valid as long as the Fab lives.
Definition fab2d.hpp:91
Field distributed over a level: decomposition (BoxArray) + distribution (DistributionMapping) + ncomp...
Definition multifab.hpp:33
Fab2D & fab(int li)
Local fab at index li (0 <= li < local_size()), for writing.
Definition multifab.hpp:67
const DistributionMapping & dmap() const
GLOBAL distribution (owner rank per box).
Definition multifab.hpp:58
const BoxArray & box_array() const
GLOBAL decomposition of the level (all boxes, all ranks).
Definition multifab.hpp:56
const Box2D & box(int li) const
VALID box of local fab li.
Definition multifab.hpp:71
int local_size() const
Number of fabs OWNED by this rank (bound on local indices).
Definition multifab.hpp:65
Definition implicit_stepper.hpp:72
Definition implicit_stepper.hpp:45
Definition implicit_stepper.hpp:52
for_each_cell and reductions: the parallelism SEAM over the cells of a Box2D; sync_host / sync_device...
MultiFab: a field DISTRIBUTED over a level (equivalent of AMReX's MultiFab).
POPS_HD void assemble_newton_jacobian(const Model &m, const typename Model::State &W, const Aux &a, Real dt, const NewtonOptions &opts, const int impl[N], int m_impl, const typename Model::State &S0, Real J[N][N])
Definition implicit_stepper.hpp:237
POPS_HD bool solve_dense(Real J[N][N], Real b[N], Real x[N], int n)
Definition implicit_stepper.hpp:190
POPS_HD Model::State newton_source_solve(const Model &m, const typename Model::State &Un, const Aux &a, Real dt, const NewtonOptions &opts, const ImplicitMask< Model::n_vars > &mask={}, NewtonCellStat *stat=nullptr)
Definition implicit_stepper.hpp:276
Definition amr_hierarchy.hpp:29
POPS_HD bool model_is_implicit(int c)
Definition implicit_stepper.hpp:58
void for_each_cell(const Box2D &b, F f)
Applies f to EACH cell (i, j) of box b (bounds inclusive), via Kokkos::parallel_for (Serial / OpenMP ...
Definition for_each.hpp:138
double Real
Definition types.hpp:30
void backward_euler_source(const Model &model, const MultiFab &aux, MultiFab &U, Real dt, const NewtonOptions &opts, const ImplicitMask< Model::n_vars > &mask={}, NewtonReport *report=nullptr)
Definition implicit_stepper.hpp:486
double all_reduce_sum(double x)
Definition comm.hpp:143
void validate_newton_options(const NewtonOptions &newton, const char *where)
Range-validate a NewtonOptions POD; shared by System::add_block and AmrSystem::add_block,...
Definition implicit_stepper.hpp:132
POPS_HD bool newton_finite(Real x)
Finite? (device-safe, without <cmath>: NaN fails x == x; +-inf fails the bounds).
Definition implicit_stepper.hpp:178
Real reduce_sum_cell(const Box2D &b, F f)
SUM reduction with a REDUCING FUNCTOR: f receives (i, j, Real& acc) and accumulates,...
Definition for_each.hpp:274
Real reduce_max_cell(const Box2D &b, F f)
MAX reduction with a REDUCING FUNCTOR: f receives (i, j, Real& acc) and updates acc,...
Definition for_each.hpp:240
int my_rank()
Definition comm.hpp:136
double all_reduce_max(double x)
Definition comm.hpp:146
POPS_HD bool is_implicit_component(const ImplicitMask< N > &mask, int c)
Definition implicit_stepper.hpp:92
Cartesian spatial operator: assembles R(U, aux) = -div F + S over the cells of a level.
Pointwise types of the physics layer: StateVec<N> (conserved state) and Aux (auxiliary fields from th...
WRITE POD handle (raw pointer + strides) over a Fab2D buffer, indexed by (i, j, c) IN GLOBAL INDICES ...
Definition fab2d.hpp:29
POINTWISE auxiliary fields shared with the physics: single coupling channel.
Definition state.hpp:123
READ-only handle (const counterpart of Array4): same layout and same contract (POD device-copyable,...
Definition fab2d.hpp:44
Definition implicit_stepper.hpp:84
bool active
Definition implicit_stepper.hpp:85
bool flag[N]
Definition implicit_stepper.hpp:86
Definition implicit_stepper.hpp:578
void operator()(Coupler &coupler, Block &block, Real dt, int, int) const
Definition implicit_stepper.hpp:582
int iters
Definition implicit_stepper.hpp:579
OUTPUT statistic of the Newton of ONE cell (device POD, written into the diagnostics scratch): res = ...
Definition implicit_stepper.hpp:152
Real failed
Definition implicit_stepper.hpp:155
Real res
Definition implicit_stepper.hpp:153
Real iters
Definition implicit_stepper.hpp:154
Real comp
Definition implicit_stepper.hpp:156
Options of the local Newton of the implicit source (backward-Euler).
Definition implicit_stepper.hpp:114
Real fd_eps
Definition implicit_stepper.hpp:121
Real damping
Definition implicit_stepper.hpp:122
Real rel_tol
Definition implicit_stepper.hpp:119
static constexpr int kFailNone
Definition implicit_stepper.hpp:115
int fail_policy
Definition implicit_stepper.hpp:123
Real abs_tol
Definition implicit_stepper.hpp:120
int max_iters
Definition implicit_stepper.hpp:118
static constexpr int kFailThrow
Definition implicit_stepper.hpp:117
static constexpr int kFailWarn
Definition implicit_stepper.hpp:116
AGGREGATED report (whole block, all substeps of one advance) of the implicit-source Newton.
Definition implicit_stepper.hpp:165
double failed_i
Definition implicit_stepper.hpp:172
double failed_comp
one offending cell (-1 if none)
Definition implicit_stepper.hpp:172
double failed_j
Definition implicit_stepper.hpp:172
Real max_iters_used
max over cells/substeps of iterations consumed
Definition implicit_stepper.hpp:169
Real max_residual
max over cells/substeps of ||F||_inf at exit
Definition implicit_stepper.hpp:168
bool converged
no failed cell over the advance
Definition implicit_stepper.hpp:167
double n_failed
number of (cells x substeps) failed (non-finite / pivot / non-convergence)
Definition implicit_stepper.hpp:170
void reset()
Definition implicit_stepper.hpp:173
bool enabled
a report was computed (at least one instrumented substep)
Definition implicit_stepper.hpp:166
Definition implicit_stepper.hpp:412
ConstArray4 uc
Definition implicit_stepper.hpp:414
POPS_HD void operator()(int i, int j) const
Definition implicit_stepper.hpp:419
ImplicitMask< Model::n_vars > mask
Definition implicit_stepper.hpp:418
Real dt
Definition implicit_stepper.hpp:416
ConstArray4 ax
Definition implicit_stepper.hpp:414
NewtonOptions opts
Definition implicit_stepper.hpp:417
Model m
Definition implicit_stepper.hpp:413
Array4 u
Definition implicit_stepper.hpp:415
Definition implicit_stepper.hpp:436
ImplicitMask< Model::n_vars > mask
Definition implicit_stepper.hpp:442
POPS_HD void operator()(int i, int j) const
Definition implicit_stepper.hpp:443
Array4 u
Definition implicit_stepper.hpp:439
Model m
Definition implicit_stepper.hpp:437
ConstArray4 uc
Definition implicit_stepper.hpp:438
Array4 st
Definition implicit_stepper.hpp:439
ConstArray4 ax
Definition implicit_stepper.hpp:438
Real dt
Definition implicit_stepper.hpp:440
NewtonOptions opts
Definition implicit_stepper.hpp:441
REDUCTION kernels of the diagnostics scratch (max / sum of one component).
Definition implicit_stepper.hpp:461
int comp
Definition implicit_stepper.hpp:463
ConstArray4 st
Definition implicit_stepper.hpp:462
POPS_HD void operator()(int i, int j, Real &acc) const
Definition implicit_stepper.hpp:464
Definition implicit_stepper.hpp:470
ConstArray4 st
Definition implicit_stepper.hpp:471
int comp
Definition implicit_stepper.hpp:472
POPS_HD void operator()(int i, int j, Real &acc) const
Definition implicit_stepper.hpp:473
Base scalar types and the POPS_HD macro (host+device portability).
#define POPS_HD
Definition types.hpp:25