include/pops/runtime/builders/factory/model_factory.hpp Source FileΒΆ

adc_cpp: include/pops/runtime/builders/factory/model_factory.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
model_factory.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <pops/core/foundation/cold.hpp> // POPS_COLD_FN: COLD-factory no-optimize attribute (ADC-337)
4#include <pops/core/state/variables.hpp> // VariableSet/VariableRole/role_from_name/roles_csv (resolve_implicit_components)
6#include <pops/runtime/dynamic/model_registry.hpp> // kTransports/kSources/kElliptics: builtin-brick tag registry (ADC-331)
8
9#include <algorithm> // std::find, std::sort (resolve_implicit_components)
10#include <stdexcept>
11#include <string>
12#include <vector>
13
21
22namespace pops::detail {
23
32inline void validate_model_spec(const ModelSpec& m) {
33 if (m.transport.empty())
34 throw std::runtime_error(
35 "ModelSpec: transport not set (required) -- choose 'exb' | 'compressible' | 'isothermal'. "
36 "The core infers no physics default (no silent 'compressible').");
37 if (m.elliptic.empty())
38 throw std::runtime_error(
39 "ModelSpec: elliptic not set (required) -- choose 'charge' | 'background' | 'gravity'. "
40 "The core infers no physics default (no silent 'charge').");
41 if (m.source.empty())
42 throw std::runtime_error("ModelSpec: source not set -- choose " + source_choices() +
43 " ('none' = no source term).");
44}
45
49static_assert(ExBVelocity::n_vars == transport_n_vars_ct("exb"), "registry n_vars drift: exb");
50static_assert(CompressibleFlux::n_vars == transport_n_vars_ct("compressible"),
51 "registry n_vars drift: compressible");
52static_assert(IsothermalFlux::n_vars == transport_n_vars_ct("isothermal"),
53 "registry n_vars drift: isothermal");
54
56template <class Visitor>
57POPS_COLD_FN void dispatch_transport(const ModelSpec& m, Visitor&& v) {
59 m.transport); // registry rejection (single source of the valid tags + message)
60 if (m.transport == "exb")
61 return v(ExBVelocity{Real(m.B0)});
62 if (m.transport == "compressible")
63 return v(CompressibleFlux{Real(m.gamma)});
64 if (m.transport == "isothermal")
65 return v(IsothermalFlux{Real(m.cs2), Real(m.vacuum_floor)});
66 // Reached only if a registry tag is not routed by the if-chain (a registry/dispatch inconsistency,
67 // i.e. a programming bug); user typos were already rejected by validate_transport above.
68 throw std::runtime_error("transport '" + m.transport +
69 "' valid in registry but not routed (add the dispatch case)");
70}
71
84template <int NV, class Visitor>
85POPS_COLD_FN void dispatch_source(const ModelSpec& m, Visitor&& v) {
86 if (m.source == "none")
87 return v(NoSource{});
88 if constexpr (NV >= 3) {
89 if (m.source == "potential")
90 return v(PotentialForce{Real(m.qom)});
91 if (m.source == "gravity")
92 return v(GravityForce{});
93 if (m.source == "magnetic" || m.source == "lorentz")
94 return v(MagneticLorentzForce{Real(m.qom)});
95 if (m.source == "potential_magnetic" || m.source == "potential_lorentz")
98 }
99 throw std::runtime_error("source '" + m.source +
100 "' invalid here (requires a fluid transport >= 3 variables, or 'none')");
101}
102
104template <class Visitor>
105POPS_COLD_FN void dispatch_elliptic(const ModelSpec& m, Visitor&& v) {
106 validate_elliptic(m.elliptic); // registry rejection (single source of the valid tags + message)
107 if (m.elliptic == "charge")
108 return v(ChargeDensity{Real(m.q)});
109 if (m.elliptic == "background")
110 return v(BackgroundDensity{Real(m.alpha), Real(m.n0)});
111 if (m.elliptic == "gravity")
112 return v(GravityCoupling{Real(m.sign), Real(m.four_pi_G), Real(m.rho0)});
113 // Reached only on a registry/dispatch inconsistency (see dispatch_transport): unknown user tags
114 // were already rejected by validate_elliptic above.
115 throw std::runtime_error("elliptic '" + m.elliptic +
116 "' valid in registry but not routed (add the dispatch case)");
117}
118
134template <class Brick>
135POPS_COLD_FN void bind_variable_roles(Brick& brk, const VariableSet& cons) {
136 const int i_rho = cons.index_of(VariableRole::Density);
137 const int i_mx = cons.index_of(VariableRole::MomentumX);
138 const int i_my = cons.index_of(VariableRole::MomentumY);
139 const int i_E = cons.index_of(VariableRole::Energy);
140 if constexpr (requires { brk.c_rho; }) {
141 if (i_rho >= 0)
142 brk.c_rho = i_rho;
143 }
144 if constexpr (requires { brk.c_mx; }) {
145 if (i_mx >= 0)
146 brk.c_mx = i_mx;
147 }
148 if constexpr (requires { brk.c_my; }) {
149 if (i_my >= 0)
150 brk.c_my = i_my;
151 }
152 if constexpr (requires { brk.c_E; }) {
153 if (i_E >= 0)
154 brk.c_E = i_E;
155 }
156 if constexpr (requires {
157 brk.a;
158 brk.b;
159 }) { // CompositeSource<A,B>: recursion into the sub-bricks
160 bind_variable_roles(brk.a, cons);
161 bind_variable_roles(brk.b, cons);
162 }
163}
164
167template <class Visitor>
168POPS_COLD_FN void dispatch_model(const ModelSpec& m, Visitor&& visitor) {
169 validate_model_spec(m); // explicit completeness contract (ADC-290): no silent physics default
170 dispatch_transport(m, [&](auto tr) {
171 using TR = decltype(tr);
172 // Transport roles (host): used to resolve the indices of the source / elliptic bricks before
173 // freezing the composite. Native transport -> canonical roles -> resolved indices == defaults.
174 const VariableSet cons = TR::conservative_vars();
175 dispatch_source<TR::n_vars>(m, [&](auto src) {
176 dispatch_elliptic(m, [&](auto ell) {
178 cons); // AUTOMATIC resolution by roles (transparent, bit-identical)
179 bind_variable_roles(ell, cons);
180 visitor(CompositeModel<TR, decltype(src), decltype(ell)>{tr, src, ell});
181 });
182 });
183 });
184}
185
195template <class TR, class Visitor>
196POPS_COLD_FN void dispatch_model_for(const ModelSpec& m, TR tr, Visitor&& visitor) {
197 const VariableSet cons = TR::conservative_vars();
198 dispatch_source<TR::n_vars>(m, [&](auto src) {
199 dispatch_elliptic(m, [&](auto ell) {
200 bind_variable_roles(src, cons);
201 bind_variable_roles(ell, cons);
202 visitor(CompositeModel<TR, decltype(src), decltype(ell)>{tr, src, ell});
203 });
204 });
205}
206
214 const std::string& block, const VariableSet& cons, const std::vector<std::string>& names,
215 const std::vector<std::string>& roles) {
216 std::vector<int> out;
217 auto push_unique = [&out](int c) {
218 if (std::find(out.begin(), out.end(), c) == out.end())
219 out.push_back(c);
220 };
221 for (const std::string& nm : names) {
222 int idx = -1;
223 for (int i = 0; i < static_cast<int>(cons.names.size()); ++i)
224 if (cons.names[i] == nm) {
225 idx = i;
226 break;
227 }
228 if (idx < 0) {
229 std::string have;
230 for (std::size_t i = 0; i < cons.names.size(); ++i) {
231 if (i)
232 have += ", ";
233 have += cons.names[i];
234 }
235 throw std::runtime_error("System::add_block : implicit_vars : variable '" + nm +
236 "' absent from block '" + block +
237 "' (conserved variables : " + have + ")");
238 }
239 push_unique(idx);
240 }
241 for (const std::string& rn : roles) {
242 const int idx = cons.index_of(rn); // canonical role name OR user-defined role label (ADC-292)
243 if (idx < 0) {
244 std::string have = roles_csv(cons);
245 throw std::runtime_error(
246 "System::add_block : implicit_roles : role '" + rn + "' absent from block '" + block +
247 "' (roles : " + (have.empty() ? std::string("<not provided>") : have) + ")");
248 }
249 push_unique(idx);
250 }
251 std::sort(out.begin(), out.end());
252 return out;
253}
254
262inline POPS_COLD_FN int resolve_selected_component(const std::string& origin,
263 const std::string& block, const VariableSet& cons,
264 const std::string& name,
265 const std::string& role) {
266 if (name.empty() && role.empty())
267 return -1; // default selector -> caller's component 0
268 if (!name.empty() && !role.empty())
269 throw std::runtime_error(origin +
270 " : select the refinement variable by NAME or by ROLE, not both");
271 if (!name.empty()) {
272 for (int i = 0; i < static_cast<int>(cons.names.size()); ++i)
273 if (cons.names[i] == name)
274 return i;
275 std::string have;
276 for (std::size_t i = 0; i < cons.names.size(); ++i) {
277 if (i)
278 have += ", ";
279 have += cons.names[i];
280 }
281 throw std::runtime_error(origin + " : variable '" + name + "' absent from block '" + block +
282 "' (conserved variables : " + have + ")");
283 }
284 const int idx = cons.index_of(role); // canonical role name OR user-defined role label (ADC-292)
285 if (idx < 0) {
286 const std::string have = roles_csv(cons);
287 throw std::runtime_error(origin + " : role '" + role + "' absent from block '" + block +
288 "' (roles : " + (have.empty() ? std::string("<not provided>") : have) +
289 ")");
290 }
291 return idx;
292}
293
294} // namespace pops::detail
Umbrella for composable GENERIC physics bricks (compat).
POPS_COLD_FN: opt out the COLD host factories from the optimizer (ADC-337, P1-B).
#define POPS_COLD_FN
Definition cold.hpp:25
SINGLE registry of builtin MODEL BRICK tags (transport / source / elliptic): the shared source of tru...
Flat specification of a model: chosen bricks plus their parameters.
Definition cluster.hpp:37
void validate_model_spec(const ModelSpec &m)
Completeness contract of a ModelSpec (ADC-290): transport and elliptic MUST be chosen explicitly.
Definition model_factory.hpp:32
POPS_COLD_FN void dispatch_source(const ModelSpec &m, Visitor &&v)
Builds the source brick and calls v(source).
Definition model_factory.hpp:85
POPS_COLD_FN void dispatch_model(const ModelSpec &m, Visitor &&visitor)
Assembles the CompositeModel designated by m and calls visitor(model).
Definition model_factory.hpp:168
POPS_COLD_FN void dispatch_elliptic(const ModelSpec &m, Visitor &&v)
Builds the elliptic right-hand-side brick and calls v(elliptic).
Definition model_factory.hpp:105
POPS_COLD_FN void dispatch_model_for(const ModelSpec &m, TR tr, Visitor &&visitor)
Same as dispatch_model but with the transport brick ALREADY chosen (tr).
Definition model_factory.hpp:196
POPS_COLD_FN std::vector< int > resolve_implicit_components(const std::string &block, const VariableSet &cons, const std::vector< std::string > &names, const std::vector< std::string > &roles)
Resolves the IMPLICIT MASK of a block (add_block: implicit_vars / implicit_roles) into a list of cons...
Definition model_factory.hpp:213
POPS_COLD_FN int resolve_selected_component(const std::string &origin, const std::string &block, const VariableSet &cons, const std::string &name, const std::string &role)
Resolves a SINGLE selector variable of block (the AMR regrid variable, ADC-296) into its conserved-co...
Definition model_factory.hpp:262
POPS_COLD_FN void dispatch_transport(const ModelSpec &m, Visitor &&v)
Non-drift guard (ADC-331): the registry's n_vars column (model_registry.hpp, a LIGHT header with no b...
Definition model_factory.hpp:57
POPS_COLD_FN void bind_variable_roles(Brick &brk, const VariableSet &cons)
AUTOMATIC resolution by ROLES (audit sec.5): fills the component indices of a SOURCE or ELLIPTIC bric...
Definition model_factory.hpp:135
std::string source_choices()
Definition model_registry.hpp:139
void validate_elliptic(const std::string &tag)
Definition model_registry.hpp:212
constexpr int transport_n_vars_ct(const char *name)
Definition model_registry.hpp:176
double Real
Definition types.hpp:30
std::string roles_csv(const VariableSet &vs)
CSV of a VariableSet's roles (role_name, separator ',').
Definition variables.hpp:172
void validate_transport(const std::string &tag)
Validates a transport / elliptic tag against the builtin registry.
Definition model_registry.hpp:208
Neutralizing background f = alpha (n - n0).
Definition elliptic.hpp:39
Charge density f = q n.
Definition elliptic.hpp:23
Composite physical model: one HYPERBOLIC brick + one source + one elliptic right-hand side.
Definition composite.hpp:30
SUM of two source bricks: S(U, aux) = A.apply(U, aux) + B.apply(U, aux).
Definition source.hpp:136
2D compressible Euler for an ideal gas: HYPERBOLIC brick (HyperbolicModel concept).
Definition euler.hpp:34
static constexpr int n_vars
number of conserved variables
Definition euler.hpp:38
Scalar advection by the E x B drift: v = (-d_y phi, d_x phi)/B0 (divergence-free).
Definition hyperbolic.hpp:27
static constexpr int n_vars
Definition hyperbolic.hpp:28
Self-consistent coupling f = sign * 4piG * (rho - rho0).
Definition elliptic.hpp:55
Gravitational force rho g (+ work if 4 variables).
Definition source.hpp:73
ISOTHERMAL Euler flux (p = cs2 rho), 3 variables (rho, rho u, rho v).
Definition hyperbolic.hpp:127
static constexpr int n_vars
Definition hyperbolic.hpp:128
MAGNETIC Lorentz force q (v x B) on momentum, field B = B_z z_hat out of plane.
Definition source.hpp:104
Brick composition of a block plus parameters.
Definition model_spec.hpp:27
double rho0
GravityCoupling: background.
Definition model_spec.hpp:47
double qom
PotentialForce / MagneticLorentzForce: q/m (sign included)
Definition model_spec.hpp:41
double n0
BackgroundDensity: neutralizing background.
Definition model_spec.hpp:44
std::string source
"none" (default, neutral: no force) | "potential" | "gravity"
Definition model_spec.hpp:29
std::string elliptic
REQUIRED (unset): "charge" | "background" | "gravity".
Definition model_spec.hpp:31
double gamma
CompressibleFlux: adiabatic index.
Definition model_spec.hpp:34
std::string transport
REQUIRED (unset): "exb" | "compressible" | "isothermal".
Definition model_spec.hpp:28
double cs2
IsothermalFlux: sound speed squared.
Definition model_spec.hpp:35
double four_pi_G
GravityCoupling: coupling intensity.
Definition model_spec.hpp:46
double vacuum_floor
IsothermalFlux: quasi-vacuum density floor for u=m/max(rho,floor) (ADC-77).
Definition model_spec.hpp:36
double q
ChargeDensity: charge q.
Definition model_spec.hpp:42
double alpha
BackgroundDensity: Poisson coupling.
Definition model_spec.hpp:43
double sign
GravityCoupling: +1 gravity, -1 electrostatic.
Definition model_spec.hpp:45
double B0
ExBVelocity: magnetic field.
Definition model_spec.hpp:33
No source: S(U, aux) = 0.
Definition source.hpp:27
Electrostatic potential force (q/m) rho E on momentum (+ work on energy if 4 variables).
Definition source.hpp:47
A model's variable set: kind (cons/prim), names, size, canonical roles (optional, parallel to names; ...
Definition variables.hpp:58
std::vector< std::string > names
Definition variables.hpp:60
int index_of(VariableRole role) const
Index of the component carrying role (first occurrence), -1 if absent.
Definition variables.hpp:67
Descriptor of a model's variables (Vars).