include/pops/core/state/state.hpp Source FileΒΆ

adc_cpp: include/pops/core/state/state.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
state.hpp
Go to the documentation of this file.
1
6
7#pragma once
8
10
11// State and Aux: the two pointwise types handled by the physics layer.
12//
13// Architecture rule: these are trivially copyable aggregates (POD).
14// A PhysicalModel never sees anything else. No box, no MPI rank,
15// no Kokkos view enters here. This is what makes the physics portable:
16// the same code compiles for CPU and device because it touches no
17// parallelism.
18
19namespace pops {
20
28template <int N>
29struct StateVec {
30 Real v[N]{}; // C array: trivially usable on device (not std::array)
31
32 POPS_HD Real& operator[](int i) { return v[i]; }
33 POPS_HD Real operator[](int i) const { return v[i]; }
34
35 POPS_HD static constexpr int size() { return N; }
36};
37
40template <int N>
42 for (int i = 0; i < N; ++i)
43 a[i] += b[i];
44 return a;
45}
46
47template <int N>
49 for (int i = 0; i < N; ++i)
50 a[i] -= b[i];
51 return a;
52}
53
54template <int N>
56 for (int i = 0; i < N; ++i)
57 a[i] *= s;
58 return a;
59}
61
62// Auxiliary fields derived from the elliptic solve: the potential and
63// its gradient at the point. This is the single channel through which the coupling enters
64// the physics. It feeds BOTH the flux (drift transport: the
65// E x B velocity comes from grad phi) and the source (self-gravitating
66// compressible fluid: S = -rho grad phi). This duality is what lets a single
67// spatial operator serve both target problems.
68//
69// EXTENSIBLE aux channel (cf. aux_comps()/load_aux in spatial_operator.hpp). The first three
70// components are the BASE contract, identical to the legacy layout:
71// [0] = phi, [1] = grad_x, [2] = grad_y.
72// The following ones are ADDITIONAL auxiliary fields, optional, in a fixed
73// canonical order ([3] = B_z, [4] = T_e, ...). A model declares how many components it reads
74// via a static member n_aux (default kAuxBaseComps = 3); a model without n_aux never reads
75// the extra fields and stays strictly bit-identical. The extra fields default to 0:
76// load_aux overwrites them only if the model requests them.
77//
92#define POPS_AUX_FIELDS(X) \
93 X(B_z, 3) \
94 X(T_e, 4)
95
96// MAXIMUM number of NAMED aux fields (declared by a model via aux_field("...") on the DSL side,
97// ADC-70 phase 1) that an Aux can carry. FIXED bound: Aux stays a trivially copyable POD (raw C
98// array, device-clean) -- no allocation, no std::vector. Update HERE and AUX_NAMED_MAX
99// on the DSL side (python/pops/dsl.py) if more than four named fields per model are wanted.
100inline constexpr int kAuxMaxExtra = 4;
101
123struct Aux {
124 Real phi{}; // potential (aux component 0)
125 Real grad_x{}; // d phi / d x (aux component 1)
126 Real grad_y{}; // d phi / d y (aux component 2)
127 // EXTRA members generated from POPS_AUX_FIELDS (single source). B_z = out-of-plane B field
128 // provided by the system (comp 3); T_e = electron temperature p/rho of a fluid block
129 // (comp 4). All optional, defaulting to 0.
130#define POPS_AUX_DECL(name, idx) Real name{};
132#undef POPS_AUX_DECL
133 // Aux fields NAMED by the model (ADC-70 phase 1). Components of the aux channel starting at
134 // kAuxNamedBase (= 5, right after T_e): extra[k] <-> component (kAuxNamedBase + k). Defaulting to
135 // 0; load_aux loads them ONLY if the model declares n_aux > kAuxNamedBase (if constexpr,
136 // zero cost by default). Read in a DSL formula via aux.extra_field(k).
138
142 POPS_HD Real extra_field(int k) const { return (k >= 0 && k < kAuxMaxExtra) ? extra[k] : Real(0); }
143};
144
145// Width of the aux channel of the base contract (phi, grad phi). A model reading additional
146// fields declares a larger n_aux; cf. aux_comps()/load_aux().
147inline constexpr int kAuxBaseComps = 3;
148
149// First component of the NAMED aux fields (ADC-70 phase 1): right AFTER the canonical fields
150// B_z (3) and T_e (4), so index 5. A model declaring K named fields sets n_aux = kAuxNamedBase +
151// K; extra[k] is component (kAuxNamedBase + k). Placed AFTER the canonical channel so that user
152// names never encroach on B_z / T_e (which keep their dedicated paths
153// set_magnetic_field / set_electron_temperature_from). Python MIRROR: AUX_NAMED_BASE (dsl.py).
154inline constexpr int kAuxNamedBase = kAuxBaseComps + 2; // = 5 (after B_z=3, T_e=4)
155
156// Safeguard: the base of the named fields must be STRICTLY beyond the last canonical extra
157// field (the largest index of POPS_AUX_FIELDS + 1). If a canonical field is added beyond T_e,
158// this static_assert forces raising kAuxNamedBase (and AUX_NAMED_BASE on the DSL side) accordingly.
159#define POPS_AUX_NAMED_BASE_CHECK(name, idx) \
160 static_assert(kAuxNamedBase > (idx), \
161 "kAuxNamedBase must be beyond the canonical aux field '" #name "'");
163#undef POPS_AUX_NAMED_BASE_CHECK
164
165// Safeguard: the indices declared in POPS_AUX_FIELDS are strictly EXTRA (>= base) and
166// start right after the base contract. Checked at compile time that the table stays
167// consistent with kAuxBaseComps (the 1st extra field is at index kAuxBaseComps).
168#define POPS_AUX_IDX_CHECK(name, idx) \
169 static_assert((idx) >= kAuxBaseComps, \
170 "extra aux field '" #name "': index must be >= kAuxBaseComps (3)");
172#undef POPS_AUX_IDX_CHECK
173
174// EXPLICIT total cap of the aux channel (ADC-291): the maximum n_aux a model may declare is the
175// named base plus the bounded number of model-NAMED fields. NAMING the cap (rather than leaving the
176// bound an ad-hoc literal scattered across sites) makes the only remaining compile-time aux limit
177// DECLARATIVE; it is mirrored by AUX_NAMED_MAX on the DSL side (python/pops/dsl.py) and reported by
178// pops.capabilities() (the _pops module exposes kAuxMaxExtra/kAuxNamedBase so Python cannot silently
179// drift). A model declaring n_aux > kAuxMaxComps is out of contract.
180inline constexpr int kAuxMaxComps = kAuxNamedBase + kAuxMaxExtra;
181
182// The named-aux bound must allow at least one field, and the Aux POD storage must match it EXACTLY:
183// extra[] is the only place named fields live, so a mismatch between the declared limit and the array
184// would silently truncate (read-as-0). These pin the limit at compile time (tested via test_aux_names
185// and the Python<->C++ parity in test_capabilities.py).
186static_assert(kAuxMaxExtra >= 1, "kAuxMaxExtra must allow at least one model-named aux field");
187static_assert(static_cast<int>(sizeof(Aux::extra) / sizeof(Real)) == kAuxMaxExtra,
188 "Aux::extra[] size must equal kAuxMaxExtra (the declared named-aux limit)");
189
190} // namespace pops
Definition amr_hierarchy.hpp:29
POPS_HD StateVec< N > operator-(StateVec< N > a, const StateVec< N > &b)
Definition state.hpp:48
constexpr int kAuxBaseComps
Definition state.hpp:147
double Real
Definition types.hpp:30
constexpr int kAuxMaxComps
Definition state.hpp:180
constexpr int kAuxMaxExtra
Definition state.hpp:100
POPS_HD StateVec< N > operator+(StateVec< N > a, const StateVec< N > &b)
Definition state.hpp:41
POPS_HD StateVec< N > operator*(Real s, StateVec< N > a)
Definition state.hpp:55
constexpr int kAuxNamedBase
Definition state.hpp:154
#define POPS_AUX_IDX_CHECK(name, idx)
Definition state.hpp:168
#define POPS_AUX_DECL(name, idx)
Definition state.hpp:130
#define POPS_AUX_FIELDS(X)
SINGLE SOURCE of the layout of the EXTRA aux fields (X-macro).
Definition state.hpp:92
#define POPS_AUX_NAMED_BASE_CHECK(name, idx)
Definition state.hpp:159
POINTWISE auxiliary fields shared with the physics: single coupling channel.
Definition state.hpp:123
Real grad_y
Definition state.hpp:126
Real grad_x
Definition state.hpp:125
POPS_HD Real extra_field(int k) const
BOUNDED read of a named aux field (component kAuxNamedBase + k).
Definition state.hpp:142
Real phi
Definition state.hpp:124
Real extra[kAuxMaxExtra]
Definition state.hpp:137
Conserved state vector of fixed size, known at compile time.
Definition state.hpp:29
POPS_HD Real operator[](int i) const
Definition state.hpp:33
POPS_HD Real & operator[](int i)
Definition state.hpp:32
Real v[N]
Definition state.hpp:30
static POPS_HD constexpr int size()
Definition state.hpp:35
Base scalar types and the POPS_HD macro (host+device portability).
#define POPS_HD
Definition types.hpp:25