ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
main.cpp File Reference
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <AMReX.H>
#include <AMReX_BLProfiler.H>
#include <AMReX_ParallelDescriptor.H>
#include "ERF.H"
#include "ERF_InputsName.H"
Include dependency graph for main.cpp:

Functions

void add_par ()
 
static void erf_flush_fortran_units ()
 
void install_exit_trap ()
 
int main (int argc, char *argv[])
 

Variables

std::string inputs_name
 

Function Documentation

◆ add_par()

void add_par ( )

Function to set the refine_grid_layout flags to (1,1,0) by default since the ERF default is different from the amrex default (1,1,1) Also set max_grid_size to very large since the only reason for chopping grids is if Nprocs > Ngrids, and set no_box_split_dir to 2 so that grids are never decomposed in the vertical direction

32  {
33  ParmParse pp("amr");
34  ParmParse pp_erf("erf");
35 
36  // Set the refine_grid_layout flags to (1,1,0) by default
37  pp.add("refine_grid_layout_x",1);
38  pp.add("refine_grid_layout_y",1);
39  pp.add("refine_grid_layout_z",0);
40 
41  // no_box_split_dir tells amrex that the grids must never be split in that
42  // direction; max_grid_size and refine_grid_layout are then ignored there.
43  // ERF sets this to 2 (the z-direction) by default so that no grid is ever
44  // decomposed in the vertical -- no two grids then share a face normal to z, so
45  // no column is chopped between them, which is required, e.g., by the implicit
46  // vertical diffusion solves and by the implicit acoustic substepping. (Several
47  // grids may still sit over the same column, as long as they do not touch; what
48  // those solves cannot do is split one contiguous column at a box seam.) Setting
49  // amr.no_box_split_dir = -1 in the inputs file restores the amrex behavior of
50  // allowing grids to be chopped in every direction; ERF::ReadParameters rejects
51  // that if any level uses erf.substepping_type = Implicit.
52  int no_box_split_dir = 2;
53  pp.queryAdd("no_box_split_dir",no_box_split_dir);
54 
55  // n_proper is the minimum number of coarse cells between coarse-fine boundaries
56  // between levels (ell and ell+1) and levels (ell-1 and ell). We want this to be
57  // greater than or equal to the stencil width (a function of spatial order) divided by
58  // ref_ratio (which can be 2,3 or 4). This ensures that fillpatch at level (ell)
59  // does not need to reach beyond level (ell-1). Here to be conservative we set this to 2
60  // (rather than the amrex default of 1).
61  pp.add("n_proper",2);
62 
63  int max_grid_size = 2048;
64  pp.queryAdd("max_grid_size",max_grid_size);
65 
66  // This will set the default value of blocking_factor to be 1, but will allow
67  // the user to override it in the inputs file or on command line
68  int blocking_factor = 1;
69  pp.queryAdd("blocking_factor",blocking_factor);
70 
71  int n_error_buf = 0;
72  pp.queryAdd("n_error_buf",n_error_buf);
73 
74  amrex::Vector<int> legacy_n_cell;
75  amrex::Vector<int> erf_n_cell;
76  const bool has_legacy_n_cell = pp.queryarr("n_cell", legacy_n_cell);
77  const bool has_erf_n_cell = pp_erf.queryarr("n_cell", erf_n_cell);
78  if (has_erf_n_cell && has_legacy_n_cell) {
79  amrex::Abort("erf.n_cell and amr.n_cell are both specified. Please use only one!");
80  }
81  if (has_erf_n_cell) {
82  pp.addarr("n_cell", erf_n_cell);
83  }
84 
85  ParmParse pp_geometry("geometry");
86  amrex::Vector<amrex::Real> legacy_prob_lo;
87  amrex::Vector<amrex::Real> legacy_prob_hi;
88  amrex::Vector<amrex::Real> legacy_prob_extent;
89  amrex::Vector<int> legacy_is_periodic;
90  amrex::Vector<amrex::Real> erf_prob_lo;
91  amrex::Vector<amrex::Real> erf_prob_hi;
92  amrex::Vector<amrex::Real> erf_prob_extent;
93  amrex::Vector<int> erf_is_periodic;
94 
95  const bool has_legacy_prob_lo = pp_geometry.queryarr("prob_lo", legacy_prob_lo);
96  const bool has_legacy_prob_hi = pp_geometry.queryarr("prob_hi", legacy_prob_hi);
97  const bool has_legacy_prob_extent = pp_geometry.queryarr("prob_extent", legacy_prob_extent);
98  const bool has_legacy_is_periodic = pp_geometry.queryarr("is_periodic", legacy_is_periodic);
99 
100  const bool has_erf_prob_lo = pp_erf.queryarr("prob_lo", erf_prob_lo);
101  const bool has_erf_prob_hi = pp_erf.queryarr("prob_hi", erf_prob_hi);
102  const bool has_erf_prob_extent = pp_erf.queryarr("prob_extent", erf_prob_extent);
103  const bool has_erf_is_periodic = pp_erf.queryarr("is_periodic", erf_is_periodic);
104 
105  if (has_erf_prob_lo && has_legacy_prob_lo) {
106  amrex::Abort("erf.prob_lo and geometry.prob_lo are both specified. Please use only one!");
107  }
108  if ((has_erf_prob_hi || has_erf_prob_extent) && !has_erf_prob_lo) {
109  amrex::Abort("ERF prefixed geometry requires erf.prob_lo together with erf.prob_hi or erf.prob_extent.");
110  }
111  if (has_erf_is_periodic && has_legacy_is_periodic) {
112  amrex::Abort("erf.is_periodic and geometry.is_periodic are both specified. Please use only one!");
113  }
114  if (has_erf_prob_hi && has_legacy_prob_hi) {
115  amrex::Abort("erf.prob_hi and geometry.prob_hi are both specified. Please use only one!");
116  }
117  if (has_erf_prob_extent && has_legacy_prob_extent) {
118  amrex::Abort("erf.prob_extent and geometry.prob_extent are both specified. Please use only one!");
119  }
120  if (has_erf_prob_hi || has_erf_prob_extent) {
121  if (has_legacy_prob_hi || has_legacy_prob_extent) {
122  amrex::Abort("ERF prefixed geometry and legacy geometry.prob_hi/prob_extent are both specified. Please use only one form.");
123  }
124  }
125  if (has_erf_prob_hi && has_erf_prob_extent) {
126  amrex::Vector<amrex::Real> derived_prob_hi(erf_prob_extent.size(), 0.0);
127  for (int i = 0; i < static_cast<int>(erf_prob_extent.size()); ++i) {
128  derived_prob_hi[i] = (i < static_cast<int>(erf_prob_lo.size()) ? erf_prob_lo[i] : 0.0) + erf_prob_extent[i];
129  }
131  derived_prob_hi.size() == erf_prob_hi.size(),
132  "erf.prob_hi and erf.prob_extent have inconsistent lengths.");
133  for (int i = 0; i < static_cast<int>(erf_prob_hi.size()); ++i) {
134  if (derived_prob_hi[i] != erf_prob_hi[i]) {
135  amrex::Abort("erf.prob_hi and erf.prob_extent are both specified but inconsistent.");
136  }
137  }
138  }
139 
140  if (has_erf_prob_lo) {
141  pp_geometry.addarr("prob_lo", erf_prob_lo);
142  }
143  if (has_erf_prob_hi) {
144  pp_geometry.addarr("prob_hi", erf_prob_hi);
145  } else if (has_erf_prob_extent) {
146  amrex::Vector<amrex::Real> derived_prob_hi(erf_prob_extent.size(), 0.0);
147  for (int i = 0; i < static_cast<int>(erf_prob_extent.size()); ++i) {
148  derived_prob_hi[i] = (i < static_cast<int>(erf_prob_lo.size()) ? erf_prob_lo[i] : 0.0) + erf_prob_extent[i];
149  }
150  pp_geometry.addarr("prob_hi", derived_prob_hi);
151  }
152  if (has_erf_is_periodic) {
153  pp_geometry.addarr("is_periodic", erf_is_periodic);
154  }
155 }
ParmParse pp("prob")
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")

Referenced by main().

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

◆ erf_flush_fortran_units()

static void erf_flush_fortran_units ( )
static
161 {}

Referenced by install_exit_trap().

Here is the caller graph for this function:

◆ install_exit_trap()

void install_exit_trap ( )

Make a run that leaves through exit() before amrex::Finalize report failure.

A normal run calls amrex::Finalize before main returns, so a process that reaches exit() while AMReX is still initialised has stopped part-way – with no final plotfile or checkpoint – whatever status it asked for. The ways in are a Fortran STOP in a coupled library (Noah-MP has 27, all exit status 0) and the exit() calls left in ERF itself, some of which also pass 0. Run under Open MPI's launcher such a run is still flagged, because the rank never calls MPI_Finalize; run directly, or under a launcher that does not enforce that, it reports success. Every return from main after amrex::Initialize goes through amrex::Finalize, so this never fires on a normal exit.

amrex::Abort ends in MPI_Abort, or std::_Exit in a serial build. Open MPI's MPI_Abort kills the job without exit(), but MPICH's (and Cray MPICH's) ends in exit(errorcode), so there the trap runs after an abort as well: the run is still reported as a failure, with status 1 in place of the abort's code, and the message below allows for it.

A Fortran STOP or an exit() fires on the one rank that hit the problem; ending only that rank would leave the others blocked in their next collective until the launcher notices (Open MPI does; srun without –kill-on-bad-exit and some PMI setups may not). So the trap calls MPI_Abort to tear the whole job down, guarded so that an MPI whose own MPI_Abort ends in exit() (MPICH, Cray MPICH) cannot re-enter it, and skipped when MPI is not initialised or already finalised.

std::_Exit is the only way to change the status from here, and it skips the rest of the exit sequence, which is what would flush the output buffers. Standard output is fully buffered when it is redirected to a file, so flush everything first – C++ streams, C stdio and the Fortran units – or the lines that explain the stop are lost.

194 {
195  std::atexit([]() {
196  if (amrex::Initialized()) {
197  std::cout.flush();
198  std::clog.flush();
199  std::cerr.flush();
201  std::fflush(nullptr);
202  std::fputs("ERF: exit() was called while AMReX was still initialised, so the run "
203  "stopped before finishing; the reason should be printed above (a "
204  "Fortran STOP in a coupled library such as Noah-MP, an error exit in "
205  "ERF, or an amrex::Abort under an MPI whose MPI_Abort calls exit()). "
206  "Reporting failure whatever exit status was requested.\n",
207  stderr);
208  std::fflush(stderr);
209 #ifdef AMREX_USE_MPI
210  // atexit handlers run single-threaded, so a plain flag is enough of a
211  // reentrancy guard for the MPI_Abort-calls-exit() implementations.
212  static bool aborting = false;
213  int mpi_up = 0, mpi_done = 0;
214  MPI_Initialized(&mpi_up);
215  MPI_Finalized(&mpi_done);
216  if (mpi_up && !mpi_done && !aborting) {
217  aborting = true;
218  MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
219  }
220 #endif
221  std::_Exit(EXIT_FAILURE);
222  }
223  });
224 }
static void erf_flush_fortran_units()
Definition: main.cpp:161

Referenced by main().

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

◆ main()

int main ( int  argc,
char *  argv[] 
)

Main driver – creates the ERF object, calls ERF.InitData() and ERF.Evolve()

230 {
231 
232 auto finalize_mpi_and_return = [](int code) {
233 #ifdef AMREX_USE_MPI
234 #ifdef ERF_USE_WW3_COUPLING
235  amrex::MPMD::Finalize();
236 #else
237  MPI_Finalize();
238 #endif
239 #endif
240 return code;
241 };
242 
243 #if defined(AMREX_MPI_THREAD_MULTIPLE)
244  int requested = MPI_THREAD_MULTIPLE;
245  int provided = -1;
246  MPI_Init_thread(&argc, &argv, requested, &provided);
247 #elif defined(AMREX_USE_MPI)
248  MPI_Init(&argc, &argv);
249 #endif
250 
251  if (argc < 2) {
252  // Print usage and exit with error code if no input file was provided.
253  ERF::print_usage(MPI_COMM_WORLD, std::cout);
255  MPI_COMM_WORLD, "No input file provided. Exiting!!");
256  return 1;
257  }
258 
259  // Look for "-h" or "--help" flag and print usage
260  for (auto i = 1; i < argc; i++) {
261  const std::string param(argv[i]);
262  if ((param == "--help") || (param == "-h") || (param == "--usage")) {
263  ERF::print_banner(MPI_COMM_WORLD, std::cout);
264  ERF::print_usage(MPI_COMM_WORLD, std::cout);
265  return 0;
266  }
267  }
268 
269  if (argc >= 2) {
270  for (auto i = 1; i < argc; i++) {
271  if (std::string(argv[i]) == "--describe") {
272  ERF::writeBuildInfo(std::cout);
273  return finalize_mpi_and_return(0);
274  }
275  }
276  }
277 
278  if (!strchr(argv[1], '=') && !amrex::FileSystem::Exists(std::string(argv[1])))
279  {
280  // Print usage and exit with error code if we cannot find the input file
281  ERF::print_usage(MPI_COMM_WORLD, std::cout);
283  MPI_COMM_WORLD, "Input file does not exist = " +
284  std::string(argv[1]) + ". Exiting!!");
285  return finalize_mpi_and_return(1);
286  }
287 
288  // print_banner(MPI_COMM_WORLD, std::cout);
289  // Check to see if the command line contains --describe
290  if (argc >= 2) {
291  for (auto i = 1; i < argc; i++) {
292  if (std::string(argv[i]) == "--describe") {
293  ERF::writeBuildInfo(std::cout);
294  return 0;
295  }
296  }
297  }
298 #ifdef ERF_USE_WW3_COUPLING
299  MPI_Comm comm = amrex::MPMD::Initialize(argc, argv);
300  amrex::Initialize(argc,argv,true,comm,add_par);
301 #else
302  amrex::Initialize(argc,argv,true,MPI_COMM_WORLD,add_par);
303 #endif
304 
306 
307  // Only when argv[1] is actually an inputs file (same test the surrounding code uses).
308  if (argc > 1 && std::string(argv[1]).find('=') == std::string::npos) {
309  CheckForDuplicateInputs(argv[1]);
310  }
311 
312 #ifdef ERF_USE_KOKKOS
313  // Initialize kokkos
314  if (!Kokkos::is_initialized()) {
315  Kokkos::initialize(Kokkos::InitializationSettings()
316  .set_device_id(amrex::Gpu::Device::deviceId()));
317  }
318 #endif
319 
320  // Save the inputs file name for later.
321  if (!strchr(argv[1], '=')) {
322  inputs_name = argv[1];
323  }
324 
325  // timer for profiling
326  BL_PROFILE_VAR("main()", pmain);
327 
328  // wallclock time
329  const double strt_total = amrex::second();
330 
331  {
332  // constructor - reads in parameters from inputs file
333  // - sizes multilevel arrays and data structures
334  ERF erf;
335 
336  // initialize AMR data
337  erf.InitData();
338 
339  // advance solution to final time
340  erf.Evolve();
341 
342  // wallclock time
343  double end_total = amrex::second() - strt_total;
344 
345  // print wallclock time
346  ParallelDescriptor::ReduceRealMax(end_total ,ParallelDescriptor::IOProcessorNumber());
347  if (erf.Verbose()) {
348  amrex::Print() << "\nTotal Time: " << end_total << '\n';
349  }
350  }
351 
352  // destroy timer for profiling
353  BL_PROFILE_VAR_STOP(pmain);
354 #ifdef ERF_USE_WW3_COUPLING
355  MPI_Barrier(MPI_COMM_WORLD);
356 #endif
357 
358 #ifdef ERF_USE_KOKKOS
359  Kokkos::finalize();
360 #endif
361 
362  amrex::Finalize();
363 #ifdef AMREX_USE_MPI
364 #ifdef ERF_USE_WW3_COUPLING
365  amrex::MPMD::Finalize();
366 #else
367  MPI_Finalize();
368 #endif
369 #endif
370 }
void CheckForDuplicateInputs(const std::string &inputs_file)
Definition: ERF_InputsName.H:272
void Evolve()
Definition: ERF.cpp:147
static void print_banner(MPI_Comm, std::ostream &)
Definition: ERF_ConsoleIO.cpp:65
void InitData()
Definition: ERF.cpp:889
static void print_error(MPI_Comm, const std::string &msg)
Definition: ERF_ConsoleIO.cpp:48
static void writeBuildInfo(std::ostream &os)
Definition: ERF_WriteJobInfo.cpp:183
static void print_usage(MPI_Comm, std::ostream &)
Definition: ERF_ConsoleIO.cpp:31
void add_par()
Definition: main.cpp:32
std::string inputs_name
Definition: main.cpp:21
void install_exit_trap()
Definition: main.cpp:193
Definition: ERF_InterpolationUtils.H:15
Here is the call graph for this function:

Variable Documentation

◆ inputs_name

std::string inputs_name

Referenced by main(), and ERF::writeJobInfo().