ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
CloudChamberBudget Class Reference

#include <ERF_CloudChamberBudget.H>

Collaboration diagram for CloudChamberBudget:

Public Types

enum  Scalar : int { RhoTheta = 0 , RhoQv = 1 , RhoQc = 2 , NumScalars = 3 }
 

Public Member Functions

 CloudChamberBudget (int interval)
 
bool enabled () const noexcept
 
bool due (int step) const noexcept
 
void set_initial_state (const amrex::MultiFab &cons, const amrex::Geometry &geom, int step=0, double time=0.0)
 
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, bool trapezoidal=true)
 
std::array< amrex::Real, NumScalarsstate_integrals (const amrex::MultiFab &cons, const amrex::Geometry &geom) const
 
void record_internal_source (const std::array< amrex::Real, NumScalars > &before, const std::array< amrex::Real, NumScalars > &after)
 
void report (int step, double time, const amrex::MultiFab &cons, const amrex::Geometry &geom, bool cloudy)
 

Static Public Member Functions

static const char * row_status (Scalar scalar, bool cloudy, bool closes) noexcept
 

Static Public Attributes

static constexpr int NumFaces = 2 * AMREX_SPACEDIM
 

Private Attributes

int m_interval = 0
 
int m_interval_start_step = 0
 
double m_interval_start_time = 0.0
 
bool m_have_initial = false
 
std::array< bool, NumScalarsm_have_stage0 {}
 
std::array< amrex::Real, NumScalarsm_initial_state {}
 
std::array< amrex::Real, NumScalarsm_internal_source {}
 
std::array< std::array< amrex::Real, NumFaces >, NumScalarsm_stage0 {}
 
std::array< std::array< amrex::Real, NumFaces >, NumScalarsm_cumulative {}
 

Detailed Description

Low-overhead Stage 1 conserved-scalar budget accumulator.

Fluxes are positive in the positive coordinate direction. The reported boundary balance is therefore low-minus-high for each coordinate pair. Local face sums are held until a configured report interval, then one packed MPI reduction produces the six-face values. For the anelastic RK2 path, nrk=0 is retained and nrk=1 applies the trapezoidal stage weight; this avoids counting both RHS evaluations as two physical timesteps. For the anelastic MidPoint path there is nothing to average: the first stage advances only half a timestep and the second advances the whole timestep with the source evaluated there, so only nrk=1 contributes.

Member Enumeration Documentation

◆ Scalar

Enumerator
RhoTheta 
RhoQv 
RhoQc 
NumScalars 
33 : int { RhoTheta = 0, RhoQv = 1, RhoQc = 2, NumScalars = 3 };
@ RhoTheta
Definition: ERF_CloudChamberBudget.H:33
@ RhoQv
Definition: ERF_CloudChamberBudget.H:33
@ NumScalars
Definition: ERF_CloudChamberBudget.H:33
@ RhoQc
Definition: ERF_CloudChamberBudget.H:33

Constructor & Destructor Documentation

◆ CloudChamberBudget()

CloudChamberBudget::CloudChamberBudget ( int  interval)
inlineexplicit
36 : m_interval(interval) {}
int m_interval
Definition: ERF_CloudChamberBudget.H:281

Member Function Documentation

◆ capture_stage()

void CloudChamberBudget::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,
bool  trapezoidal = true 
)
inline

Integrate boundary fluxes for a specific Runge-Kutta stage.

Parameters
scalarIndex of the scalar being tracked
nrkRunge-Kutta stage index
dtTimestep size
xfluxFluxes in the x-direction
yfluxFluxes in the y-direction
zfluxFluxes in the z-direction
geomGeometry used for face area calculation
flux_compComponent index of the flux
trapezoidalTrue for the RK2 stages, whose two sources are averaged; false for the MidPoint stages, where only nrk=1 counts
101  {
102  if (!enabled() || scalar < 0 || scalar >= NumScalars) { return; }
103  std::array<amrex::Real, NumFaces> current{};
104  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
105  amrex::Box low = geom.Domain();
106  amrex::Box high = low;
107  low.surroundingNodes(d);
108  high.surroundingNodes(d);
109  low.setSmall(d, geom.Domain().smallEnd(d));
110  low.setBig(d, geom.Domain().smallEnd(d));
111  high.setSmall(d, geom.Domain().bigEnd(d)+1);
112  high.setBig(d, geom.Domain().bigEnd(d)+1);
113  const amrex::MultiFab* flux = (d == 0) ? &xflux : ((d == 1) ? &yflux : &zflux);
114  const amrex::Real area = (d == 0) ? geom.CellSize(1)*geom.CellSize(2) :
115  ((d == 1) ? geom.CellSize(0)*geom.CellSize(2) :
116  geom.CellSize(0)*geom.CellSize(1));
117  current[2*d] = flux->sum(low, flux_comp, true) * area * dt;
118  current[2*d+1] = flux->sum(high, flux_comp, true) * area * dt;
119  }
120 
121  if (!trapezoidal) {
122  // MidPoint: the first stage spans only dt/2 and exists solely to make the
123  // midpoint state, so the second stage alone carries the step's flux.
124  if (nrk == 1) {
125  for (int face = 0; face < NumFaces; ++face) {
126  m_cumulative[scalar][face] += current[face];
127  }
128  }
129  m_have_stage0[scalar] = false;
130  } else if (nrk == 0) {
131  m_stage0[scalar] = current;
132  m_have_stage0[scalar] = true;
133  } else if (nrk == 1 && m_have_stage0[scalar]) {
134  for (int face = 0; face < NumFaces; ++face) {
135  m_cumulative[scalar][face] +=
136  amrex::Real(0.5) * (m_stage0[scalar][face] + current[face]);
137  }
138  m_have_stage0[scalar] = false;
139  }
140  }
amrex::Real Real
Definition: ERF_ShocInterface.H:19
bool enabled() const noexcept
Definition: ERF_CloudChamberBudget.H:38
std::array< std::array< amrex::Real, NumFaces >, NumScalars > m_stage0
Definition: ERF_CloudChamberBudget.H:288
std::array< bool, NumScalars > m_have_stage0
Definition: ERF_CloudChamberBudget.H:285
static constexpr int NumFaces
Definition: ERF_CloudChamberBudget.H:34
std::array< std::array< amrex::Real, NumFaces >, NumScalars > m_cumulative
Definition: ERF_CloudChamberBudget.H:289

Referenced by erf_slow_rhs_post(), and erf_slow_rhs_pre().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ due()

bool CloudChamberBudget::due ( int  step) const
inlinenoexcept

Determine if a budget report is due for the given timestep.

Parameters
stepCurrent timestep
Returns
True if a report is due, false otherwise
52  {
53  return enabled() && step > 0 && (step % m_interval == 0);
54  }

Referenced by report().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ enabled()

bool CloudChamberBudget::enabled ( ) const
inlinenoexcept
38 { return m_interval > 0; }

Referenced by capture_stage(), and due().

Here is the caller graph for this function:

◆ record_internal_source()

void CloudChamberBudget::record_internal_source ( const std::array< amrex::Real, NumScalars > &  before,
const std::array< amrex::Real, NumScalars > &  after 
)
inline

Accumulate internal source terms for the tracked scalars.

Parameters
beforeState of scalars before source application
afterState of scalars after source application
162  {
163  for (int s = 0; s < NumScalars; ++s) {
164  m_internal_source[s] += after[s] - before[s];
165  }
166  }
std::array< amrex::Real, NumScalars > m_internal_source
Definition: ERF_CloudChamberBudget.H:287

◆ report()

void CloudChamberBudget::report ( int  step,
double  time,
const amrex::MultiFab &  cons,
const amrex::Geometry &  geom,
bool  cloudy 
)
inline

Perform global reduction and write the budget report to file.

Parameters
stepCurrent timestep
timeCurrent simulation time
consCurrent conserved variable MultiFab
geomGeometry used for state integration
cloudyFlag indicating if the state is cloudy
179  {
180  if (!due(step) || !m_have_initial) { return; }
181  const auto current = state_integrals(cons, geom);
182  std::array<amrex::Real, NumScalars*NumFaces + NumScalars*3> packed{};
183  int index = 0;
184  for (int s = 0; s < NumScalars; ++s) {
185  for (int f = 0; f < NumFaces; ++f) { packed[index++] = m_cumulative[s][f]; }
186  }
187  for (int s = 0; s < NumScalars; ++s) { packed[index++] = m_initial_state[s]; }
188  for (int s = 0; s < NumScalars; ++s) { packed[index++] = current[s]; }
189  for (int s = 0; s < NumScalars; ++s) { packed[index++] = m_internal_source[s]; }
190  amrex::ParallelDescriptor::ReduceRealSum(packed.data(), static_cast<int>(packed.size()));
191 
192  std::array<std::array<amrex::Real, NumFaces>, NumScalars> global_faces{};
193  index = 0;
194  for (int s = 0; s < NumScalars; ++s) {
195  for (int f = 0; f < NumFaces; ++f) { global_faces[s][f] = packed[index++]; }
196  }
197  std::array<amrex::Real, NumScalars> initial{};
198  std::array<amrex::Real, NumScalars> global_current{};
199  index = NumScalars*NumFaces;
200  for (int s = 0; s < NumScalars; ++s) { initial[s] = packed[index++]; }
201  for (int s = 0; s < NumScalars; ++s) { global_current[s] = packed[index++]; }
202  std::array<amrex::Real, NumScalars> global_internal{};
203  for (int s = 0; s < NumScalars; ++s) { global_internal[s] = packed[index++]; }
204 
205  if (amrex::ParallelDescriptor::IOProcessor()) {
206  std::ofstream out("cloud_chamber_budget.dat", std::ios::app);
207  if (out.tellp() == std::streampos(0)) {
208  out << "# start_step start_time end_step end_time scalar units "
209  "xlo xhi ylo yhi zlo zhi net_boundary volume_change "
210  "internal_source residual tolerance status\n";
211  }
212  out << std::setprecision(17);
213  const char* names[NumScalars] = {"rhoTheta", "water_vapor", "cloud_water"};
214  const char* units[NumScalars] = {"conserved_scalar", "mixing_ratio_density", "mixing_ratio_density"};
215  for (int s = 0; s < NumScalars; ++s) {
216  std::array<amrex::Real, NumFaces> faces = global_faces[s];
217  amrex::Real net = faces[0] - faces[1] + faces[2] - faces[3] + faces[4] - faces[5];
218  const amrex::Real change = global_current[s] - initial[s];
219  const amrex::Real internal = global_internal[s];
220  const amrex::Real residual = change - net - internal;
221  const amrex::Real scale = std::max({amrex::Real(1.0),
222  std::abs(initial[s]), std::abs(global_current[s]),
223  std::abs(net), std::abs(internal)});
224  const amrex::Real effective_operations =
225  static_cast<amrex::Real>(geom.Domain().numPts()) +
226  static_cast<amrex::Real>(NumFaces);
227  amrex::Real boundary_scale = amrex::Real(0.0);
228  for (const auto value : faces) { boundary_scale += std::abs(value); }
229  const amrex::Real tolerance = amrex::Real(128.0) *
231  (effective_operations * scale + boundary_scale + std::abs(internal));
232  const bool status = std::abs(residual) <= tolerance;
233  const char* status_text = row_status(
234  static_cast<Scalar>(s), cloudy, status);
235  out << m_interval_start_step << ' ' << m_interval_start_time << ' '
236  << step << ' ' << time << ' ' << names[s] << ' ' << units[s];
237  for (const auto value : faces) { out << ' ' << value; }
238  out << ' ' << net << ' ' << change << ' ' << internal << ' '
239  << residual << ' ' << tolerance << ' ' << status_text << '\n';
240  }
241  std::array<amrex::Real, NumFaces> total_faces{};
242  for (int f = 0; f < NumFaces; ++f) {
243  total_faces[f] = global_faces[RhoQv][f] + global_faces[RhoQc][f];
244  }
245  const amrex::Real total_net = total_faces[0] - total_faces[1] +
246  total_faces[2] - total_faces[3] + total_faces[4] - total_faces[5];
247  const amrex::Real total_change = global_current[RhoQv] + global_current[RhoQc] -
248  initial[RhoQv] - initial[RhoQc];
249  const amrex::Real total_internal = global_internal[RhoQv] + global_internal[RhoQc];
250  const amrex::Real total_residual = total_change - total_net - total_internal;
251  const amrex::Real total_scale = std::max({amrex::Real(1.0), std::abs(total_change),
252  std::abs(total_net), std::abs(total_internal)});
253  amrex::Real total_boundary_scale = amrex::Real(0.0);
254  for (const auto value : total_faces) { total_boundary_scale += std::abs(value); }
255  const amrex::Real total_tolerance = amrex::Real(128.0) *
257  (static_cast<amrex::Real>(geom.Domain().numPts() + NumFaces) * total_scale +
258  total_boundary_scale + std::abs(total_internal));
259  const bool total_status = std::abs(total_residual) <= total_tolerance;
260  out << m_interval_start_step << ' ' << m_interval_start_time << ' '
261  << step << ' ' << time << " total_nonprecipitating_water mixing_ratio_density";
262  for (const auto value : total_faces) { out << ' ' << value; }
263  out << ' ' << total_net << ' ' << total_change << ' ' << total_internal << ' '
264  << total_residual << ' ' << total_tolerance << ' '
265  << (total_status ? "PASS" : "FAIL") << '\n';
266  }
267  // The next report is an interval-local balance beginning at the
268  // state just reported. Keep this reference rank-local: assigning the
269  // MPI-global value here would make every rank contribute the same
270  // global reference at the next reduction.
271  m_initial_state = current;
272  m_cumulative = {};
273  m_internal_source = {};
274  m_stage0 = {};
275  m_have_stage0 = {};
276  m_interval_start_step = step;
277  m_interval_start_time = time;
278  }
std::string units
Definition: ERF_Plotfile2DCatalog.cpp:103
std::array< amrex::Real, NumScalars > m_initial_state
Definition: ERF_CloudChamberBudget.H:286
double m_interval_start_time
Definition: ERF_CloudChamberBudget.H:283
std::array< amrex::Real, NumScalars > state_integrals(const amrex::MultiFab &cons, const amrex::Geometry &geom) const
Definition: ERF_CloudChamberBudget.H:143
bool m_have_initial
Definition: ERF_CloudChamberBudget.H:284
Scalar
Definition: ERF_CloudChamberBudget.H:33
static const char * row_status(Scalar scalar, bool cloudy, bool closes) noexcept
Definition: ERF_CloudChamberBudget.H:40
int m_interval_start_step
Definition: ERF_CloudChamberBudget.H:282
bool due(int step) const noexcept
Definition: ERF_CloudChamberBudget.H:52
@ cons
Definition: ERF_IndexDefines.H:214
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
Here is the call graph for this function:

◆ row_status()

static const char* CloudChamberBudget::row_status ( Scalar  scalar,
bool  cloudy,
bool  closes 
)
inlinestaticnoexcept
41  {
42  if (cloudy && scalar == RhoTheta) { return "UNSUPPORTED_SOURCE"; }
43  return closes ? "PASS" : "FAIL";
44  }

Referenced by report().

Here is the caller graph for this function:

◆ set_initial_state()

void CloudChamberBudget::set_initial_state ( const amrex::MultiFab &  cons,
const amrex::Geometry &  geom,
int  step = 0,
double  time = 0.0 
)
inline

Initialize the base state for the budget accumulation period.

Parameters
consConserved variable MultiFab
geomGeometry used for volume calculation
stepTimestep at the start of the interval
timeSimulation time at the start of the interval
68  {
69  if (m_have_initial) { return; }
70  const amrex::Real volume = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2);
71  for (int s = 0; s < NumScalars; ++s) {
72  const int comp = (s == RhoTheta) ? RhoTheta_comp : RhoQ1_comp + (s-1);
73  m_initial_state[s] = (comp < cons.nComp()) ? cons.sum(comp, true) * volume : amrex::Real(0.0);
74  }
75  m_interval_start_step = step;
76  m_interval_start_time = time;
77  m_have_initial = true;
78  }
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:40
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:45

◆ state_integrals()

std::array<amrex::Real, NumScalars> CloudChamberBudget::state_integrals ( const amrex::MultiFab &  cons,
const amrex::Geometry &  geom 
) const
inline
145  {
146  const amrex::Real volume = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2);
147  std::array<amrex::Real, NumScalars> result{};
148  result[RhoTheta] = cons.sum(RhoTheta_comp, true) * volume;
149  result[RhoQv] = (RhoQ1_comp < cons.nComp()) ? cons.sum(RhoQ1_comp, true) * volume : amrex::Real(0.0);
150  result[RhoQc] = (RhoQ2_comp < cons.nComp()) ? cons.sum(RhoQ2_comp, true) * volume : amrex::Real(0.0);
151  return result;
152  }
#define RhoQ2_comp
Definition: ERF_IndexDefines.H:46

Referenced by report().

Here is the caller graph for this function:

Member Data Documentation

◆ m_cumulative

std::array<std::array<amrex::Real, NumFaces>, NumScalars> CloudChamberBudget::m_cumulative {}
private

Referenced by capture_stage(), and report().

◆ m_have_initial

bool CloudChamberBudget::m_have_initial = false
private

Referenced by report(), and set_initial_state().

◆ m_have_stage0

std::array<bool, NumScalars> CloudChamberBudget::m_have_stage0 {}
private

Referenced by capture_stage(), and report().

◆ m_initial_state

std::array<amrex::Real, NumScalars> CloudChamberBudget::m_initial_state {}
private

Referenced by report(), and set_initial_state().

◆ m_internal_source

std::array<amrex::Real, NumScalars> CloudChamberBudget::m_internal_source {}
private

Referenced by record_internal_source(), and report().

◆ m_interval

int CloudChamberBudget::m_interval = 0
private

Referenced by due(), and enabled().

◆ m_interval_start_step

int CloudChamberBudget::m_interval_start_step = 0
private

Referenced by report(), and set_initial_state().

◆ m_interval_start_time

double CloudChamberBudget::m_interval_start_time = 0.0
private

Referenced by report(), and set_initial_state().

◆ m_stage0

std::array<std::array<amrex::Real, NumFaces>, NumScalars> CloudChamberBudget::m_stage0 {}
private

Referenced by capture_stage(), and report().

◆ NumFaces

constexpr int CloudChamberBudget::NumFaces = 2 * AMREX_SPACEDIM
staticconstexpr

Referenced by capture_stage(), and report().


The documentation for this class was generated from the following file: