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

#include <ERF_WriteBndryPlanes.H>

Collaboration diagram for WriteBndryPlanes:

Public Member Functions

 WriteBndryPlanes (amrex::Vector< amrex::BoxArray > &grids, amrex::Vector< amrex::Geometry > &geom)
 
void write_planes (int t_step, double time, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars_new, bool is_moist)
 
bool start_series (int start_step, double start_time, bool restarting)
 

Static Public Member Functions

static int rows_to_keep (const amrex::Vector< int > &steps, const amrex::Vector< double > &times, int start_step, double start_time, bool restarting)
 

Private Attributes

amrex::Box target_box
 IO output box region. More...
 
amrex::Vector< amrex::Geometry > & m_geom
 Geometry objects for all levels. More...
 
std::string m_filename {""}
 File name for IO. More...
 
std::string m_time_file {""}
 File name for Native time file. More...
 
amrex::Vector< std::string > m_var_names
 Variables for IO. More...
 
amrex::Vector< std::string > m_kept_rows
 Rows of time.dat the run continues from, written back before its first plane (I/O rank) More...
 
bool m_rewrite_time_file = false
 Whether time.dat still has to be rewritten to m_kept_rows (I/O rank) More...
 
amrex::Vector< double > m_in_times
 Timestep and times to be stored in time.dat. More...
 
amrex::Vector< int > m_in_timesteps
 
int m_in_rad = 1
 controls extents on native bndry output More...
 
const int m_out_rad = 1
 
const int m_extent_rad = 0
 

Static Private Attributes

static int bndry_lev = 0
 

Detailed Description

Interface for writing boundary planes

This class performs the necessary file operations to write boundary planes

Constructor & Destructor Documentation

◆ WriteBndryPlanes()

WriteBndryPlanes::WriteBndryPlanes ( amrex::Vector< amrex::BoxArray > &  grids,
amrex::Vector< amrex::Geometry > &  geom 
)
explicit

Constructor for the WriteBndryPlanes class for writing the contents of boundary planes to an output file

Parameters
gridsVector of BoxArrays containing the grids at each level in the AMR
geomVector of Geometry containing the geometry at each level in the AMR
66  : m_geom(geom)
67 {
68  ParmParse pp("erf");
69 
70  // Get the radius inside the domain
71  pp.queryAdd("in_rad",m_in_rad);
72 
73  // User-specified region is given in physical coordinates, not index space
74  std::vector<Real> box_lo(3), box_hi(3);
75  pp.getarr("bndry_output_box_lo",box_lo,0,2);
76  pp.getarr("bndry_output_box_hi",box_hi,0,2);
77 
78  // If the target area is contained at a finer level, use the finest data possible.
79  // target_box is expressed in the index space of bndry_lev, so it must only be set
80  // together with bndry_lev; each level's candidate box is kept in a local variable.
81  for (int ilev = 0; ilev < grids.size(); ilev++) {
82 
83  const Real* xLo = m_geom[ilev].ProbLo();
84  auto const dxi = geom[ilev].InvCellSizeArray();
85  const Box& domain = m_geom[ilev].Domain();
86 
87  // We create the smallest box that contains all of the cell centers
88  // in the physical region specified
89  int ilo = static_cast<int>(Math::floor((box_lo[0] - xLo[0]) * dxi[0] + Real(.5)));
90  int jlo = static_cast<int>(Math::floor((box_lo[1] - xLo[1]) * dxi[1] + Real(.5)));
91  int ihi = static_cast<int>(Math::floor((box_hi[0] - xLo[0]) * dxi[0] + Real(.5)))-1;
92  int jhi = static_cast<int>(Math::floor((box_hi[1] - xLo[1]) * dxi[1] + Real(.5)))-1;
93 
94  // Map this to index space -- for now we do no interpolation
95  Box box_this_lev(IntVect(ilo,jlo,0),
96  IntVect(ihi,jhi,domain.bigEnd(2)));
97 
98  // Test if the target box at this level fits in the grids at this level
99  Box gbx = box_this_lev; gbx.grow(IntVect(1,1,0));
100 
101  // Ensure that the box is no larger than can fit in the (periodically grown) domain
102  // at level 0
103  if (ilev == 0) {
104  Box per_grown_domain(domain);
105  int growx = (geom[0].isPeriodic(0)) ? 1 : 0;
106  int growy = (geom[0].isPeriodic(1)) ? 1 : 0;
107  per_grown_domain.grow(IntVect(growx,growy,0));
108  /*
109  if (!per_grown_domain.contains(gbx))
110  Error("WriteBndryPlanes: Requested box is too large to fill");
111  */
112 
113  // Default to level 0
114  target_box = box_this_lev;
115  }
116 
117  // Use this level's data only if its grids fully contain the requested region
118  if (grids[ilev].contains(gbx)) {
119  bndry_lev = ilev;
120  target_box = box_this_lev;
121  }
122  }
123 
124  // The folder "m_filename" will contain the time series of data and the time.dat file
125  pp.get("bndry_output_planes_file", m_filename);
126 
127  m_time_file = m_filename + "/time.dat";
128 
129  if (pp.contains("bndry_output_var_names"))
130  {
131  int num_vars = pp.countval("bndry_output_var_names");
132  m_var_names.resize(num_vars);
133  pp.queryarr("bndry_output_var_names",m_var_names,0,num_vars);
134  }
135 }
ParmParse pp("prob")
amrex::Real Real
Definition: ERF_ShocInterface.H:19
amrex::Vector< amrex::Geometry > & m_geom
Geometry objects for all levels.
Definition: ERF_WriteBndryPlanes.H:48
int m_in_rad
controls extents on native bndry output
Definition: ERF_WriteBndryPlanes.H:74
static int bndry_lev
Definition: ERF_WriteBndryPlanes.H:71
amrex::Box target_box
IO output box region.
Definition: ERF_WriteBndryPlanes.H:45
amrex::Vector< std::string > m_var_names
Variables for IO.
Definition: ERF_WriteBndryPlanes.H:57
std::string m_time_file
File name for Native time file.
Definition: ERF_WriteBndryPlanes.H:54
std::string m_filename
File name for IO.
Definition: ERF_WriteBndryPlanes.H:51
Here is the call graph for this function:

Member Function Documentation

◆ rows_to_keep()

int WriteBndryPlanes::rows_to_keep ( const amrex::Vector< int > &  steps,
const amrex::Vector< double > &  times,
int  start_step,
double  start_time,
bool  restarting 
)
static

How many leading rows of a time.dat with these steps and times a run starting at start_step and start_time keeps: on a restart the rows up to and including the restart that increase in step and time and are not later than the restart; on a fresh start none.

Number of leading rows of time.dat that a run starting at step start_step keeps.

A restart replays the steps after its checkpoint, so the rows for those steps – written by a run that went on past the checkpoint – are dropped and written again. Rows also stop being kept at the first one whose step or time does not increase, or that is later than the restart, or that is at the restart step but not at the restart time: those were not written by the run being continued. A fresh start begins a new series.

Parameters
stepsStep column of time.dat, in file order
timesTime column of time.dat, in file order
start_stepStep the run starts at
start_timeTime the run starts at, as written in time.dat
restartingWhether the run starts from a checkpoint
155 {
156  if (!restarting) { return 0; }
157  // The times of the run being continued are the restart's own to the last bit (time.dat and
158  // the checkpoint both hold 17 digits); a few units in the last place only allow for a
159  // time.dat written by hand, and stay far below any plane spacing also at a calendar clock
160  const double tol = 16.0 * std::numeric_limits<double>::epsilon() * std::max(1.0, std::abs(start_time));
161  int nkeep = 0;
162  for (int i = 0; i < steps.size(); ++i) {
163  if (steps[i] > start_step || times[i] > start_time + tol) { break; }
164  if (i > 0 && (steps[i] <= steps[i-1] || times[i] <= times[i-1])) { break; }
165  if (steps[i] == start_step && std::abs(times[i] - start_time) > tol) { break; }
166  ++nkeep;
167  }
168  return nkeep;
169 }
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12

Referenced by start_series().

Here is the caller graph for this function:

◆ start_series()

bool WriteBndryPlanes::start_series ( int  start_step,
double  start_time,
bool  restarting 
)

Decide which rows of time.dat a run starting at step start_step (at time start_time, with start_time the absolute time written in time.dat) continues from: a restart keeps the rows the run before it wrote up to that step, a fresh start keeps none. The file is rewritten only when the run writes its first plane, so a run that stops before then leaves it as it was. Returns whether the kept rows end with a plane at start_step itself.

Decide which rows of time.dat a run starting at step start_step continues from, and say whether the series already holds the plane at that step. Only the I/O rank reads the file; every rank gets the answer. The file itself is rewritten by write_planes, just before the run's first row, so a run that stops before its first plane leaves the series as it found it.

Parameters
start_stepStep the run starts at (the restart step, or 0)
start_timeTime the run starts at, as written in time.dat
restartingWhether the run starts from a checkpoint
183 {
184  int has_start_plane = 0;
185  if (ParallelDescriptor::IOProcessor()) {
186  std::ifstream in(m_time_file);
187  if (in.good()) {
188  // Every line counts, so that a line that does not parse -- a partial last row left
189  // by a run that stopped while writing it, say -- is dropped with the rows after it
190  Vector<std::string> rows;
191  Vector<int> steps;
192  Vector<double> times;
193  int nlines = 0;
194  std::string line;
195  while (std::getline(in, line)) {
196  ++nlines;
197  std::istringstream row(line);
198  int step;
199  double time;
200  if (static_cast<int>(rows.size()) + 1 < nlines || !(row >> step >> time)) { continue; }
201  rows.push_back(line);
202  steps.push_back(step);
203  times.push_back(time);
204  }
205  in.close();
206 
207  const int nkeep = rows_to_keep(steps, times, start_step, start_time, restarting);
208  has_start_plane = (nkeep > 0 && steps[nkeep-1] == start_step) ? 1 : 0;
209 
210  // A row whose step or time does not increase, anywhere in the file, or a row at or
211  // before the restart that does not continue the kept rows, cannot come from one run
212  bool broken = false;
213  for (int i = 1; i < rows.size(); ++i) {
214  if (steps[i] <= steps[i-1] || times[i] <= times[i-1]) { broken = true; }
215  }
216  if (restarting && nkeep < rows.size() && steps[nkeep] <= start_step) { broken = true; }
217 
218  if (restarting && broken) {
219  Warning("WriteBndryPlanes: " + m_time_file + " does not hold one run's series: a step "
220  "or time does not increase, or a row at or before the restart step " +
221  std::to_string(start_step) + " is not at a time of this run. Only its first " +
222  std::to_string(nkeep) + " rows are kept. A series that an earlier ERF version "
223  "wrote across a restart has such a row, and its step-0 plane holds the "
224  "restart state; write that series again.");
225  } else if (nkeep < nlines) {
226  if (restarting) {
227  Print() << "WriteBndryPlanes: keeping " << nkeep << " of the " << nlines
228  << " rows of " << m_time_file << " up to the restart step "
229  << start_step << ", from this run's first plane on" << std::endl;
230  } else {
231  Print() << "WriteBndryPlanes: a fresh start begins a new series; the "
232  << nlines << " rows of " << m_time_file
233  << " are dropped with this run's first plane" << std::endl;
234  }
235  }
236 
237  // Rewrite even when every row is kept: a last row without its newline would
238  // otherwise run into the next row written
239  m_kept_rows.clear();
240  for (int i = 0; i < nkeep; ++i) {
241  m_kept_rows.push_back(rows[i]);
242  }
243  m_rewrite_time_file = true;
244  }
245  }
246  ParallelDescriptor::Bcast(&has_start_plane, 1, ParallelDescriptor::IOProcessorNumber(),
247  ParallelDescriptor::Communicator());
248  return has_start_plane == 1;
249 }
static int rows_to_keep(const amrex::Vector< int > &steps, const amrex::Vector< double > &times, int start_step, double start_time, bool restarting)
Definition: ERF_WriteBndryPlanes.cpp:152
bool m_rewrite_time_file
Whether time.dat still has to be rewritten to m_kept_rows (I/O rank)
Definition: ERF_WriteBndryPlanes.H:63
amrex::Vector< std::string > m_kept_rows
Rows of time.dat the run continues from, written back before its first plane (I/O rank)
Definition: ERF_WriteBndryPlanes.H:60
Here is the call graph for this function:

◆ write_planes()

void WriteBndryPlanes::write_planes ( int  t_step,
double  time,
amrex::Vector< amrex::Vector< amrex::MultiFab >> &  vars_new,
bool  is_moist 
)

Function to write the specified grid data to an output file

Parameters
t_stepTimestep number
timeCurrent time
vars_newGrid data for all variables across the AMR hierarchy
is_moistWhether moist state components are included in the output
262 {
263  BL_PROFILE("ERF::WriteBndryPlanes::write_planes");
264 
265  MultiFab& S = vars_new[bndry_lev][Vars::cons];
266  MultiFab& xvel = vars_new[bndry_lev][Vars::xvel];
267  MultiFab& yvel = vars_new[bndry_lev][Vars::yvel];
268  MultiFab& zvel = vars_new[bndry_lev][Vars::zvel];
269 
270  const std::string chkname =
271  m_filename + Concatenate("/bndry_output", t_step);
272 
273  //Print() << "Writing boundary planes at time " << time_d << std::endl;
274 
275  const std::string level_prefix = "Level_";
276  PreBuildDirectorHierarchy(chkname, level_prefix, 1, true);
277 
278  // note: by using the entire domain box we end up using 1 processor
279  // to hold all boundaries
280  BoxArray ba(target_box);
281  DistributionMapping dm{ba};
282 
283  IntVect new_hi = target_box.bigEnd() - target_box.smallEnd();
284  Box target_box_shifted(IntVect(0,0,0),new_hi);
285  BoxArray ba_shifted(target_box_shifted);
286 
287  for (int i = 0; i < m_var_names.size(); i++)
288  {
289  std::string var_name = m_var_names[i];
290  std::string filename = MultiFabFileFullPrefix(bndry_lev, chkname, level_prefix, var_name);
291 
292  int ncomp;
293  if (var_name == "velocity") {
294  ncomp = AMREX_SPACEDIM;
295  } else {
296  ncomp = 1;
297  }
298 
299  BndryRegister bndry (ba , dm, m_in_rad, m_out_rad, m_extent_rad, ncomp);
300  BndryRegister bndry_shifted(ba_shifted, dm, m_in_rad, m_out_rad, m_extent_rad, ncomp);
301 
302  int nghost = 0;
303  if (var_name == "density")
304  {
305  bndry.copyFrom(S, nghost, Rho_comp, 0, ncomp, m_geom[bndry_lev].periodicity());
306 
307  } else if (var_name == "temperature") {
308  MultiFab Temp(S.boxArray(),S.DistributionMap(),ncomp,0);
309  for (MFIter mfi(Temp, TilingIfNotGPU()); mfi.isValid(); ++mfi)
310  {
311  const Box& bx = mfi.tilebox();
312  Real time = static_cast<Real>(time_d);
313  if (is_moist) {
314  // NOTE: we send in S[mfi] where we should send in (*z_phys_cc[lev])[mfi] because we know this routine doesn't use it
315  derived::erf_dermoisttemp(bx, Temp[mfi], 0, 1, S[mfi], S[mfi], m_geom[bndry_lev], time, nullptr, bndry_lev);
316  } else {
317  // NOTE: we send in S[mfi] where we should send in (*z_phys_cc[lev])[mfi] because we know this routine doesn't use it
318  derived::erf_dertemp(bx, Temp[mfi], 0, 1, S[mfi], S[mfi], m_geom[bndry_lev], time, nullptr, bndry_lev);
319  }
320  }
321  bndry.copyFrom(Temp, nghost, 0, 0, ncomp, m_geom[bndry_lev].periodicity());
322  } else if (var_name == "theta") {
323  MultiFab Temp(S.boxArray(),S.DistributionMap(),ncomp,0);
324  for (MFIter mfi(Temp, TilingIfNotGPU()); mfi.isValid(); ++mfi)
325  {
326  const Box& bx = mfi.tilebox();
327  derived::erf_derrhodivide(bx, Temp[mfi], S[mfi], RhoTheta_comp);
328  }
329  bndry.copyFrom(Temp, nghost, 0, 0, ncomp, m_geom[bndry_lev].periodicity());
330  } else if (var_name == "scalar") {
331  MultiFab Temp(S.boxArray(),S.DistributionMap(),ncomp,0);
332  for (MFIter mfi(Temp, TilingIfNotGPU()); mfi.isValid(); ++mfi)
333  {
334  const Box& bx = mfi.tilebox();
335  derived::erf_derrhodivide(bx, Temp[mfi], S[mfi], RhoScalar_comp);
336  }
337  bndry.copyFrom(Temp, nghost, 0, 0, ncomp, m_geom[bndry_lev].periodicity());
338  } else if (var_name == "ke") {
339  MultiFab Temp(S.boxArray(),S.DistributionMap(),ncomp,0);
340  for (MFIter mfi(Temp, TilingIfNotGPU()); mfi.isValid(); ++mfi)
341  {
342  const Box& bx = mfi.tilebox();
343  derived::erf_derrhodivide(bx, Temp[mfi], S[mfi], RhoKE_comp);
344  }
345  bndry.copyFrom(Temp, nghost, 0, 0, ncomp, m_geom[bndry_lev].periodicity());
346  } else if (var_name == "qv") {
347  MultiFab Temp(S.boxArray(),S.DistributionMap(),ncomp,0);
348  if (S.nComp() > RhoQ2_comp) {
349  for (MFIter mfi(Temp, TilingIfNotGPU()); mfi.isValid(); ++mfi)
350  {
351  const Box& bx = mfi.tilebox();
352  derived::erf_derrhodivide(bx, Temp[mfi], S[mfi], RhoQ1_comp);
353  }
354  } else {
355  Temp.setVal(0.);
356  }
357  bndry.copyFrom(Temp, nghost, 0, 0, ncomp, m_geom[bndry_lev].periodicity());
358  } else if (var_name == "qc") {
359  MultiFab Temp(S.boxArray(),S.DistributionMap(),ncomp,0);
360  if (S.nComp() > RhoQ2_comp) {
361  for (MFIter mfi(Temp, TilingIfNotGPU()); mfi.isValid(); ++mfi)
362  {
363  const Box& bx = mfi.tilebox();
364  derived::erf_derrhodivide(bx, Temp[mfi], S[mfi], RhoQ2_comp);
365  }
366  } else {
367  Temp.setVal(0.);
368  }
369  bndry.copyFrom(Temp, nghost, 0, 0, ncomp, m_geom[bndry_lev].periodicity());
370  } else if (var_name == "velocity") {
371  MultiFab Vel(S.boxArray(), S.DistributionMap(), 3, m_out_rad);
372  average_face_to_cellcenter(Vel,0,Array<const MultiFab*,3>{&xvel,&yvel,&zvel});
373  bndry.copyFrom(Vel, nghost, 0, 0, ncomp, m_geom[bndry_lev].periodicity());
374  } else {
375  //Print() << "Trying to write planar output for " << var_name << std::endl;
376  Error("Don't know how to output this variable");
377  }
378 
379  for (OrientationIter oit; oit != nullptr; ++oit) {
380  auto ori = oit();
381  if (ori.coordDir() < 2) {
382  std::string facename = Concatenate(filename + '_', ori, 1);
383  br_shift(oit, bndry, bndry_shifted);
384  bndry_shifted[ori].write(facename);
385  }
386  }
387 
388  } // loop over num_vars
389 
390  // Writing time.dat
391  if (ParallelDescriptor::IOProcessor()) {
392  // The rows this run continues from replace the file before its first row, through a
393  // temporary file so that a crash leaves either the old file or the new one
394  if (m_rewrite_time_file) {
395  const std::string tmp_file = m_time_file + ".tmp";
396  std::ofstream out(tmp_file, std::ios::out | std::ios::trunc);
397  for (const auto& row : m_kept_rows) {
398  out << row << '\n';
399  }
400  out.close();
401  if (out.fail()) {
402  Abort("WriteBndryPlanes: cannot write " + tmp_file);
403  }
404  std::error_code ec;
405  std::filesystem::rename(tmp_file, m_time_file, ec);
406  if (ec) {
407  Abort("WriteBndryPlanes: cannot replace " + m_time_file + " with " + tmp_file + ": " + ec.message());
408  }
409  m_rewrite_time_file = false;
410  m_kept_rows.clear();
411  }
412  std::ofstream oftime(m_time_file, std::ios::out | std::ios::app);
413  oftime << std::setprecision(17) << t_step << ' ' << time_d << '\n';
414  oftime.close();
415  }
416 }
#define RhoScalar_comp
Definition: ERF_IndexDefines.H:46
#define Rho_comp
Definition: ERF_IndexDefines.H:42
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:43
#define RhoQ2_comp
Definition: ERF_IndexDefines.H:49
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:48
#define RhoKE_comp
Definition: ERF_IndexDefines.H:44
void br_shift(OrientationIter oit, const BndryRegister &b1, BndryRegister &b2)
Definition: ERF_WriteBndryPlanes.cpp:29
const int m_out_rad
Definition: ERF_WriteBndryPlanes.H:75
const int m_extent_rad
Definition: ERF_WriteBndryPlanes.H:76
constexpr int S
Definition: ERF_TwoStreamColumn.H:604
@ xvel
Definition: ERF_IndexDefines.H:218
@ cons
Definition: ERF_IndexDefines.H:217
@ zvel
Definition: ERF_IndexDefines.H:220
@ yvel
Definition: ERF_IndexDefines.H:219
void erf_derrhodivide(const Box &bx, FArrayBox &derfab, const FArrayBox &datfab, const int scalar_index)
Definition: ERF_Derive.cpp:183
void erf_dermoisttemp(const Box &bx, FArrayBox &derfab, int, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:281
void erf_dertemp(const Box &bx, FArrayBox &derfab, int, int, const FArrayBox &datfab, const FArrayBox &, const Geometry &, Real, const int *, const int)
Definition: ERF_Derive.cpp:258
Here is the call graph for this function:

Member Data Documentation

◆ bndry_lev

int WriteBndryPlanes::bndry_lev = 0
staticprivate

Referenced by write_planes(), and WriteBndryPlanes().

◆ m_extent_rad

const int WriteBndryPlanes::m_extent_rad = 0
private

Referenced by write_planes().

◆ m_filename

std::string WriteBndryPlanes::m_filename {""}
private

File name for IO.

Referenced by write_planes(), and WriteBndryPlanes().

◆ m_geom

amrex::Vector<amrex::Geometry>& WriteBndryPlanes::m_geom
private

Geometry objects for all levels.

Referenced by write_planes(), and WriteBndryPlanes().

◆ m_in_rad

int WriteBndryPlanes::m_in_rad = 1
private

controls extents on native bndry output

Referenced by write_planes(), and WriteBndryPlanes().

◆ m_in_times

amrex::Vector<double> WriteBndryPlanes::m_in_times
private

Timestep and times to be stored in time.dat.

◆ m_in_timesteps

amrex::Vector<int> WriteBndryPlanes::m_in_timesteps
private

◆ m_kept_rows

amrex::Vector<std::string> WriteBndryPlanes::m_kept_rows
private

Rows of time.dat the run continues from, written back before its first plane (I/O rank)

Referenced by start_series(), and write_planes().

◆ m_out_rad

const int WriteBndryPlanes::m_out_rad = 1
private

Referenced by write_planes().

◆ m_rewrite_time_file

bool WriteBndryPlanes::m_rewrite_time_file = false
private

Whether time.dat still has to be rewritten to m_kept_rows (I/O rank)

Referenced by start_series(), and write_planes().

◆ m_time_file

std::string WriteBndryPlanes::m_time_file {""}
private

File name for Native time file.

Referenced by start_series(), write_planes(), and WriteBndryPlanes().

◆ m_var_names

amrex::Vector<std::string> WriteBndryPlanes::m_var_names
private

Variables for IO.

Referenced by write_planes(), and WriteBndryPlanes().

◆ target_box

amrex::Box WriteBndryPlanes::target_box
private

IO output box region.

Referenced by write_planes(), and WriteBndryPlanes().


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