ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_CloudChamberBudget.H
Go to the documentation of this file.
1 #ifndef ERF_CLOUD_CHAMBER_BUDGET_H_
2 #define ERF_CLOUD_CHAMBER_BUDGET_H_
3 
4 #include <AMReX_Geometry.H>
5 #include <AMReX_MultiFab.H>
6 #include <AMReX_ParallelDescriptor.H>
7 #include <ERF_IndexDefines.H>
8 
9 #include <array>
10 #include <algorithm>
11 #include <cmath>
12 #include <fstream>
13 #include <iomanip>
14 #include <limits>
15 #include <string>
16 
17 /**
18  * Low-overhead Stage 1 conserved-scalar budget accumulator.
19  *
20  * Fluxes are positive in the positive coordinate direction. The reported
21  * boundary balance is therefore low-minus-high for each coordinate pair.
22  * Local face sums are held until a configured report interval, then one
23  * packed MPI reduction produces the six-face values. For the anelastic RK2
24  * path, nrk=0 is retained and nrk=1 applies the trapezoidal stage weight;
25  * this avoids counting both RHS evaluations as two physical timesteps.
26  */
28 {
29 public:
30  enum Scalar : int { RhoTheta = 0, RhoQv = 1, RhoQc = 2, NumScalars = 3 };
31  static constexpr int NumFaces = 2 * AMREX_SPACEDIM;
32 
33  explicit CloudChamberBudget (int interval) : m_interval(interval) {}
34 
35  bool enabled () const noexcept { return m_interval > 0; }
36  bool due (int step) const noexcept {
37  return enabled() && step > 0 && (step % m_interval == 0);
38  }
39 
40  void set_initial_state (const amrex::MultiFab& cons,
41  const amrex::Geometry& geom,
42  int step = 0,
43  double time = 0.0)
44  {
45  if (m_have_initial) { return; }
46  const amrex::Real volume = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2);
47  for (int s = 0; s < NumScalars; ++s) {
48  const int comp = (s == RhoTheta) ? RhoTheta_comp : RhoQ1_comp + (s-1);
49  m_initial_state[s] = (comp < cons.nComp()) ? cons.sum(comp, true) * volume : amrex::Real(0.0);
50  }
51  m_interval_start_step = step;
52  m_interval_start_time = time;
53  m_have_initial = true;
54  }
55 
56  void capture_stage (int scalar, int nrk, amrex::Real dt,
57  const amrex::MultiFab& xflux,
58  const amrex::MultiFab& yflux,
59  const amrex::MultiFab& zflux,
60  const amrex::Geometry& geom,
61  int flux_comp = 0)
62  {
63  if (!enabled() || scalar < 0 || scalar >= NumScalars) { return; }
64  std::array<amrex::Real, NumFaces> current{};
65  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
66  amrex::Box low = geom.Domain();
67  amrex::Box high = low;
68  low.surroundingNodes(d);
69  high.surroundingNodes(d);
70  low.setSmall(d, geom.Domain().smallEnd(d));
71  low.setBig(d, geom.Domain().smallEnd(d));
72  high.setSmall(d, geom.Domain().bigEnd(d)+1);
73  high.setBig(d, geom.Domain().bigEnd(d)+1);
74  const amrex::MultiFab* flux = (d == 0) ? &xflux : ((d == 1) ? &yflux : &zflux);
75  const amrex::Real area = (d == 0) ? geom.CellSize(1)*geom.CellSize(2) :
76  ((d == 1) ? geom.CellSize(0)*geom.CellSize(2) :
77  geom.CellSize(0)*geom.CellSize(1));
78  current[2*d] = flux->sum(low, flux_comp, true) * area * dt;
79  current[2*d+1] = flux->sum(high, flux_comp, true) * area * dt;
80  }
81 
82  if (nrk == 0) {
83  m_stage0[scalar] = current;
84  m_have_stage0[scalar] = true;
85  } else if (nrk == 1 && m_have_stage0[scalar]) {
86  for (int face = 0; face < NumFaces; ++face) {
87  m_cumulative[scalar][face] +=
88  amrex::Real(0.5) * (m_stage0[scalar][face] + current[face]);
89  }
90  m_have_stage0[scalar] = false;
91  }
92  }
93 
94  std::array<amrex::Real, NumScalars>
95  state_integrals (const amrex::MultiFab& cons,
96  const amrex::Geometry& geom) const
97  {
98  const amrex::Real volume = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2);
99  std::array<amrex::Real, NumScalars> result{};
100  result[RhoTheta] = cons.sum(RhoTheta_comp, true) * volume;
101  result[RhoQv] = (RhoQ1_comp < cons.nComp()) ? cons.sum(RhoQ1_comp, true) * volume : amrex::Real(0.0);
102  result[RhoQc] = (RhoQ2_comp < cons.nComp()) ? cons.sum(RhoQ2_comp, true) * volume : amrex::Real(0.0);
103  return result;
104  }
105 
106  void record_internal_source (const std::array<amrex::Real, NumScalars>& before,
107  const std::array<amrex::Real, NumScalars>& after)
108  {
109  for (int s = 0; s < NumScalars; ++s) {
110  m_internal_source[s] += after[s] - before[s];
111  }
112  }
113 
114  void report (int step, double time, const amrex::MultiFab& cons,
115  const amrex::Geometry& geom, bool cloudy)
116  {
117  if (!due(step) || !m_have_initial) { return; }
118  const auto current = state_integrals(cons, geom);
119  std::array<amrex::Real, NumScalars*NumFaces + NumScalars*3> packed{};
120  int index = 0;
121  for (int s = 0; s < NumScalars; ++s) {
122  for (int f = 0; f < NumFaces; ++f) { packed[index++] = m_cumulative[s][f]; }
123  }
124  for (int s = 0; s < NumScalars; ++s) { packed[index++] = m_initial_state[s]; }
125  for (int s = 0; s < NumScalars; ++s) { packed[index++] = current[s]; }
126  for (int s = 0; s < NumScalars; ++s) { packed[index++] = m_internal_source[s]; }
127  amrex::ParallelDescriptor::ReduceRealSum(packed.data(), static_cast<int>(packed.size()));
128 
129  std::array<std::array<amrex::Real, NumFaces>, NumScalars> global_faces{};
130  index = 0;
131  for (int s = 0; s < NumScalars; ++s) {
132  for (int f = 0; f < NumFaces; ++f) { global_faces[s][f] = packed[index++]; }
133  }
134  std::array<amrex::Real, NumScalars> initial{};
135  std::array<amrex::Real, NumScalars> global_current{};
136  index = NumScalars*NumFaces;
137  for (int s = 0; s < NumScalars; ++s) { initial[s] = packed[index++]; }
138  for (int s = 0; s < NumScalars; ++s) { global_current[s] = packed[index++]; }
139  std::array<amrex::Real, NumScalars> global_internal{};
140  for (int s = 0; s < NumScalars; ++s) { global_internal[s] = packed[index++]; }
141 
142  if (amrex::ParallelDescriptor::IOProcessor()) {
143  std::ofstream out("cloud_chamber_budget.dat", std::ios::app);
144  if (out.tellp() == std::streampos(0)) {
145  out << "# start_step start_time end_step end_time scalar units "
146  "xlo xhi ylo yhi zlo zhi net_boundary volume_change "
147  "internal_source residual tolerance status\n";
148  }
149  out << std::setprecision(17);
150  const char* names[NumScalars] = {"rhoTheta", "water_vapor", "cloud_water"};
151  const char* units[NumScalars] = {"conserved_scalar", "mixing_ratio_density", "mixing_ratio_density"};
152  for (int s = 0; s < NumScalars; ++s) {
153  std::array<amrex::Real, NumFaces> faces = global_faces[s];
154  amrex::Real net = faces[0] - faces[1] + faces[2] - faces[3] + faces[4] - faces[5];
155  const amrex::Real change = global_current[s] - initial[s];
156  const amrex::Real internal = global_internal[s];
157  const amrex::Real residual = change - net - internal;
158  const amrex::Real scale = std::max({amrex::Real(1.0),
159  std::abs(initial[s]), std::abs(global_current[s]),
160  std::abs(net), std::abs(internal)});
161  const amrex::Real effective_operations =
162  static_cast<amrex::Real>(geom.Domain().numPts()) +
163  static_cast<amrex::Real>(NumFaces);
164  amrex::Real boundary_scale = amrex::Real(0.0);
165  for (const auto value : faces) { boundary_scale += std::abs(value); }
166  const amrex::Real tolerance = amrex::Real(128.0) *
168  (effective_operations * scale + boundary_scale + std::abs(internal));
169  const bool status = std::abs(residual) <= tolerance;
170  const char* status_text = status ? "PASS" :
171  (cloudy && s == RhoTheta ? "UNSUPPORTED_SOURCE" : "FAIL");
173  << step << ' ' << time << ' ' << names[s] << ' ' << units[s];
174  for (const auto value : faces) { out << ' ' << value; }
175  out << ' ' << net << ' ' << change << ' ' << internal << ' '
176  << residual << ' ' << tolerance << ' ' << status_text << '\n';
177  }
178  std::array<amrex::Real, NumFaces> total_faces{};
179  for (int f = 0; f < NumFaces; ++f) {
180  total_faces[f] = global_faces[RhoQv][f] + global_faces[RhoQc][f];
181  }
182  const amrex::Real total_net = total_faces[0] - total_faces[1] +
183  total_faces[2] - total_faces[3] + total_faces[4] - total_faces[5];
184  const amrex::Real total_change = global_current[RhoQv] + global_current[RhoQc] -
185  initial[RhoQv] - initial[RhoQc];
186  const amrex::Real total_internal = global_internal[RhoQv] + global_internal[RhoQc];
187  const amrex::Real total_residual = total_change - total_net - total_internal;
188  const amrex::Real total_scale = std::max({amrex::Real(1.0), std::abs(total_change),
189  std::abs(total_net), std::abs(total_internal)});
190  amrex::Real total_boundary_scale = amrex::Real(0.0);
191  for (const auto value : total_faces) { total_boundary_scale += std::abs(value); }
192  const amrex::Real total_tolerance = amrex::Real(128.0) *
194  (static_cast<amrex::Real>(geom.Domain().numPts() + NumFaces) * total_scale +
195  total_boundary_scale + std::abs(total_internal));
196  const bool total_status = std::abs(total_residual) <= total_tolerance;
198  << step << ' ' << time << " total_nonprecipitating_water mixing_ratio_density";
199  for (const auto value : total_faces) { out << ' ' << value; }
200  out << ' ' << total_net << ' ' << total_change << ' ' << total_internal << ' '
201  << total_residual << ' ' << total_tolerance << ' '
202  << (total_status ? "PASS" : "FAIL") << '\n';
203  }
204  // The next report is an interval-local balance beginning at the
205  // state just reported. Keep this reference rank-local: assigning the
206  // MPI-global value here would make every rank contribute the same
207  // global reference at the next reduction.
208  m_initial_state = current;
209  m_cumulative = {};
210  m_internal_source = {};
211  m_stage0 = {};
212  m_have_stage0 = {};
213  m_interval_start_step = step;
214  m_interval_start_time = time;
215  }
216 
217 private:
218  int m_interval = 0;
220  double m_interval_start_time = 0.0;
221  bool m_have_initial = false;
222  std::array<bool, NumScalars> m_have_stage0{};
223  std::array<amrex::Real, NumScalars> m_initial_state{};
224  std::array<amrex::Real, NumScalars> m_internal_source{};
225  std::array<std::array<amrex::Real, NumFaces>, NumScalars> m_stage0{};
226  std::array<std::array<amrex::Real, NumFaces>, NumScalars> m_cumulative{};
227 };
228 
229 #endif
struct @28 out
Real value
Definition: ERF_HurricaneDiagnostics.cpp:30
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:37
#define RhoQ2_comp
Definition: ERF_IndexDefines.H:43
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:42
std::string units
Definition: ERF_Plotfile2DCatalog.cpp:103
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_CloudChamberBudget.H:28
bool enabled() const noexcept
Definition: ERF_CloudChamberBudget.H:35
void report(int step, double time, const amrex::MultiFab &cons, const amrex::Geometry &geom, bool cloudy)
Definition: ERF_CloudChamberBudget.H:114
std::array< std::array< amrex::Real, NumFaces >, NumScalars > m_stage0
Definition: ERF_CloudChamberBudget.H:225
std::array< amrex::Real, NumScalars > m_initial_state
Definition: ERF_CloudChamberBudget.H:223
double m_interval_start_time
Definition: ERF_CloudChamberBudget.H:220
std::array< amrex::Real, NumScalars > state_integrals(const amrex::MultiFab &cons, const amrex::Geometry &geom) const
Definition: ERF_CloudChamberBudget.H:95
void set_initial_state(const amrex::MultiFab &cons, const amrex::Geometry &geom, int step=0, double time=0.0)
Definition: ERF_CloudChamberBudget.H:40
CloudChamberBudget(int interval)
Definition: ERF_CloudChamberBudget.H:33
bool m_have_initial
Definition: ERF_CloudChamberBudget.H:221
void capture_stage(int scalar, int nrk, amrex::Real dt, const amrex::MultiFab &xflux, const amrex::MultiFab &yflux, const amrex::MultiFab &zflux, const amrex::Geometry &geom, int flux_comp=0)
Definition: ERF_CloudChamberBudget.H:56
Scalar
Definition: ERF_CloudChamberBudget.H:30
@ RhoTheta
Definition: ERF_CloudChamberBudget.H:30
@ RhoQv
Definition: ERF_CloudChamberBudget.H:30
@ NumScalars
Definition: ERF_CloudChamberBudget.H:30
@ RhoQc
Definition: ERF_CloudChamberBudget.H:30
std::array< bool, NumScalars > m_have_stage0
Definition: ERF_CloudChamberBudget.H:222
int m_interval
Definition: ERF_CloudChamberBudget.H:218
std::array< amrex::Real, NumScalars > m_internal_source
Definition: ERF_CloudChamberBudget.H:224
static constexpr int NumFaces
Definition: ERF_CloudChamberBudget.H:31
std::array< std::array< amrex::Real, NumFaces >, NumScalars > m_cumulative
Definition: ERF_CloudChamberBudget.H:226
int m_interval_start_step
Definition: ERF_CloudChamberBudget.H:219
bool due(int step) const noexcept
Definition: ERF_CloudChamberBudget.H:36
void record_internal_source(const std::array< amrex::Real, NumScalars > &before, const std::array< amrex::Real, NumScalars > &after)
Definition: ERF_CloudChamberBudget.H:106
@ cons
Definition: ERF_IndexDefines.H:176
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12