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)
 
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 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.

Member Enumeration Documentation

◆ Scalar

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

Constructor & Destructor Documentation

◆ CloudChamberBudget()

CloudChamberBudget::CloudChamberBudget ( int  interval)
inlineexplicit
33 : m_interval(interval) {}
int m_interval
Definition: ERF_CloudChamberBudget.H:259

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 
)
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
88  {
89  if (!enabled() || scalar < 0 || scalar >= NumScalars) { return; }
90  std::array<amrex::Real, NumFaces> current{};
91  for (int d = 0; d < AMREX_SPACEDIM; ++d) {
92  amrex::Box low = geom.Domain();
93  amrex::Box high = low;
94  low.surroundingNodes(d);
95  high.surroundingNodes(d);
96  low.setSmall(d, geom.Domain().smallEnd(d));
97  low.setBig(d, geom.Domain().smallEnd(d));
98  high.setSmall(d, geom.Domain().bigEnd(d)+1);
99  high.setBig(d, geom.Domain().bigEnd(d)+1);
100  const amrex::MultiFab* flux = (d == 0) ? &xflux : ((d == 1) ? &yflux : &zflux);
101  const amrex::Real area = (d == 0) ? geom.CellSize(1)*geom.CellSize(2) :
102  ((d == 1) ? geom.CellSize(0)*geom.CellSize(2) :
103  geom.CellSize(0)*geom.CellSize(1));
104  current[2*d] = flux->sum(low, flux_comp, true) * area * dt;
105  current[2*d+1] = flux->sum(high, flux_comp, true) * area * dt;
106  }
107 
108  if (nrk == 0) {
109  m_stage0[scalar] = current;
110  m_have_stage0[scalar] = true;
111  } else if (nrk == 1 && m_have_stage0[scalar]) {
112  for (int face = 0; face < NumFaces; ++face) {
113  m_cumulative[scalar][face] +=
114  amrex::Real(0.5) * (m_stage0[scalar][face] + current[face]);
115  }
116  m_have_stage0[scalar] = false;
117  }
118  }
amrex::Real Real
Definition: ERF_ShocInterface.H:19
bool enabled() const noexcept
Definition: ERF_CloudChamberBudget.H:35
std::array< std::array< amrex::Real, NumFaces >, NumScalars > m_stage0
Definition: ERF_CloudChamberBudget.H:266
std::array< bool, NumScalars > m_have_stage0
Definition: ERF_CloudChamberBudget.H:263
static constexpr int NumFaces
Definition: ERF_CloudChamberBudget.H:31
std::array< std::array< amrex::Real, NumFaces >, NumScalars > m_cumulative
Definition: ERF_CloudChamberBudget.H:267

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
42  {
43  return enabled() && step > 0 && (step % m_interval == 0);
44  }

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
35 { 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
140  {
141  for (int s = 0; s < NumScalars; ++s) {
142  m_internal_source[s] += after[s] - before[s];
143  }
144  }
std::array< amrex::Real, NumScalars > m_internal_source
Definition: ERF_CloudChamberBudget.H:265

◆ 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
157  {
158  if (!due(step) || !m_have_initial) { return; }
159  const auto current = state_integrals(cons, geom);
160  std::array<amrex::Real, NumScalars*NumFaces + NumScalars*3> packed{};
161  int index = 0;
162  for (int s = 0; s < NumScalars; ++s) {
163  for (int f = 0; f < NumFaces; ++f) { packed[index++] = m_cumulative[s][f]; }
164  }
165  for (int s = 0; s < NumScalars; ++s) { packed[index++] = m_initial_state[s]; }
166  for (int s = 0; s < NumScalars; ++s) { packed[index++] = current[s]; }
167  for (int s = 0; s < NumScalars; ++s) { packed[index++] = m_internal_source[s]; }
168  amrex::ParallelDescriptor::ReduceRealSum(packed.data(), static_cast<int>(packed.size()));
169 
170  std::array<std::array<amrex::Real, NumFaces>, NumScalars> global_faces{};
171  index = 0;
172  for (int s = 0; s < NumScalars; ++s) {
173  for (int f = 0; f < NumFaces; ++f) { global_faces[s][f] = packed[index++]; }
174  }
175  std::array<amrex::Real, NumScalars> initial{};
176  std::array<amrex::Real, NumScalars> global_current{};
177  index = NumScalars*NumFaces;
178  for (int s = 0; s < NumScalars; ++s) { initial[s] = packed[index++]; }
179  for (int s = 0; s < NumScalars; ++s) { global_current[s] = packed[index++]; }
180  std::array<amrex::Real, NumScalars> global_internal{};
181  for (int s = 0; s < NumScalars; ++s) { global_internal[s] = packed[index++]; }
182 
183  if (amrex::ParallelDescriptor::IOProcessor()) {
184  std::ofstream out("cloud_chamber_budget.dat", std::ios::app);
185  if (out.tellp() == std::streampos(0)) {
186  out << "# start_step start_time end_step end_time scalar units "
187  "xlo xhi ylo yhi zlo zhi net_boundary volume_change "
188  "internal_source residual tolerance status\n";
189  }
190  out << std::setprecision(17);
191  const char* names[NumScalars] = {"rhoTheta", "water_vapor", "cloud_water"};
192  const char* units[NumScalars] = {"conserved_scalar", "mixing_ratio_density", "mixing_ratio_density"};
193  for (int s = 0; s < NumScalars; ++s) {
194  std::array<amrex::Real, NumFaces> faces = global_faces[s];
195  amrex::Real net = faces[0] - faces[1] + faces[2] - faces[3] + faces[4] - faces[5];
196  const amrex::Real change = global_current[s] - initial[s];
197  const amrex::Real internal = global_internal[s];
198  const amrex::Real residual = change - net - internal;
199  const amrex::Real scale = std::max({amrex::Real(1.0),
200  std::abs(initial[s]), std::abs(global_current[s]),
201  std::abs(net), std::abs(internal)});
202  const amrex::Real effective_operations =
203  static_cast<amrex::Real>(geom.Domain().numPts()) +
204  static_cast<amrex::Real>(NumFaces);
205  amrex::Real boundary_scale = amrex::Real(0.0);
206  for (const auto value : faces) { boundary_scale += std::abs(value); }
207  const amrex::Real tolerance = amrex::Real(128.0) *
209  (effective_operations * scale + boundary_scale + std::abs(internal));
210  const bool status = std::abs(residual) <= tolerance;
211  const char* status_text = status ? "PASS" :
212  (cloudy && s == RhoTheta ? "UNSUPPORTED_SOURCE" : "FAIL");
213  out << m_interval_start_step << ' ' << m_interval_start_time << ' '
214  << step << ' ' << time << ' ' << names[s] << ' ' << units[s];
215  for (const auto value : faces) { out << ' ' << value; }
216  out << ' ' << net << ' ' << change << ' ' << internal << ' '
217  << residual << ' ' << tolerance << ' ' << status_text << '\n';
218  }
219  std::array<amrex::Real, NumFaces> total_faces{};
220  for (int f = 0; f < NumFaces; ++f) {
221  total_faces[f] = global_faces[RhoQv][f] + global_faces[RhoQc][f];
222  }
223  const amrex::Real total_net = total_faces[0] - total_faces[1] +
224  total_faces[2] - total_faces[3] + total_faces[4] - total_faces[5];
225  const amrex::Real total_change = global_current[RhoQv] + global_current[RhoQc] -
226  initial[RhoQv] - initial[RhoQc];
227  const amrex::Real total_internal = global_internal[RhoQv] + global_internal[RhoQc];
228  const amrex::Real total_residual = total_change - total_net - total_internal;
229  const amrex::Real total_scale = std::max({amrex::Real(1.0), std::abs(total_change),
230  std::abs(total_net), std::abs(total_internal)});
231  amrex::Real total_boundary_scale = amrex::Real(0.0);
232  for (const auto value : total_faces) { total_boundary_scale += std::abs(value); }
233  const amrex::Real total_tolerance = amrex::Real(128.0) *
235  (static_cast<amrex::Real>(geom.Domain().numPts() + NumFaces) * total_scale +
236  total_boundary_scale + std::abs(total_internal));
237  const bool total_status = std::abs(total_residual) <= total_tolerance;
238  out << m_interval_start_step << ' ' << m_interval_start_time << ' '
239  << step << ' ' << time << " total_nonprecipitating_water mixing_ratio_density";
240  for (const auto value : total_faces) { out << ' ' << value; }
241  out << ' ' << total_net << ' ' << total_change << ' ' << total_internal << ' '
242  << total_residual << ' ' << total_tolerance << ' '
243  << (total_status ? "PASS" : "FAIL") << '\n';
244  }
245  // The next report is an interval-local balance beginning at the
246  // state just reported. Keep this reference rank-local: assigning the
247  // MPI-global value here would make every rank contribute the same
248  // global reference at the next reduction.
249  m_initial_state = current;
250  m_cumulative = {};
251  m_internal_source = {};
252  m_stage0 = {};
253  m_have_stage0 = {};
254  m_interval_start_step = step;
255  m_interval_start_time = time;
256  }
std::string units
Definition: ERF_Plotfile2DCatalog.cpp:103
std::array< amrex::Real, NumScalars > m_initial_state
Definition: ERF_CloudChamberBudget.H:264
double m_interval_start_time
Definition: ERF_CloudChamberBudget.H:261
std::array< amrex::Real, NumScalars > state_integrals(const amrex::MultiFab &cons, const amrex::Geometry &geom) const
Definition: ERF_CloudChamberBudget.H:121
bool m_have_initial
Definition: ERF_CloudChamberBudget.H:262
int m_interval_start_step
Definition: ERF_CloudChamberBudget.H:260
bool due(int step) const noexcept
Definition: ERF_CloudChamberBudget.H:42
@ 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:

◆ 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
58  {
59  if (m_have_initial) { return; }
60  const amrex::Real volume = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2);
61  for (int s = 0; s < NumScalars; ++s) {
62  const int comp = (s == RhoTheta) ? RhoTheta_comp : RhoQ1_comp + (s-1);
63  m_initial_state[s] = (comp < cons.nComp()) ? cons.sum(comp, true) * volume : amrex::Real(0.0);
64  }
65  m_interval_start_step = step;
66  m_interval_start_time = time;
67  m_have_initial = true;
68  }
#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
123  {
124  const amrex::Real volume = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2);
125  std::array<amrex::Real, NumScalars> result{};
126  result[RhoTheta] = cons.sum(RhoTheta_comp, true) * volume;
127  result[RhoQv] = (RhoQ1_comp < cons.nComp()) ? cons.sum(RhoQ1_comp, true) * volume : amrex::Real(0.0);
128  result[RhoQc] = (RhoQ2_comp < cons.nComp()) ? cons.sum(RhoQ2_comp, true) * volume : amrex::Real(0.0);
129  return result;
130  }
#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: