include/pops/numerics/elliptic/interface/elliptic_interface.hpp Source FileΒΆ

adc_cpp: include/pops/numerics/elliptic/interface/elliptic_interface.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
elliptic_interface.hpp
Go to the documentation of this file.
1#pragma once
2
23
28#include <pops/numerics/elliptic/interface/elliptic_problem.hpp> // FieldPostProcess (spec), field_postprocess
29#include <pops/numerics/elliptic/interface/elliptic_solver.hpp> // concept EllipticSolver (already in place)
30
31#include <concepts>
32
33namespace pops {
34
35// ---------------------------------------------------------------------------
36// (1) EllipticOperator: OPERATOR role of the elliptic stencil at the MultiFab level.
37//
38// Contract = what a matrix-free matvec consumer (TensorKrylovSolver) reads from
39// its operator to apply L_int(phi) = div(A grad phi) - kappa phi in a way
40// CONSISTENT with the MG residual (poisson_residual): the geometry, the physical BC, the
41// BoxArray/DistributionMapping of the fine level, and the operator coefficient POINTERS
42// (eps_x, eps_y, kappa, cut-cell weights, cross terms Axy/Ayx, mask).
43// An inactive term returns nullptr (cf. internal op_*_ptr), which the concept only requires
44// to be CALLABLE and convertible to const MultiFab*; it does not constrain the value.
45//
46// GeometricMG models this role (and it is the only TYPE that carries it today). The
47// free functions apply_laplacian/poisson_residual/gs_smooth are its lowest-level
48// realization (the APPLICATION proper): not constrainable by a concept
49// because they are not types. EllipticOperator thus NAMES the interface that SUPPLIES
50// the coefficients (the "what to apply"), not the application kernel (the "how").
51//
52// NOTE: EllipticOperator does NOT REQUIRE solve()/rhs()/phi(); it describes the operator
53// role only. GeometricMG completes it with EllipticSolver (it is also a solver), but
54// a purely operator type (without solve) would already satisfy EllipticOperator.
55template <class Op>
56concept EllipticOperator = requires(Op op) {
57 { op.geom() } -> std::convertible_to<const Geometry&>;
58 { op.bc() } -> std::convertible_to<const BCRec&>;
59 // Fine-level coefficient pointers: nullptr when the term is inactive.
60 { op.op_mask() } -> std::convertible_to<const MultiFab*>;
61 { op.op_coef() } -> std::convertible_to<const MultiFab*>;
62 { op.op_eps() } -> std::convertible_to<const MultiFab*>;
63 { op.op_kappa() } -> std::convertible_to<const MultiFab*>;
64 { op.op_eps_y() } -> std::convertible_to<const MultiFab*>;
65 { op.op_a_xy() } -> std::convertible_to<const MultiFab*>;
66 { op.op_a_yx() } -> std::convertible_to<const MultiFab*>;
67};
68
69// ---------------------------------------------------------------------------
70// (2) LinearSolver: ITERATIVE solver with an explicit stopping criterion.
71//
72// Contract = solve(rel_tol, max_iters) that returns a RESULT (the convention "solve
73// up to a relative tolerance in at most max_iters steps, returning a report").
74// The return type is NOT constrained to a precise struct: GeometricMG
75// returns int (number of V-cycles performed) and TensorKrylovSolver returns KrylovResult
76// (iters + relative residual + convergence). The only REAL common invariant is: the return
77// is NOT void (it carries stopping information). The concept thus reflects this
78// reality via !std::same_as<void>, without imposing a shared result type that
79// does not exist (forcing it would require modifying one of the two classes: forbidden).
80//
81// We also require the EllipticSolver base (rhs/phi/solve()/residual/geom): an
82// elliptic LinearSolver IS an EllipticSolver that, IN ADDITION, exposes the
83// tolerance variant. GeometricMG and TensorKrylovSolver model both.
84//
85// DOCUMENTED GAP (concept DELIBERATELY separated from EllipticSolver). The DIRECT solvers
86// PoissonFFTSolver, DistributedFFTSolver and PolarPoissonSolver solve in ONE pass
87// (FFT + Thomas): they have NO solve(rel_tol, max_iters) nor any notion of iterative
88// tolerance. They model EllipticSolver (at the Cartesian MultiFab level) or
89// PolarEllipticSolver (polar), but NOT LinearSolver, and that is CORRECT: a direct
90// solver is not an iterative solver. LinearSolver thus captures the ITERATIVE
91// subset of the contract, without claiming that all elliptic backends carry it.
92template <class S>
93concept LinearSolver = EllipticSolver<S> && requires(S s, Real tol, int it) {
94 // Tolerance variant: solves up to rel_tol (or max_iters) and returns a stopping
95 // report. Return type FREE but NON void (int for MG, KrylovResult for Krylov).
96 s.solve(tol, it);
97 requires !std::same_as<decltype(s.solve(tol, it)), void>;
98};
99
100// ---------------------------------------------------------------------------
101// (3) FieldPostProcessor: field derivation from the potential, phi -> aux/grad.
102//
103// Contract = an APPLICATOR callable with the signature of field_postprocess:
104// (const MultiFab& phi, MultiFab& out, Real cx, Real cy, FieldPostProcess spec) -> void
105// i.e. write into out the convention (phi in component 0 if requested) + the
106// centered gradient (+/- depending on the spec sign), with cx = 1/(2 dx), cy = 1/(2 dy). The SPEC
107// (gradient sign, phi storage) stays the existing FieldPostProcess struct
108// (elliptic_problem.hpp): we do not redefine it, we PARAMETERIZE it.
109//
110// We constrain a CALLABLE type (functor or function pointer), not a class with
111// methods: that is what field_postprocess IS (a free function). The static_assert
112// below proves that &field_postprocess satisfies FieldPostProcessor.
113template <class F>
115 requires(F f, const MultiFab& phi, MultiFab& out, Real cx, Real cy, FieldPostProcess spec) {
116 { f(phi, out, cx, cy, spec) } -> std::same_as<void>;
117 };
118
119} // namespace pops
Definition elliptic_interface.hpp:56
Definition elliptic_solver.hpp:30
Definition elliptic_interface.hpp:114
Definition elliptic_interface.hpp:93
DESCRIPTIVE types of the elliptic stage: EllipticProblem (problem definition) and FieldPostProcess (f...
EllipticSolver concept: common contract for elliptic solvers at the MultiFab level (solve D phi = f),...
Geometry: index-space (Box2D) <-> Cartesian physical-space mapping; PolarGeometry: SIBLING for a glob...
MultiFab: a field DISTRIBUTED over a level (equivalent of AMReX's MultiFab).
Definition amr_hierarchy.hpp:29
double Real
Definition types.hpp:30
PHYSICAL boundary conditions at the domain edge (BCType, BCRec, fill_physical_bc, fill_ghosts).
Base scalar types and the POPS_HD macro (host+device portability).