-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathamr_system.hpp
More file actions
1194 lines (1135 loc) · 83.8 KB
/
Copy pathamr_system.hpp
File metadata and controls
1194 lines (1135 loc) · 83.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#pragma once
#include <limits>
#include <pops/mesh/boundary/prepared_hyperbolic_boundary.hpp>
#include <pops/mesh/topology/boundary_topology.hpp>
#include <pops/numerics/nonlinear/newton_options.hpp>
#include <pops/coupling/source/coupling_operator.hpp> // CouplingOperator / CouplingOperatorView (typed contract, ADC-595)
#include <pops/runtime/export.hpp> // POPS_EXPORT: exact package seams resolved by native loaders
#include <pops/runtime/facade_options.hpp> // CoupledSourceProgram (facade POD, ADC-214)
#include <pops/runtime/config/model_spec.hpp>
#include <pops/runtime/config/runtime_params.hpp> // RuntimeParams (compiled-Program runtime params on AMR, ADC-508)
#include <pops/runtime/config/spatial_domain.hpp>
#include <pops/runtime/amr_patch.hpp>
#include <pops/mesh/storage/multifab.hpp>
#include <pops/runtime/numerical_defaults.hpp>
#include <pops/runtime/amr/prepared_component_providers.hpp>
#include <pops/runtime/amr/prepared_tagging_execution.hpp>
#include <pops/runtime/amr/exact_field_solver_provider.hpp>
#include <pops/runtime/amr/field_solver_options.hpp>
#include <pops/runtime/amr/hierarchy_tensor_solver_provider.hpp>
#include <pops/runtime/amr/hierarchy_policy_authority.hpp>
#include <pops/parallel/prepared_load_balance.hpp>
#include <pops/runtime/output_piece.hpp>
#include <pops/runtime/system/system_poisson_options.hpp>
#include <pops/runtime/system/auxiliary_checkpoint.hpp>
#include <pops/runtime/system/exact_aux_registry.hpp>
#include <array>
#include <functional>
#include <cstddef>
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <vector>
/// @file
/// @brief Multi-species composition on AMR at runtime: the refined counterpart of System.
///
/// One or SEVERAL blocks (species, described by ModelSpec of generic bricks) carried on an
/// AMR hierarchy. Like System but on an adaptive mesh.
///
/// One and many-block systems use the same AmrRuntime engine. Every block is co-located on ONE
/// SHARED AMR hierarchy (same ranked patches, ownership and spacing per level, guarded by
/// same_layout_or_throw). All blocks live on ALL patches. A single aux per level (phi,
/// grad phi) and a single coarse Poisson whose right-hand side is the CO-LOCATED SUM of the blocks'
/// elliptic bricks (f = Sum_b q_b n_b read at the same cells). Conservation PER BLOCK (reflux +
/// average_down). AmrRuntime runtime engine (type-erased registry by name). Blocks with potentially
/// DIFFERENT spatial schemes. Per-block temporal descriptors are immutable authoring metadata for
/// Program normalization; this spatial runtime does not decode or execute them. Union-of-tags
/// regridding is supported (multi-block + regrid_every > 0 regrids from the union; zero freezes the
/// hierarchy). Multirate cadence and inter-species coupled rates execute only where the installed
/// ProgramGraph places their typed operations. Multiple COMPILED blocks (add_compiled_model) and a
/// MIX of compiled + native blocks share the same hierarchy (capstone v, multi-block production DSL).
///
/// @note Resolved explicit Programs support N ratio-2 levels. An implicit/IMEX AMR composition
/// without a typed Program primitive fails closed; there is no private Newton or time-step fallback.
namespace pops {
template <int Dim>
class FieldNullspaceProvider;
struct FieldLogicalTimePoint;
struct AuxHaloPolicy;
template <int Dim>
struct CompiledFieldBoundaryKernel;
class SolveOutcome;
namespace component {
class LoadedComponent;
}
class ObserverMpiLane;
namespace runtime::program {
template <int Dim, class MemorySpace>
class AmrProgramContext;
} // namespace runtime::program
namespace runtime::amr {
struct PreparedTaggerSpec;
struct PreparedClusteringSpec;
struct PreparedRefluxSpec;
} // namespace runtime::amr
namespace runtime::field {
struct PreparedFieldSolverSpec;
struct FieldTopologyReportRow;
} // namespace runtime::field
/// Exact read-only backend configuration retained for one resolved AMR field solver.
struct AmrFieldSolverConfiguration {
std::string plan_identity;
std::string provider_identity;
std::string solver;
AmrFieldHierarchyPolicyAuthority hierarchy_policy;
AmrFieldSolverOptions options;
};
// Forward declarations of the exact-ranked runtime package types. The generated-package header
// materializes PreparedAmrSystemBlock<Dim>; this public facade retains it without importing the
// implementation-heavy AMR/operator headers into every binding and native loader translation unit.
template <int Dim>
struct AmrRuntimeBlock;
template <int Dim, class MemorySpace>
struct PreparedAmrSystemBlock;
template <int Dim, class MemorySpace>
struct PreparedAmrLevelEvaluation;
namespace runtime::amr {
template <int Dim, class MemorySpace>
class AmrRuntime;
}
namespace detail {
template <int Dim>
struct SharedAmrLayout;
}
namespace runtime {
namespace program {
class
Profiler; // forward-declared so engine()/profiler_handle() do not pull profiler.hpp into this header
template <int Dim>
struct ProgramRuntimeState;
} // namespace program
namespace multiblock {
template <int Dim>
struct AxisAlignedInterface;
template <int Dim>
struct PreparedInterfaceFluxSpec;
struct BoundaryEvaluationPoint;
} // namespace multiblock
} // namespace runtime
/// Exact ranked AMR mesh and cadence (per-block physical parameters live in the ModelSpec).
template <int Dim>
struct AmrSystemConfig : RuntimeSpatialDomain<Dim> {
static_assert(Dim >= 1 && Dim <= 3, "AmrSystemConfig only supports dimensions 1, 2, and 3");
AmrSystemConfig() { this->shape = runtime_config_detail::filled_extent<Dim>(128); }
int regrid_every = 20; ///< re-refinement every N steps (0 = never after init)
int level_count = 2; ///< maximum active hierarchy depth (>= 1)
/// Exact level-to-level hierarchy graph. Each table contains one ranked row per transition;
/// ratios are >= 2 and buffers/lookaheads are >= 0 component-wise.
std::vector<Extent<Dim>> transition_ratios{runtime_config_detail::filled_extent<Dim>(2)};
std::vector<Extent<Dim>> transition_buffers{runtime_config_detail::filled_extent<Dim>(2)};
std::vector<Extent<Dim>> transition_lookaheads{runtime_config_detail::filled_extent<Dim>(2)};
bool explicit_bootstrap = false; ///< coarse-only start; BootstrapPlan creates fine levels
/// OWNERSHIP POLICY of the coarse level (cf. AmrCouplerMP::replicated_coarse).
/// false (DEFAULT, historical): coarse mono-box REPLICATED on all ranks. The coarse Poisson
/// and the coarse transport are REDUNDANT on each GPU (zero communication,
/// better geometric MG) but DO NOT SCALE: only the fine patches are distributed.
/// true (strong-scaling mode): coarse MULTI-BOX (BoxArray::from_domain, tile size
/// coarse_max_grid) distributed round-robin across the ranks. The coarse Poisson and the coarse
/// transport distribute (each rank carries only its tiles), which removes the redundancy
/// and enables AMR strong-scaling. The geometric MG then operates on a multi-box coarse
/// (cf. geometric_mg.hpp): convergence to be measured (may require more cycles).
bool distribute_coarse = false;
/// Per-axis coarse tile limits when distribute_coarse=true. Zero selects the hierarchy policy's
/// deterministic ranked default on that axis. Ignored otherwise.
Extent<Dim> coarse_max_grid{};
/// ADC-616: Berger-Rigoutsos clustering params of the regrid layout. <= 0 (default) keeps the
/// historical ClusterParams {0.7, 1, 32}, bit-identical. min_efficiency in (0,1], sizes >= 1,
/// min_box_size <= max_box_size (validated at set_clustering / the facade descriptor).
double cluster_min_efficiency = 0.0;
int cluster_min_box_size = 0;
int cluster_max_box_size = 0;
/// Prepared ownership provider selected by the resolved adaptive-layout authority. The semantic
/// identity covers the public route, exact provider options and its weight capability.
std::string load_balance_route = "space_filling_curve";
std::string load_balance_identity = "pops.amr.default.space-filling-curve@1";
PreparedProviderOptions load_balance_options{"pops.amr.load-balance.space-filling-curve@1", {}};
};
/// Frozen parameters passed to the deferred build of the compiled path (add_compiled_model). Materialized
/// by AmrSystem at ensure_built time: the geometry, Poisson and initial-state choices known
/// at that moment. The amr_dsl_block header consumes them to instantiate AmrCouplerMP<Model>.
///
/// STRUCTURE (ADC-610). Settings are grouped into NAMED sub-structs by ownership/role
/// (mesh, poisson, initial data, named aux) instead of one flat
/// append-only bag. A new setting goes INTO its semantic group -- the historical "add at the tail so an
/// older .so loader falls back silently" idiom is RETIRED because it no longer describes how this struct
/// evolves. The ABI story is now the VERSIONED KEY, not tail-only placement: this struct crosses the
/// dlopen .so boundary BY VALUE, and any layout change (a new field, a regroup) shifts POPS_HEADER_SIG
/// (a sha256 over include/, cf. abi_key.hpp / python/CMakeLists.txt), which re-keys pops_native_abi_key.
/// A .so generated before the change then diverges from the module key and add_native_block REJECTS it
/// with a clear "regenerate" error (never silent UB). So an older .so is refused, not silently truncated.
template <int Dim>
struct AmrBuildParams {
static_assert(Dim >= 1 && Dim <= 3, "AmrBuildParams only supports dimensions 1, 2, and 3");
/// Coarse mesh geometry + coarse ownership policy (AMR strong-scaling).
struct Mesh : RuntimeSpatialDomain<Dim> {
Mesh() { this->shape = runtime_config_detail::filled_extent<Dim>(128); }
int regrid_every = 20; ///< re-refinement cadence (0 = never after init)
bool distribute_coarse = false; ///< distributed multi-box coarse (AMR strong-scaling)
Extent<Dim> coarse_max_grid{}; ///< exact per-axis tile cap
std::shared_ptr<const PreparedLoadBalanceAuthority<Dim>>
load_balance; ///< one prepared authority reused by coarse, fine seeds and every regrid
} mesh;
/// Exact prepared field provider slot. Legacy 2D Poisson/embedded-boundary objects are installed
/// through their capability-qualified provider and never become part of this ranked build POD.
std::string field_provider_slot;
/// Initial coarse seed: density only (historical) OR the FULL conservative state (priority).
struct InitialData {
bool has_density = false;
std::vector<double> density; ///< initial coarse density (component 0), ny*nx
// FULL initial conservative state (all components), takes priority over `density` when has_state.
bool has_state = false;
std::vector<double> state; ///< ncomp*ny*nx, component-major c*cells + j*nx + i
} initial;
};
/// Frozen argument type of the rejected pre-final loader ABI. It remains only so an old shared
/// object can resolve set_compiled_block and receive a deterministic fail-closed error; the final
/// runtime never stores or invokes this callable.
template <int Dim>
using AmrCompiledBlockBuilder = std::function<AmrRuntimeBlock<Dim>(
const detail::SharedAmrLayout<Dim>& layout, const std::string& name,
const std::vector<double>& density, bool has_density, const std::vector<double>& state,
bool has_state, double gamma, int substeps, bool recon_prim, int stride,
// Compatibility slots in the frozen builder ABI. Registration rejects every non-empty selector
// before this callable is stored; the spatial builder never resolves or executes an IMEX mask.
const std::vector<std::string>& implicit_vars, const std::vector<std::string>& implicit_roles,
double pos_floor, double weno_epsilon, bool wave_speed_cache)>;
/// Single block carried on an AMR hierarchy, composed at runtime.
///
/// @code{.cpp}
/// pops::AmrSystemConfig cfg; // base level: n x ny on independent x/y bounds
/// cfg.n = 64;
/// pops::AmrSystem amr(cfg);
///
/// pops::ModelSpec ne;
/// ne.transport = "exb"; ne.source = "none"; ne.elliptic = "charge";
/// amr.add_block("ne", ne, "minmod", "rusanov", "conservative", "explicit");
/// amr.set_poisson("charge_density", "geometric_mg");
/// amr.set_density("ne", rho0); // rho0: initial density on the base level
/// amr.step_cfl(0.4); // conservative refluxed step + composite FAC Poisson
/// @endcode
template <int Dim>
class AmrSystem {
static_assert(Dim >= 1 && Dim <= 3, "AmrSystem only supports dimensions 1, 2, and 3");
public:
using HyperbolicBoundary = PreparedHyperbolicBoundary<Dim>;
using memory_space = typename MultiFab<Dim>::memory_space;
using PreparedBlock = PreparedAmrSystemBlock<Dim, memory_space>;
using PreparedLevelEvaluation = PreparedAmrLevelEvaluation<Dim, memory_space>;
static constexpr int dimension = Dim;
explicit AmrSystem(const AmrSystemConfig<Dim>& cfg);
~AmrSystem();
// RULE OF FIVE (C.21): move-only (PIMPL unique_ptr). The copy was already IMPLICITLY deleted
// (move ctor declared); we make it EXPLICIT for intent. No API change (the copy was
// already unusable).
AmrSystem(const AmrSystem&) = delete;
AmrSystem& operator=(const AmrSystem&) = delete;
AmrSystem(AmrSystem&&) noexcept;
AmrSystem& operator=(AmrSystem&&) noexcept;
/// GLOBAL time-step bound (AMR counterpart of System::add_dt_bound): fn() evaluated ONCE
/// per step_cfl (host), all_reduce_min (identical dt on all ranks), <= 0 / non-finite =
/// inert this step. Hook for non-local constraints (coupling, scheduler, user ramp).
void add_dt_bound(const std::string& label, std::function<double()> fn);
/// ACTIVE bound of the last step_cfl: "transport:<block>" | "source_frequency:<block>" |
/// "stability_dt:<block>" | "global:<label>" | "degenerate" | "" (no CFL step yet).
std::string last_dt_bound() const;
/// Adds a block carried on the AMR. Same spatial-scheme parameters as System
/// (limiter x riemann x recon), applied to each level/patch of the hierarchy. The FIRST
/// add_block defines the block; a 2nd (or more) switches to the multi-block engine (shared
/// hierarchy, co-located sum Poisson). Blocks can have DIFFERENT SPATIAL SCHEMES.
/// @param name block name: INDEXES the block (set_density(name), mass(name), density(name)). In
/// multi-block the name must be unique; mono-block an empty name targets the single block.
/// @param model composition of bricks (transport/source/elliptic + parameters)
/// @param limiter "none" | "minmod" | "vanleer" | "weno5" | "mc" | "superbee"
/// (weno5 = WENO5-Z, 3 ghosts; native low-level stencil route). The resolved
/// Case route derives its
/// coarse/fine order and halo requirements from this spatial descriptor and
/// selects the minimum sufficient conservative provider.
/// @param riemann "rusanov" | "hll" (generic signed-wave, requires model.wave_speeds) | "hllc"
/// | "roe" (requires the model's exact HasHLLCStructure /
/// HasRoeDissipation capability; no layout inference or fallback)
/// @param time "explicit" (SSPRK2) | "euler" (forward Euler) | "ssprk3"
/// (SSPRK3, order 3, reflux per stage; explicit transport, EXCLUSIVE of imex) |
/// "imex". This is immutable authoring metadata consumed by Program normalization;
/// the spatial block never executes a time method. A composition without a typed
/// Program primitive fails closed instead of reaching a private fallback.
/// @param substeps declared Program substeps of the block (>= 1): the normalized ProgramGraph
/// partitions the effective step into equal pieces.
/// @param stride HOLD-THEN-CATCH-UP cadence of the block (>= 1; default 1 = each macro-step). stride=M
/// holds the block M-1 macro-steps then catches it up by an effective step M*dt (multirate).
/// Program-owned step_cfl honors the cadence: dt =
/// cfl*h*min_b(substeps_b/(stride_b*w_b)), mirror of System::step_cfl.
/// @param implicit_vars / implicit_roles reserved partial-IMEX selectors. The current AMR target
/// has no typed implicit-source Program primitive, so every non-empty selector
/// fails closed, including with time="imex"; it is never stored or ignored.
/// @param newton compatibility input validated like System::add_block. The spatial AMR block does
/// not retain or execute source-Newton configuration, so every non-default value
/// fails closed until the AMR target provides an executable typed local
/// nonlinear/Newton Program primitive.
/// @param newton_diagnostics request for an aggregated Newton report owned by the typed implicit
/// Program solve. The spatial runtime has no such report carrier, so true fails
/// closed instead of exposing a permanently empty diagnostic.
/// @param positivity_floor Zhang-Shu positivity floor (ADC-259): if > 0, the AMR transport floors
/// the Density-role face states (reconstruct_pp / zhang_shu_scale) AND the C/F fine
/// ghost means to >= floor. Default 0 = inactive, bit-identical. Guarantee = face /
/// ghost-state Density positivity only (order-1 fallback), NOT updated-mean nor
/// pressure positivity (parity with System::add_block). A model without a Density
/// role rejects floor > 0. The COMPILED .so path carries it too (ADC-322): a loader
/// regenerated against this header marshals the floor (pops_install_native_amr).
/// @throws std::runtime_error if a block is already defined, if substeps < 1, if stride < 1, if time
/// is not in {explicit, euler, ssprk3, imex}, if recon is not in {conservative,
/// primitive}, if a non-empty implicit selector is requested, or if source-Newton options
/// or diagnostics are requested on the spatial AMR block.
void add_block(const std::string& name, const ModelSpec& model,
const std::string& limiter = "minmod", const std::string& riemann = "rusanov",
const std::string& recon = "conservative", const std::string& time = "explicit",
int substeps = 1, int stride = 1,
const std::vector<std::string>& implicit_vars = {},
const std::vector<std::string>& implicit_roles = {},
const NewtonOptions& newton = {}, bool newton_diagnostics = false,
double positivity_floor = 0.0,
double weno_epsilon = static_cast<double>(kWenoEpsilon),
bool wave_speed_cache = false);
/// Retired deferred-builder ABI fence. It always fails before mutation; generated loaders must
/// publish the complete PreparedAmrSystemBlock below so no metadata-only runtime can survive.
POPS_EXPORT void set_compiled_block(
int ncomp, double gamma, int substeps, AmrCompiledBlockBuilder<Dim> runtime_builder,
const std::string& name = std::string(), bool recon_prim = false,
const std::string& time = "euler", int stride = 1,
const std::vector<std::string>& implicit_vars = {},
const std::vector<std::string>& implicit_roles = {}, double pos_floor = 0.0,
double weno_epsilon = static_cast<double>(kWenoEpsilon), bool wave_speed_cache = false);
/// Atomically retain one complete generated spatial package. No deferred builder, legacy
/// runtime block, or dimension-erased fallback is published by this route.
POPS_EXPORT void install_prepared_amr_block(PreparedBlock block);
/// Materialize every level-bound operator, auxiliary owner, halo provider and flux ledger for
/// the current exact hierarchy generation. A topology mutation invalidates the prior graph and
/// this operation prepares a complete replacement before publication.
POPS_EXPORT void refresh_prepared_amr_levels();
/// Evaluate the installed generated operator on one level and atomically publish its residual
/// together with the exact face-integrated fluxes used to assemble it.
POPS_EXPORT const PreparedLevelEvaluation& evaluate_prepared_amr_level(
const runtime::multiblock::BoundaryEvaluationPoint& point);
/// Prepare/evaluate an exact stage candidate without replacing the hierarchy's accepted state.
/// The candidate must retain the active level's complete layout/distribution/component contract.
POPS_EXPORT void prepare_generated_amr_level_state(
const runtime::multiblock::BoundaryEvaluationPoint& point, MultiFab<Dim>& state);
POPS_EXPORT const PreparedLevelEvaluation& evaluate_prepared_amr_level_at(
const runtime::multiblock::BoundaryEvaluationPoint& point, MultiFab<Dim>& state);
POPS_EXPORT const PreparedLevelEvaluation& prepared_amr_level_evaluation(int level) const;
/// Exact level geometry/topology and model speed retained by the prepared hierarchy graph.
POPS_EXPORT Geometry<Dim> prepared_amr_level_geometry(int level) const;
POPS_EXPORT BoundaryTopology<Dim> prepared_amr_boundary_topology() const;
POPS_EXPORT Real prepared_amr_level_maximum_speed(int level, const MultiFab<Dim>& state) const;
/// Accumulate the generated block's exact elliptic right-hand side on one live level.
POPS_EXPORT void add_prepared_amr_poisson_rhs(int level, MultiFab<Dim>& rhs);
/// Install the same exact-ranked hyperbolic authority as System. Same-level halo exchange and
/// coarse/fine transfer remain separate hierarchy operations; physical laws are evaluated only
/// by this retained model-qualified object.
POPS_EXPORT void install_hyperbolic_boundary(
const std::string& name, const std::string& identity, int required_depth,
const std::vector<std::string>& face_types, const std::vector<double>& face_values,
const std::vector<std::string>& face_identities,
const std::vector<std::string>& component_roles, const std::string& state_identity,
const std::vector<std::string>& face_representations = {},
const std::vector<std::string>& face_converter_identities = {},
const std::vector<std::vector<std::string>>& face_analytic_opcodes = {},
const std::vector<std::vector<double>>& face_analytic_literals = {},
const std::vector<std::string>& face_analytic_clocks = {});
POPS_EXPORT void install_prepared_hyperbolic_boundary(
const std::string& name, const std::string& identity, int required_depth,
const std::string& state_identity, std::shared_ptr<const HyperbolicBoundary> boundary);
/// Register the exact state Handle independently from physical-boundary ownership.
POPS_EXPORT void install_block_state_route(const std::string& name,
const std::string& state_identity);
/// Bind one exact solved-field Handle identity to its authenticated provider storage slot.
/// Boundary components and AMR tagging consume this common prepared route.
POPS_EXPORT void install_field_storage_route(const std::string& field_identity,
const std::string& provider_slot);
/// Roll back a failed pre-build runtime-authority transaction. Internal bind seam only.
POPS_EXPORT void discard_hyperbolic_boundaries();
POPS_EXPORT void install_amr_tagger_component(
runtime::amr::PreparedTaggerSpec spec, std::shared_ptr<component::LoadedComponent> component);
POPS_EXPORT void install_amr_clustering_component(
runtime::amr::PreparedClusteringSpec spec,
std::shared_ptr<component::LoadedComponent> component);
POPS_EXPORT void install_amr_reflux_component(
runtime::amr::PreparedRefluxSpec spec, std::shared_ptr<component::LoadedComponent> component);
POPS_EXPORT void discard_amr_provider_components();
/// Materialize one exact shared NumericalFlux route on a frozen AMR level. This seam is called
/// only after the lazy AmrRuntime has been built and before bind freezes composition.
POPS_EXPORT void install_interface_flux_component(
runtime::multiblock::AxisAlignedInterface<Dim> route,
runtime::multiblock::PreparedInterfaceFluxSpec<Dim> spec,
std::shared_ptr<component::LoadedComponent> component);
/// Roll back a failed all-interface post-block installation transaction.
POPS_EXPORT void discard_interface_flux_components();
/// Internal bind transaction checkpoint for incremental per-level interface installation.
POPS_EXPORT std::size_t interface_flux_installation_checkpoint() const;
POPS_EXPORT void rollback_interface_flux_installations(std::size_t accepted_size);
POPS_EXPORT std::size_t interface_evaluation_count(const std::string& identity,
int level = 0) const;
/// Internal installation seam for a compiled AMR production package. The .so inlines the header
/// template add_compiled_model(AmrSystem&, ...), prepares one complete
/// PreparedAmrSystemBlock<Dim>, and publishes it atomically through install_prepared_amr_block.
/// No deferred runtime builder, flat-array numerical fallback, or alternate temporal engine is
/// involved.
///
/// The _pops host module is PROMOTED to global scope (RTLD_NOLOAD), then the generated package is
/// opened RTLD_LOCAL: it can resolve the exact package installation symbol without exporting its
/// generated templates to later semantic artifacts. The ABI key baked in the package
/// (pops_native_abi_key) is compared to the module's (abi_key()) -- mismatch => clear error (no
/// silent UB at the C++ boundary). Same scheme guard-rails as System (upstream validation).
///
/// The exact generated core currently accepts one complete package; a second package fails before
/// mutation rather than constructing a metadata-only or dimension-erased multi-block route.
/// time accepts canonical Program-authoring tokens {explicit, euler, ssprk3, imex}; the spatial
/// loader never executes one of those methods. In particular, ``imex`` requires a typed implicit
/// Program primitive and has no backward-Euler/Newton fallback. The multirate stride and partial
/// IMEX mask do not transit through the flat block-loader ABI: the Python facade rejects them
/// rather than ignoring them silently. They must be expressed by a compiled typed Program whose
/// target provides the corresponding primitive. recon "primitive" and flux "roe"/"hllc" are WIRED at parity (#113);
/// the Python facade applies a pressure guard for hllc/roe.
/// The low-level loader contains a WENO5-Z stencil route and allocates its three-cell halo. The
/// resolved Case route authenticates the matching order-five conservative coarse/fine provider;
/// neither the facade nor AmrRuntime substitutes the order-two route.
/// @throws std::runtime_error if the ABI diverges, if a symbol is missing, or substeps < 1.
/// @param name block name: cosmetic in mono-block, INDEXES the block in multi-block (set_density/
/// mass/density; must be unique and non-empty from the 2nd block on, like add_block).
/// @param positivity_floor Zhang-Shu positivity floor of the block (ADC-322): the .so flat ABI now
/// carries it (pops_install_native_amr -> add_compiled_model -> prepared package), so
/// a loader regenerated against this header floors the Density-role face states like
/// a native add_block. 0 (default) = inactive, bit-identical.
void add_native_block(const std::string& name, const std::string& so_path,
const std::string& limiter = "minmod",
const std::string& riemann = "rusanov",
const std::string& recon = "conservative",
const std::string& time = "explicit",
double gamma = static_cast<double>(kPhysicalDefaultGamma), int substeps = 1,
const std::vector<double>& params = {}, double positivity_floor = 0.0,
double weno_epsilon = static_cast<double>(kWenoEpsilon),
bool wave_speed_cache = false);
/// AMR twin of System::add_external_riemann_block. The external flux is instantiated directly
/// in the deferred AmrRuntime builder and therefore retains native reflux/halo execution.
void add_external_riemann_block(const std::string& name, const std::string& so_path,
const std::string& brick_id, const std::string& sha256,
const std::string& limiter, const std::string& recon,
const std::string& time, double gamma, int substeps, int stride,
int expected_nvars, int expected_naux,
const std::string& expected_model_identity,
double positivity_floor = 0.0,
double weno_epsilon = static_cast<double>(kWenoEpsilon));
/// Install the exact prepared AMRTagging program resolved from the layout authority.
/// This is the only tagging installation seam: the runtime never synthesizes a scalar
/// threshold, component-zero default, or shared-potential fallback. The native tagger owns
/// `min_cycles > 0` as accepted sparse state and persists it through AMR checkpoint/restart.
void set_bootstrap_tagging(
const std::vector<std::string>& leaf_subject_kinds,
const std::vector<std::string>& leaf_subject_identities,
const std::vector<std::string>& leaf_blocks, const std::vector<std::string>& leaf_variables,
const std::vector<int>& leaf_field_component_indices, const std::vector<int>& leaf_ops,
const std::vector<double>& leaf_thresholds, const std::vector<int>& leaf_stencil_indices,
const std::vector<typename runtime::amr::PreparedTaggingProgram<Dim>::Stencil>& stencils,
const std::vector<std::int32_t>& refine_ops, const std::vector<std::int32_t>& refine_args,
const std::vector<std::int32_t>& coarsen_ops, const std::vector<std::int32_t>& coarsen_args,
int min_cycles, const std::string& equality_policy, const std::string& conflict_policy,
const std::string& clock_identity, const std::string& provider_identity);
/// Execute the immutable exact-ranked tagging program against one live parent level. The
/// returned masks are owner-local candidates; no clustering, hysteresis, or topology mutation is
/// performed by this inspection route.
runtime::amr::PreparedTaggerCandidates<Dim> execute_prepared_tagging(int parent_level);
/// Consume the prepared candidates for one parent, cluster their exact global union, transfer
/// accepted state into the candidate child, and publish the regrid atomically. Returns whether a
/// child remains active after publication.
bool regrid_from_prepared_tagging(int parent_level);
/// Install one exact parent/child temporal relation per AMR transition. These ratios are an
/// independent execution authority and are never inferred from spatial refinement.
void set_temporal_relations(const std::vector<std::int64_t>& numerators,
const std::vector<std::int64_t>& denominators,
const std::vector<std::string>& remainder_policies);
/// Configures the default coarse elliptic field. This convenience uses the same provider registry
/// and exact option contract as resolved named fields; it is not a second solver path. The
/// right-hand side is always the sum of the blocks' elliptic bricks.
/// @param rhs "charge_density" | "composite" (same composed right-hand side as System)
/// @param solver exact registered provider identity
/// @param bc "auto" | "periodic" | "dirichlet" | "neumann"
/// @param solver_options provider-owned typed options. An empty carrier requests the provider's
/// exact defaults; the AMR facade never interprets option keys.
/// @throws std::runtime_error if rhs, provider, options, or bc violates the provider contract.
void set_poisson(const std::string& rhs = "charge_density",
const std::string& solver = "geometric_mg", const std::string& bc = "auto",
const AmrFieldSolverOptions& solver_options = {});
/// Install one fully resolved AMR field route. The registry key is the digest of its
/// block-qualified provider identity. ``plan_identity`` independently commits the complete
/// resolved semantics. Before lazy runtime materialization, the canonical ordered
/// (slot, plan_identity) registry must agree exactly on every MPI rank. Duplicate slots are
/// refused, including exact repeats.
void set_field_solver_plan(const std::string& provider_slot, const std::string& plan_identity,
const std::string& provider_identity,
const std::string& output_owner_identity,
const std::string& output_block, const std::string& output_key,
const std::vector<std::string>& provider_identities,
const std::vector<std::string>& provider_blocks,
const std::vector<std::string>& provider_keys,
const std::vector<double>& provider_coefficients,
const std::string& solver,
const AmrFieldHierarchyPolicyAuthority& hierarchy_policy,
const AmrFieldSolverOptions& solver_options);
/// Adds one native AMR field solver provider before binding. Builtins and extensions are resolved
/// through the same per-system registry and must expose exact collective contracts.
void register_field_solver_provider(
std::shared_ptr<const runtime::amr::ExactAmrFieldSolverProvider<Dim>> provider);
/// Installs one authenticated external FieldTopology@2 + FieldSolver@2 pair as an AMR provider.
/// The returned route is exactly ``provider_slot`` and is suitable for set_field_solver_plan.
POPS_EXPORT std::string register_field_solver_provider(
const std::string& provider_slot, runtime::field::PreparedFieldSolverSpec spec,
std::shared_ptr<component::LoadedComponent> topology,
std::shared_ptr<component::LoadedComponent> solver);
/// Adds one native field-nullspace provider before binding. The selected route is resolved only
/// after operator, boundary, topology and distribution facts have materialized.
void register_field_nullspace_provider(
std::shared_ptr<const FieldNullspaceProvider<Dim>> provider);
/// Select the provider for the principal field configured by set_poisson. The AMR facade retains
/// only the opaque provider identity and its exact typed options; it never interprets a nullspace
/// family or gauge name.
void set_default_field_nullspace(const std::string& nullspace_provider_identity,
const PreparedProviderOptions& options);
/// Adds one hierarchy tensor-solver provider before binding. Compiled Programs resolve their
/// opaque provider identity through this per-system registry; builtins and extensions use the
/// same preparation protocol.
void register_hierarchy_tensor_solver_provider(
std::shared_ptr<const runtime::program::HierarchyTensorSolverProvider<Dim>> provider);
/// Collective generated-Program extension seam. Unlike the pre-build authoring registration
/// above, this may run after materialization: it mutates only the provider registry, authenticates
/// the exact declaration on every rank, and is idempotent for the same component declaration.
POPS_EXPORT void register_program_hierarchy_tensor_solver_provider(
std::shared_ptr<const runtime::program::HierarchyTensorSolverProvider<Dim>> provider);
/// Internal generated-Program seam. The returned immutable registry outlives every installed
/// Program context because it is owned by this facade.
POPS_EXPORT std::shared_ptr<const runtime::program::HierarchyTensorSolverProviderRegistry<Dim>>
hierarchy_tensor_solver_provider_registry() const;
/// Exact read-only backend configuration retained by one resolved field plan.
AmrFieldSolverConfiguration field_solver_configuration(const std::string& provider_slot) const;
/// Install the resolved scalar reaction coefficient of one named screened field.
void set_field_reaction(const std::string& provider_slot, double reaction);
void set_field_topology_authority(const std::string& provider_slot,
const std::string& provider_kind, const std::string& provenance,
const std::string& topology_digest);
std::vector<runtime::field::FieldTopologyReportRow> field_topology_report(
const std::string& provider_slot) const;
void set_field_boundary_plan(const std::string& provider_slot,
const std::vector<std::string>& kind,
const std::vector<double>& alpha, const std::vector<double>& beta,
const std::vector<double>& value);
void set_field_boundary_dependencies(const std::string& provider_slot,
const std::vector<std::string>& state_blocks,
const std::vector<int>& state_components,
const std::vector<std::string>& field_blocks,
const std::vector<std::string>& field_keys,
const std::vector<int>& field_components);
POPS_EXPORT void set_field_boundary_kernel(const std::string& provider_slot,
const CompiledFieldBoundaryKernel<Dim>& kernel);
POPS_EXPORT void set_field_logical_timepoint(const std::string& provider_slot,
const FieldLogicalTimePoint& point);
POPS_EXPORT void set_field_boundary_parameters(const std::string& provider_slot,
const std::vector<double>& parameters);
void set_field_newton_plan(const std::string& provider_slot, double tolerance, int max_iterations,
double linear_tolerance, int linear_max_iterations, int restart,
double armijo, double minimum_step);
/// Install one immutable analytic embedded-boundary definition. The expression is sampled
/// independently on every live AMR level whenever the hierarchy is materialized or regridded.
/// Staircase transport is exact-ranked in dimensions 1, 2, and 3. Cut-cell transport is an
/// explicit rank-two capability and dimensions 1/3 reject it before facade mutation.
void set_analytic_level_set(const std::vector<std::string>& opcodes,
const std::vector<double>& literals, const std::string& mode = "none",
double kappa_min = 0.0, double face_open_eps = 0.0,
double cut_theta_min = 0.0);
/// Rank-two convenience authoring for hypot(x-cx,y-cy)-R. Other ranks reject before mutation.
void set_disc_domain(double cx, double cy, double radius, const std::string& mode = "none",
double kappa_min = 0.0, double face_open_eps = 0.0,
double cut_theta_min = 0.0);
/// Change only the numerical EB mode while retaining the accepted analytic definition.
void set_geometry_mode(const std::string& mode);
void set_field_nullspace(const std::string& provider_slot,
const std::string& nullspace_provider_identity,
const PreparedProviderOptions& options);
/// Sets the initial density on the coarse level (component 0), ny*nx row-major.
/// @param name cosmetic label (mono-block AMR: the density targets the single block).
void set_density(const std::string& name, const std::vector<double>& rho);
/// Sets the FULL INITIAL CONSERVATIVE STATE (all components) on the coarse level, then
/// prolongs it to the fine levels at build (constant injection, like the density). @p U is flat
/// component-major (c*ny*nx + j*nx + i) of size ncomp*ny*nx; ncomp == n_vars of the model (checked at
/// build, where only Model::n_vars is known). Takes priority over set_density: allows starting the AMR
/// from a full drift state (rho, rho*u, rho*v) instead of m=0. The conversion
/// primitive -> conservative (rho_u = rho*u) is done on the Python side (the caller already supplies the
/// conservative). Wired on native and compiled blocks, mono-block as well as multi-block: the full
/// state is threaded to the deferred concrete builder, seeds the coarse, then is injected to the
/// fine levels. In multi-block @p name indexes the target block.
/// @throws std::runtime_error if the system is already built, if U is empty, or if its size
/// is not a multiple of ny*nx.
void set_conservative_state(const std::string& name, const std::vector<double>& U);
void begin_bootstrap_plan();
bool bootstrap_next_level(); ///< execute the next exact ranked transition if tagged
void commit_bootstrap_level();
void rollback_bootstrap_level();
void register_bootstrap_transfer_route(
const std::string& identity, const std::vector<std::string>& subjects,
const std::string& provider_identity, const std::string& space, const std::string& centering,
const std::string& representation, const std::string& storage, const std::string& operation,
const std::string& kernel, int order, const Extent<Dim>& ghost_depth,
const Extent<Dim>& refinement_ratio);
void register_bootstrap_array(const std::string& subject, const std::string& centering, int ncomp,
Extent<Dim> shape, const std::vector<double>& values);
void register_bootstrap_face_vector(const std::vector<std::string>& subjects);
void bind_bootstrap_block_subject(const std::string& subject, const std::string& block);
void register_analytic_constant(const std::string& subject, const std::string& block,
const std::string& space, const std::string& centering,
const std::vector<double>& components);
void register_analytic_gaussian(const std::string& subject, const std::string& block,
const RealVector<Dim>& center, double background,
double amplitude, double inverse_width);
void register_analytic_expression(const std::string& subject, const std::string& block,
const std::string& space, const std::string& centering,
const std::vector<std::vector<std::string>>& opcodes,
const std::vector<std::vector<double>>& literals);
std::int64_t bootstrap_analytic_reproject(const std::string& subject, int level);
int apply_bootstrap_component_floor(const std::string& subject, int level, int component,
double floor);
std::int64_t recompute_bootstrap_field(const std::string& subject, const std::string& field_name);
std::int64_t bootstrap_prolong_array(const std::string& subject, int level);
void synchronize_bootstrap_state(const std::string& subject, int fine_level);
std::vector<double> bootstrap_array_level(const std::string& subject, int level) const;
void invalidate_bootstrap_cache(const std::string& subject, int level);
std::vector<AmrPatch<Dim>> rebuild_bootstrap_topology_cache(const std::string& subject,
int level);
std::uint64_t bootstrap_cache_epoch(const std::string& subject) const;
/// Register immutable owner-qualified auxiliary producers and native consumer views. The graph
/// is sealed before hierarchy materialization; physical aliases and raw component indices are
/// intentionally absent from this interface.
POPS_EXPORT void install_prepared_auxiliary_provider(
runtime::system::PreparedAuxiliaryProvider<Dim> provider);
POPS_EXPORT void install_auxiliary_consumer_plan(
runtime::system::AuxiliaryConsumerProviderPlan<Dim> plan);
POPS_EXPORT void seal_auxiliary_providers();
POPS_EXPORT void stage_auxiliary_input(const runtime::system::AuxiliaryComponentKey& key,
const std::vector<double>& values);
POPS_EXPORT void refresh_auxiliary(const runtime::system::AuxiliaryEvaluationPoint& point);
[[nodiscard]] POPS_EXPORT runtime::system::AuxiliaryStorageAddress<Dim> auxiliary_address(
const runtime::system::AuxiliaryComponentKey& key) const;
[[nodiscard]] POPS_EXPORT std::vector<double> auxiliary_component(
const runtime::system::AuxiliaryComponentKey& key, int level = 0) const;
[[nodiscard]] POPS_EXPORT std::string auxiliary_registry_contract() const;
[[nodiscard]] POPS_EXPORT const runtime::system::ResolvedAuxiliaryConsumerPlan<Dim>&
prepared_auxiliary_consumer_plan(const std::string& consumer_qid) const;
/// Level-qualified group and plan access for generated AMR Program contexts. Every consumer
/// binds its compact local view against the active hierarchy level; no shared auxiliary slab is
/// exposed at this seam.
[[nodiscard]] POPS_EXPORT const runtime::system::AuxiliaryStorageGroups<Dim>*
prepared_amr_provider_storage_groups(int level) const;
[[nodiscard]] POPS_EXPORT const runtime::system::ResolvedAuxiliaryConsumerPlan<Dim>&
prepared_amr_auxiliary_consumer_plan(const std::string& consumer_qid, int level) const;
/// Durable accepted metadata for each AMR hierarchy level. The native checkpoint backend owns
/// rank-local group payload staging; this image authenticates its exact group identities,
/// owner-qualified ComponentKeys, shapes and accepted provider generations before publication.
[[nodiscard]] POPS_EXPORT std::vector<runtime::system::AuxiliaryCheckpointAcceptedState<Dim>>
capture_auxiliary_checkpoint_accepted_state() const;
/// Restore only after the caller has staged compatible rank-local group payloads privately. A
/// communicator preflight and a full registry rollback image prevent a rejected level from
/// exposing a partial accepted generation.
POPS_EXPORT void restore_auxiliary_checkpoint_accepted_state(
const std::vector<runtime::system::AuxiliaryCheckpointAcceptedState<Dim>>& state);
/// @name Named multi-elliptic fields (ADC-428)
/// Exact-ranked API for a SECOND elliptic solve on the AMR hierarchy. Installation is accepted
/// only when the selected native specialization owns a dimension-qualified hierarchy field-solver
/// provider; the facade never stores an unused contract or falls back to the historical 2-D engine.
/// @{
/// Registers named @p field's exact-ranked provider outputs. ``output_keys`` contains either the
/// potential alone or the potential followed by one gradient component per native axis. Every key
/// must be owned by a sealed ``field_output`` provider; no raw carrier component crosses this API.
/// @throws if the system is already built, the output contract is malformed, or no exact-ranked
/// hierarchy field-solver provider is installed. Provider-unavailable failure happens before mutation.
POPS_EXPORT void register_elliptic_field(const std::string& block_name,
const std::string& provider_key,
const std::vector<runtime::system::AuxiliaryComponentKey>&
output_keys,
int gradient_sign);
/// Attaches named @p field's RHS closure (rhs += elliptic_field_rhs(U)) to block @p block_name. Called
/// by the native AMR loader (make_poisson_rhs of the per-field brick). @throws before mutation if
/// the system is already built, the block is unknown, or no exact-ranked hierarchy provider exists.
POPS_EXPORT void set_block_elliptic_field(
const std::string& block_name, const std::string& field,
std::function<void(const MultiFab<Dim>&, MultiFab<Dim>&)> rhs);
/// Solved potential of named @p field on the COARSE level, ny*nx row-major (read-back). Solves the
/// hierarchy fields if needed (so it is current even before any step), then reads the field's phi
/// component. AMR counterpart of System::aux_field_component for a named elliptic field. @throws if the
/// field is unregistered.
std::vector<double> named_field_values(const std::string& field);
std::vector<std::string> field_provider_slots() const;
int field_provider_levels(const std::string& provider_slot);
void set_field_potential(const std::string& provider_slot, const std::vector<double>& phi);
void set_field_potential_level(const std::string& provider_slot, int level,
const std::vector<double>& phi);
std::vector<double> field_potential_global(const std::string& provider_slot);
std::vector<double> field_potential_level_global(const std::string& provider_slot, int level);
/// Exact rank-local valid-cell pieces for one qualified field provider. The returned metadata
/// explicitly marks replicated level-zero ownership so output modes never infer it from box counts.
std::vector<OutputPiece<Dim>> output_field_local_pieces(const std::string& provider_slot,
int level);
std::vector<OutputPiece<Dim>> output_field_root_pieces(const ObserverMpiLane& lane,
const std::string& provider_slot,
int level);
/// Transaction bracket used by the accepted-state reader after complete payload preflight. Every
/// hierarchy,
/// state, aux, field warm-start, history and clock mutation is rolled back if any restore step fails.
void begin_restart_transaction();
void commit_restart_transaction();
void rollback_restart_transaction();
/// Force exactly one artifact-owned scientific regrid inside an active restart transaction.
/// The exact recorded accepted state must already have been restored and authenticated.
void preflight_regrid_on_restart();
void regrid_on_restart();
int checkpoint_regrid_count() const;
std::uint64_t checkpoint_topology_epoch() const;
void restore_checkpoint_counters(int regrid_count, std::uint64_t topology_epoch);
std::vector<std::vector<std::string>> checkpoint_temporal_relations() const;
/// Canonical rows for every required bootstrap transfer route: subject, operation, route identity,
/// provider, kernel, descriptor fields. The sealed checkpoint compares these rows byte-for-byte.
std::vector<std::vector<std::string>> checkpoint_transfer_routes() const;
/// @}
/// Registers an inter-species COUPLED SOURCE (compiled pops.dsl.CoupledSource, flat bytecode ABI
/// P5), refined counterpart of System::add_coupled_source but on the SHARED AMR hierarchy.
/// Registration stores the typed operator only: the installed Program owns its temporal placement,
/// applies it to candidate states level by level, and then performs the authored synchronization.
/// The coupling is baked into a device-clean stack machine (CoupledSourceKernel): NO per-cell Python
/// callback in the hot path. MULTI-BLOCK only (>= 2 add_block: the coupling reads/writes
/// SEVERAL named blocks). Must be called BEFORE the first step (the runtime engine is built
/// at lazy build; the source is injected into it).
///
/// CONSERVATION: an add_pair construction (a term +expr on a block, -expr exactly on the other,
/// SAME cell) makes the sum of the two blocks conserved PER CELL (and globally) to machine
/// precision. The engine does NOT IMPOSE it (an ionization creating an e/i pair is legal): it is a
/// property of the constructed coupling (verify_conservation on the DSL side checks it symbolically).
///
/// @throws std::runtime_error if called in mono-block, if the system is already built, or if the
/// shape of the bytecode / a role / a block is invalid (same guards as System).
/// @param prog bytecode description of the coupling grouped in a POD (ADC-214; cf.
/// CoupledSourceProgram; parity with System::add_coupled_source): in_blocks /
/// in_roles / consts / out_blocks / out_roles + prog_ops / prog_args / prog_lens
/// (stack machine) + freq_prog_ops / freq_prog_args (PER-CELL frequency mu(U)
/// optional; EMPTY = constant frequency only, bit-identical; non-empty:
/// evaluated on the COARSE LEVEL of the input blocks at each step_cfl, MAX +
/// all_reduce_max, bound dt <= cfl / max(mu) on the coarse, not the patches).
/// @param frequency CONSTANT declared frequency mu [1/s] of the coupling (wave 3): bound
/// dt <= cfl/mu on the macro-step of step_cfl; <= 0 (default) = no bound.
/// @param label name of the coupling (reason "coupled_source:<label>" of last_dt_bound).
void add_coupled_source(const CoupledSourceProgram& prog, double frequency = 0.0,
const std::string& label = "coupled_source");
/// Registers a TYPED coupling operator (ADC-595, parity with System::add_coupling_operator): the
/// same coupled-source program PLUS its declared conservation contract and frequency bound. The
/// declared contract is VALIDATED at registration (host, fail-loud) against the actual output terms,
/// then the program is lowered through the SAME add_coupled_source path (bit-identical numerics), and
/// the declared contract is recorded for coupled_operators(). An empty (unchecked) contract is
/// equivalent to add_coupled_source.
void add_coupling_operator(const CouplingOperator& op);
/// Read-only view of the registered coupling operators (ADC-595, parity with System): label plus the
/// declared conservation / frequency contracts, in registration order, so a Program or a runtime
/// report enumerates the AMR couplings as typed operators. A raw add_coupled_source registers an
/// "unchecked" entry (empty contract). Empty until the first coupling is added.
const std::vector<CouplingOperatorView>& coupled_operators() const;
void step(double dt); ///< one AMR macro-step (periodic regrid included)
void advance(double dt, int nsteps);
void begin_step_transaction();
void commit_step_transaction();
void finalize_step_transaction();
void rollback_step_transaction();
/// Internal rollback authority used by the installed Program context. A Program nested in a
/// public AmrSystem step borrows the facade's accepted image instead of taking a second full engine
/// snapshot; a direct C++ Program context remains autonomous.
POPS_EXPORT bool has_active_step_transaction() const noexcept;
POPS_EXPORT void restore_active_step_transaction_for_program();
/// Volume-weighted L2 norm of each block's accepted AMR macro-step change. Collective and valid
/// while the retained outer transaction snapshot still owns U^n.
POPS_EXPORT std::map<std::string, double> step_change_l2() const;
/// Advances at dt = cfl * coarse_dx / max wave speed. @return the dt used.
double step_cfl(double cfl, double speed_floor = static_cast<double>(kCflSpeedFloor),
double max_dt = std::numeric_limits<double>::infinity(), double min_dt = 0.0);
/// @name Compiled time-program install seam on the AMR hierarchy (epic ADC-511 / ADC-508, Spec 6)
/// AMR counterpart of System::install_program: load a generated problem.so and install its compiled
/// time Program over the AMR hierarchy. Mirrors the System seam (install_program_step registers the
/// macro-step body; the cadence + per-block RuntimeParams stores live HERE on the Impl, NOT in the
/// .so closure, so a value change reaches the captured context and a later checkpoint can reach
/// them). A generated AMR Program .so resolves these POPS_EXPORT seams from the globally promoted
/// host while the generated package remains RTLD_LOCAL, exactly like the exact spatial-package
/// installation seam on the native AMR loader.
/// @{
/// Install the mandatory macro-step body. AmrSystem::step, advance and step_cfl reject before lazy
/// hierarchy construction or any other mutation while it is absent. An empty std::function is
/// rejected: there is no public temporal route that silently clears the whole-system Program.
/// POPS_EXPORT: the generated AMR Program .so resolves it across the dlopen boundary. The closure
/// executes the normalized ProgramGraph on the hierarchy through
/// an AmrProgramContext (the AMR counterpart of ProgramContext).
POPS_EXPORT void install_program_step(std::function<void(double)> step);
/// Install the companion callback that republishes Program-owned accepted clocks/history whenever
/// explicit bootstrap commits a hierarchy level. Generated artifacts own this seam; direct
/// low-level steps may omit it because they have no authenticated checkpoint context.
POPS_EXPORT void install_program_hierarchy_refresh(std::function<void()> refresh);
/// Install the artifact-owned restart preflight, transform and forced rollback-resynchronization
/// hooks.
POPS_EXPORT void install_program_restart_hooks(std::function<void()> preflight,
std::function<void()> regrid,
std::function<void()> resync);
/// Set the compiled-Program macro-step cadence (parity with System::set_program_cadence, ADC-411):
/// GLOBAL @p substeps and @p stride around the installed program closure. @p substeps subdivides each
/// effective step into @p substeps program closure calls; @p stride runs the program once per @p
/// stride macro-steps (hold-then-catch-up). Both must be >= 1 (throws std::invalid_argument).
/// Default 1/1 -> a single program closure call per macro-step. Kept SEPARATE from install_program so
/// the generated .so ABI is untouched (the cadence is runtime metadata).
POPS_EXPORT void set_program_cadence(int substeps, int stride);
/// Installed GLOBAL macro-step cadence (ADC-594, parity System): the current @c substeps / @c stride
/// the compiled Program runs at (default 1/1 with no cadence set). Const, side-effect-free -- the
/// structured ProgramRuntimeReport reads them; there was no Python-visible getter before.
POPS_EXPORT int program_substeps() const;
POPS_EXPORT int program_stride() const;
/// Exact duration, accepted public-step count and physical start currently held by the GLOBAL
/// Program stride window. All are zero at a stride boundary and form mandatory checkpoint state.
POPS_EXPORT double program_cadence_window_dt() const;
POPS_EXPORT int program_cadence_window_steps() const;
POPS_EXPORT double program_cadence_window_start_time() const;
/// Exact accepted Program interval provenance. Zero means no Program invocation has been accepted.
POPS_EXPORT double program_last_dt() const;
/// Stage the exact held-window image before set_clock during strict restart. The image must match
/// the exact accepted (@p accepted_time, @p macro_step) cursor, @p accepted_last_dt and installed
/// stride; malformed, missing or mismatched state is rejected without mutating accepted state.
POPS_EXPORT void restore_program_cadence_window(double accumulated_dt, int held_steps,
double window_start_time, double accepted_last_dt,
double accepted_time, int macro_step);
/// Install the program-index -> AMR-block-index map (entry p = the AMR block index of Program block
/// p), built by install_program after matching the .so's block names to the instantiated AMR blocks
/// BY NAME (Spec 3 criterion 23, ADC-457). Empty clears it and is never an implicit positional
/// identity. Read by the AmrProgramContext to resolve a Program block index to the name-matched block.
POPS_EXPORT void set_program_block_map(const std::vector<int>& prog_to_sys);
/// The installed program-index -> AMR-block-index map. Empty means no authenticated mapping and is
/// rejected by AmrProgramContext.
POPS_EXPORT const std::vector<int>& program_block_map() const;
/// Load a generated problem.so and install its compiled time Program on the AMR hierarchy. dlopens
/// @p so_path, checks its ABI key against this module (fail-loud on mismatch), runs the section-24
/// install-time requirement validation (aux / solver / block instance, verbatim spec messages), binds
/// the Program's blocks to the AMR blocks BY NAME, seeds each block's RuntimeParams from the .so
/// pops_program_param_* metadata, then calls the .so's pops_install_program_amr(this), whose shared
/// facade factory selects the hierarchy provider and installs the macro-step closure. Mirrors
/// add_native_block and System::install_program; the .so stays loaded for the process lifetime.
POPS_EXPORT void install_program(const std::string& so_path);
/// IR hash of the installed compiled Program (the string returned by the .so's pops_program_hash), or
/// "" if no program is installed. Parity with System::installed_program_hash (checkpoint guard).
POPS_EXPORT std::string installed_program_hash() const;
/// The last macro-step dt handed to the installed Program (ADC-631): the AmrProgramContext reads it
/// so a pre-commit history sample records its outgoing interval (variable-dt replay). POPS_EXPORT
/// for the dlopen boundary (the generated AMR Program .so reads it via the AmrProgramContext).
/// Authenticated accepted-state image owned by the compiled AMR Program context. This is distinct
/// from the dense field/history arrays: it preserves exact level clocks, qualified history-slot
/// identities and lagged effective-flux publications required for conservative multistep restart.
POPS_EXPORT std::vector<std::uint8_t> program_accepted_state() const;
/// Copy the same authenticated image into caller-owned storage. The Program attempt transaction
/// keeps this storage resident so a stable retry snapshots bytes without allocating a temporary
/// vector on every macro-step; the returned-by-value accessor remains the public convenience API.
POPS_EXPORT void copy_program_accepted_state_into(std::vector<std::uint8_t>& state) const;
/// Replace the accepted image during strict restart. Each replacement advances a revision observed
/// by the persistent AmrProgramContext before its next attempt; no stale context state is reused.
POPS_EXPORT void restore_program_accepted_state(const std::vector<std::uint8_t>& state);
/// Strict checkpoint counterpart: authenticate the complete accepted image and its runtime-owned
/// tagging payload before atomically publishing either. A rejected payload changes neither bytes,
/// revision nor the live AMR hysteresis state.
POPS_EXPORT void restore_checkpoint_accepted_state(const std::vector<std::uint8_t>& state);
/// Validate the exact history registry encoded by @p state and materialize its native per-level
/// rings on the already rebuilt restart hierarchy. This is a transactional restart seam: it never
/// advances the Program and refuses any name/depth/component/owner mismatch before allocation.
POPS_EXPORT void materialize_program_restart_histories(const std::vector<std::uint8_t>& state,
const std::vector<std::string>& names,
const std::vector<int>& depths,
const std::vector<int>& ncomps);
POPS_EXPORT std::uint64_t program_accepted_state_revision() const;
/// Human/audit-readable qualification rows decoded from the same accepted image persisted as bytes.
POPS_EXPORT std::vector<std::vector<std::string>> program_accepted_state_manifest() const;
POPS_EXPORT std::vector<std::vector<std::string>> program_clock_manifest() const;
/// Accepted temporal-partition provider, synchronization tick and per-rung cell counts. The rows
/// are decoded from the same opaque image used by strict restart, never a capability ledger.
POPS_EXPORT std::vector<std::vector<std::string>> program_temporal_partition_manifest() const;
POPS_EXPORT std::vector<std::vector<std::string>> program_flux_ledger_manifest() const;
POPS_EXPORT std::vector<std::vector<std::string>> program_interface_flux_ledger_manifest() const;
POPS_EXPORT std::vector<std::vector<std::string>> program_sync_manifest() const;
/// @name Runtime freeze lifecycle (ADC-592, parity with System)
/// Assembly mutable BEFORE bind, composition FROZEN once pops.bind completes. mark_bound() is
/// called LAST by the Python bind flow (after every install call), so the install sequence itself
/// never trips the structural-setter guards. NOTE: 'bound' (bind completed) is DISTINCT from the
/// lazy 'built' materialization (bind runs BEFORE the first step/mass/density triggers ensure_built),
/// so the existing 'already built' messages of the lazy path are untouched; the structural guards
/// now also refuse a call once bound_ is set, with the bind-vocabulary message.
/// @{
/// Mark the composition as bound (frozen): every structural setter then rejects with a precise
/// error. Runtime-data setters (set_density / set_conservative_state on the base level BEFORE a step
/// / set_program_params / set_clock) that DATA-write stay allowed. A second mark_bound() throws.
void mark_bound();
/// The runtime lifecycle state: "assembling" (not bound), "bound" (mark_bound() ran, no macro-step),
/// "running" (bound AND macro_step() > 0). Parity with System::lifecycle_state.
std::string lifecycle_state() const;
/// @}
/// @name Compiled-Program RUNTIME parameters on AMR (epic ADC-511 / ADC-508, parity with ADC-510)
/// Per-PROGRAM-block RuntimeParams of a compiled time Program whose physics reads a
/// dsl.Param(..., kind="runtime"), owned HERE so set_program_params changes it at run time WITHOUT
/// recompiling (the same no-recompile contract as System). install_program seeds each block's defaults
/// from the .so pops_program_param_* metadata. The lowered kernels read the CURRENT value via the
/// AmrProgramContext.
/// @{
/// Overwrite block @p prog_block's RuntimeParams with @p values (the COMPLETE block, sorted-name order
/// matching the .so pops_program_param_* metadata). @p prog_block is the PROGRAM block index. @throws
/// std::out_of_range if the block was not seeded by a runtime-param Program, std::runtime_error on a
/// size mismatch. Effect on the next step.
POPS_EXPORT void set_program_params(int prog_block, const std::vector<double>& values);
/// Block @p prog_block's CURRENT RuntimeParams (a device-clean by-value copy). An UNSEEDED block
/// returns a default-constructed RuntimeParams (count 0). Read by the AmrProgramContext.
POPS_EXPORT RuntimeParams program_params(int prog_block) const;
/// Seed block @p prog_block's RuntimeParams to its DECLARATION defaults (@p count values, the .so
/// pops_program_param_default metadata). Called by install_program once per runtime-param Program
/// block; a later set_program_params overwrites only the supplied values. Idempotent.
POPS_EXPORT void seed_program_params(int prog_block, const std::vector<double>& defaults);
/// @}
/// The built AMR spatial engine (the AmrRuntime the AmrProgramContext driver wraps), or nullptr
/// before the lazy build. install_program forces the build so the .so's pops_install_program_amr
/// receives a live engine. POPS_EXPORT: the generated AMR Program .so resolves it across the dlopen
/// boundary.
POPS_EXPORT runtime::amr::AmrRuntime<Dim>* engine() const;
/// Compatibility inspection seam. Once built, every resolved AMR system uses AmrRuntime, so this
/// returns true; before build engine() remains null. POPS_EXPORT for dlopen-boundary parity.
POPS_EXPORT bool uses_runtime_engine() const;
/// The facade-owned Profiler (the AmrProgramContext forwards count_kernel / profile_record to it).
/// POPS_EXPORT for the dlopen boundary. Disabled by default -> zero hot-path cost.
POPS_EXPORT runtime::program::Profiler& profiler_handle();
/// Record a runtime Scalar diagnostic under @p name (the AmrProgramContext's record_scalar seam),
/// retrievable via program_diagnostic / program_diagnostics. A pure side effect (inspection / logging).
POPS_EXPORT void record_program_diagnostic(const std::string& name, double value);
/// The recorded diagnostic @p name (0 if absent) / the whole map. Exposed to Python for inspection.
POPS_EXPORT double program_diagnostic(const std::string& name) const;
POPS_EXPORT std::map<std::string, double> program_diagnostics() const;
/// Five current-attempt scalars for one typed balance route. RuntimeInstance calls this only
/// inside its active outer accepted-step transaction; missing/stale/non-finite evidence fails.
POPS_EXPORT std::map<std::string, double> accepted_balance_terms(const std::string& route) const;
/// The same accepted route with selected attempt-local native reflux/projection producers.
POPS_EXPORT std::map<std::string, double> selected_accepted_balance_terms(
const std::string& route, const std::string& block, int component,
const std::vector<int>& levels, const std::vector<std::string>& automatic_terms) const;
POPS_EXPORT void begin_step_projection_report();
POPS_EXPORT void note_step_projection(const std::string& name);
POPS_EXPORT std::vector<std::string> consume_step_projections();
/// LEVEL-COMPOSITE collective reduction over a named block, the AMR counterpart of
/// System::reduce_component the diagnostics driver drives (ADC-542). @p kind is per-component
/// "sum" / "min" / "max" / "abs_sum" / "sum_sq" / "abs_max", or the full-state "*_all" variants.
/// @p levels is the exact strictly-increasing level selection; empty is the low-level C++
/// all-level convention. Volume-weighted sums and extrema mask coarser selected cells covered by
/// the next selected finer level. Multi-block and single-block both remain native/Kokkos.
POPS_EXPORT double composite_reduce(const std::string& block, const std::string& kind, int comp,
const std::vector<int>& levels = {}) const;
/// Composite integral/reduction of one qualified native field provider. This route is available
/// only after the AMR runtime has been built because that engine owns the typed provider hierarchy.
POPS_EXPORT double composite_reduce_field(const std::string& provider_slot,
const std::string& kind, int comp,
const std::vector<int>& levels = {});
/// @}
/// @}
Extent<Dim> spatial_shape() const;
/// Generated Program shared libraries read the accepted clock through the flat loader ABI.
POPS_EXPORT double time() const;