ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_SBMRemapping.H
Go to the documentation of this file.
1 #ifndef ERF_SBM_REMAPPING_H_
2 #define ERF_SBM_REMAPPING_H_
3 
4 #include "ERF_SBMLayout.H"
5 
6 #include <AMReX_Algorithm.H>
7 #include <AMReX_Array4.H>
8 #include <AMReX_GpuQualifiers.H>
9 #include <AMReX_Math.H>
10 #include <AMReX_REAL.H>
11 
12 #include <cmath>
13 #include <limits>
14 #include <vector>
15 
16 namespace erf_sbm {
17 
18 enum class RemapStatus {
20 };
22 
23 /**
24  * Compact, non-owning scientific context for one population. Its referenced
25  * arrays must be immutable and accessible to the execution backend for the
26  * full lifetime of any plan or kernel work using the view. Device callers
27  * construct the same POD with device-accessible metadata arrays.
28  */
30  int population_id{-1};
33  const amrex::Real* edges{nullptr};
34  const amrex::Real* pivots{nullptr};
35  int nbins{0};
36  int mass_offset{-1};
37  int number_offset{-1};
39  const int* property_component_offsets{nullptr};
41 };
42 
43 /** Host adapter building a stable view into the immutable SBMLayout. */
44 [[nodiscard]] PopulationRemapView
45 population_remap_view (const SBMLayout& layout, int population_id) noexcept;
46 
47 /**
48  * Constant-time structural/context checks for already qualified immutable
49  * metadata. Callers must derive views from a validated SBMLayout or call the
50  * full validator below once before using a raw view in the remapping cores.
51  */
52 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
53 bool valid_population_remap_context (const PopulationRemapView& population) noexcept
54 {
55  if (population.population_id < 0 ||
56  population.coordinate_kind != CoordinateKind::Mass ||
57  (population.moment_mode != MomentMode::OneMoment &&
58  population.moment_mode != MomentMode::TwoMoment) ||
59  population.nbins <= 0 || population.edges == nullptr ||
60  population.state_components < population.nbins || population.mass_offset < 0 ||
61  population.property_count < 0 ||
62  population.property_count > population.state_components / population.nbins ||
63  (population.property_count > 0 && population.property_component_offsets == nullptr)) {
64  return false;
65  }
66  if (population.mass_offset > population.state_components - population.nbins) return false;
67  if (population.moment_mode == MomentMode::OneMoment) {
68  if (population.number_offset != -1 || population.pivots == nullptr) return false;
69  } else if (population.number_offset < 0 ||
70  population.number_offset > population.state_components - population.nbins) {
71  return false;
72  }
73  if (population.moment_mode == MomentMode::TwoMoment &&
74  population.number_offset < population.mass_offset + population.nbins &&
75  population.mass_offset < population.number_offset + population.nbins) {
76  return false;
77  }
78  return true;
79 }
80 
81 /** Full O(nbins + properties^2) validator for untrusted/arbitrary views. */
82 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
83 bool valid_population_remap_view (const PopulationRemapView& population) noexcept
84 {
85  if (!valid_population_remap_context(population)) return false;
86  for (int edge = 0; edge <= population.nbins; ++edge) {
87  const amrex::Real value = population.edges[edge];
88  if (!amrex::Math::isfinite(value) || value < amrex::Real(0.0) ||
89  (edge > 0 && !(population.edges[edge - 1] < value))) {
90  return false;
91  }
92  }
93  if (population.moment_mode == MomentMode::OneMoment) {
94  for (int bin = 0; bin < population.nbins; ++bin) {
95  const amrex::Real pivot = population.pivots[bin];
96  if (!amrex::Math::isfinite(pivot) || pivot <= amrex::Real(0.0) ||
97  pivot < population.edges[bin] || pivot > population.edges[bin + 1] ||
98  (bin > 0 && !(population.pivots[bin - 1] < pivot))) {
99  return false;
100  }
101  }
102  }
103  for (int property = 0; property < population.property_count; ++property) {
104  const int offset = population.property_component_offsets[property];
105  if (offset < 0 || offset > population.state_components - population.nbins ||
106  (offset < population.mass_offset + population.nbins &&
107  population.mass_offset < offset + population.nbins)) {
108  return false;
109  }
110  if (population.moment_mode == MomentMode::TwoMoment &&
111  offset < population.number_offset + population.nbins &&
112  population.number_offset < offset + population.nbins) {
113  return false;
114  }
115  for (int previous = 0; previous < property; ++previous) {
116  const int previous_offset = population.property_component_offsets[previous];
117  if (offset < previous_offset + population.nbins &&
118  previous_offset < offset + population.nbins) {
119  return false;
120  }
121  }
122  }
123  return true;
124 }
125 
126 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
128  const PopulationRemapView& right) noexcept
129 {
130  return left.population_id == right.population_id &&
131  left.moment_mode == right.moment_mode &&
132  left.coordinate_kind == right.coordinate_kind &&
133  left.edges == right.edges && left.pivots == right.pivots &&
134  left.nbins == right.nbins && left.mass_offset == right.mass_offset &&
135  left.number_offset == right.number_offset &&
136  left.state_components == right.state_components &&
137  left.property_component_offsets == right.property_component_offsets &&
138  left.property_count == right.property_count;
139 }
140 
142  int bin{-1};
145 };
146 
147 /** Device-safe, fixed-capacity route bound to its originating population view. */
156  bool normalized_roundoff{false};
157 };
158 
159 namespace remap_detail {
160 
163 
164 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
165 bool finite_nonnegative (const amrex::Real value) noexcept
166 {
167  return amrex::Math::isfinite(value) && value >= amrex::Real(0.0);
168 }
169 
170 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
172  amrex::Real& result) noexcept
173 {
174  if (!finite_nonnegative(left) || !finite_nonnegative(right)) return ProductStatus::Invalid;
175  result = left * right;
176  if (!amrex::Math::isfinite(result)) return ProductStatus::Invalid;
177  if (left > amrex::Real(0.0) && right > amrex::Real(0.0) &&
178  result == amrex::Real(0.0)) return ProductStatus::Underflow;
179  return ProductStatus::Ok;
180 }
181 
182 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
184  const amrex::Real denominator,
185  amrex::Real& result) noexcept
186 {
187  if (!finite_nonnegative(numerator) || !amrex::Math::isfinite(denominator) ||
188  !(denominator > amrex::Real(0.0))) return QuotientStatus::Invalid;
189  result = numerator / denominator;
190  if (!amrex::Math::isfinite(result)) return QuotientStatus::Invalid;
191  if (numerator > amrex::Real(0.0) && result == amrex::Real(0.0)) {
193  }
194  return QuotientStatus::Ok;
195 }
196 
197 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
198 bool finite_product (const amrex::Real left, const amrex::Real right,
199  amrex::Real& result) noexcept
200 {
201  return checked_product(left, right, result) == ProductStatus::Ok;
202 }
203 
204 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
206 {
209 }
210 
211 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
213 {
216 }
217 
218 /** Accept at most four representable steps outward from a global endpoint. */
219 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
220 bool within_boundary_roundoff (const amrex::Real value, const amrex::Real endpoint,
221  const bool lower_boundary) noexcept
222 {
223  const amrex::Real direction = lower_boundary ?
224  -std::numeric_limits<amrex::Real>::infinity() :
225  std::numeric_limits<amrex::Real>::infinity();
226  amrex::Real allowance = endpoint;
227  for (int step = 0; step < 4; ++step) {
228  allowance = std::nextafter(allowance, direction);
229  }
230  return lower_boundary ? value >= allowance : value <= allowance;
231 }
232 
233 /** Validate a route against its local claimed bin(s), without searching. */
234 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
236  const PacketRoutingPlan& plan) noexcept
237 {
238  if (!valid_population_remap_context(population) ||
239  !same_population_context(population, plan.population) ||
240  !amrex::Math::isfinite(plan.packet_number) ||
241  !amrex::Math::isfinite(plan.packet_particle_mass) ||
242  plan.packet_number < amrex::Real(0.0) || plan.packet_particle_mass < amrex::Real(0.0) ||
243  plan.destination_count < 0 || plan.destination_count > 2 ||
244  !finite_nonnegative(plan.residual_number_weight) ||
245  plan.residual_number_weight > amrex::Real(1.0)) return false;
246 
247  if (plan.packet_number == amrex::Real(0.0)) {
248  return plan.status == RemapStatus::Ok && plan.destination_count == 0 &&
249  plan.residual_number_weight == amrex::Real(0.0) &&
250  !plan.normalized_roundoff;
251  }
252 
253  if (population.moment_mode == MomentMode::TwoMoment) {
254  if (plan.packet_particle_mass == amrex::Real(0.0)) {
255  return plan.status == RemapStatus::ZeroWaterResidual &&
256  plan.destination_count == 0 &&
257  plan.residual_number_weight == amrex::Real(1.0) &&
258  !plan.normalized_roundoff;
259  }
260  const amrex::Real lower = population.edges[0];
261  const amrex::Real top = population.edges[population.nbins];
262  int required_bin = -1;
263  amrex::Real expected_mass = plan.packet_particle_mass;
264  bool normalized = false;
265  if (plan.packet_particle_mass < lower) {
266  if (!within_boundary_roundoff(plan.packet_particle_mass, lower, true)) return false;
267  required_bin = 0;
268  expected_mass = lower;
269  normalized = true;
270  } else if (plan.packet_particle_mass > top) {
271  if (!within_boundary_roundoff(plan.packet_particle_mass, top, false)) return false;
272  required_bin = population.nbins - 1;
273  expected_mass = top;
274  normalized = true;
275  }
276  if (plan.status != RemapStatus::Ok || plan.destination_count != 1 ||
277  plan.residual_number_weight != amrex::Real(0.0) ||
278  plan.normalized_roundoff != normalized) return false;
279  const auto& route = plan.destinations[0];
280  return (required_bin < 0 || route.bin == required_bin) &&
281  route.number_weight == amrex::Real(1.0) &&
282  route.particle_mass == expected_mass && route.bin >= 0 &&
283  route.bin < population.nbins && expected_mass >= population.edges[route.bin] &&
284  (expected_mass < population.edges[route.bin + 1] ||
285  (route.bin == population.nbins - 1 &&
286  expected_mass <= population.edges[route.bin + 1]));
287  }
288 
289  const amrex::Real first = population.pivots[0];
290  const amrex::Real last = population.pivots[population.nbins - 1];
291  if (plan.packet_particle_mass == amrex::Real(0.0)) {
292  return plan.status == RemapStatus::ZeroWaterResidual &&
293  plan.destination_count == 0 &&
294  plan.residual_number_weight == amrex::Real(1.0) &&
295  !plan.normalized_roundoff;
296  }
297  if (plan.packet_particle_mass < first) {
298  amrex::Real wet_weight = amrex::Real(0.0);
299  if (checked_quotient(plan.packet_particle_mass, first, wet_weight) !=
300  QuotientStatus::Ok) return false;
301  return plan.status == RemapStatus::ZeroWaterResidual && !plan.normalized_roundoff &&
302  plan.destination_count == 1 && plan.residual_number_weight ==
303  amrex::Real(1.0) - wet_weight && plan.destinations[0].bin == 0 &&
304  plan.destinations[0].number_weight == wet_weight &&
305  plan.destinations[0].particle_mass == first;
306  }
307  if (plan.packet_particle_mass > last || plan.status == RemapStatus::ZeroWaterResidual) {
308  return false;
309  }
310  if (plan.destination_count == 1) {
311  const int pivot_bin = plan.destinations[0].bin;
312  if (pivot_bin < 0 || pivot_bin >= population.nbins) return false;
313  return plan.status == RemapStatus::Ok && !plan.normalized_roundoff &&
314  plan.destination_count == 1 && plan.residual_number_weight == amrex::Real(0.0) &&
315  plan.packet_particle_mass == population.pivots[pivot_bin] &&
316  plan.destinations[0].bin == pivot_bin &&
317  plan.destinations[0].number_weight == amrex::Real(1.0) &&
318  plan.destinations[0].particle_mass == population.pivots[pivot_bin];
319  }
320  if (plan.destination_count != 2) return false;
321  const int lower_bin = plan.destinations[0].bin;
322  if (lower_bin < 0 || lower_bin + 1 >= population.nbins ||
323  plan.destinations[1].bin != lower_bin + 1) return false;
324  const amrex::Real lower = population.pivots[lower_bin];
325  const amrex::Real upper = population.pivots[lower_bin + 1];
326  if (!(plan.packet_particle_mass > lower && plan.packet_particle_mass < upper)) return false;
327  const amrex::Real width = upper - lower;
328  amrex::Real lower_weight = amrex::Real(0.0);
329  amrex::Real upper_weight = amrex::Real(0.0);
330  if (checked_quotient(upper - plan.packet_particle_mass, width, lower_weight) !=
332  checked_quotient(plan.packet_particle_mass - lower, width, upper_weight) !=
333  QuotientStatus::Ok) return false;
334  return plan.status == RemapStatus::Ok && !plan.normalized_roundoff &&
335  plan.destination_count == 2 && plan.residual_number_weight == amrex::Real(0.0) &&
336  plan.destinations[0].bin == lower_bin &&
337  plan.destinations[0].number_weight == lower_weight &&
338  plan.destinations[0].particle_mass == lower &&
339  plan.destinations[1].bin == lower_bin + 1 &&
340  plan.destinations[1].number_weight == upper_weight &&
341  plan.destinations[1].particle_mass == upper;
342 }
343 
344 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
346  const int bin, const amrex::Real number,
347  const amrex::Real mass) noexcept
348 {
349  if (!valid_population_remap_context(population) ||
350  population.moment_mode != MomentMode::TwoMoment || bin < 0 || bin >= population.nbins ||
351  !finite_nonnegative(number) || !finite_nonnegative(mass)) return false;
352  if (number == amrex::Real(0.0)) return mass == amrex::Real(0.0);
353  if (mass <= amrex::Real(0.0)) return false;
354  amrex::Real mean = amrex::Real(0.0);
355  if (checked_quotient(mass, number, mean) != QuotientStatus::Ok) return false;
356  const amrex::Real lower = population.edges[bin];
357  const amrex::Real upper = population.edges[bin + 1];
358  return amrex::Math::isfinite(mean) && mean >= lower &&
359  (mean < upper || (bin == population.nbins - 1 && mean <= upper));
360 }
361 
362 /** A component accessor for a cell in a component-major Array4. */
364  amrex::Array4<const amrex::Real> values;
365  int i{0};
366  int j{0};
367  int k{0};
368 
369  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
370  amrex::Real operator[] (const int component) const noexcept
371  {
372  return values(i, j, k, component);
373  }
374 };
375 
376 /** Strict persisted-state test shared by reconstruction, restart, and fixtures. */
377 template <typename StateView>
378 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
380  const int bin, const StateView& state,
381  const int state_count) noexcept
382 {
383  if (!valid_population_remap_context(population) ||
384  state_count != population.state_components || bin < 0 || bin >= population.nbins ||
385  population.moment_mode != MomentMode::TwoMoment) return false;
386  const amrex::Real mass = state[population.mass_offset + bin];
387  const amrex::Real number = state[population.number_offset + bin];
388  if (!canonical_two_moment_moments(population, bin, number, mass)) return false;
389  if (number == amrex::Real(0.0)) {
390  for (int property = 0; property < population.property_count; ++property) {
391  if (state[population.property_component_offsets[property] + bin] != amrex::Real(0.0)) {
392  return false;
393  }
394  }
395  }
396  for (int property = 0; property < population.property_count; ++property) {
397  const amrex::Real amount = state[population.property_component_offsets[property] + bin];
398  if (!finite_nonnegative(amount)) return false;
399  if (number == amrex::Real(0.0)) continue;
400  amrex::Real per_particle = amrex::Real(0.0);
401  if (checked_quotient(amount, number, per_particle) != QuotientStatus::Ok) return false;
402  }
403  return true;
404 }
405 
406 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
408  const int bin, const amrex::Real* state,
409  const int state_count) noexcept
410 {
411  if (state == nullptr) return false;
412  return canonical_two_moment_bin_state_view(population, bin, state, state_count);
413 }
414 
415 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
417  const int bin,
418  const RemapCellStateView& state,
419  const int state_count) noexcept
420 {
421  return canonical_two_moment_bin_state_view(population, bin, state, state_count);
422 }
423 
424 /** Strict persisted-state test for a one-moment bin and its attached inventories. */
425 template <typename StateView>
426 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
428  const int bin, const StateView& state,
429  const int state_count) noexcept
430 {
431  if (!valid_population_remap_context(population) ||
432  state_count != population.state_components || bin < 0 || bin >= population.nbins ||
433  population.moment_mode != MomentMode::OneMoment) return false;
434  const amrex::Real mass = state[population.mass_offset + bin];
435  if (!finite_nonnegative(mass)) return false;
436  amrex::Real number = amrex::Real(0.0);
437  if (checked_quotient(mass, population.pivots[bin], number) != QuotientStatus::Ok) {
438  return false;
439  }
440  for (int property = 0; property < population.property_count; ++property) {
441  const amrex::Real amount = state[population.property_component_offsets[property] + bin];
442  if (!finite_nonnegative(amount)) return false;
443  if (number == amrex::Real(0.0)) {
444  if (amount != amrex::Real(0.0)) return false;
445  } else {
446  amrex::Real per_particle = amrex::Real(0.0);
447  if (checked_quotient(amount, number, per_particle) != QuotientStatus::Ok) {
448  return false;
449  }
450  }
451  }
452  return true;
453 }
454 
455 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
457  const int bin, const amrex::Real* state,
458  const int state_count) noexcept
459 {
460  if (state == nullptr) return false;
461  return canonical_one_moment_bin_state_view(population, bin, state, state_count);
462 }
463 
464 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
466  const int bin,
467  const RemapCellStateView& state,
468  const int state_count) noexcept
469 {
470  return canonical_one_moment_bin_state_view(population, bin, state, state_count);
471 }
472 
473 /** Shared admission rule for every persisted bin accepted by reconstruction. */
474 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
476  const int bin, const amrex::Real* state,
477  const int state_count) noexcept
478 {
479  return population.moment_mode == MomentMode::OneMoment ?
480  canonical_one_moment_bin_state(population, bin, state, state_count) :
481  canonical_two_moment_bin_state(population, bin, state, state_count);
482 }
483 
484 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
486  const int bin,
487  const RemapCellStateView& state,
488  const int state_count) noexcept
489 {
490  return population.moment_mode == MomentMode::OneMoment ?
491  canonical_one_moment_bin_state(population, bin, state, state_count) :
492  canonical_two_moment_bin_state(population, bin, state, state_count);
493 }
494 
495 } // namespace remap_detail
496 
497 /**
498  * Classify one packet without allocation or state mutation. Two-moment routing
499  * uses half-open intervals, includes the global top edge, and admits only four
500  * representable steps of outer-boundary roundoff. The plan keeps the original
501  * packet mass while its destination records the exact boundary mass. The
502  * immutable metadata must have been fully qualified once before this call;
503  * the per-packet path performs only constant-time context checks and a
504  * logarithmic interval search.
505  */
506 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
508  const amrex::Real packet_number,
509  const amrex::Real particle_mass) noexcept
510 {
511  PacketRoutingPlan plan;
512  plan.population = population;
513  plan.packet_number = packet_number;
514  plan.packet_particle_mass = particle_mass;
515  if (!valid_population_remap_context(population) ||
516  !amrex::Math::isfinite(packet_number) || !amrex::Math::isfinite(particle_mass) ||
517  packet_number < amrex::Real(0.0) || particle_mass < amrex::Real(0.0)) {
518  return plan;
519  }
520  if (packet_number == amrex::Real(0.0)) {
521  plan.status = RemapStatus::Ok;
522  return plan;
523  }
524 
525  if (population.moment_mode == MomentMode::TwoMoment) {
526  // A dry positive-number packet is residual material, not a populated
527  // zero-water liquid interval, even when the first edge is exactly zero.
528  if (particle_mass == amrex::Real(0.0)) {
531  return plan;
532  }
533  if (particle_mass < population.edges[0]) {
535  particle_mass, population.edges[0], true)) {
537  return plan;
538  }
539  plan.status = RemapStatus::Ok;
540  plan.destination_count = 1;
541  plan.destinations[0] = {0, amrex::Real(1.0), population.edges[0]};
542  plan.normalized_roundoff = true;
543  return plan;
544  }
545  if (particle_mass > population.edges[population.nbins]) {
547  particle_mass, population.edges[population.nbins], false)) {
549  return plan;
550  }
551  plan.status = RemapStatus::Ok;
552  plan.destination_count = 1;
553  plan.destinations[0] = {population.nbins - 1, amrex::Real(1.0),
554  population.edges[population.nbins]};
555  plan.normalized_roundoff = true;
556  return plan;
557  }
558  const int bin = particle_mass == population.edges[population.nbins] ?
559  population.nbins - 1 :
560  amrex::bisect(population.edges, 0, population.nbins, particle_mass);
561  plan.status = RemapStatus::Ok;
562  plan.destination_count = 1;
563  plan.destinations[0] = {bin, amrex::Real(1.0), particle_mass};
564  return plan;
565  }
566 
567  if (particle_mass == amrex::Real(0.0)) {
570  return plan;
571  }
572  if (particle_mass < population.pivots[0]) {
573  amrex::Real wet_weight = amrex::Real(0.0);
574  const auto quotient_status = remap_detail::checked_quotient(
575  particle_mass, population.pivots[0], wet_weight);
576  if (quotient_status != remap_detail::QuotientStatus::Ok) {
577  plan.status = remap_detail::remap_quotient_status(quotient_status);
578  return plan;
579  }
581  plan.destination_count = 1;
582  plan.destinations[0] = {0, wet_weight, population.pivots[0]};
583  plan.residual_number_weight = amrex::Real(1.0) - wet_weight;
584  return plan;
585  }
586  if (particle_mass > population.pivots[population.nbins - 1]) {
588  return plan;
589  }
590  const int lower_bin = particle_mass == population.pivots[population.nbins - 1] ?
591  population.nbins - 1 :
592  amrex::bisect(population.pivots, 0, population.nbins - 1, particle_mass);
593  if (particle_mass == population.pivots[lower_bin]) {
594  plan.status = RemapStatus::Ok;
595  plan.destination_count = 1;
596  plan.destinations[0] = {lower_bin, amrex::Real(1.0), population.pivots[lower_bin]};
597  return plan;
598  }
599  if (lower_bin + 1 < population.nbins) {
600  const amrex::Real lower = population.pivots[lower_bin];
601  const amrex::Real upper = population.pivots[lower_bin + 1];
602  const amrex::Real width = upper - lower;
603  amrex::Real lower_weight = amrex::Real(0.0);
604  amrex::Real upper_weight = amrex::Real(0.0);
605  const auto lower_status = remap_detail::checked_quotient(
606  upper - particle_mass, width, lower_weight);
607  const auto upper_status = remap_detail::checked_quotient(
608  particle_mass - lower, width, upper_weight);
609  if (lower_status != remap_detail::QuotientStatus::Ok ||
610  upper_status != remap_detail::QuotientStatus::Ok) {
611  const auto quotient_status =
612  lower_status != remap_detail::QuotientStatus::Ok ? lower_status : upper_status;
613  plan.status = remap_detail::remap_quotient_status(quotient_status);
614  return plan;
615  }
616  plan.status = RemapStatus::Ok;
617  plan.destination_count = 2;
618  plan.destinations[0] = {lower_bin, lower_weight, lower};
619  plan.destinations[1] = {lower_bin + 1, upper_weight, upper};
620  return plan;
621  }
622  return plan;
623 }
624 
630  bool normalized_roundoff{false};
631 };
632 
633 /**
634  * Allocation-free packet application. The first pass validates the complete
635  * plan, residual outputs, and every candidate increment. The second pass
636  * commits only after all failure paths have been ruled out. Input/output and
637  * property/state storage supplied by the caller must not overlap.
638  */
639 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
641  const PopulationRemapView population, const PacketRoutingPlan plan,
642  const amrex::Real* property_packet_amounts, const int property_count,
643  amrex::Real* residual_properties, const int residual_capacity,
644  amrex::Real* candidate_state, const int state_count) noexcept
645 {
647  result.status = plan.status;
648  if (plan.status != RemapStatus::Ok && plan.status != RemapStatus::ZeroWaterResidual) {
649  return result;
650  }
651  if (!valid_population_remap_context(population) ||
652  !same_population_context(population, plan.population) ||
653  !amrex::Math::isfinite(plan.packet_number) ||
654  !amrex::Math::isfinite(plan.packet_particle_mass) ||
655  plan.packet_number < amrex::Real(0.0) || plan.packet_particle_mass < amrex::Real(0.0) ||
656  property_count != population.property_count ||
657  (property_count > 0 && (property_packet_amounts == nullptr || residual_properties == nullptr)) ||
658  residual_capacity < property_count || candidate_state == nullptr ||
659  state_count != population.state_components || plan.destination_count < 0 ||
660  plan.destination_count > 2 || !amrex::Math::isfinite(plan.residual_number_weight) ||
661  plan.residual_number_weight < amrex::Real(0.0) ||
662  plan.residual_number_weight > amrex::Real(1.0)) {
663  result.status = RemapStatus::Invalid;
664  return result;
665  }
666  if (!remap_detail::route_plan_is_consistent(population, plan)) {
667  result.status = RemapStatus::Invalid;
668  return result;
669  }
670  for (int property = 0; property < property_count; ++property) {
671  if (!remap_detail::finite_nonnegative(property_packet_amounts[property])) {
672  result.status = RemapStatus::Invalid;
673  return result;
674  }
675  }
676  if (plan.packet_number == amrex::Real(0.0)) {
677  for (int property = 0; property < property_count; ++property) {
678  if (property_packet_amounts[property] != amrex::Real(0.0)) {
679  result.status = RemapStatus::Invalid;
680  return result;
681  }
682  }
683  }
684  amrex::Real weight_sum = plan.residual_number_weight;
685  for (int destination = 0; destination < plan.destination_count; ++destination) {
686  const auto& route = plan.destinations[destination];
687  if (route.bin < 0 || route.bin >= population.nbins ||
688  !amrex::Math::isfinite(route.number_weight) || route.number_weight < amrex::Real(0.0) ||
689  route.number_weight > amrex::Real(1.0) ||
690  !remap_detail::finite_nonnegative(route.particle_mass)) {
691  result.status = RemapStatus::Invalid;
692  return result;
693  }
694  for (int previous = 0; previous < destination; ++previous) {
695  if (plan.destinations[previous].bin == route.bin) {
696  result.status = RemapStatus::Invalid;
697  return result;
698  }
699  }
700  weight_sum += route.number_weight;
701  }
702  if (plan.packet_number > amrex::Real(0.0)) {
703  const amrex::Real weight_tolerance = amrex::Real(16.0) *
705  if (!amrex::Math::isfinite(weight_sum) ||
706  std::abs(weight_sum - amrex::Real(1.0)) > weight_tolerance ||
707  ((plan.status == RemapStatus::ZeroWaterResidual) !=
708  (plan.residual_number_weight > amrex::Real(0.0)))) {
709  result.status = RemapStatus::Invalid;
710  return result;
711  }
712  } else if (plan.destination_count != 0 ||
713  plan.residual_number_weight != amrex::Real(0.0)) {
714  result.status = RemapStatus::Invalid;
715  return result;
716  }
717 
718  amrex::Real residual_number = amrex::Real(0.0);
719  auto product_status = remap_detail::checked_product(
720  plan.packet_number, plan.residual_number_weight, residual_number);
721  if (product_status != remap_detail::ProductStatus::Ok) {
722  result.status = remap_detail::remap_product_status(product_status);
723  return result;
724  }
725  amrex::Real roundoff_water_mass_correction = amrex::Real(0.0);
726  // Validate all outputs and candidate increments before changing either.
727  for (int property = 0; property < property_count; ++property) {
728  amrex::Real residual = amrex::Real(0.0);
729  product_status = remap_detail::checked_product(
730  property_packet_amounts[property], plan.residual_number_weight, residual);
731  if (product_status != remap_detail::ProductStatus::Ok) {
732  result.status = remap_detail::remap_product_status(product_status);
733  return result;
734  }
735  }
736  for (int destination = 0; destination < plan.destination_count; ++destination) {
737  const auto& route = plan.destinations[destination];
738  amrex::Real number_increment = amrex::Real(0.0);
739  amrex::Real mass_increment = amrex::Real(0.0);
740  const amrex::Real deposited_mass = route.particle_mass;
741  product_status = remap_detail::checked_product(
742  plan.packet_number, route.number_weight, number_increment);
743  if (product_status == remap_detail::ProductStatus::Ok) {
744  product_status = remap_detail::checked_product(
745  number_increment, deposited_mass, mass_increment);
746  }
747  if (product_status != remap_detail::ProductStatus::Ok) {
748  result.status = remap_detail::remap_product_status(product_status);
749  return result;
750  }
751  if (population.moment_mode == MomentMode::TwoMoment && plan.normalized_roundoff) {
752  const amrex::Real mass_delta =
753  route.particle_mass - plan.packet_particle_mass;
754  if (!amrex::Math::isfinite(mass_delta) ||
755  mass_delta == amrex::Real(0.0)) {
756  result.status = RemapStatus::Invalid;
757  return result;
758  }
759  const amrex::Real mass_delta_magnitude = mass_delta < amrex::Real(0.0) ?
760  -mass_delta : mass_delta;
761  amrex::Real correction_magnitude = amrex::Real(0.0);
762  product_status = remap_detail::checked_product(
763  plan.packet_number, mass_delta_magnitude, correction_magnitude);
764  if (product_status != remap_detail::ProductStatus::Ok) {
765  result.status = remap_detail::remap_product_status(product_status);
766  return result;
767  }
768  const amrex::Real correction = mass_delta < amrex::Real(0.0) ?
769  -correction_magnitude : correction_magnitude;
770  if (!amrex::Math::isfinite(correction) ||
771  !amrex::Math::isfinite(roundoff_water_mass_correction + correction)) {
772  result.status = RemapStatus::Invalid;
773  return result;
774  }
775  roundoff_water_mass_correction += correction;
776  }
777  const int mass_component = population.mass_offset + route.bin;
778  const amrex::Real old_mass = candidate_state[mass_component];
779  if (!remap_detail::finite_nonnegative(old_mass) ||
780  !remap_detail::finite_nonnegative(old_mass + mass_increment)) {
781  result.status = RemapStatus::Invalid;
782  return result;
783  }
784  const amrex::Real next_mass = old_mass + mass_increment;
785  amrex::Real next_number = amrex::Real(0.0);
786  if (population.number_offset >= 0) {
787  const int number_component = population.number_offset + route.bin;
788  const amrex::Real old_number = candidate_state[number_component];
789  if (!remap_detail::finite_nonnegative(old_number)) {
790  result.status = RemapStatus::Invalid;
791  return result;
792  }
793  next_number = old_number + number_increment;
794  if (!remap_detail::finite_nonnegative(next_number)) {
795  result.status = RemapStatus::Invalid;
796  return result;
797  }
798  if (next_number == amrex::Real(0.0)) {
799  if (next_mass != amrex::Real(0.0)) {
800  result.status = RemapStatus::Invalid;
801  return result;
802  }
803  } else {
804  amrex::Real mean = amrex::Real(0.0);
805  const auto quotient_status = remap_detail::checked_quotient(
806  next_mass, next_number, mean);
807  if (quotient_status != remap_detail::QuotientStatus::Ok) {
808  result.status = remap_detail::remap_quotient_status(quotient_status);
809  return result;
810  }
812  population, route.bin, next_number, next_mass)) {
813  result.status = RemapStatus::Invalid;
814  return result;
815  }
816  }
817  } else {
818  const auto quotient_status = remap_detail::checked_quotient(
819  next_mass, population.pivots[route.bin], next_number);
820  if (quotient_status != remap_detail::QuotientStatus::Ok) {
821  result.status = remap_detail::remap_quotient_status(quotient_status);
822  return result;
823  }
824  }
825  for (int property = 0; property < property_count; ++property) {
826  amrex::Real increment = amrex::Real(0.0);
827  const int component = population.property_component_offsets[property] + route.bin;
828  const amrex::Real old_value = candidate_state[component];
829  product_status = remap_detail::checked_product(
830  property_packet_amounts[property], route.number_weight, increment);
831  if (product_status != remap_detail::ProductStatus::Ok) {
832  result.status = remap_detail::remap_product_status(product_status);
833  return result;
834  }
835  if (!remap_detail::finite_nonnegative(old_value)) {
836  result.status = RemapStatus::Invalid;
837  return result;
838  }
839  const amrex::Real next_value = old_value + increment;
840  if (!remap_detail::finite_nonnegative(next_value)) {
841  result.status = RemapStatus::Invalid;
842  return result;
843  }
844  if (next_number == amrex::Real(0.0)) {
845  if (next_value != amrex::Real(0.0)) {
846  result.status = RemapStatus::Invalid;
847  return result;
848  }
849  } else {
850  amrex::Real per_particle = amrex::Real(0.0);
851  const auto quotient_status = remap_detail::checked_quotient(
852  next_value, next_number, per_particle);
853  if (quotient_status != remap_detail::QuotientStatus::Ok) {
854  result.status = remap_detail::remap_quotient_status(quotient_status);
855  return result;
856  }
857  }
858  }
859  }
860 
861  for (int destination = 0; destination < plan.destination_count; ++destination) {
862  const auto& route = plan.destinations[destination];
863  const amrex::Real number_increment = plan.packet_number * route.number_weight;
864  const amrex::Real deposited_mass = route.particle_mass;
865  candidate_state[population.mass_offset + route.bin] += number_increment * deposited_mass;
866  if (population.number_offset >= 0) {
867  candidate_state[population.number_offset + route.bin] += number_increment;
868  }
869  for (int property = 0; property < property_count; ++property) {
870  candidate_state[population.property_component_offsets[property] + route.bin] +=
871  property_packet_amounts[property] * route.number_weight;
872  }
873  }
874  for (int property = 0; property < property_count; ++property) {
875  residual_properties[property] =
876  property_packet_amounts[property] * plan.residual_number_weight;
877  }
878  result.residual_number = residual_number;
879  result.residual_water_mass = amrex::Real(0.0);
880  result.roundoff_water_mass_correction = roundoff_water_mass_correction;
881  result.normalized_roundoff = plan.normalized_roundoff;
882  return result;
883 }
884 
885 /** Non-owning result from allocation-free one-bin reconstruction. */
888  int bin{-1};
889  bool empty{true};
890  bool normalized_roundoff{false};
895 };
896 
897 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
899  const PopulationRemapView population, const int bin,
900  const amrex::Real* persisted_state, const int state_count,
901  amrex::Real* property_per_particle, const int property_capacity,
902  ReconstructionDeltaView& reconstruction) noexcept
903 {
904  if (!valid_population_remap_context(population) || bin < 0 || bin >= population.nbins ||
905  persisted_state == nullptr || state_count != population.state_components ||
906  property_capacity < population.property_count ||
907  (population.property_count > 0 && property_per_particle == nullptr)) {
909  }
911  population, bin, persisted_state, state_count)) {
913  }
914  const amrex::Real mass = persisted_state[population.mass_offset + bin];
917  next.population = population;
918  next.bin = bin;
919  next.property_per_particle = property_per_particle;
920  next.property_count = population.property_count;
921  if (population.moment_mode == MomentMode::OneMoment) {
922  next.particle_mass = population.pivots[bin];
926  }
927  } else {
928  const amrex::Real number = persisted_state[population.number_offset + bin];
931  population, bin, persisted_state, state_count)) {
933  }
934  const amrex::Real lower = population.edges[bin];
935  const amrex::Real upper = population.edges[bin + 1];
936  EndpointTransform endpoints;
937  if (!try_two_moment_to_endpoints(number, mass, lower, upper, endpoints)) {
939  }
940  next.number = number;
941  // Endpoint tolerance is only a realizability aid for the transform.
942  // Persisted state has already passed strict canonical ownership and is
943  // never normalized by no-process reconstruction.
944  next.normalized_roundoff = false;
945  if (number > amrex::Real(0.0)) {
947  mass, number, next.particle_mass) != remap_detail::QuotientStatus::Ok) {
949  }
950  }
951  }
952  next.empty = next.number == amrex::Real(0.0);
953  if (next.empty) next.particle_mass = amrex::Real(0.0);
954  for (int property = 0; property < population.property_count; ++property) {
955  const amrex::Real amount = persisted_state[
956  population.property_component_offsets[property] + bin];
958  if (next.empty) {
959  if (amount != amrex::Real(0.0)) return ReconstructionStatus::Invalid;
960  } else {
961  amrex::Real per_particle = amrex::Real(0.0);
963  amount, next.number, per_particle) != remap_detail::QuotientStatus::Ok) {
965  }
966  }
967  }
968  for (int property = 0; property < population.property_count; ++property) {
969  const amrex::Real amount = persisted_state[
970  population.property_component_offsets[property] + bin];
971  if (next.empty) {
972  property_per_particle[property] = amrex::Real(0.0);
974  amount, next.number, property_per_particle[property]) !=
977  }
978  }
979  reconstruction = next;
981 }
982 
986 };
987 
988 /** Integrate one reconstructed delta over [lower, upper), optionally including upper. */
989 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
991  const amrex::Real lower, const amrex::Real upper,
992  const bool include_upper, amrex::Real* attached_properties,
993  const int property_capacity,
994  IntegratedMomentsCoreResult& integral) noexcept
995 {
996  if (!amrex::Math::isfinite(lower) || !amrex::Math::isfinite(upper) || !(lower < upper) ||
997  !remap_detail::finite_nonnegative(reconstruction.number) ||
998  !remap_detail::finite_nonnegative(reconstruction.particle_mass) ||
999  reconstruction.property_count < 0 || property_capacity < reconstruction.property_count ||
1000  (reconstruction.property_count > 0 &&
1001  (reconstruction.property_per_particle == nullptr || attached_properties == nullptr)) ||
1002  (!reconstruction.empty && reconstruction.number <= amrex::Real(0.0)) ||
1003  (reconstruction.empty && (reconstruction.number != amrex::Real(0.0) ||
1004  reconstruction.particle_mass != amrex::Real(0.0)))) {
1005  return false;
1006  }
1007  for (int property = 0; property < reconstruction.property_count; ++property) {
1008  const amrex::Real value = reconstruction.property_per_particle[property];
1009  if (!remap_detail::finite_nonnegative(value) ||
1010  (reconstruction.empty && value != amrex::Real(0.0))) {
1011  return false;
1012  }
1013  }
1014  const bool contains = !reconstruction.empty && reconstruction.particle_mass >= lower &&
1015  (reconstruction.particle_mass < upper ||
1016  (include_upper && reconstruction.particle_mass == upper));
1018  if (contains) {
1019  next.number = reconstruction.number;
1020  if (!remap_detail::finite_product(reconstruction.number,
1021  reconstruction.particle_mass, next.water_mass)) {
1022  return false;
1023  }
1024  for (int property = 0; property < reconstruction.property_count; ++property) {
1025  amrex::Real amount = amrex::Real(0.0);
1026  if (!remap_detail::finite_product(reconstruction.number,
1027  reconstruction.property_per_particle[property], amount)) {
1028  return false;
1029  }
1030  }
1031  }
1032  for (int property = 0; property < reconstruction.property_count; ++property) {
1033  amrex::Real amount = amrex::Real(0.0);
1034  if (contains && !remap_detail::finite_product(reconstruction.number,
1035  reconstruction.property_per_particle[property], amount)) {
1036  return false;
1037  }
1038  attached_properties[property] = amount;
1039  }
1040  integral = next;
1041  return true;
1042 }
1043 
1044 /** Project one reconstructed bin into caller-owned state after full validation. */
1045 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
1047  const ReconstructionDeltaView reconstruction,
1048  amrex::Real* candidate_state,
1049  const int state_count) noexcept
1050 {
1051  if (!valid_population_remap_context(population) ||
1052  !same_population_context(population, reconstruction.population) ||
1053  reconstruction.bin < 0 || reconstruction.bin >= population.nbins ||
1054  candidate_state == nullptr || state_count != population.state_components ||
1055  reconstruction.property_count != population.property_count ||
1056  (reconstruction.property_count > 0 && reconstruction.property_per_particle == nullptr) ||
1057  !remap_detail::finite_nonnegative(reconstruction.number) ||
1058  !remap_detail::finite_nonnegative(reconstruction.particle_mass) ||
1059  (!reconstruction.empty && reconstruction.number <= amrex::Real(0.0)) ||
1060  (reconstruction.empty && (reconstruction.number != amrex::Real(0.0) ||
1061  reconstruction.particle_mass != amrex::Real(0.0)))) {
1062  return false;
1063  }
1064  if (!reconstruction.empty) {
1065  if (population.moment_mode == MomentMode::OneMoment) {
1066  if (reconstruction.particle_mass != population.pivots[reconstruction.bin]) return false;
1067  }
1068  }
1069  amrex::Real mass = amrex::Real(0.0);
1070  if (!remap_detail::finite_product(reconstruction.number,
1071  reconstruction.particle_mass, mass)) {
1072  return false;
1073  }
1074  amrex::Real projected_number = reconstruction.number;
1075  if (population.moment_mode == MomentMode::OneMoment) {
1077  mass, population.pivots[reconstruction.bin], projected_number) !=
1078  remap_detail::QuotientStatus::Ok) return false;
1080  population, reconstruction.bin, projected_number, mass)) {
1081  return false;
1082  }
1083  for (int property = 0; property < population.property_count; ++property) {
1084  const amrex::Real value = reconstruction.property_per_particle[property];
1085  amrex::Real amount = amrex::Real(0.0);
1086  if (!remap_detail::finite_nonnegative(value) ||
1087  (reconstruction.empty && value != amrex::Real(0.0)) ||
1088  !remap_detail::finite_product(reconstruction.number, value, amount)) {
1089  return false;
1090  }
1091  if (projected_number == amrex::Real(0.0)) {
1092  if (amount != amrex::Real(0.0)) return false;
1093  } else {
1094  amrex::Real per_particle = amrex::Real(0.0);
1096  amount, projected_number, per_particle) !=
1097  remap_detail::QuotientStatus::Ok) return false;
1098  }
1099  }
1100  const int bin = reconstruction.bin;
1101  candidate_state[population.mass_offset + bin] = mass;
1102  if (population.number_offset >= 0) {
1103  candidate_state[population.number_offset + bin] = reconstruction.number;
1104  }
1105  for (int property = 0; property < population.property_count; ++property) {
1106  candidate_state[population.property_component_offsets[property] + bin] =
1107  reconstruction.number * reconstruction.property_per_particle[property];
1108  }
1109  return true;
1110 }
1111 
1112 // Host-owning reference adapters. These allocate diagnostic vectors as
1113 // convenient for tests, then delegate every numerical operation to the POD
1114 // routines above.
1120  bool normalized_roundoff{false};
1121  std::vector<amrex::Real> residual_properties;
1122 };
1123 
1124 [[nodiscard]] PacketApplicationResult
1125 apply_packet_routing (const SBMLayout& layout, const PacketRoutingPlan& plan,
1126  const std::vector<amrex::Real>& property_packet_amounts,
1127  std::vector<amrex::Real>& candidate_state);
1128 
1130  int population_id{-1};
1131  int bin{-1};
1132  bool empty{true};
1133  bool normalized_roundoff{false};
1136  std::vector<amrex::Real> property_per_particle;
1137 };
1138 
1139 [[nodiscard]] ReconstructionStatus
1140 reconstruct_bin (const SBMLayout& layout, int population_id, int bin,
1141  const std::vector<amrex::Real>& persisted_state,
1142  ReconstructionDelta& reconstruction);
1143 
1147  std::vector<amrex::Real> attached_properties;
1148 };
1149 
1150 [[nodiscard]] bool
1151 integrate_interval (const ReconstructionDelta& reconstruction,
1152  amrex::Real lower, amrex::Real upper, bool include_upper,
1153  IntegratedMoments& integral);
1154 
1155 [[nodiscard]] bool
1156 project_reconstruction_bin (const SBMLayout& layout,
1157  const ReconstructionDelta& reconstruction,
1158  std::vector<amrex::Real>& candidate_state);
1159 
1160 } // namespace erf_sbm
1161 
1162 #endif
AMREX_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:32
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_SBMLayout.H:94
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool canonical_two_moment_moments(const PopulationRemapView &population, const int bin, const amrex::Real number, const amrex::Real mass) noexcept
Definition: ERF_SBMRemapping.H:345
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool canonical_two_moment_bin_state(const PopulationRemapView &population, const int bin, const amrex::Real *state, const int state_count) noexcept
Definition: ERF_SBMRemapping.H:407
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE ProductStatus checked_product(const amrex::Real left, const amrex::Real right, amrex::Real &result) noexcept
Definition: ERF_SBMRemapping.H:171
ProductStatus
Definition: ERF_SBMRemapping.H:161
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool within_boundary_roundoff(const amrex::Real value, const amrex::Real endpoint, const bool lower_boundary) noexcept
Definition: ERF_SBMRemapping.H:220
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool canonical_one_moment_bin_state_view(const PopulationRemapView &population, const int bin, const StateView &state, const int state_count) noexcept
Definition: ERF_SBMRemapping.H:427
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool finite_nonnegative(const amrex::Real value) noexcept
Definition: ERF_SBMRemapping.H:165
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE RemapStatus remap_product_status(const ProductStatus status) noexcept
Definition: ERF_SBMRemapping.H:205
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool route_plan_is_consistent(const PopulationRemapView &population, const PacketRoutingPlan &plan) noexcept
Definition: ERF_SBMRemapping.H:235
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool canonical_persisted_bin_state(const PopulationRemapView &population, const int bin, const amrex::Real *state, const int state_count) noexcept
Definition: ERF_SBMRemapping.H:475
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE QuotientStatus checked_quotient(const amrex::Real numerator, const amrex::Real denominator, amrex::Real &result) noexcept
Definition: ERF_SBMRemapping.H:183
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE RemapStatus remap_quotient_status(const QuotientStatus status) noexcept
Definition: ERF_SBMRemapping.H:212
QuotientStatus
Definition: ERF_SBMRemapping.H:162
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool canonical_two_moment_bin_state_view(const PopulationRemapView &population, const int bin, const StateView &state, const int state_count) noexcept
Definition: ERF_SBMRemapping.H:379
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool finite_product(const amrex::Real left, const amrex::Real right, amrex::Real &result) noexcept
Definition: ERF_SBMRemapping.H:198
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool canonical_one_moment_bin_state(const PopulationRemapView &population, const int bin, const amrex::Real *state, const int state_count) noexcept
Definition: ERF_SBMRemapping.H:456
Definition: ERF.H:108
bool project_reconstruction_bin(const SBMLayout &layout, const ReconstructionDelta &reconstruction, std::vector< amrex::Real > &candidate_state)
Definition: ERF_SBMRemapping.cpp:125
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool project_reconstruction_bin_core(const PopulationRemapView population, const ReconstructionDeltaView reconstruction, amrex::Real *candidate_state, const int state_count) noexcept
Definition: ERF_SBMRemapping.H:1046
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool try_two_moment_to_endpoints(const amrex::Real C, const amrex::Real M, const amrex::Real lower, const amrex::Real upper, EndpointTransform &result) noexcept
Definition: ERF_SpectralGrid.H:48
bool integrate_interval(const ReconstructionDelta &reconstruction, const amrex::Real lower, const amrex::Real upper, const bool include_upper, IntegratedMoments &integral)
Definition: ERF_SBMRemapping.cpp:98
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool integrate_interval_core(const ReconstructionDeltaView reconstruction, const amrex::Real lower, const amrex::Real upper, const bool include_upper, amrex::Real *attached_properties, const int property_capacity, IntegratedMomentsCoreResult &integral) noexcept
Definition: ERF_SBMRemapping.H:990
RemapStatus
Definition: ERF_SBMRemapping.H:18
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE PacketRoutingPlan plan_packet_routing(const PopulationRemapView population, const amrex::Real packet_number, const amrex::Real particle_mass) noexcept
Definition: ERF_SBMRemapping.H:507
MomentMode
Definition: ERF_SpectralGrid.H:19
PacketApplicationResult apply_packet_routing(const SBMLayout &layout, const PacketRoutingPlan &plan, const std::vector< amrex::Real > &property_packet_amounts, std::vector< amrex::Real > &candidate_state)
Definition: ERF_SBMRemapping.cpp:40
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool same_population_context(const PopulationRemapView &left, const PopulationRemapView &right) noexcept
Definition: ERF_SBMRemapping.H:127
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE ReconstructionStatus reconstruct_bin_core(const PopulationRemapView population, const int bin, const amrex::Real *persisted_state, const int state_count, amrex::Real *property_per_particle, const int property_capacity, ReconstructionDeltaView &reconstruction) noexcept
Definition: ERF_SBMRemapping.H:898
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool valid_population_remap_context(const PopulationRemapView &population) noexcept
Definition: ERF_SBMRemapping.H:53
ReconstructionStatus reconstruct_bin(const SBMLayout &layout, const int population_id, const int bin, const std::vector< amrex::Real > &persisted_state, ReconstructionDelta &reconstruction)
Definition: ERF_SBMRemapping.cpp:72
CoordinateKind
Definition: ERF_SpectralGrid.H:18
PopulationRemapView population_remap_view(const SBMLayout &layout, const int population_id) noexcept
Definition: ERF_SBMRemapping.cpp:8
ReconstructionStatus
Definition: ERF_SBMRemapping.H:21
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool valid_population_remap_view(const PopulationRemapView &population) noexcept
Definition: ERF_SBMRemapping.H:83
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE PacketApplicationCoreResult apply_packet_routing_core(const PopulationRemapView population, const PacketRoutingPlan plan, const amrex::Real *property_packet_amounts, const int property_count, amrex::Real *residual_properties, const int residual_capacity, amrex::Real *candidate_state, const int state_count) noexcept
Definition: ERF_SBMRemapping.H:640
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
Definition: ERF_SpectralGrid.H:34
Definition: ERF_SBMRemapping.H:983
amrex::Real water_mass
Definition: ERF_SBMRemapping.H:985
amrex::Real number
Definition: ERF_SBMRemapping.H:984
Definition: ERF_SBMRemapping.H:1144
amrex::Real water_mass
Definition: ERF_SBMRemapping.H:1146
amrex::Real number
Definition: ERF_SBMRemapping.H:1145
std::vector< amrex::Real > attached_properties
Definition: ERF_SBMRemapping.H:1147
Definition: ERF_SBMRemapping.H:625
amrex::Real residual_number
Definition: ERF_SBMRemapping.H:627
RemapStatus status
Definition: ERF_SBMRemapping.H:626
amrex::Real roundoff_water_mass_correction
Definition: ERF_SBMRemapping.H:629
amrex::Real residual_water_mass
Definition: ERF_SBMRemapping.H:628
bool normalized_roundoff
Definition: ERF_SBMRemapping.H:630
Definition: ERF_SBMRemapping.H:1115
bool normalized_roundoff
Definition: ERF_SBMRemapping.H:1120
std::vector< amrex::Real > residual_properties
Definition: ERF_SBMRemapping.H:1121
amrex::Real residual_number
Definition: ERF_SBMRemapping.H:1117
amrex::Real residual_water_mass
Definition: ERF_SBMRemapping.H:1118
RemapStatus status
Definition: ERF_SBMRemapping.H:1116
amrex::Real roundoff_water_mass_correction
Definition: ERF_SBMRemapping.H:1119
Definition: ERF_SBMRemapping.H:148
amrex::Real packet_number
Definition: ERF_SBMRemapping.H:151
RemapStatus status
Definition: ERF_SBMRemapping.H:150
amrex::Real packet_particle_mass
Definition: ERF_SBMRemapping.H:152
RouteDestination destinations[2]
Definition: ERF_SBMRemapping.H:154
bool normalized_roundoff
Definition: ERF_SBMRemapping.H:156
amrex::Real residual_number_weight
Definition: ERF_SBMRemapping.H:155
int destination_count
Definition: ERF_SBMRemapping.H:153
PopulationRemapView population
Definition: ERF_SBMRemapping.H:149
Definition: ERF_SBMRemapping.H:29
int property_count
Definition: ERF_SBMRemapping.H:40
CoordinateKind coordinate_kind
Definition: ERF_SBMRemapping.H:32
MomentMode moment_mode
Definition: ERF_SBMRemapping.H:31
int state_components
Definition: ERF_SBMRemapping.H:38
int nbins
Definition: ERF_SBMRemapping.H:35
int number_offset
Definition: ERF_SBMRemapping.H:37
int population_id
Definition: ERF_SBMRemapping.H:30
const amrex::Real * pivots
Definition: ERF_SBMRemapping.H:34
int mass_offset
Definition: ERF_SBMRemapping.H:36
const int * property_component_offsets
Definition: ERF_SBMRemapping.H:39
const amrex::Real * edges
Definition: ERF_SBMRemapping.H:33
Definition: ERF_SBMRemapping.H:886
int bin
Definition: ERF_SBMRemapping.H:888
bool normalized_roundoff
Definition: ERF_SBMRemapping.H:890
bool empty
Definition: ERF_SBMRemapping.H:889
amrex::Real particle_mass
Definition: ERF_SBMRemapping.H:892
const amrex::Real * property_per_particle
Definition: ERF_SBMRemapping.H:893
PopulationRemapView population
Definition: ERF_SBMRemapping.H:887
amrex::Real number
Definition: ERF_SBMRemapping.H:891
int property_count
Definition: ERF_SBMRemapping.H:894
Definition: ERF_SBMRemapping.H:1129
std::vector< amrex::Real > property_per_particle
Definition: ERF_SBMRemapping.H:1136
bool normalized_roundoff
Definition: ERF_SBMRemapping.H:1133
int bin
Definition: ERF_SBMRemapping.H:1131
amrex::Real particle_mass
Definition: ERF_SBMRemapping.H:1135
int population_id
Definition: ERF_SBMRemapping.H:1130
bool empty
Definition: ERF_SBMRemapping.H:1132
amrex::Real number
Definition: ERF_SBMRemapping.H:1134
Definition: ERF_SBMRemapping.H:141
amrex::Real number_weight
Definition: ERF_SBMRemapping.H:143
amrex::Real particle_mass
Definition: ERF_SBMRemapping.H:144
int bin
Definition: ERF_SBMRemapping.H:142
Definition: ERF_SBMRemapping.H:363
amrex::Array4< const amrex::Real > values
Definition: ERF_SBMRemapping.H:364
int k
Definition: ERF_SBMRemapping.H:367
int j
Definition: ERF_SBMRemapping.H:366
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real operator[](const int component) const noexcept
Definition: ERF_SBMRemapping.H:370
int i
Definition: ERF_SBMRemapping.H:365