REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA.cpp
Go to the documentation of this file.
1/**
2 * \file REMORA.cpp
3 */
4
6#include <REMORA.H>
8
9#ifdef REMORA_USE_NETCDF
10#include "REMORA_NCFile.H"
11#endif
12
13#include <AMReX_buildInfo.H>
14
15using namespace amrex;
16
17amrex::Real REMORA::startCPUTime = zero;
19
20Vector<REMORAErrorTag> REMORA::ref_tags;
21
23
24// Time step control
25amrex::Real REMORA::cfl = Real(0.8);
26amrex::Real REMORA::fixed_dt = -one;
27amrex::Real REMORA::change_max = Real(1.1);
28
29int REMORA::ndtfast = 0;
30
31// Dictate verbosity in screen output
32int REMORA::verbose = 0;
33
34// Frequency of diagnostic output
36amrex::Real REMORA::sum_per = -one;
37
38// Minimum number of digits in plotfile name
40
41// Do we include staggered velocities in the plotfile?
43
44// Do we include nodal data (Nu_nd) in the plotfile?
45bool REMORA::plot_nodal_data = true;
46
47// Native AMReX vs NetCDF
49
50#ifdef REMORA_USE_NETCDF
51
53
54// Do we write one file per timestep (false) or one file for all timesteps (true)
56
57// NetCDF initialization file
58amrex::Vector<std::string> REMORA::nc_bdry_file = {""}; // Must provide via input
59amrex::Vector<amrex::Vector<std::string>> REMORA::nc_init_file = {{""}}; // Must provide via input
60amrex::Vector<amrex::Vector<std::string>> REMORA::nc_grid_file = {{""}}; // Must provide via input
61#endif
62
63/**
64 * constructor:
65 * - reads in parameters from inputs file
66 * - sizes multilevel arrays and data structures
67 * - initializes BCRec boundary condition object
68 */
70{
71 BL_PROFILE("REMORA::REMORA()");
72
73 if (ParallelDescriptor::IOProcessor()) {
76 const char* buildgithash = amrex::buildInfoGetBuildGitHash();
77 const char* buildgitname = amrex::buildInfoGetBuildGitName();
78
79 if (strlen(remora_hash) > 0) {
80 amrex::Print() << "\n"
81 << "REMORA git hash: " << remora_hash << "\n";
82 }
83 if (strlen(amrex_hash) > 0) {
84 amrex::Print() << "AMReX git hash: " << amrex_hash << "\n";
85 }
86 if (strlen(buildgithash) > 0) {
87 amrex::Print() << buildgitname << " git hash: " << buildgithash << "\n";
88 }
89
90 amrex::Print() << "\n";
91 }
92
94
95 // Blocking factor in z set to very large value to be > nz
96 // This guarantees that there will be no domain decomposition in the z-direction
97 // We have to set this by hand here because setting it in the input file will
98 // cause checks in the AmrCore constructor to fail.
101 for (int lev = 0; lev <= max_level; ++lev) {
103 blocking_factor_vec[lev][2] = 4096;
104 }
106
107 const std::string& pv3d = "plot_vars_3d"; set3DPlotVariables(pv3d);
108 const std::string& pv2d = "plot_vars_2d"; set2DPlotVariables(pv2d);
109
111
112 // Geometry on all levels has been defined already.
113
114 // No valid BoxArray and DistributionMapping have been defined.
115 // But the arrays for them have been resized.
116
117 int nlevs_max = max_level + 1;
118
119 istep.resize(nlevs_max, 0);
121
122 physbcs.resize(nlevs_max);
123
124 t_new.resize(nlevs_max, zero);
127
128 cons_new.resize(nlevs_max);
129 cons_old.resize(nlevs_max);
130 xvel_new.resize(nlevs_max);
131 xvel_old.resize(nlevs_max);
132 yvel_new.resize(nlevs_max);
133 yvel_old.resize(nlevs_max);
134 zvel_new.resize(nlevs_max);
135 zvel_old.resize(nlevs_max);
136
137 advflux_reg.resize(nlevs_max);
138
139 // Initialize tagging criteria for mesh refinement
141
143}
144
145REMORA::REMORA (const amrex::RealBox& rb, int max_level_in, const amrex::Vector<int>& n_cell_in, int coord, const amrex::Vector<amrex::IntVect>& ref_ratio_in, const amrex::Array<int,AMREX_SPACEDIM>& is_per, std::string prefix)
147{
148 BL_PROFILE("REMORA::REMORA(explicit)");
150
151 if (ParallelDescriptor::IOProcessor()) {
153 const char* amrex_hash = amrex::buildInfoGetGitHash(2);
154 const char* buildgithash = amrex::buildInfoGetBuildGitHash();
155 const char* buildgitname = amrex::buildInfoGetBuildGitName();
156
157 if (strlen(remora_hash) > 0) {
158 amrex::Print() << "\n"
159 << "REMORA git hash: " << remora_hash << "\n";
160 }
161 if (strlen(amrex_hash) > 0) {
162 amrex::Print() << "AMReX git hash: " << amrex_hash << "\n";
163 }
164 if (strlen(buildgithash) > 0) {
165 amrex::Print() << buildgitname << " git hash: " << buildgithash << "\n";
166 }
167
168 amrex::Print() << "\n";
169 }
170
172
173 const std::string& pv3d = "plot_vars_3d"; set3DPlotVariables(pv3d);
174 const std::string& pv2d = "plot_vars_2d"; set2DPlotVariables(pv2d);
175
177
178 int nlevs_max = max_level + 1;
179
180 istep.resize(nlevs_max, 0);
182
183 physbcs.resize(nlevs_max);
184
185 t_new.resize(nlevs_max, zero);
188
189 cons_new.resize(nlevs_max);
190 cons_old.resize(nlevs_max);
191 xvel_new.resize(nlevs_max);
192 xvel_old.resize(nlevs_max);
193 yvel_new.resize(nlevs_max);
194 yvel_old.resize(nlevs_max);
195 zvel_new.resize(nlevs_max);
196 zvel_old.resize(nlevs_max);
197
198 advflux_reg.resize(nlevs_max);
199
201
203}
204
206{
207}
208
209/**
210 * Reject refinement in the vertical, and accumulate the refinement ratios.
211 *
212 * Shared by both constructors, so both fill cum_ref_ratios, which the
213 * full-domain hires arrays and the mask coarsening index into.
214 */
215void
217{
219
220 IntVect cum_ref_ratio = IntVect(1,1,0);
221 cum_ref_ratios.push_back(cum_ref_ratio);
222 // We have already read in the ref_ratio (via amr.ref_ratio =) but we need to enforce
223 // that there is no refinement in the vertical so we test on that here.
224 for (int lev = 0; lev < max_level; ++lev)
225 {
226 amrex::Print() << "Refinement ratio at level " << lev << " set to be " <<
227 ref_ratio[lev][0] << " " << ref_ratio[lev][1] << " " << ref_ratio[lev][2] << std::endl;
228
229 if (ref_ratio[lev][2] != 1)
230 {
231 amrex::Print() << "********************************************************************************" << std::endl;
232 amrex::Print() << "We don't allow refinement in the vertical -- make sure to set ref_ratio = 1 in z" << std::endl;
233 amrex::Print() << "It's possible you set amr.ref_ratio when you meant to set amr.ref_ratio_vect " << std::endl;
234 amrex::Print() << "********************************************************************************" << std::endl;
235 amrex::Abort();
236 }
237
238 cum_ref_ratio[0] *= ref_ratio[lev][0];
239 cum_ref_ratio[1] *= ref_ratio[lev][1];
240 cum_ref_ratios.push_back(cum_ref_ratio);
241 }
242}
243
244void
246{
247 cons_names.clear();
248 cons_names.reserve(ncons);
249 cons_names.emplace_back("temp");
250 cons_names.emplace_back("salt");
251
252 // Passive (dye) scalars come first, then the biology block, matching the component
253 // layout: temp, salt, tracer, tracer_1, ..., NO3, NH4, ...
254 if (nscalar > 0) {
255 cons_names.emplace_back("tracer");
256 for (int i = 1; i < nscalar; ++i) {
257 cons_names.emplace_back("tracer_" + std::to_string(i));
258 }
259 }
260
263 for (const auto& name : bio_names) {
264 cons_names.emplace_back(name);
265 }
266 }
267
268 AMREX_ALWAYS_ASSERT(static_cast<int>(cons_names.size()) == ncons);
269}
270
271void
273{
274 BL_PROFILE_VAR("REMORA::Evolve()",evolve);
275 Real cur_time = t_new[0];
276 const Real stop_elapsed = elapsed_time(stop_time);
277
278 // istep[0] advances inside the loop, so keep the value it started at.
279 const int first_step = istep[0];
280
281 // Levels appear as tagging occurs, so reprint the hierarchy when finest_level changes.
282 int reported_finest = -1;
283
284 // Take one coarse timestep by calling timeStep -- which recursively calls timeStep
285 // for finer levels (with or without subcycling)
286 for (int step = istep[0]; step < max_step && cur_time < stop_elapsed; ++step)
287 {
288 amrex::Print() << "\nCoarse STEP " << step+1 << " starts ..." << std::endl;
289
290 ComputeDt();
291
292 // dt is only populated once ComputeDt has run.
296 }
297
298 int lev = 0;
299 int iteration = 1;
300 auto dEvolveTime0 = amrex::second();
301
302 // timeStep recurses into finer levels nsubsteps[lev+1] times; timeStepML advances
303 // every level once through one shared barotropic loop.
304 if (max_level == 0 || do_substep) {
306 }
307 else {
309 }
310
311 cur_time += dt[0];
312
313 amrex::Print() << "Coarse STEP " << step+1 << " ends." << " TIME = " << cur_time
314 << " DT = " << dt[0] << std::endl;
315
316 if (verbose > 0)
317 {
318 auto dEvolveTime = amrex::second() - dEvolveTime0;
319 ParallelDescriptor::ReduceRealMax(dEvolveTime,ParallelDescriptor::IOProcessorNumber());
320 amrex::Print() << "Timestep time = " << dEvolveTime << " seconds." << '\n';
321 }
322
324
326
327#ifdef AMREX_MEM_PROFILING
328 {
329 std::ostringstream ss;
330 ss << "[STEP " << step+1 << "]";
331 MemProfiler::report(ss.str());
332 }
333#endif
334
335 if (cur_time >= stop_elapsed - 1.e-6*dt[0]) break;
336 }
337
339
341}
342
343void
345{
346
347 if ( (plot_int > 0 || plot_int_time > zero) && istep[0] > last_plot_file_step)
348 {
353 }
354
355 if ((check_int > 0 || check_int_time > zero) && istep[0] > last_check_file_step) {
357 }
358}
359
360void
379
380/**
381 * Apply the tracer flux correction accumulated at the lev/lev+1 interface onto lev.
382 *
383 * Once per step of lev, pairing the reset at the top of Advance(lev), so the accumulate and
384 * apply window sits inside one step of lev. Applying it per step of level 0 instead would
385 * keep only the last correction whenever lev was itself substepped, and would let the regrid
386 * of lev+1 rebuild the register mid-accumulation.
387 *
388 * @param[in] lev coarse level of the interface
389 */
390void
392{
393 if (!(do_reflux && do_substep) || lev >= finest_level ||
395 return;
396 }
397
398 BL_PROFILE("REMORA::reflux_to()");
399
400 // The register holds the correction in Hz*t units, the form the tracer is advanced in.
401 // Reflux into a scratch fab and divide that down, rather than scaling the level into
402 // those units and back: that round trip is inexact for about a tenth of cells, which
403 // perturbs cells the correction never reached and leaves the clamp below unable to tell
404 // which ones it did.
406 dcons.setVal(zero);
407 getAdvFluxReg(lev+1)->Reflux(dcons, 0, 0, ncons);
408
409 const bool clamp = reflux_clamp;
410
411#ifdef _OPENMP
412#pragma omp parallel if (Gpu::notInLaunchRegion())
413#endif
414 for (MFIter mfi(*cons_new[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
415 {
416 Array4<Real > const& c = cons_new[lev]->array(mfi);
417 Array4<Real const> const& dc = dcons.const_array(mfi);
418 Array4<Real const> const& hz = vec_Hz[lev]->const_array(mfi);
419
420 ParallelFor(mfi.tilebox(), ncons, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n)
421 {
422 // No thickness is land or dry: leave it rather than divide by zero.
423 if (dc(i,j,k,n) == zero || hz(i,j,k) <= zero) { return; }
424
425 c(i,j,k,n) += dc(i,j,k,n) / hz(i,j,k);
426
427 // Clamping restores the mass the correction removed, so a step that clamps is
428 // not conservative: ROMS's trade, and why it is an option. Zero suits a
429 // concentration, less so temperature in Celsius.
430 if (clamp && c(i,j,k,n) < zero) { c(i,j,k,n) = zero; }
431 });
432 }
433}
434
435/**
436 * @param[in ] nstep which step we're on
437 * @param[in ] time current time
438 * @param[in ] dt_lev0 time step on level 0
439 */
440void
442{
443 BL_PROFILE("REMORA::post_timestep()");
444
445#ifdef REMORA_USE_PARTICLES
447#endif
448
450 {
451 for (int lev = finest_level-1; lev >= 0; lev--)
452 {
454 }
455 }
456
459 }
460}
461
462/**
463 * This is called from main.cpp and handles all initialization, whether from start or restart
464 */
465void
467{
468 BL_PROFILE("REMORA::InitData()");
470 amrex::Print() << "REMORA InitData: driver-managed atm2ocn coupling enabled"
471 << " two_way=" << (driver_uses_two_way_coupling ? 1 : 0)
472 << " active_contract="
474 << "\n";
475 }
476 // Initialize the start time for our CPU-time tracker
477 startCPUTime = Real(ParallelDescriptor::second());
478
479 // Map the words in the inputs file to BC types, then translate
480 // those types into what they mean for each variable
481 init_bcs();
482
483 // Init vertical stretching coeffs
485
490
491 if (restart_chkfile == "") {
492 // start simulation from the beginning
493
494 // t_new counts from start_time, so a fresh run starts at zero.
496
498 AverageDown();
499 }
500
501 } else { // Restart from a checkpoint
502
503 restart();
504
505 }
506
507 // Every level's mask exists by now, whether built from scratch or read back
509
510#ifdef REMORA_USE_MOAB
511 InitMOABMesh();
512#endif
513 // Levels that appear later, or are regridded, define their own from make_new_level.
515 advflux_reg[0] = nullptr;
516 for (int lev = 1; lev <= finest_level; lev++) {
518 }
519 }
520
521 // Fill ghost cells/faces
522 for (int lev = 0; lev <= finest_level; ++lev)
523 {
524 if (lev > 0 && cf_width >= 0) {
526 }
527
528 if (restart_chkfile == "") {
530 FillPatch(lev, t_new[lev], *xvel_new[lev], xvel_new, xvel_bc(), BdyVars::u, 0, true, false,0,0,zero,*xvel_new[lev]);
531 FillPatch(lev, t_new[lev], *yvel_new[lev], yvel_new, yvel_bc(), BdyVars::v, 0, true, false,0,0,zero,*yvel_new[lev]);
532 // ROMS diagnoses w before its first history record, so do the same: DU_avg1 is
533 // still zero here, as in ROMS, so the free-surface term vanishes
535 FillPatch(lev, t_new[lev], *zvel_new[lev], zvel_new, zvel_bc(), BdyVars::null, 0, true, false);
536
537 // Copy from new into old just in case when initializing from scratch
538 int ngs = cons_new[lev]->nGrow();
539 int ngvel = xvel_new[lev]->nGrow();
540 MultiFab::Copy(*cons_old[lev],*cons_new[lev],0,0,ncons,ngs);
541 MultiFab::Copy(*xvel_old[lev],*xvel_new[lev],0,0,1,ngvel);
542 MultiFab::Copy(*yvel_old[lev],*yvel_new[lev],0,0,1,ngvel);
543 MultiFab::Copy(*zvel_old[lev],*zvel_new[lev],0,0,1,IntVect(ngvel,ngvel,0));
544 }
545 } // lev
546
547 // Check for additional plotting variables that are available after
548 // particle containers are setup.
549 const std::string& pv3d = "plot_vars_3d"; append3DPlotVariables(pv3d);
550 const std::string& pv2d = "plot_vars_2d"; append2DPlotVariables(pv2d);
551
552 if (restart_chkfile == "" && (check_int > 0 || check_int_time > zero))
553 {
556 }
557
558 // plot_file_on_restart currently always 1
559 if ( (restart_chkfile == "") ||
561 {
562 if (plot_int > 0 || plot_int_time > zero)
563 {
567 }
568 }
569
572 }
573
574 // dt is read from checkpoint on restart so it only needs to be computed if
575 // not restarting
576 if (restart_chkfile == "") {
577 ComputeDt();
578 }
579
580}
581
582/**
583 * @param[in ] lev level to operate on
584 */
585void
587{
588 BL_PROFILE("REMORA::Construct_REMORAFillPatchers()");
589 amrex::Print() << ":::Construct_REMORAFillPatchers " << lev << std::endl;
590
591 auto& ba_fine = cons_new[lev ]->boxArray();
592 auto& ba_crse = cons_new[lev-1]->boxArray();
593 auto& dm_fine = cons_new[lev ]->DistributionMap();
594 auto& dm_crse = cons_new[lev-1]->DistributionMap();
595
596 BoxList bl2d_fine = ba_fine.boxList();
597 for (auto& b : bl2d_fine) {
598 b.setRange(2,0);
599 }
600 BoxArray ba2d_fine(std::move(bl2d_fine));
601
602 BoxList bl2d_crse = ba_crse.boxList();
603 for (auto& b : bl2d_crse) {
604 b.setRange(2,0);
605 }
606 BoxArray ba2d_crse(std::move(bl2d_crse));
607
608 int ncomp = cons_new[lev]->nComp();
609
610 FPr_c.emplace_back(ba_fine, dm_fine, geom[lev] ,
611 ba_crse, dm_crse, geom[lev-1],
613 FPr_u.emplace_back(convert(ba_fine, IntVect(1,0,0)), dm_fine, geom[lev] ,
614 convert(ba_crse, IntVect(1,0,0)), dm_crse, geom[lev-1],
616 FPr_v.emplace_back(convert(ba_fine, IntVect(0,1,0)), dm_fine, geom[lev] ,
617 convert(ba_crse, IntVect(0,1,0)), dm_crse, geom[lev-1],
619 FPr_w.emplace_back(convert(ba_fine, IntVect(0,0,1)), dm_fine, geom[lev] ,
620 convert(ba_crse, IntVect(0,0,1)), dm_crse, geom[lev-1],
622
623 FPr_ubar.emplace_back(convert(ba2d_fine, IntVect(1,0,0)), dm_fine, geom[lev] ,
624 convert(ba2d_crse, IntVect(1,0,0)), dm_crse, geom[lev-1],
626 FPr_vbar.emplace_back(convert(ba2d_fine, IntVect(0,1,0)), dm_fine, geom[lev] ,
627 convert(ba2d_crse, IntVect(0,1,0)), dm_crse, geom[lev-1],
629
630 FPr_Dubar.emplace_back(convert(ba2d_fine, IntVect(1,0,0)), dm_fine, geom[lev] ,
631 convert(ba2d_crse, IntVect(1,0,0)), dm_crse, geom[lev-1],
633 FPr_Dvbar.emplace_back(convert(ba2d_fine, IntVect(0,1,0)), dm_fine, geom[lev] ,
634 convert(ba2d_crse, IntVect(0,1,0)), dm_crse, geom[lev-1],
636}
637
638/**
639 * @param[in ] lev level to operate on
640 */
641void
643{
644 BL_PROFILE("REMORA::Define_REMORAFillPatchers()");
645 if (verbose > 0) {
646 amrex::Print() << ":::Define_REMORAFillPatchers " << lev << std::endl;
647 }
648
649 auto& ba_fine = cons_new[lev ]->boxArray();
650 auto& ba_crse = cons_new[lev-1]->boxArray();
651 auto& dm_fine = cons_new[lev ]->DistributionMap();
652 auto& dm_crse = cons_new[lev-1]->DistributionMap();
653
654 BoxList bl2d_fine = ba_fine.boxList();
655 for (auto& b : bl2d_fine) {
656 b.setRange(2,0);
657 }
658 BoxArray ba2d_fine(std::move(bl2d_fine));
659
660 BoxList bl2d_crse = ba_crse.boxList();
661 for (auto& b : bl2d_crse) {
662 b.setRange(2,0);
663 }
664 BoxArray ba2d_crse(std::move(bl2d_crse));
665
666
667 int ncomp = cons_new[lev]->nComp();
668
669 FPr_c[lev-1].Define(ba_fine, dm_fine, geom[lev] ,
670 ba_crse, dm_crse, geom[lev-1],
672 FPr_u[lev-1].Define(convert(ba_fine, IntVect(1,0,0)), dm_fine, geom[lev] ,
673 convert(ba_crse, IntVect(1,0,0)), dm_crse, geom[lev-1],
675 FPr_v[lev-1].Define(convert(ba_fine, IntVect(0,1,0)), dm_fine, geom[lev] ,
676 convert(ba_crse, IntVect(0,1,0)), dm_crse, geom[lev-1],
678 FPr_w[lev-1].Define(convert(ba_fine, IntVect(0,0,1)), dm_fine, geom[lev] ,
679 convert(ba_crse, IntVect(0,0,1)), dm_crse, geom[lev-1],
681
682 FPr_ubar[lev-1].Define(convert(ba2d_fine, IntVect(1,0,0)), dm_fine, geom[lev] ,
683 convert(ba2d_crse, IntVect(1,0,0)), dm_crse, geom[lev-1],
685 FPr_vbar[lev-1].Define(convert(ba2d_fine, IntVect(0,1,0)), dm_fine, geom[lev] ,
686 convert(ba2d_crse, IntVect(0,1,0)), dm_crse, geom[lev-1],
688
689 FPr_Dubar[lev-1].Define(convert(ba2d_fine, IntVect(1,0,0)), dm_fine, geom[lev] ,
690 convert(ba2d_crse, IntVect(1,0,0)), dm_crse, geom[lev-1],
692 FPr_Dvbar[lev-1].Define(convert(ba2d_fine, IntVect(0,1,0)), dm_fine, geom[lev] ,
693 convert(ba2d_crse, IntVect(0,1,0)), dm_crse, geom[lev-1],
695}
696
697void
699{
700 BL_PROFILE("REMORA::restart()");
702
703 // We set this here so that we don't over-write the checkpoint file we just started from
705 // last_plot_file_step will be updated when plotfile is unconditionally written after restart
706
709}
710
711/**
712 * @param[in ] lev level to operate on
713 */
714void
716{
717 BL_PROFILE("REMORA::set_zeta()");
718 if (lev==0) {
719 if (hires_init_level < 0) {
721 prob->init_analytic_zeta(lev, geom[lev], solverChoice, *this, prob_coords(lev), *vec_zeta[lev]);
722 } else if (solverChoice.ic_type == IC_Type::netcdf) {
723#ifdef REMORA_USE_NETCDF
724 amrex::Print() << "Calling init_zeta_from_netcdf on level " << lev << std::endl;
726 amrex::Print() << "Sea surface height loaded from netcdf file \n " << std::endl;
727#endif
728 } else {
729 amrex::Abort("Unknown IC_Type");
730 }
731 } else {
733 }
734 vec_zeta[lev]->FillBoundary(geom[lev].periodicity());
735 } else {
736 // If our level is higher than the high resolution grid or initialization
737 // is analytic, interpolate from level below. Otherwise, copy over the bathymetry
738 // data that has been averaged down
739 if (lev > hires_init_level) {
740 Real dummy_time = zero;
742 } else {
744 vec_zeta[lev]->FillBoundary(geom[lev].periodicity());
745 }
746 }
748}
749
750/**
751 * @param[in ] lev level to operate on
752 */
753void
755{
756 BL_PROFILE("REMORA::bathymetry()");
757 // Only set bathymetry on level 0, and interpolate for finer levels
758 if (lev==0) {
759 // If grid data is not defined on a level > 0 (negative level) then
760 // initialize from low-resolution grid normally. Otherwise use high-resolution
761 // grid data averaged down to level 0
762 if (hires_grid_level < 0) {
764 prob->init_analytic_bathymetry(lev, geom[lev], solverChoice, *this, *vec_h[lev]);
765 } else if (solverChoice.ic_type == IC_Type::netcdf) {
766#ifdef REMORA_USE_NETCDF
767 amrex::Print() << "Calling init_bathymetry_from_netcdf " << std::endl;
769 amrex::Print() << "Bathymetry loaded from netcdf file \n " << std::endl;
770 amrex::Print() << "Calling init_grid_vars_from_netcdf " << std::endl;
772 amrex::Print() << "Grid variables loaded from netcdf file \n " << std::endl;
773#endif
774 } else {
775 amrex::Abort("Unknown IC_Type");
776 }
777 } else {
779 // Only the netcdf path fills vec_pm/pn_full_domain; with analytic initialization
780 // init_bathymetry_full_domain_from_analytic fills h alone, and set_grid_scale
781 // below derives pm/pn from the geometry.
784 }
785 }
786 // Need FillBoundary to fill at grid-grid boundaries, and EnforcePeriodicity
787 // to make sure ghost cells in the domain corners are consistent.
788 vec_h[lev]->FillBoundary(geom[lev].periodicity());
789 vec_h[lev]->EnforcePeriodicity(geom[lev].periodicity());
790 } else {
791 // If our level is higher than the high resolution grid or initialization
792 // is analytic, interpolate from level below. Otherwise, copy over the bathymetry
793 // data that has been averaged down
794 if (lev > hires_grid_level) {
795 Real dummy_time = zero;
800 } else {
802 vec_h[lev]->FillBoundary(geom[lev].periodicity());
803 vec_h[lev]->EnforcePeriodicity(geom[lev].periodicity());
804 }
805 }
807}
808
809/**
810 * @param[in ] lev level to operate on
811 */
812void
814 // The full-domain array holds this level's own bathymetry everywhere, grow cells included,
815 // so a refined level's ghost ring takes the file's h rather than an interpolant of the
816 // coarse level's. Only the ghosts beyond the file's halo need extrapolating.
819 vec_h[lev]->FillBoundary(geom[lev].periodicity());
820 vec_h[lev]->EnforcePeriodicity(geom[lev].periodicity());
822}
823
824/**
825 * @param[in ] lev level to operate on
826 */
827void
829 // A level's grid metrics never come from another level: FillPatch here interpolated the
830 // coarse pm/pn into the refined level's ghost ring without the refinement ratio, which
831 // made every coarse-fine interface face 50% too long. The full-domain array has this
832 // level's metrics in every grow cell; periodic and physical ghosts follow from them.
833 ParallelCopy(*vec_pm[lev].get(), *vec_pm_full_domain[lev].get(), 0, 0, 1,
835 ParallelCopy(*vec_pn[lev].get(), *vec_pn_full_domain[lev].get(), 0, 0, 1,
837 for (MultiFab* mf : {vec_pm[lev].get(), vec_pn[lev].get()}) {
838 mf->FillBoundary(geom[lev].periodicity());
839 mf->EnforcePeriodicity(geom[lev].periodicity());
841 }
842}
843
844/**
845 * Copy the outermost in-domain ghost row of a cell-centred field outward over the
846 * remaining ghost rows at the physical boundaries. Used for grid metrics and bathymetry
847 * in every build, so it lives here rather than in the NetCDF-only initialisation.
848 *
849 * @param[inout] mf multifab of data to extrapolate on
850 * @param[in ] geom geometry
851 */
852void
854{
855 const IntVect ng = mf.nGrowVect();
856
857 const auto& dom_lo = amrex::lbound(geom.Domain());
858 const auto& dom_hi = amrex::ubound(geom.Domain());
859
860 for ( MFIter mfi(mf); mfi.isValid(); ++mfi )
861 {
862 Box bx = mfi.tilebox();
863
864 auto mf_arr = mf.array(mfi);
865
866 Box gbx_lox = adjCellLo(bx,0,ng[0]); gbx_lox.grow(1,ng[1]); gbx_lox.setBig (0,dom_lo.x-2);
867 Box gbx_hix = adjCellHi(bx,0,ng[0]); gbx_hix.grow(1,ng[1]); gbx_hix.setSmall(0,dom_hi.x+2);
868 Box gbx_loy = adjCellLo(bx,1,ng[1]); gbx_loy.grow(0,ng[0]); gbx_loy.setBig (1,dom_lo.y-2);
869 Box gbx_hiy = adjCellHi(bx,1,ng[1]); gbx_hiy.grow(0,ng[0]); gbx_hiy.setSmall(1,dom_hi.y+2);
870
871 if (gbx_lox.ok()) {
872 ParallelFor(gbx_lox, [=] AMREX_GPU_DEVICE (int i, int j, int k)
873 {
874 mf_arr(i,j,k,0) = mf_arr(dom_lo.x-1,j,k,0);
875 });
876 }
877 if (gbx_hix.ok()) {
878 ParallelFor(gbx_hix, [=] AMREX_GPU_DEVICE (int i, int j, int k)
879 {
880 mf_arr(i,j,k,0) = mf_arr(dom_hi.x+1,j,k,0);
881 });
882 }
883 if (gbx_loy.ok()) {
884 ParallelFor(gbx_loy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
885 {
886 mf_arr(i,j,k,0) = mf_arr(i,dom_lo.y-1,k,0);
887 });
888 }
889 if (gbx_hiy.ok()) {
890 ParallelFor(gbx_hiy, [=] AMREX_GPU_DEVICE (int i, int j, int k)
891 {
892 mf_arr(i,j,k,0) = mf_arr(i,dom_hi.y+1,k,0);
893 });
894 }
895 } // mfi
896}
897
898/**
899 * @param[in ] lev level to operate on
900 */
901void
908
909/**
910 * @param[in ] lev level to operate on
911 */
912void
925
926/**
927 * @param[in ] lev level to operate on
928 */
929void
931 BL_PROFILE("REMORA::set_coriolis()");
934 prob->init_analytic_coriolis(lev, geom[lev], solverChoice, *this, *vec_fcor[lev]);
937#ifdef REMORA_USE_NETCDF
939 if (lev == 0) {
940 amrex::Print() << "Calling init_coriolis_from_netcdf " << std::endl;
942 amrex::Print() << "Coriolis loaded from netcdf file \n" << std::endl;
943 } else {
944 Real dummy_time = zero;
946 }
947#endif
948 } else {
949 Abort("Don't know this coriolis_type!");
950 }
951
952 Real time = zero;
954 vec_fcor[lev]->EnforcePeriodicity(geom[lev].periodicity());
955 }
956}
957
958void
960 BL_PROFILE("REMORA::init_set_vmix()");
965 // The GLS initialization just sets the multifab to a value, so there's
966 // no need to call FillPatch here
967 } else {
968 Abort("Don't know this vertical mixing type");
969 }
970}
971
972/**
973 * @param[in ] lev level to operate on
974 */
975void
977 BL_PROFILE("REMORA::set_analytic_vmix()");
978 Real time = zero;
980 for (int n = 0; n < NAT; n++) {
981 vec_Akt[lev]->setVal(solverChoice.Akt_bak[n], n, 1);
982 }
983 prob->init_analytic_vmix(lev, geom[lev], solverChoice, *this,*vec_Akv[lev], *vec_Akt[lev]);
985 for (int n = 0; n < NAT; n++) {
987 }
988}
989
990/**
991 * Initialize the land-sea mask on this level.
992 *
993 * Mirrors set_bathymetry: the mask is specified once, on level 0 or at hires_grid_level, and
994 * every other level derived from it, so the levels cannot disagree about the coastline.
995 *
996 * @param[in ] lev level to operate on
997 */
998void
1000{
1001 // Ahead of the mask_type == none return as well: that branch still fills the masks, and
1002 // AverageDownTo still reads them, so its cached copies go stale here too.
1004
1007 return;
1008 }
1009
1010 if (lev == 0) {
1011 // If grid data is not defined on a level > 0 (negative level) then initialize from
1012 // the low-resolution grid normally. Otherwise use high-resolution grid data
1013 // coarsened down to level 0.
1014 if (hires_grid_level < 0) {
1016 prob->init_analytic_masks(lev,geom[lev], solverChoice, *this, *vec_mskr[lev]);
1017 // The analytic hook writes each grid's own cells only, so this is what makes
1018 // the mask agree across grid-grid and periodic boundaries.
1019 vec_mskr[lev]->FillBoundary(geom[lev].periodicity());
1021 } else if (solverChoice.mask_type == MaskType::netcdf) {
1022#ifdef REMORA_USE_NETCDF
1023 amrex::Print() << "Calling init_masks_from_netcdf level " << lev << std::endl;
1025 amrex::Print() << "Masks loaded from netcdf file \n " << std::endl;
1026#endif
1027 }
1028 } else {
1030 }
1031 } else {
1032 // If our level is higher than the high resolution grid, interpolate from the level
1033 // below. Otherwise, copy over the mask that has been coarsened down.
1034 if (lev > hires_grid_level) {
1035 Real dummy_time = zero;
1037 foextrap_bc());
1040 } else {
1042 }
1043 }
1045}
1046
1047/**
1048 * Overwrite lev's rho-mask ghost cells beyond the domain edge with the coarser level's mask
1049 * there, injected piecewise-constant.
1050 *
1051 * FillCoarsePatchPC fills those cells with foextrap, which copies the interior outward, even
1052 * across a periodic edge. At a river mouth on a wall that turns the land behind the river
1053 * wet, and the clamped boundary's salt then mixes into the river cell; at a periodic edge it
1054 * ignores whatever lies across the seam. Level 0 takes these cells from its grid file, or
1055 * from a periodic FillBoundary, so the coarser level's ghost cells hold the right mask.
1056 *
1057 * @param[in ] lev level to operate on, > 0
1058 */
1059void
1061{
1062 MultiFab& mf = *vec_mskr[lev];
1063 const MultiFab& mc = *vec_mskr[lev-1];
1064 const IntVect rr = refRatio(lev-1);
1065 const IntVect ngf = mf.nGrowVect();
1066 const IntVect ngc((ngf[0] + rr[0] - 1) / rr[0], (ngf[1] + rr[1] - 1) / rr[1], 0);
1067 AMREX_ALWAYS_ASSERT(mc.nGrowVect().allGE(ngc));
1068
1069 // The coarse mask on the fine layout, ghost cells beyond the domain included. A cell the
1070 // copy cannot reach stays water, the placeholder every mask starts from.
1071 MultiFab ctmp(amrex::coarsen(mf.boxArray(), rr), mf.DistributionMap(), 1, ngc);
1072 ctmp.setVal(one);
1073 ctmp.ParallelCopy(mc, 0, 0, 1, mc.nGrowVect(), ngc);
1074
1075 const Box dom = makeSlab(geom[lev].Domain(), 2, 0);
1076 const int lox = dom.smallEnd(0), hix = dom.bigEnd(0);
1077 const int loy = dom.smallEnd(1), hiy = dom.bigEnd(1);
1078 const int rx = rr[0], ry = rr[1];
1079
1080 for (MFIter mfi(mf); mfi.isValid(); ++mfi)
1081 {
1082 if (dom.contains(mfi.fabbox())) { continue; }
1083 Array4< Real> const& f = mf.array(mfi);
1084 Array4<const Real> const& c = ctmp.const_array(mfi);
1085 ParallelFor(mfi.fabbox(), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1086 {
1088 f(i,j,k) = c(amrex::coarsen(i, rx), amrex::coarsen(j, ry), 0);
1089 }
1090 });
1091 }
1092 // ctmp goes out of scope next
1093 Gpu::streamSynchronize();
1094}
1095
1096/**
1097 * @param[in ] lev level to operate on
1098 */
1099void
1101 ParallelCopy(*vec_mskr[lev].get(), *vec_mskr_full_domain[lev].get(), 0, 0, 1,
1103 // Not a FillPatch, unlike the bathymetry analogue: its interpolation from the coarser
1104 // level is not piecewise constant, so it would put fractional values in a mask the rest
1105 // of the code compares against 0 and 1 exactly.
1106 vec_mskr[lev]->FillBoundary(geom[lev].periodicity());
1108}
1109
1110/**
1111 * Coarsen the full-domain rho-mask from crse_lev+1 onto crse_lev, grow cells included, so a
1112 * coarse cell is land only if every one of its fine cells is land.
1113 *
1114 * average_down_with_grow_cells cannot be used: an arithmetic mean over a partly wet group of
1115 * fine cells gives a fractional value, and the mask has to stay exactly 0 or 1. Taking the
1116 * cell as wet also means the coarse level never declares land where the fine grid found water.
1117 *
1118 * @param[in ] crse_lev level to coarsen onto
1119 */
1120void
1122{
1123 auto const& crsema = vec_mskr_full_domain[crse_lev]->arrays();
1124 auto const& finema = vec_mskr_full_domain[crse_lev+1]->const_arrays();
1125 auto ratio = refRatio(crse_lev);
1126 // As in average_down_with_grow_cells, but cell-centered, so no index-type correction.
1129 [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k, int n) noexcept
1130 {
1131 const int ii = i * ratio[0];
1132 const int jj = j * ratio[1];
1133 Real wet = zero;
1134 for (int jref = 0; jref < ratio[1]; ++jref) {
1135 for (int iref = 0; iref < ratio[0]; ++iref) {
1136 wet += amrex::min(Real(1.0), finema[box_no](ii+iref, jj+jref, k, n));
1137 }
1138 }
1139 crsema[box_no](i,j,k,n) = (wet > zero) ? one : zero;
1140 });
1141 Gpu::streamSynchronize();
1142}
1143
1144
1145/**
1146 * Check the land-sea masks for what the rest of the code relies on. Only runs when
1147 * remora.check_mask_consistency is set; remora.mask_consistency picks abort or warn.
1148 *
1149 * Per level, that the masks hold only the values they are meant to and that no water cell has
1150 * a non-positive depth. Per level pair, over the region the finer level covers, that no coarse
1151 * water point sits over fine points that are all land.
1152 */
1153void
1155{
1156 BL_PROFILE("REMORA::check_mask_consistency()");
1158 return;
1159 }
1160
1161 Long nbad_val = 0, nbad_h = 0, ndry_r = 0, ndry_u = 0, ndry_v = 0, nmissed = 0;
1162
1163 // Per-level checks. Mask values matter because the plotfile writer decides what to blank
1164 // by comparing them against 0 exactly, so a fractional mask stops masking; a water cell
1165 // with h <= 0 matters because stretch_transform divides by hc + h, giving quiet garbage
1166 // rather than a crash.
1167 for (int lev = 0; lev <= finest_level; ++lev)
1168 {
1171 using ReduceTuple = typename decltype(reduce_data)::Type;
1172
1173 for ( MFIter mfi(*vec_mskr[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi )
1174 {
1175 Array4<const Real> const& mskr = vec_mskr[lev]->const_array(mfi);
1176 Array4<const Real> const& msku = vec_msku[lev]->const_array(mfi);
1177 Array4<const Real> const& mskv = vec_mskv[lev]->const_array(mfi);
1178 Array4<const Real> const& mskp = vec_mskp[lev]->const_array(mfi);
1179 Array4<const Real> const& h = vec_h[lev]->const_array(mfi);
1180
1181 Box bx = mfi.tilebox(); bx.makeSlab(2,0);
1182
1183 reduce_op.eval(bx, reduce_data, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1184 -> ReduceTuple
1185 {
1186 auto is_01 = [] (Real v) {
1187 return v == Real(0.0) || v == Real(1.0);
1188 };
1189 const bool bad = !is_01(mskr(i,j,k)) || !is_01(msku(i,j,k)) ||
1190 !is_01(mskv(i,j,k)) ||
1191 !(is_01(mskp(i,j,k)) || mskp(i,j,k) == Real(2.0));
1192 const bool bad_h = (mskr(i,j,k) > Real(0.5)) && (h(i,j,k) <= Real(0.0));
1193 return {static_cast<Long>(bad), static_cast<Long>(bad_h)};
1194 });
1195 }
1197 nbad_val += amrex::get<0>(hv);
1198 nbad_h += amrex::get<1>(hv);
1199 }
1200
1201 // Level-pair checks, over the region the finer level covers. A coarse water point over
1202 // nothing but land would leave the average-down nothing to divide by. Coarsening makes
1203 // that unreachable for cell centers -- a wet coarse cell is wet because some fine cell in
1204 // its own block is -- but not for faces: a coarse u-face is open whenever both its cells
1205 // are wet, only the fine faces in its own plane count, and the wet fine cells that made
1206 // those coarse cells wet may all lie elsewhere in their blocks.
1207 for (int crse_lev = 0; crse_lev < finest_level; ++crse_lev)
1208 {
1209 const int flev = crse_lev + 1;
1210 const IntVect ratio = refRatio(crse_lev);
1211 const BoxArray cba = amrex::coarsen(vec_mskr[flev]->boxArray(), ratio);
1212 const DistributionMapping& dmf = vec_mskr[flev]->DistributionMap();
1213
1214 // Sentinel, so an incomplete copy shows up as itself rather than as a coarse land
1215 // point that the checks below would quietly pass over.
1216 MultiFab cmskr(cba, dmf, 1, 0);
1217 MultiFab cmsku(amrex::convert(cba, IntVect(1,0,0)), dmf, 1, 0);
1218 MultiFab cmskv(amrex::convert(cba, IntVect(0,1,0)), dmf, 1, 0);
1219 cmskr.setVal(-one); cmsku.setVal(-one); cmskv.setVal(-one);
1220 cmskr.ParallelCopy(*vec_mskr[crse_lev], 0, 0, 1);
1221 cmsku.ParallelCopy(*vec_msku[crse_lev], 0, 0, 1);
1222 cmskv.ParallelCopy(*vec_mskv[crse_lev], 0, 0, 1);
1223
1226 using ReduceTuple = typename decltype(reduce_data)::Type;
1227
1228 for ( MFIter mfi(cmskr, TilingIfNotGPU()); mfi.isValid(); ++mfi )
1229 {
1230 Array4<const Real> const& cr = cmskr.const_array(mfi);
1231 Array4<const Real> const& cu = cmsku.const_array(mfi);
1232 Array4<const Real> const& cv = cmskv.const_array(mfi);
1233 Array4<const Real> const& fr = vec_mskr[flev]->const_array(mfi);
1234 Array4<const Real> const& fu = vec_msku[flev]->const_array(mfi);
1235 Array4<const Real> const& fv = vec_mskv[flev]->const_array(mfi);
1236
1237 Box bx = mfi.tilebox(); bx.makeSlab(2,0);
1238 const int rx = ratio[0];
1239 const int ry = ratio[1];
1240
1241 // Three passes: a cell-centered box stops at hi, so it would miss the high-side
1242 // face that average_down_masked does iterate. nodaltilebox partitions the nodal
1243 // range; faces shared between boxes are still counted twice, which can only
1244 // inflate a diagnostic.
1245 const Long lzero = 0;
1246
1247 // Cell centers: the whole rx by ry block.
1248 reduce_op.eval(bx, reduce_data, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1249 -> ReduceTuple
1250 {
1251 const int ii = i * rx;
1252 const int jj = j * ry;
1253
1254 Real wet_r = zero;
1255 for (int jref = 0; jref < ry; ++jref) {
1256 for (int iref = 0; iref < rx; ++iref) {
1257 wet_r += amrex::min(Real(1.0), fr(ii+iref, jj+jref, k));
1258 }
1259 }
1260
1261 const bool missed = cr(i,j,k) < zero;
1262 return {static_cast<Long>(missed),
1263 static_cast<Long>(!missed && cr(i,j,k) > Real(0.5) && wet_r == zero),
1264 lzero, lzero};
1265 });
1266
1267 // u-faces: only the fine faces in the coarse face's plane. Tests the sentinel too,
1268 // so an incomplete copy reports itself instead of failing the > 0.5 test.
1269 Box ubx = mfi.nodaltilebox(0); ubx.makeSlab(2,0);
1270 reduce_op.eval(ubx, reduce_data, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1271 -> ReduceTuple
1272 {
1273 const int ii = i * rx;
1274 const int jj = j * ry;
1275
1276 Real wet_u = zero;
1277 for (int jref = 0; jref < ry; ++jref) {
1278 wet_u += amrex::min(Real(1.0), fu(ii, jj+jref, k));
1279 }
1280
1281 const bool missed = cu(i,j,k) < zero;
1282 return {static_cast<Long>(missed), lzero,
1283 static_cast<Long>(!missed && cu(i,j,k) > Real(0.5) && wet_u == zero),
1284 lzero};
1285 });
1286
1287 // v-faces, likewise.
1288 Box vbx = mfi.nodaltilebox(1); vbx.makeSlab(2,0);
1289 reduce_op.eval(vbx, reduce_data, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1290 -> ReduceTuple
1291 {
1292 const int ii = i * rx;
1293 const int jj = j * ry;
1294
1295 Real wet_v = zero;
1296 for (int iref = 0; iref < rx; ++iref) {
1297 wet_v += amrex::min(Real(1.0), fv(ii+iref, jj, k));
1298 }
1299
1300 const bool missed = cv(i,j,k) < zero;
1301 return {static_cast<Long>(missed), lzero, lzero,
1302 static_cast<Long>(!missed && cv(i,j,k) > Real(0.5) && wet_v == zero)};
1303 });
1304 }
1306 nmissed += amrex::get<0>(hv);
1307 ndry_r += amrex::get<1>(hv);
1308 ndry_u += amrex::get<2>(hv);
1309 ndry_v += amrex::get<3>(hv);
1310 }
1311
1312 ParallelDescriptor::ReduceLongSum(nbad_val);
1313 ParallelDescriptor::ReduceLongSum(nbad_h);
1314 ParallelDescriptor::ReduceLongSum(nmissed);
1315 ParallelDescriptor::ReduceLongSum(ndry_r);
1316 ParallelDescriptor::ReduceLongSum(ndry_u);
1317 ParallelDescriptor::ReduceLongSum(ndry_v);
1318
1319 if (nbad_val == 0 && nbad_h == 0 && nmissed == 0 &&
1320 ndry_r == 0 && ndry_u == 0 && ndry_v == 0) {
1321 if (verbose > 0) {
1322 amrex::Print() << "Land-sea masks are consistent across " << finest_level+1
1323 << " level(s)" << std::endl;
1324 }
1325 return;
1326 }
1327
1328 std::string msg = "Land-sea mask problems:";
1329 if (nbad_val > 0) {
1330 msg += "\n " + std::to_string(nbad_val) + " point(s) where a mask is neither 0 nor 1"
1331 " (psi may also be 2). The plotfile writer decides what to blank by comparing"
1332 " masks against 0 exactly, so a fractional mask silently stops masking.";
1333 }
1334 if (nbad_h > 0) {
1335 msg += "\n " + std::to_string(nbad_h) + " water point(s) with depth <= 0."
1336 " stretch_transform divides by hc + h, so this is quiet garbage rather than a"
1337 " crash.";
1338 }
1339 if (nmissed > 0) {
1340 msg += "\n " + std::to_string(nmissed) + " refined point(s) with no coarse point"
1341 " beneath them, which should be impossible under proper nesting.";
1342 }
1343 if (ndry_r > 0) {
1344 msg += "\n " + std::to_string(ndry_r) + " coarse water cell(s) with only land"
1345 " beneath them.";
1346 }
1347 if (ndry_u > 0 || ndry_v > 0) {
1348 msg += "\n " + std::to_string(ndry_u) + " coarse u-face(s) and " +
1349 std::to_string(ndry_v) + " v-face(s) that are open with no open fine face"
1350 " beneath them. The coarse grid cannot see a barrier the fine grid resolves."
1351 " Closing the coarse face would contradict the coarsening rule, so move the"
1352 " refined grids off it or coarsen the mask by hand.";
1353 }
1354 if (ndry_r > 0 || ndry_u > 0 || ndry_v > 0) {
1355 msg += "\nThe two-way average-down divides by the number of wet fine points, so these"
1356 " have no value to take.";
1357 }
1358 msg += "\nSet remora.mask_consistency = warn to continue anyway.";
1359
1361 amrex::Abort(msg);
1362 } else {
1363 amrex::Print() << "WARNING: " << msg << std::endl;
1364 }
1365}
1366
1367/**
1368 * Coarsen the full-domain bathymetry from crse_lev+1 onto crse_lev, grow cells included,
1369 * weighted by the land/sea mask.
1370 *
1371 * Averaging every fine cell would mix in whatever the grid file holds under land, which on a
1372 * ROMS grid is a fill value with no physical meaning. A wet coarse cell should take the depth
1373 * of the water under it. See REMORA_MaskedAverageDown.H for why the arithmetic is written the
1374 * way it is.
1375 *
1376 * @param[in ] crse_lev level to coarsen onto
1377 */
1378void
1380{
1381 // The mask is indexed by the same local box number as the bathymetry, so the two have to
1382 // be distributed alike. full_domain_dmap is what makes that true; assert it here rather
1383 // than read another rank's box.
1386
1387 auto const& crsema = vec_h_full_domain[crse_lev]->arrays();
1388 auto const& finema = vec_h_full_domain[crse_lev+1]->const_arrays();
1389 auto const& fmskma = vec_mskr_full_domain[crse_lev+1]->const_arrays();
1390 auto ratio = refRatio(crse_lev);
1392 const int ncomp = vec_h_full_domain[crse_lev]->nComp();
1394 [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k, int n) noexcept
1395 {
1396 const int ii = i * ratio[0];
1397 const int jj = j * ratio[1];
1398 Real num = zero, den = zero, sum_all = zero;
1399 for (int jref = 0; jref < ratio[1]; ++jref) {
1400 for (int iref = 0; iref < ratio[0]; ++iref) {
1401 const Real hf = finema[box_no](ii+iref, jj+jref, k, n);
1402 const Real m = amrex::min(Real(1.0), fmskma[box_no](ii+iref, jj+jref, k));
1403 num += hf * m;
1404 den += m;
1405 sum_all += hf;
1406 }
1407 }
1408 // All-land: no water to average, but h still has to hold something, so fall back to
1409 // the plain mean. That also makes an all-wet or all-land group reproduce
1410 // average_down_with_grow_cells bit for bit. This is where the bathymetry parts company
1411 // with avgdown_masked, which multiplies by the coarse mask and so leaves an all-land
1412 // point at zero: a free surface under land need not hold anything, a depth does.
1413 crsema[box_no](i,j,k,n) = (den > zero)
1414 ? num * (one/den)
1415 : sum_all * (one/Real(ratio[0]*ratio[1]));
1416 });
1417 Gpu::streamSynchronize();
1418}
1419
1420/**
1421 * @param[in ] lev level to operate on
1422 */
1423void
1425{
1426 BL_PROFILE("REMORA::set_hmixcoef()");
1427
1428 // Optional AMR scaling: decrease coefficients on refined levels linearly
1429 // with grid size (i.e., proportional to sqrt(cell area)). For a horizontal
1430 // refinement ratio rx x ry, the effective scale factor is 1/sqrt(rx*ry).
1431 Real lev_scale = one;
1433 Real rf = one;
1434 for (int l = 0; l < lev; ++l) {
1435 rf *= std::sqrt(static_cast<Real>(ref_ratio[l][0]) * static_cast<Real>(ref_ratio[l][1]));
1436 }
1437 lev_scale = one / rf;
1438 }
1439
1441 prob->init_analytic_hmix(lev, geom[lev], solverChoice,
1442 *this, *vec_visc2_p[lev], *vec_visc2_r[lev], *vec_diff2[lev]);
1443
1447 for (int n = 0; n < ncons; n++) {
1448 vec_diff2[lev]->setVal(solverChoice.tnu2[n] * lev_scale, n, 1);
1449 }
1450
1451 // Scale harmonic viscosity and diffusivity by the grid size as ROMS
1452 // does in Utility/ini_hmixcoef.F. Intended for curvilinear grids.
1453 //
1454 // Define the ROMS grid factor (grdscl):
1455 // G(i,j) = sqrt( 1 / (pm(i,j) * pn(i,j)) )
1456 // = sqrt(cell area)
1457 // Gmax = max over grid of G(i,j)
1458 //
1459 // Then horizontal harmonic mixing coefficients are scaled as:
1460 // nu(i,j) = nu0 * G(i,j) / Gmax
1461 // kappa_n(i,j) = kappa0 * G(i,j) / Gmax
1462 //
1463 // where:
1464 // nu0 = solverChoice.visc2
1465 // kappa0 = solverChoice.tnu2[n]
1466 //
1467 // This makes mixing strongest where grid spacing is largest.
1468 //
1469 // NOTE: The normalization (Gmax) is computed over the entire grid (ignoring masks).
1470 // Therefore, if the largest cell area occurs over land, the maximum over *wet* cells
1471 // (or in masked output files) may be smaller than the user-specified value.
1472
1474
1475 // ------------------------------------------------------------
1476 // Step 1: Compute grdmax over entire grid
1477 // ------------------------------------------------------------
1480 for (int n = 0; n < ncons; n++) {
1481 vec_diff2[lev]->setVal(solverChoice.tnu2[n], n, 1);
1482 }
1483
1484 // NOTE: This must be GPU-safe. Do not dereference MultiFab data on host.
1485 // Force the reduction to run in the GPU launch region if GPUs are enabled.
1486 // (If the launch region is disabled at runtime, ReduceMax may fall back to
1487 // a host path that can try to read device-only data.)
1488 amrex::Gpu::LaunchSafeGuard lsg(true);
1489 Real denom_min = amrex::ReduceMin(*vec_pm[lev], *vec_pn[lev], 0,
1490 [=] AMREX_GPU_HOST_DEVICE (Box const& bx,
1491 Array4<Real const> const& pm,
1492 Array4<Real const> const& pn) -> Real
1493 {
1495 amrex::Loop(bx, [=,&local_min] (int i, int j, int) noexcept
1496 {
1497 local_min = amrex::min(local_min, pm(i,j,0) * pn(i,j,0));
1498 });
1499 return local_min;
1500 });
1501
1502 ParallelDescriptor::ReduceRealMin(denom_min);
1503 if (denom_min <= zero) {
1504 Abort("scaled_to_grid: found non-positive pm*pn (grid metrics must be > 0)");
1505 }
1506
1507 Real grdmax = amrex::ReduceMax(*vec_pm[lev], *vec_pn[lev], 0,
1508 [=] AMREX_GPU_HOST_DEVICE (Box const& bx,
1509 Array4<Real const> const& pm,
1510 Array4<Real const> const& pn) -> Real
1511 {
1512 Real local_max = zero;
1513 amrex::Loop(bx, [=,&local_max] (int i, int j, int) noexcept
1514 {
1515 Real denom = pm(i,j,0) * pn(i,j,0);
1516 if (denom > zero) {
1517 Real G = std::sqrt(one / denom);
1518 local_max = amrex::max(local_max, G);
1519 }
1520 });
1521 return local_max;
1522 });
1523
1524 ParallelDescriptor::ReduceRealMax(grdmax);
1525 if (grdmax <= zero) {
1526 Abort("scaled_to_grid: grdmax <= 0");
1527 }
1528
1529 // Optional AMR scaling: decrease coefficients on refined levels linearly
1530 // with grid size (i.e., proportional to sqrt(cell area)). For a horizontal
1531 // refinement ratio rx x ry, the effective scale factor is 1/sqrt(rx*ry).
1532 lev_scale = one;
1534 Real rf = one;
1535 for (int l = 0; l < lev; ++l) {
1536 rf *= std::sqrt(static_cast<Real>(ref_ratio[l][0]) * static_cast<Real>(ref_ratio[l][1]));
1537 }
1538 lev_scale = one / rf;
1539 }
1540
1542 Real cff = visc0 / grdmax;
1543
1544 // ------------------------------------------------------------
1545 // Step 2: Set rho coefficients everywhere
1546 // ------------------------------------------------------------
1547 amrex::Gpu::DeviceVector<Real> diff0_d(ncons);
1548 amrex::Gpu::copy(amrex::Gpu::hostToDevice,
1549 solverChoice.tnu2.begin(), solverChoice.tnu2.begin() + ncons,
1550 diff0_d.begin());
1551 Real const* diff0_ptr = diff0_d.data();
1552
1553 for (MFIter mfi(*vec_visc2_r[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
1554 {
1555 const Box& bx = mfi.validbox();
1556 auto pm = vec_pm[lev]->const_array(mfi);
1557 auto pn = vec_pn[lev]->const_array(mfi);
1558 auto visc2_r = vec_visc2_r[lev]->array(mfi);
1559 auto diff2 = vec_diff2[lev]->array(mfi);
1560
1561 int ncons_local = ncons;
1562 ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept
1563 {
1564 Real denom = pm(i,j,0) * pn(i,j,0);
1565 Real grdscl = (denom > zero) ? std::sqrt(one / denom) : zero;
1566 visc2_r(i,j,0) = cff * grdscl;
1567
1568 for (int n = 0; n < ncons_local; n++) {
1569 diff2(i,j,0,n) = ((diff0_ptr[n] * lev_scale) / grdmax) * grdscl;
1570 }
1571 });
1572 }
1573
1574 // Fill ghost cells for rho coefficients BEFORE psi averaging
1575 Real time = zero;
1577
1578 // ------------------------------------------------------------
1579 // Step 3: Psi coefficients = average of 4 surrounding rho
1580 // ------------------------------------------------------------
1581 for (MFIter mfi(*vec_visc2_p[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
1582 {
1583 const Box& bx = mfi.validbox();
1584 auto visc2_p = vec_visc2_p[lev]->array(mfi);
1585 auto visc2_r = vec_visc2_r[lev]->const_array(mfi);
1586
1587 ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept
1588 {
1589 visc2_p(i,j,0) = fourth * (
1590 visc2_r(i-1,j-1,0) +
1591 visc2_r(i ,j-1,0) +
1592 visc2_r(i-1,j ,0) +
1593 visc2_r(i ,j ,0)
1594 );
1595 });
1596 }
1597
1599
1600 // Diagnostics
1601 // NOTE: coefficients are computed everywhere (including land). Output routines may later
1602 // mask land points (e.g., to FillValue in NetCDF/plotfiles), and analysis tools may
1603 // additionally apply mask_rho (setting land to 0). Report both conventions.
1604 //
1605 // Global (MPI-reduced) extrema over all valid cells (no ghost).
1606 Real visc_min_all = vec_visc2_r[lev]->min(0,0,false);
1607 Real visc_max_all = vec_visc2_r[lev]->max(0,0,false);
1608
1609 // Global extrema over *wet* rho points only, k=0.
1610 amrex::Gpu::LaunchSafeGuard lsg_diag(true);
1611 Real visc_min_wet = amrex::ReduceMin(*vec_visc2_r[lev], *vec_mskr[lev], 0,
1612 [=] AMREX_GPU_HOST_DEVICE (Box const& bx,
1613 Array4<Real const> const& visc2,
1614 Array4<Real const> const& mskr) -> Real
1615 {
1617 amrex::Loop(bx, [=,&local_min] (int i, int j, int) noexcept
1618 {
1619 if (mskr(i,j,0) > zero) {
1620 local_min = amrex::min(local_min, visc2(i,j,0));
1621 }
1622 });
1623 return local_min;
1624 });
1625 ParallelDescriptor::ReduceRealMin(visc_min_wet);
1626
1627 Real visc_max_wet = amrex::ReduceMax(*vec_visc2_r[lev], *vec_mskr[lev], 0,
1628 [=] AMREX_GPU_HOST_DEVICE (Box const& bx,
1629 Array4<Real const> const& visc2,
1630 Array4<Real const> const& mskr) -> Real
1631 {
1633 amrex::Loop(bx, [=,&local_max] (int i, int j, int) noexcept
1634 {
1635 if (mskr(i,j,0) > zero) {
1636 local_max = amrex::max(local_max, visc2(i,j,0));
1637 }
1638 });
1639 return local_max;
1640 });
1641 ParallelDescriptor::ReduceRealMax(visc_max_wet);
1642
1643 // Mimic "apply mask_rho" convention (dry -> 0).
1644 Real visc_min_mask0 = amrex::ReduceMin(*vec_visc2_r[lev], *vec_mskr[lev], 0,
1645 [=] AMREX_GPU_HOST_DEVICE (Box const& bx,
1646 Array4<Real const> const& visc2,
1647 Array4<Real const> const& mskr) -> Real
1648 {
1650 amrex::Loop(bx, [=,&local_min] (int i, int j, int) noexcept
1651 {
1652 const Real v = (mskr(i,j,0) > zero) ? visc2(i,j,0) : zero;
1653 local_min = amrex::min(local_min, v);
1654 });
1655 return local_min;
1656 });
1657 ParallelDescriptor::ReduceRealMin(visc_min_mask0);
1658
1659 Real visc_max_mask0 = amrex::ReduceMax(*vec_visc2_r[lev], *vec_mskr[lev], 0,
1660 [=] AMREX_GPU_HOST_DEVICE (Box const& bx,
1661 Array4<Real const> const& visc2,
1662 Array4<Real const> const& mskr) -> Real
1663 {
1665 amrex::Loop(bx, [=,&local_max] (int i, int j, int) noexcept
1666 {
1667 const Real v = (mskr(i,j,0) > zero) ? visc2(i,j,0) : zero;
1668 local_max = amrex::max(local_max, v);
1669 });
1670 return local_max;
1671 });
1672 ParallelDescriptor::ReduceRealMax(visc_max_mask0);
1673 if (ParallelDescriptor::IOProcessor() && lev == 0)
1674 {
1675 Print() << "\nHorizontal mixing scaled by grid metric\n";
1676 Print() << "grdmax = " << grdmax << "\n";
1678 Print() << "AMR scaling (linear) lev_scale = " << lev_scale << "\n";
1679 }
1680 Print() << "visc2(all) min/max = "
1681 << visc_min_all << " / "
1682 << visc_max_all << "\n";
1683 Print() << "visc2(wet,k=0) min/max = "
1684 << visc_min_wet << " / "
1685 << visc_max_wet << "\n";
1686 Print() << "visc2(mask->0) min/max = "
1687 << visc_min_mask0 << " / "
1688 << visc_max_mask0 << "\n";
1689 }
1690
1691 } else {
1692 Abort("Don't know this horizontal mixing type");
1693 }
1694
1695 // Final FillPatch for all fields
1696 Real time = zero;
1699 for (int n = 0; n < ncons; n++) {
1701 foextrap_periodic_bc(), BdyVars::null, n, false);
1702 }
1703}
1704
1705/**
1706 * @param[in ] lev level to operate on
1707 */
1708void
1710{
1711 BL_PROFILE("REMORA::set_smflux()");
1713 prob->init_analytic_smflux(lev, geom[lev], solverChoice, *this,*vec_sustr[lev], *vec_svstr[lev]);
1715#ifdef REMORA_USE_NETCDF
1716 sustr_data_from_file->update_interpolated_to_time(model_time(t_old[lev]), lev, vec_sustr[lev].get(), geom, ref_ratio);
1717 svstr_data_from_file->update_interpolated_to_time(model_time(t_old[lev]), lev, vec_svstr[lev].get(), geom, ref_ratio);
1720#endif
1721 }
1722}
1723
1724/**
1725 * @param[in ] lev level to operate on
1726 */
1727void
1729{
1730 BL_PROFILE("REMORA::set_surface_state()");
1731
1732 auto& bulk_flux_type = solverChoice.bulk_flux_type;
1733
1734 // Every update below skips driver-supplied lanes individually, on
1735 // !driver_atmos_state_from_driver[...], so in a coupled run the lanes the
1736 // driver withholds still get this deck's forcing.
1737
1738#ifdef REMORA_USE_NETCDF
1739 auto update_from_netcdf = [&](std::unique_ptr<NCTimeSeries>& data_from_file,
1741 data_from_file->update_interpolated_to_time(model_time(t_old[lev]), lev, mf_vec[lev].get(), geom, ref_ratio);
1743 foextrap_periodic_bc(), BdyVars::null, 0, false);
1744 };
1745
1748 }
1751 }
1752
1755 }
1759 vec_qair[lev]->mult(amrex::Real(0.01));
1760 }
1761 }
1764 }
1767 }
1770 }
1773 }
1776 }
1777 if (bulk_flux_type[BulkFlux::EminusP] == BulkForcingType::netcdf) {
1779 }
1780#else
1781 for (int idx = 0; idx < BulkFlux::NumTypes; ++idx) {
1782 if (bulk_flux_type[idx] == BulkForcingType::netcdf) {
1783 amrex::Abort("NetCDF bulk-flux forcing requires building with NetCDF");
1784 }
1785 }
1786#endif
1787
1788 MultiFab* analytic_uwind = (bulk_flux_type[BulkFlux::Uwind] == BulkForcingType::analytic &&
1790 MultiFab* analytic_vwind = (bulk_flux_type[BulkFlux::Vwind] == BulkForcingType::analytic &&
1792 MultiFab* analytic_Tair = (bulk_flux_type[BulkFlux::Tair] == BulkForcingType::analytic &&
1794 MultiFab* analytic_qair = (bulk_flux_type[BulkFlux::Qair] == BulkForcingType::analytic &&
1796 MultiFab* analytic_Pair = (bulk_flux_type[BulkFlux::Pair] == BulkForcingType::analytic &&
1798 MultiFab* analytic_srflx = (bulk_flux_type[BulkFlux::SWrad] == BulkForcingType::analytic &&
1800 MultiFab* analytic_lwrad = (bulk_flux_type[BulkFlux::LWrad] == BulkForcingType::analytic &&
1802 MultiFab* analytic_rain = (bulk_flux_type[BulkFlux::Rain] == BulkForcingType::analytic &&
1804 MultiFab* analytic_cloud = (bulk_flux_type[BulkFlux::Cloud] == BulkForcingType::analytic &&
1806 MultiFab* analytic_EminusP = bulk_flux_type[BulkFlux::EminusP] == BulkForcingType::analytic ? vec_EminusP[lev].get() : nullptr;
1807
1808 if (analytic_uwind != nullptr || analytic_vwind != nullptr ||
1809 analytic_Tair != nullptr || analytic_qair != nullptr || analytic_Pair != nullptr ||
1810 analytic_srflx != nullptr || analytic_lwrad != nullptr || analytic_rain != nullptr ||
1811 analytic_cloud != nullptr || analytic_EminusP != nullptr) {
1812 // Every field has to be passed to init_analytic_surface_var, but only the
1813 // analytic ones may be modified: the others hold constant or NetCDF data that is
1814 // set once at level creation or interpolated just above. Hand the non-analytic
1815 // slots scratch data that is thrown away on return, so the problem code can write
1816 // to all ten references unconditionally without clobbering anything.
1818 auto analytic_or_scratch = [&] (MultiFab* mf_analytic,
1819 const std::unique_ptr<MultiFab>& mf_lev) -> MultiFab&
1820 {
1821 if (mf_analytic != nullptr) { return *mf_analytic; }
1822 scratch_mf.emplace_back(new MultiFab(mf_lev->boxArray(), mf_lev->DistributionMap(),
1823 mf_lev->nComp(), mf_lev->nGrowVect()));
1824 return *scratch_mf.back();
1825 };
1826
1827 prob->init_analytic_surface_var(lev, geom[lev], solverChoice, *this,
1838 }
1839
1840 if (vec_uwind[lev] != nullptr) { vec_uwind[lev]->FillBoundary(geom[lev].periodicity()); }
1841 if (vec_vwind[lev] != nullptr) { vec_vwind[lev]->FillBoundary(geom[lev].periodicity()); }
1842 if (vec_Tair[lev] != nullptr) { vec_Tair[lev]->FillBoundary(geom[lev].periodicity()); }
1843 if (vec_qair[lev] != nullptr) { vec_qair[lev]->FillBoundary(geom[lev].periodicity()); }
1844 if (vec_Pair[lev] != nullptr) { vec_Pair[lev]->FillBoundary(geom[lev].periodicity()); }
1845 if (vec_srflx[lev] != nullptr) { vec_srflx[lev]->FillBoundary(geom[lev].periodicity()); }
1846 if (vec_longwave_down[lev] != nullptr) { vec_longwave_down[lev]->FillBoundary(geom[lev].periodicity()); }
1847 if (vec_rain[lev] != nullptr) { vec_rain[lev]->FillBoundary(geom[lev].periodicity()); }
1848 if (vec_cloud[lev] != nullptr) { vec_cloud[lev]->FillBoundary(geom[lev].periodicity()); }
1849 if (vec_EminusP[lev] != nullptr) { vec_EminusP[lev]->FillBoundary(geom[lev].periodicity()); }
1850}
1851
1852/**
1853 * @param[in ] lev level to operate on
1854 * @param[in ] time current time for initialization
1855 */
1856void
1858{
1859 BL_PROFILE("REMORA::init_only()");
1860 t_new[lev] = time;
1862
1863 cons_new[lev]->setVal(zero);
1864 xvel_new[lev]->setVal(zero);
1865 yvel_new[lev]->setVal(zero);
1866 zvel_new[lev]->setVal(zero);
1867
1868 xvel_old[lev]->setVal(zero);
1869 yvel_old[lev]->setVal(zero);
1870 zvel_old[lev]->setVal(zero);
1871
1872 vec_ru[lev]->setVal(zero);
1873 vec_rv[lev]->setVal(zero);
1874
1875 vec_ru2d[lev]->setVal(zero);
1876 vec_rv2d[lev]->setVal(zero);
1877
1880 }
1881
1882 // High-resolution grid data, both sources in one place because the order matters: the mask
1883 // is coarsened first, and the bathymetry is then coarsened with it, so a coarse cell only
1884 // partly covered by water takes the depth of that water. mask_type and ic_type are set
1885 // independently, so each is dispatched on its own.
1886 if (lev==0 and hires_grid_level > 0) {
1888
1891 } else if (solverChoice.mask_type == MaskType::netcdf) {
1892#ifdef REMORA_USE_NETCDF
1893 amrex::Print() << "Reading high resolution land-sea mask" << std::endl;
1895 amrex::Print() << "Done reading in high resolution land-sea mask" << std::endl;
1896#endif
1897 }
1898
1901 } else if (solverChoice.ic_type == IC_Type::netcdf) {
1902#ifdef REMORA_USE_NETCDF
1903 amrex::Print() << "Reading high resolution bathymetry and grid data" << std::endl;
1906 amrex::Print() << "Done reading in high resolution bathymetry and grid data" << std::endl;
1907#endif
1908 }
1909 }
1910
1911#ifdef REMORA_USE_NETCDF
1914
1915 if (solverChoice.do_any_clim_nudg && lev == 0) {
1916 if (nc_clim_his_file.empty() || nc_clim_his_file[0].empty()) {
1917 amrex::Error("NetCDF climatology file name must be provided via input");
1918 }
1924 ubar_clim_data_from_file->Initialize();
1925 vbar_clim_data_from_file->Initialize();
1926 }
1930 u_clim_data_from_file->Initialize();
1931 v_clim_data_from_file->Initialize();
1932 }
1933 // Since the NCTimeSeries object isn't filling the cons_new MultiFab directly, we don't have to specify a component.
1934 // It just needs to know the shape of the MultiFab
1936 for (int icomp = 0; icomp < ncons; ++icomp) {
1937 if (!solverChoice.do_cons_clim_nudg[icomp]) { continue; }
1938 // A tracer's climatology is stored in the file under the tracer's own
1939 // name, following the same convention ROMS uses. Check up front rather
1940 // than letting the read fail deep inside NCTimeSeries.
1941 for (const auto& fname : nc_clim_his_file) {
1943 amrex::Abort("Climatology file " + fname + " does not contain '" +
1944 cons_names[icomp] + "', which is required by remora.do_" +
1945 cons_names[icomp] + "_clim_nudg. Either add it to the file "
1946 "or turn that flag off.");
1947 }
1948 }
1951 cons_clim_data_from_file[icomp]->Initialize();
1952 }
1953 }
1954 }
1955
1957 amrex::Print() << "Calling init_bdry_from_netcdf at level " << lev << std::endl;
1959 amrex::Print() << "Boundary data loaded from netcdf file \n " << std::endl;
1960 }
1961
1962 // This will be a non-op if forcings specified analytically
1964 if (lev==0) {
1965 if (nc_frc_file.empty() || nc_frc_file[0].empty()) {
1966 amrex::Error("NetCDF forcing file name must be provided via input for surface momentum fluxes");
1967 }
1970 sustr_data_from_file->Initialize();
1971 svstr_data_from_file->Initialize();
1972 } else {
1975 }
1976 }
1977
1978 // Conditionally load atmospheric forcing fields from NetCDF based on source type.
1979 const auto& bulk_flux_type = solverChoice.bulk_flux_type;
1980 bool any_bulk_netcdf = false;
1981 for (int idx = 0; idx < BulkFlux::NumTypes; ++idx) {
1983 }
1984 if (lev == 0 && any_bulk_netcdf && (nc_frc_file.empty() || nc_frc_file[0].empty())) {
1985 amrex::Error("NetCDF forcing file name must be provided via input for bulk-flux atmospheric forcing");
1986 }
1987
1988 if (lev==0) {
1989 if (bulk_flux_type[BulkFlux::Uwind] == BulkForcingType::netcdf) {
1991 Uwind_data_from_file->Initialize();
1992 }
1993 if (bulk_flux_type[BulkFlux::Vwind] == BulkForcingType::netcdf) {
1995 Vwind_data_from_file->Initialize();
1996 }
1997 if (bulk_flux_type[BulkFlux::Tair] == BulkForcingType::netcdf) {
1999 Tair_data_from_file->Initialize();
2000 }
2001 if (bulk_flux_type[BulkFlux::Qair] == BulkForcingType::netcdf) {
2003 qair_data_from_file->Initialize();
2004 }
2005 if (bulk_flux_type[BulkFlux::Pair] == BulkForcingType::netcdf) {
2007 Pair_data_from_file->Initialize();
2008 }
2009 if (bulk_flux_type[BulkFlux::SWrad] == BulkForcingType::netcdf) {
2011 srflx_data_from_file->Initialize();
2012 }
2013 if (bulk_flux_type[BulkFlux::Rain] == BulkForcingType::netcdf) {
2015 rain_data_from_file->Initialize();
2016 }
2017 if (bulk_flux_type[BulkFlux::Cloud] == BulkForcingType::netcdf) {
2019 cloud_data_from_file->Initialize();
2020 }
2021 if (bulk_flux_type[BulkFlux::EminusP] == BulkForcingType::netcdf) {
2023 EminusP_data_from_file->Initialize();
2024 }
2025 if (bulk_flux_type[BulkFlux::LWrad] == BulkForcingType::netcdf) {
2027 geom[lev].Domain(), vec_longwave_down[lev].get(), true, false, solverChoice.time_ref));
2028 longwave_down_data_from_file->Initialize();
2029 }
2030 } else {
2031 if (bulk_flux_type[BulkFlux::Uwind] == BulkForcingType::netcdf) {
2033 }
2034 if (bulk_flux_type[BulkFlux::Vwind] == BulkForcingType::netcdf) {
2036 }
2037 if (bulk_flux_type[BulkFlux::Tair] == BulkForcingType::netcdf) {
2038 FillCoarsePatch(lev, time, vec_Tair[lev].get(), vec_Tair[lev-1].get(), foextrap_bc());
2039 }
2040 if (bulk_flux_type[BulkFlux::Qair] == BulkForcingType::netcdf) {
2041 FillCoarsePatch(lev, time, vec_qair[lev].get(), vec_qair[lev-1].get(), foextrap_bc());
2042 }
2043 if (bulk_flux_type[BulkFlux::Pair] == BulkForcingType::netcdf) {
2044 FillCoarsePatch(lev, time, vec_Pair[lev].get(), vec_Pair[lev-1].get(), foextrap_bc());
2045 }
2046 if (bulk_flux_type[BulkFlux::SWrad] == BulkForcingType::netcdf) {
2048 }
2049 if (bulk_flux_type[BulkFlux::Rain] == BulkForcingType::netcdf) {
2050 FillCoarsePatch(lev, time, vec_rain[lev].get(), vec_rain[lev-1].get(), foextrap_bc());
2051 }
2052 if (bulk_flux_type[BulkFlux::Cloud] == BulkForcingType::netcdf) {
2054 }
2055 if (bulk_flux_type[BulkFlux::EminusP] == BulkForcingType::netcdf) {
2057 }
2058 if (bulk_flux_type[BulkFlux::LWrad] == BulkForcingType::netcdf) {
2060 }
2061 }
2062
2063 // Only need to read in rivers on level 0
2064 // Will need to be on higher levels eventually
2065 if (solverChoice.do_rivers) {
2066 if (nc_riv_file.empty() || nc_riv_file[0].empty()) {
2067 amrex::Error("NetCDF river file name must be provided via input for rivers");
2068 }
2069 auto dom = geom[0].Domain();
2070 int nz = dom.length(2);
2071 // Every cell-centered tracer can take river input. The field is named for the
2072 // tracer, as in ROMS: river_temp, river_salt, river_tracer, river_NO3, ...
2073 river_source_cons.resize(ncons);
2074 for (int icomp = 0; icomp < ncons; ++icomp) {
2075 if (!solverChoice.do_rivers_cons[icomp]) { continue; }
2076
2077 const std::string field = "river_" + cons_names[icomp];
2078 for (const auto& fname : nc_riv_file) {
2079 if (!QueryNetCDFHasVars(fname, {field})) {
2080 // The flag may have come from remora.do_rivers_scalar rather than the
2081 // per-tracer key, so name the tracer and the key that switches it off.
2082 amrex::Abort("River file " + fname + " does not contain '" + field +
2083 "', but river input is enabled for tracer '" +
2084 cons_names[icomp] + "'. Either add that variable to the "
2085 "file, or set remora.do_rivers_" + cons_names[icomp] +
2086 " = false.");
2087 }
2088 }
2089
2091 river_source_cons[icomp]->Initialize();
2092 }
2094 river_source_transport->Initialize();
2096 river_source_transportbar->Initialize();
2098 }
2099
2100#else
2102 Abort("Not compiled with NetCDF, but remora.ic_type = netcdf reads initial and grid data from file");
2103 }
2104 // hires_grid_level and hires_init_level need no guard: with analytic initialization
2105 // neither reads NetCDF, and the netcdf case is caught above.
2107 Abort("Not compiled with NetCDF, but selected boundary conditions require NetCDF");
2108 }
2109 if (solverChoice.do_rivers) {
2110 Abort("Not compiled with NetCDF, but using river sources requires NetCDF");
2111 }
2112#endif
2113
2115 // Has to follow set_bathymetry: the mask's hires_grid_level path needs the
2116 // full-domain data read just above, and an analytic mask needs the grid
2117 // coordinates that set_bathymetry -> set_grid_scale fills on the netcdf path.
2118 set_masks(lev);
2119
2120 // Has to sit between set_masks and set_zeta. After set_masks, since
2121 // ensure_full_domain_masks seeds level 0 from vec_mskr[0], and init_masks' all-water
2122 // placeholder would reduce the weighting below to a plain mean. Before set_zeta, which
2123 // reads the vec_zeta_full_domain this fills.
2124 if (lev==0 and hires_init_level > 0) {
2126#ifdef REMORA_USE_NETCDF
2127 amrex::Print() << "Reading high resolution initial data" << std::endl;
2129 // The initial-state cascade is mask-weighted, so a mask has to exist this high up.
2132 // Biology source is chosen by remora.biology_ic_type, not by ic_type,
2133 // so this goes through the same dispatcher as the per-level path.
2134 // Must follow init_data_full_domain_from_netcdf: the analytic biology
2135 // profiles read temperature.
2138 amrex::Print() << "Done reading in high resolution initial data" << std::endl;
2139#endif
2140 } else if (solverChoice.ic_type == IC_Type::analytic) {
2144 }
2145 }
2146
2147 set_zeta(lev);
2149
2150 if (lev==0) {
2151 if (hires_init_level < 0) {
2154 } else if (solverChoice.ic_type == IC_Type::netcdf) {
2155#ifdef REMORA_USE_NETCDF
2156 amrex::Print() << "Calling init_data_from_netcdf " << std::endl;
2158 bool apply_eminusp = false;
2160 amrex::Print() << "Initial data loaded from netcdf file \n " << std::endl;
2161#endif
2162 } else {
2163 amrex::Abort("Unknown IC_Type");
2164 }
2165 // Biology last, and outside the ic_type branches: its source is
2166 // chosen independently by remora.biology_ic_type, and the analytic
2167 // profiles need the physical fields already in place.
2169 } else {
2170 set_init_data_averaged_down(lev); // also sets biology data
2171 bool apply_eminusp = false;
2173 // Since set_grid_scale is usually called from init_analytic for analytic problems
2176 }
2177 }
2178 } else {
2179 if (lev > hires_init_level) {
2184 } else {
2185 set_init_data_averaged_down(lev); // also sets biology data
2186 bool apply_eminusp = false;
2189 // Since set_grid_scale is usually called from init_analytic for analytic problems
2191 }
2192 }
2193 }
2194
2195 // Ensure that the face-based data are the same on both sides of a periodic domain.
2196 // The data associated with the lower grid ID is considered the correct value.
2197 xvel_new[lev]->OverrideSync(geom[lev].periodicity());
2198 yvel_new[lev]->OverrideSync(geom[lev].periodicity());
2199 zvel_new[lev]->OverrideSync(geom[lev].periodicity());
2200
2202
2206}
2207
2208void
2210{
2211 BL_PROFILE("REMORA::ReadParameters()");
2212 {
2213 ParmParse pp; // Traditionally, max_step and stop_time do not have prefix, so allow it for now.
2214 bool noprefix_max_step = pp.queryAdd("max_step", max_step);
2215 bool noprefix_stop_time = pp.queryAdd("stop_time", stop_time);
2216 bool remora_max_step = pp.queryAdd("remora.max_step", max_step);
2217 bool remora_stop_time = pp.queryAdd("remora.stop_time", stop_time);
2219 Abort("remora.max_step and max_step are both specified. Please use only one!");
2220 }
2222 Abort("remora.stop_time and stop_time are both specified. Please use only one!");
2223 }
2224 }
2225
2227
2228 // Common physics and simulation parameters
2230 pp.queryAdd("biology_model", biology_model_string);
2232
2235
2236 // Source of the biology initial condition, independent of ic_type.
2237 // Default "follow" takes the biology IC from NetCDF when ic_type is
2238 // netcdf, else analytic.
2240 pp.queryAdd("biology_ic_type", biology_ic_string);
2242
2243 // Bridge-vs-native selection and diagnostic verbosity are runtime
2244 // controls so a parity comparison never requires a rebuild. Both
2245 // parse unconditionally; without USE_FENNEL_FORT there is no bridge
2246 // to select, so asking for it is an error rather than a silent
2247 // fallback to the path being validated.
2248 pp.queryAdd("use_biology_cpp_answer", use_biology_cpp_answer);
2249 pp.queryAdd("biology_debug", biology_debug);
2250 pp.queryAdd("biology_debug_i", biology_debug_i);
2251 pp.queryAdd("biology_debug_j", biology_debug_j);
2252#ifndef REMORA_USE_FENNEL_FORT
2253 if (use_biology_cpp_answer == 0) {
2254 amrex::Abort("remora.use_biology_cpp_answer = 0 selects the ROMS "
2255 "Fennel Fortran bridge, which is not compiled in. "
2256 "Rebuild with USE_FENNEL_FORT=TRUE (GNUmake) or "
2257 "-DREMORA_ENABLE_FENNEL_FORT=ON (CMake).");
2258 }
2259#endif
2260#ifndef REMORA_USE_BIOLOGY_DIAG
2261 if (biology_debug > 0) {
2262 amrex::Abort("remora.biology_debug > 0 requests the Fennel parity "
2263 "diagnostics, which are not compiled in. Rebuild with "
2264 "USE_BIOLOGY_DIAG=TRUE (GNUmake) or "
2265 "-DREMORA_ENABLE_BIOLOGY_DIAG=ON (CMake).");
2266 }
2267#endif
2268
2269 // Biology tracers are counted separately from the passive scalars, so a run can
2270 // carry dye and biology at once.
2271 nbio = static_cast<int>(REMORABiology::tracer_names(biology_model, fennel_params).size());
2272 } else {
2273 nbio = 0;
2274 }
2275 // Dye is opt-in, biology or not: a component nothing asked for is one more thing to
2276 // advect, diffuse, and explain in every plotfile and boundary file.
2277 pp.queryAdd("nscalar", nscalar);
2278 if (nscalar < 0) {
2279 amrex::Abort("remora.nscalar must be non-negative");
2280 }
2284
2285 // remora.nscalar counts dye only; biology tracers are added on top. Print
2286 // the layout so a run carrying both is unmistakable.
2287 if (nbio > 0 && nscalar > 0) {
2288 amrex::Print() << "Carrying " << nscalar << " passive scalar(s) and " << nbio
2289 << " biology tracer(s), for " << ncons << " cell-centered components: ";
2290 for (int icomp = 0; icomp < ncons; ++icomp) {
2291 amrex::Print() << cons_names[icomp] << (icomp + 1 < ncons ? " " : "\n");
2292 }
2293 }
2294
2295 pp.queryAdd("check_file", check_file);
2296 pp.queryAdd("check_int", check_int);
2297 pp.queryAdd("check_int_time", check_int_time);
2298 pp.queryAdd("expand_plotvars_to_unif_rr", expand_plotvars_to_unif_rr);
2299 pp.query("plotfile_fill_value", plotfile_fill_value);
2300 pp.query("netcdf_fill_value", netcdf_fill_value);
2301 pp.queryAdd("restart", restart_chkfile);
2302 pp.queryAdd("start_time", start_time);
2303
2304 num_boxes_at_level.resize(max_level + 1, 0);
2305 boxes_at_level.resize(max_level + 1);
2306 num_boxes_at_level[0] = 1;
2307 boxes_at_level[0].resize(1);
2308 boxes_at_level[0][0] = geom[0].Domain();
2309
2310 if (pp.contains("data_log")) {
2311 int num_datalogs = pp.countval("data_log");
2312 datalog.resize(num_datalogs);
2313 datalogname.resize(num_datalogs);
2314 pp.queryarr("data_log", datalogname, 0, num_datalogs);
2315 for (int i = 0; i < num_datalogs; i++)
2317 }
2318
2319 pp.queryAdd("v", verbose);
2320 pp.queryAdd("sum_interval", sum_interval);
2321 pp.queryAdd("sum_period", sum_per);
2322 pp.queryAdd("file_min_digits", file_min_digits);
2323
2324 if (file_min_digits < 0) {
2325 amrex::Abort("remora.file_min_digits must be non-negative");
2326 }
2327
2328 pp.queryAdd("cfl", cfl);
2329 pp.queryAdd("change_max", change_max);
2330 pp.queryAdd("fixed_dt", fixed_dt);
2331
2332 // remora.fixed_fast_dt is not accepted; use remora.ndtfast. amrex does not
2333 // abort on unused inputs by default, so catch it here rather than letting a
2334 // stale input file silently fall back to whatever remora.ndtfast happens to
2335 // be.
2336 if (pp.contains("fixed_fast_dt")) {
2337 amrex::Abort("remora.fixed_fast_dt has been removed. Set remora.ndtfast (the "
2338 "number of barotropic steps per baroclinic step) instead; it is what "
2339 "fixed_fast_dt was used to infer, as remora.fixed_dt / "
2340 "remora.fixed_fast_dt");
2341 }
2342
2343 // remora.ndtfast is the preferred name; remora.fixed_ndtfast_ratio is kept as a
2344 // deprecated alias so existing input files keep working. Read the alias first, so
2345 // that the queryAdd below records the resulting value under the preferred name.
2346 if (pp.contains("fixed_ndtfast_ratio")) {
2347 if (pp.contains("ndtfast")) {
2348 amrex::Abort("remora.ndtfast and remora.fixed_ndtfast_ratio are both "
2349 "specified. Please use only remora.ndtfast");
2350 }
2351 amrex::Print() << "WARNING: remora.fixed_ndtfast_ratio is deprecated. "
2352 << "Please use remora.ndtfast instead." << std::endl;
2353 // Deprecated alias for remora.ndtfast.
2354 pp.queryAdd("fixed_ndtfast_ratio", ndtfast);
2355 }
2356 // Number of barotropic (fast) steps taken per baroclinic (slow) step.
2357 pp.queryAdd("ndtfast", ndtfast);
2358
2359 // 0 selects timeStepML, kept as a comparison path. amr.do_substep is a
2360 // deprecated alias; read it first so the queryAdd below records the value
2361 // under remora.do_substep.
2362 {
2363 ParmParse pp_amr("amr");
2364 if (pp_amr.contains("do_substep")) {
2365 if (pp.contains("do_substep")) {
2366 amrex::Abort("remora.do_substep and amr.do_substep are both specified. "
2367 "Please use only remora.do_substep");
2368 }
2369 amrex::Print() << "WARNING: amr.do_substep is deprecated. "
2370 << "Please use remora.do_substep instead." << std::endl;
2371 pp_amr.queryAdd("do_substep", do_substep);
2372 }
2373 }
2374 pp.queryAdd("do_substep", do_substep);
2375
2376 if (max_level > 0) {
2377 if (do_substep) {
2378 amrex::Print() << "WARNING: subcycling refined levels in time is EXPERIMENTAL.\n"
2379 << " Multi-level answers are not yet considered production\n"
2380 << " quality; check them against a single-level run before\n"
2381 << " relying on them. remora.do_substep = 0 selects the\n"
2382 << " lockstep driver instead.\n";
2383 } else {
2384 amrex::Print() << "NOTE: remora.do_substep = 0 selects the lockstep driver. It cannot\n"
2385 << " impose the parent's mass flux at a coarse-fine interface, so\n"
2386 << " it conserves volume less well: 2.0e-6 against 2.9e-08 on\n"
2387 << " Dogbone. Refinement is EXPERIMENTAL on either driver.\n";
2388 }
2389 }
2390
2391 // Write the parent's flux straight onto DUon/DVom instead of letting the solver rebuild it
2392 // from the imposed velocity. See set_2d_cf_flux.
2393 pp.queryAdd("cf_impose_flux", cf_impose_flux);
2394
2395 // See set_2d_cf_bcs. Only has an effect when remora.do_substep = 1.
2396 pp.queryAdd("time_interp_flux", time_interp_flux);
2397
2398 // Tracer flux correction at the coarse-fine interface. Needs remora.do_substep and
2399 // two-way coupling to do anything.
2400 pp.queryAdd("do_reflux", do_reflux);
2401
2402 // Mirror ROMS's fine2coarse, which returns every covered cell but not the perimeter's
2403 // normal-velocity faces. 0 includes the perimeter faces in the average.
2404 pp.queryAdd("cf_avgdown_perimeter", cf_avgdown_perimeter);
2405
2406 // Diagnostics; see the declarations in REMORA.H.
2407 pp.queryAdd("cf_fill_vel_after", cf_fill_vel_after);
2408 pp.queryAdd("cf_set_2d_bcs", cf_set_2d_bcs);
2409 pp.queryAdd("cf_avgdown_bar", cf_avgdown_bar);
2410 pp.queryAdd("cf_fill_all_kcomp", cf_fill_all_kcomp);
2411 pp.queryAdd("cf_time_interp_zeta", cf_time_interp_zeta);
2412 pp.queryAdd("cf_flux_pc", cf_flux_pc);
2413 pp.queryAdd("cf_avgdown_stencil", cf_avgdown_stencil);
2414 pp.queryAdd("cf_avgdown_volume_weighted", cf_avgdown_volume_weighted);
2415 pp.queryAdd("cf_print_iface", cf_print_iface);
2416
2417 // Whether to zero a tracer the correction drives negative, as ROMS does.
2418 pp.queryAdd("reflux_clamp", reflux_clamp);
2419
2420 // See check_cf_metrics. Off by default: it is a property of the grid, so one run says as
2421 // much as every run.
2422 pp.queryAdd("check_cf_metrics", check_cf_metrics_flag);
2423 pp.queryAdd("check_cf_tol", check_cf_tol);
2424 pp.queryAdd("check_plot_z", check_plot_z);
2425 pp.queryAdd("check_plot_z_tol", check_plot_z_tol);
2426
2427 // Advance and timeStepML form the fast step as dt / ndtfast, and set_weights sizes
2428 // the barotropic filter with the same number, so a non-positive value divides by zero
2429 // at all three sites. Nothing can infer it: dt is not known until run time on a
2430 // CFL-driven run.
2431 if (ndtfast <= 0) {
2432 amrex::Abort("remora.ndtfast must be a positive integer: it is the number of "
2433 "barotropic steps taken per baroclinic step");
2434 }
2435
2436 // remora.use_barotropic has been removed -- the barotropic mode is always on. Reject
2437 // it on presence rather than on value: amrex does not abort on unused inputs by
2438 // default, so a stale "= false" would otherwise silently run different physics than
2439 // the input file asks for. Tested with contains() rather than query() so the schema
2440 // scraper behind Exec/Generic does not re-advertise a parameter that no longer exists.
2441 if (pp.contains("use_barotropic")) {
2442 amrex::Abort("remora.use_barotropic has been removed. The barotropic (2D) mode is "
2443 "always active; please delete this line from your inputs file");
2444 }
2445
2447
2448 num_files_at_level.resize(max_level + 1, 0);
2449 num_boxes_at_level.resize(max_level + 1, 0);
2450 boxes_at_level.resize(max_level + 1);
2451 num_boxes_at_level[0] = 1;
2452 boxes_at_level[0].resize(1);
2453 boxes_at_level[0][0] = geom[0].Domain();
2454
2455 pp.queryAdd("plot_file", plot_file_name);
2456 pp.queryAdd("plot_int", plot_int);
2457 pp.queryAdd("plot_int_time", plot_int_time);
2458 pp.query("plot_staggered_vels", plot_staggered_vels);
2459 pp.query("plot_nodal_data", plot_nodal_data);
2460
2461 std::string plotfile_type_str = "amrex";
2462 pp.queryAdd("plotfile_type", plotfile_type_str);
2463 if (plotfile_type_str == "amrex") {
2465 } else if (plotfile_type_str == "netcdf" || plotfile_type_str == "NetCDF") {
2467#ifdef REMORA_USE_NETCDF
2468 pp.queryAdd("write_history_file",write_history_file);
2469 pp.queryAdd("chunk_history_file",chunk_history_file);
2470 pp.queryAdd("steps_per_history_file",steps_per_history_file);
2471 // CDF-5 output has no practical size limit, so REMORA doesn't size history
2472 // files. Chunking is opt-in; the writer divides by steps_per_history_file.
2474 if (steps_per_history_file <= 0) {
2475 amrex::Abort("remora.chunk_history_file requires remora.steps_per_history_file > 0");
2476 }
2477 Print() << "NetCDF history files will have " << steps_per_history_file << " steps per file." << std::endl;
2478 }
2479#endif
2480 } else {
2481 amrex::Print() << "User selected plotfile_type = " << plotfile_type_str << std::endl;
2482 amrex::Abort("Dont know this plotfile_type");
2483 }
2484#ifndef REMORA_USE_NETCDF
2486 {
2487 amrex::Abort("Please compile with NetCDF in order to enable NetCDF plotfiles");
2488 }
2489
2490#endif
2491#ifdef REMORA_USE_NETCDF
2492 nc_init_file.resize(max_level+1);
2493 nc_grid_file.resize(max_level+1);
2494 num_files_at_level.resize(max_level + 1, 0);
2495
2496 boundary_series.resize(max_level+1);
2497
2498
2499 // NetCDF initialization and grid files, read independently of each other: with
2500 // hires_grid_level or hires_init_level set, the corresponding level 0 file is not
2501 // needed. Whether a level 0 file is required is checked once the solver choices
2502 // are known, below.
2503 for (int lev = 0; lev <= max_level; lev++)
2504 {
2505 const std::string nc_file_names = amrex::Concatenate("nc_init_file_",lev,1);
2506 const std::string nc_bathy_file_names = amrex::Concatenate("nc_grid_file_",lev,1);
2507
2508 if (pp.contains(nc_file_names.c_str()))
2509 {
2510 int num_files = pp.countval(nc_file_names.c_str());
2512 nc_init_file[lev].resize(num_files);
2513 pp.queryarr(nc_file_names.c_str(), nc_init_file[lev], 0, num_files);
2514 }
2515 if (pp.contains(nc_bathy_file_names.c_str()))
2516 {
2517 int num_bathy_files = pp.countval(nc_bathy_file_names.c_str());
2519 pp.queryarr(nc_bathy_file_names.c_str(), nc_grid_file[lev], 0, num_bathy_files);
2520 }
2521 }
2522
2523 pp.queryAdd("nc_grid_file_hires", nc_grid_file_hires);
2524 pp.queryAdd("nc_init_file_hires", nc_init_file_hires);
2525
2526 // We only read boundary data at level 0
2527 pp.queryarr("nc_bdry_file", nc_bdry_file);
2528
2529 // Also only read forcings at level 0 (for now)
2530 if (pp.contains("nc_frc_file")) {
2531 int num_files = pp.countval("nc_frc_file");
2532 nc_frc_file.resize(num_files);
2533 pp.queryarr("nc_frc_file", nc_frc_file, 0, num_files);
2534 }
2535
2536 // Get river file
2537 if (pp.contains("nc_river_file")) {
2538 int num_files = pp.countval("nc_river_file");
2539 nc_riv_file.resize(num_files);
2540 pp.queryarr("nc_river_file", nc_riv_file, 0, num_files);
2541 }
2542
2543 // Read in file names for climatology history and nudging weights
2544 if (pp.contains("nc_clim_his_file")) {
2545 int num_files = pp.countval("nc_clim_his_file");
2547 pp.queryarr("nc_clim_his_file", nc_clim_his_file, 0, num_files);
2548 }
2549 pp.queryAdd("nc_clim_coeff_file", nc_clim_coeff_file);
2550
2551 for (int i=0; i<BdyVars::NumTypes(ncons); i++) {
2552 bdry_time_name_byvar.push_back("");
2553 }
2554 pp.queryAdd("bdy_time_varname",bdry_time_varname);
2555 // Every tracer takes its time-axis name from its own variable name, so temp and salt
2556 // keep bdy_temp_time_varname / bdy_salt_time_varname and a biology tracer uses e.g.
2557 // bdy_NO3_time_varname
2558 for (int icomp = 0; icomp < ncons; ++icomp) {
2559 pp.queryAdd(("bdy_"+cons_names[icomp]+"_time_varname").c_str(),
2561 }
2562 pp.queryAdd("bdy_u_time_varname",bdry_time_name_byvar[BdyVars::u]);
2563 pp.queryAdd("bdy_v_time_varname",bdry_time_name_byvar[BdyVars::v]);
2564 pp.queryAdd("bdy_ubar_time_varname",bdry_time_name_byvar[BdyVars::ubar(ncons)]);
2565 pp.queryAdd("bdy_vbar_time_varname",bdry_time_name_byvar[BdyVars::vbar(ncons)]);
2566 pp.queryAdd("bdy_zeta_time_varname",bdry_time_name_byvar[BdyVars::zeta(ncons)]);
2567
2568 // If not specified per variable, populate with the default
2569 for (int i=0; i<BdyVars::NumTypes(ncons); i++) {
2570 if (bdry_time_name_byvar[i] == "") {
2572 }
2573 }
2574
2575 pp.queryAdd("frc_time_varname",frc_time_varname);
2576
2577 pp.queryAdd("riv_time_varname",riv_time_varname);
2578
2579 pp.queryAdd("clim_ubar_time_varname",clim_ubar_time_varname);
2580 pp.queryAdd("clim_vbar_time_varname",clim_vbar_time_varname);
2581 pp.queryAdd("clim_u_time_varname",clim_u_time_varname);
2582 pp.queryAdd("clim_v_time_varname",clim_v_time_varname);
2583 // As for the boundary data, each tracer takes its climatology time-axis name from its
2584 // own variable name, so temp and salt keep clim_temp_time_varname /
2585 // clim_salt_time_varname and a biology tracer uses e.g. clim_NO3_time_varname
2586 clim_cons_time_varname.assign(ncons, "ocean_time");
2587 for (int icomp = 0; icomp < ncons; ++icomp) {
2588 pp.queryAdd(("clim_"+cons_names[icomp]+"_time_varname").c_str(),
2590 }
2591
2592#endif
2593 // A hires level of 0 is not "level 0 is the hires level", it is a null pointer: the
2594 // full-domain arrays are only allocated for lev > 0 (see allocate_init_full_domain and
2595 // allocate_bathymetry_grid_vars_full_domain), while every consumer branch tests < 0 and
2596 // so would take the averaged-down path against an unallocated MultiFab. -1 means off.
2597 pp.queryAdd("hires_grid_level", hires_grid_level);
2599 amrex::Abort("hires_grid_level must be less than or equal to amr.max_level");
2600 }
2601 if (hires_grid_level == 0) {
2602 amrex::Abort("hires_grid_level must be greater than 0; use -1 to specify grid data at level 0");
2603 }
2604 pp.queryAdd("hires_init_level", hires_init_level);
2606 amrex::Abort("hires_init_level must be less than or equal to amr.max_level");
2607 }
2608 if (hires_init_level == 0) {
2609 amrex::Abort("hires_init_level must be greater than 0; use -1 to specify initial data at level 0");
2610 }
2611#ifdef REMORA_USE_PARTICLES
2613#endif
2614
2615 {
2616 ParmParse pp_amr("amr");
2617 pp_amr.queryAdd("regrid_int", regrid_int);
2618 }
2620
2621 // The biology IC source is chosen independently of ic_type, but only one of the two
2622 // mixed combinations works: NetCDF physics with analytic biology. The reverse has no
2623 // file to read from -- nc_init_file is only populated on the netcdf path -- so catch
2624 // it here instead of failing inside PnetCDF on an empty file name.
2628 amrex::Abort("remora.biology_ic_type = netcdf requires remora.ic_type = netcdf: the biology "
2629 "initial data is read from the same files as the physical initial data, and no "
2630 "such file is given for analytic initial conditions. Use "
2631 "remora.biology_ic_type = analytic (or follow) instead.");
2632 }
2633
2634#ifndef REMORA_USE_NETCDF
2636 amrex::Abort("Please compile with NetCDF in order to use remora.ic_type = netcdf");
2637 }
2638#else
2639 // Level 0 files are read only for the fields not supplied at hires_init_level or
2640 // hires_grid_level, so require each one only where it will be read.
2641 {
2642 const bool have_init_0 = !nc_init_file[0].empty() && !nc_init_file[0][0].empty();
2643 const bool have_grid_0 = !nc_grid_file[0].empty() && !nc_grid_file[0][0].empty();
2645 amrex::Abort("remora.ic_type = netcdf requires remora.nc_init_file_0 unless "
2646 "remora.hires_init_level is set");
2647 }
2648 if (hires_grid_level < 0 && !have_grid_0 &&
2650 amrex::Abort("remora.ic_type = netcdf or remora.mask_type = netcdf requires "
2651 "remora.nc_grid_file_0 unless remora.hires_grid_level is set");
2652 }
2654 !have_grid_0) {
2655 amrex::Abort("remora.coriolis_type = netcdf requires remora.nc_grid_file_0");
2656 }
2657 }
2658#endif
2659
2660 // init_full_domain_from_analytic builds x_r and y_r from the cell size.
2663 amrex::Abort("High-resolution initialization with analytic initial conditions requires "
2664 "remora.grid_scale_type = constant");
2665 }
2666
2667}
2668
2669
2670void
2672{
2673 BL_PROFILE("REMORA::AverageDown()");
2674 for (int lev = finest_level-1; lev >= 0; --lev)
2675 {
2677 }
2678}
2679
2680/**
2681 * Drop the cached average-down masks of every level pair that involves lev, so the next
2682 * AverageDownTo rebuilds them.
2683 *
2684 * Called from set_masks, which is the one place a level's mask is written, so a regrid cannot
2685 * leave a cached copy of a mask that no longer exists behind.
2686 *
2687 * @param[in ] lev level whose mask has just been rebuilt
2688 */
2689void
2691{
2692 // lev as the coarse half of a pair, and lev as the fine half, whose layout is what the
2693 // cached arrays are built on.
2694 for (int crse_lev : {lev-1, lev}) {
2695 if (crse_lev >= 0 && crse_lev < static_cast<int>(vec_mskr_crse_on_fine.size())) {
2699 }
2700 }
2701}
2702
2703/**
2704 * Measure whether the fine cell edges tile the coarse ones across a coarse-fine interface:
2705 * the sum of on_u over the r covering fine faces against on_u on the coarse face.
2706 *
2707 * Exact where pm and pn are uniform, and not guaranteed otherwise -- on the NetCDF path a
2708 * finer level interpolates its metrics from the parent's and rescales them, which need not
2709 * preserve a sum. set_2d_cf_bcs cannot impose a conservative flux without this identity.
2710 *
2711 * @param[in ] crse_lev coarse side of the interface
2712 */
2713void
2715{
2716 const int rrx = ref_ratio[crse_lev][0];
2717 const int rry = ref_ratio[crse_lev][1];
2718
2719 auto edge_residual = [&] (int dir, int ratio) -> Real
2720 {
2721 const IntVect ixt = (dir == 0) ? IntVect(1,0,0) : IntVect(0,1,0);
2722 const MultiFab& metric_f = (dir == 0) ? *vec_pn[crse_lev+1] : *vec_pm[crse_lev+1];
2723 const MultiFab& metric_c = (dir == 0) ? *vec_pn[crse_lev ] : *vec_pm[crse_lev ];
2724
2725 auto edge_lengths = [&] (const MultiFab& metric, int lev, MultiFab& out)
2726 {
2727 // pm and pn already live on the z-flattened BoxArray.
2728 BoxArray ba = convert(metric.boxArray(), ixt);
2729 out.define(ba, metric.DistributionMap(), 1, 0);
2730 for (MFIter mfi(out, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
2731 Array4<Real > const& e = out.array(mfi);
2732 Array4<Real const> const& m = metric.const_array(mfi);
2733 const int d = dir;
2734 ParallelFor(mfi.tilebox(), [=] AMREX_GPU_DEVICE (int i, int j, int)
2735 {
2736 const int im = (d == 0) ? i-1 : i;
2737 const int jm = (d == 0) ? j : j-1;
2738 e(i,j,0) = two / (m(i,j,0) + m(im,jm,0));
2739 });
2740 }
2741 amrex::ignore_unused(lev);
2742 };
2743
2744 MultiFab edge_f, edge_c;
2747
2748 // average_down_faces leaves faces the finer level does not cover untouched. Seeding
2749 // with the coarse value over the ratio makes those contribute exactly zero below,
2750 // rather than whatever the allocation happened to hold.
2751 MultiFab avg(edge_c.boxArray(), edge_c.DistributionMap(), 1, 0);
2752 MultiFab::Copy(avg, edge_c, 0, 0, 1, 0);
2753 avg.mult(one / Real(ratio), 0, 1, 0);
2754
2756
2757 // avg is the mean over the covering fine faces, so ratio*avg is their sum.
2758 Real worst = zero;
2759 for (MFIter mfi(avg, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
2760 Array4<Real const> const& a = avg.const_array(mfi);
2761 Array4<Real const> const& c = edge_c.const_array(mfi);
2764 reduce_op.eval(mfi.tilebox(), reduce_data,
2765 [=] AMREX_GPU_DEVICE (int i, int j, int k) -> GpuTuple<Real>
2766 {
2767 if (c(i,j,k) == zero) { return {zero}; }
2768 return {amrex::Math::abs(Real(ratio) * a(i,j,k) - c(i,j,k)) /
2769 amrex::Math::abs(c(i,j,k))};
2770 });
2771 worst = amrex::max(worst, amrex::get<0>(reduce_data.value(reduce_op)));
2772 }
2773 ParallelDescriptor::ReduceRealMax(worst);
2774 return worst;
2775 };
2776
2777 const Real res_u = edge_residual(0, rry);
2778 const Real res_v = edge_residual(1, rrx);
2779
2780 amrex::Print() << "CF edge tiling, levels " << crse_lev << "/" << crse_lev+1
2781 << ": max relative residual on_u " << res_u
2782 << ", om_v " << res_v << std::endl;
2783
2785 amrex::max(res_u, res_v) < check_cf_tol,
2786 "REMORA::check_cf_metrics: fine cell edges do not sum to the coarse edge across the "
2787 "coarse-fine interface, so the mass flux set_2d_cf_bcs imposes there cannot be "
2788 "conservative. Raise remora.check_cf_tol only if you know why the grid does this.");
2789}
2790
2791/**
2792 * Build the flux register holding the tracer flux mismatch between lev and lev-1. Called
2793 * whenever a level is created or its grids change, since one appearing mid-run through
2794 * tagging would otherwise have no register.
2795 *
2796 * @param[in ] lev level of refinement, > 0
2797 */
2798void
2800{
2801 if (lev == 0 || solverChoice.coupling_type != CouplingType::two_way) { return; }
2802
2803 // Take the layout from the MultiFabs rather than grids/dmap: when a level is first
2804 // created those have not been set on AmrCore yet, as the fill patchers here also assume.
2806 cons_new[lev-1]->boxArray(),
2809 geom[lev], geom[lev-1],
2810 ref_ratio[lev-1], lev, ncons));
2811
2812 // The constructor sizes the accumulators without zeroing them, and Reflux adds all of
2813 // m_crse_data to the state, so a register must be zero before its first use whichever
2814 // path built it.
2815 advflux_reg[lev]->reset();
2816}
2817
2818/**
2819 * Set how many steps each level takes per parent step, from remora.dt_ref_ratio.
2820 *
2821 * @param[in ] nlevs_max max_level + 1
2822 */
2823void
2825{
2826 nsubsteps.resize(nlevs_max, 1);
2827 if (!do_substep) { return; }
2828
2829 for (int lev = 1; lev <= max_level; ++lev) {
2831 }
2832
2833 // Defaults to the spatial ratio, but the two are independent in ROMS (RefineSteps
2834 // against RefineScale) and in ERF. One value for all levels or one per level.
2835 if (max_level > 0) {
2836 ParmParse pp("remora");
2837 int count = pp.countval("dt_ref_ratio");
2838 if (count > 0) {
2839 Vector<int> nsub(nlevs_max, 0);
2840 if (count == 1) {
2841 pp.queryarr("dt_ref_ratio", nsub, 0, 1);
2842 for (int lev = 1; lev <= max_level; ++lev) { nsubsteps[lev] = nsub[0]; }
2843 } else {
2844 pp.queryarr("dt_ref_ratio", nsub, 0, max_level);
2845 for (int lev = 1; lev <= max_level; ++lev) { nsubsteps[lev] = nsub[lev-1]; }
2846 }
2847 for (int lev = 1; lev <= max_level; ++lev) {
2849 "remora.dt_ref_ratio must be positive: it divides the parent timestep");
2850 }
2851 }
2852 }
2853}
2854
2855/**
2856 * Make sure the coarse rho-, u- and v-masks are defined on the layout average_down_masked
2857 * computes on: level crse_lev+1's grids coarsened, rather than level crse_lev's own grids.
2858 *
2859 * The masks are a function of position alone, so between regrids this is the same answer every
2860 * step; building it once turns three allocations and three ParallelCopy calls per step into
2861 * three per regrid.
2862 *
2863 * @param[in ] crse_lev coarse level of the pair
2864 */
2865void
2867{
2868 BL_PROFILE("REMORA::update_avgdown_masks()");
2869 const int flev = crse_lev + 1;
2870 const IntVect ratio = refRatio(crse_lev);
2871
2872 const BoxArray cba = amrex::coarsen(vec_mskr[flev]->boxArray(), ratio);
2873 const DistributionMapping& dmf = vec_mskr[flev]->DistributionMap();
2874
2875 // clear_avgdown_masks drops the cache whenever a mask is rewritten; this catches anything
2876 // that gets here without having gone through it, by rebuilding when the layout has moved.
2880 return;
2881 }
2882
2883 vec_mskr_crse_on_fine[crse_lev].reset(new MultiFab(cba, dmf, 1, 0));
2885 new MultiFab(amrex::convert(cba, IntVect(1,0,0)), dmf, 1, 0));
2887 new MultiFab(amrex::convert(cba, IntVect(0,1,0)), dmf, 1, 0));
2888
2889 vec_mskr_crse_on_fine[crse_lev]->ParallelCopy(*vec_mskr[crse_lev], 0, 0, 1);
2890 vec_msku_crse_on_fine[crse_lev]->ParallelCopy(*vec_msku[crse_lev], 0, 0, 1);
2891 vec_mskv_crse_on_fine[crse_lev]->ParallelCopy(*vec_mskv[crse_lev], 0, 0, 1);
2892}
2893
2894/**
2895 * @param[in ] crse_lev level to average down to
2896 */
2897
2898namespace {
2899/** \brief Replace each face by the mean of itself and its neighbours along dir.
2900 *
2901 * ROMS's fine2coarse2d averages the donor over a (Rscale-1)/2 half-width stencil in both
2902 * horizontal directions, so at ratio 3 a coarse face takes the mean of nine fine faces;
2903 * AMReX's average_down_faces takes only the three, all tangential, that tile it. Smoothing
2904 * along the face normal first supplies the missing direction and reproduces ROMS exactly.
2905 * Masked as ROMS does it: sum over wet points, divide by the wet count.
2906 *
2907 * Deliberately not conservative -- the nine-point mean does not preserve the transport through
2908 * the interface, where average_down_faces alone does. ROMS accepts that trade.
2909 */
2910void smooth_faces_along (amrex::MultiFab& mf, const amrex::MultiFab& msk, int dir,
2911 const amrex::Geometry& geom, int ncomp)
2912{
2913 mf.FillBoundary(geom.periodicity());
2914 amrex::MultiFab orig(mf.boxArray(), mf.DistributionMap(), ncomp, mf.nGrowVect());
2915 amrex::MultiFab::Copy(orig, mf, 0, 0, ncomp, mf.nGrowVect());
2916 const int di = (dir == 0) ? 1 : 0;
2917 const int dj = (dir == 1) ? 1 : 0;
2918 for (amrex::MFIter mfi(mf, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi) {
2919 const amrex::Box& bx = mfi.tilebox();
2920 const auto& a = mf.array(mfi);
2921 const auto& o = orig.const_array(mfi);
2922 const auto& m = msk.const_array(mfi);
2923 amrex::ParallelFor(bx, ncomp,
2924 [=] AMREX_GPU_DEVICE (int i, int j, int k, int n)
2925 {
2926 amrex::Real sum = amrex::Real(0.0), cnt = amrex::Real(0.0);
2927 for (int t = -1; t <= 1; ++t) {
2928 const int ii = i + t*di, jj = j + t*dj;
2929 const amrex::Real w = amrex::min(amrex::Real(1.0), m(ii,jj,0));
2930 sum += o(ii,jj,k,n) * m(ii,jj,0);
2931 cnt += w;
2932 }
2933 if (cnt > amrex::Real(0.0)) { a(i,j,k,n) = sum / cnt; }
2934 });
2935 }
2936}
2937} // namespace
2938
2939void
2941{
2942 BL_PROFILE("REMORA::AverageDownTo()");
2943 const int flev = crse_lev + 1;
2944
2945 // average_down_masked indexes the coarse mask with the same MFIter as its coarsened-fine
2946 // temporary, so the mask has to be defined on that layout. It is the same between regrids,
2947 // so this builds it once instead of every step.
2949 const MultiFab& cmskr = *vec_mskr_crse_on_fine[crse_lev];
2950 const MultiFab& cmsku = *vec_msku_crse_on_fine[crse_lev];
2951 const MultiFab& cmskv = *vec_mskv_crse_on_fine[crse_lev];
2952
2953 // Which mask goes with which field follows the ROMS fine2coarse call sites: rmask for the
2954 // free surface and the tracers, umask and vmask for the momenta.
2960 // ROMS returns every covered cell but not the perimeter's normal-velocity faces, so the
2961 // transport its nested boundary condition imposes there never comes back to the parent.
2962 // Without that exclusion set_2d_cf_bcs writes the parent's own flux onto the interface and
2963 // the average hands it straight back.
2964 MultiFab covered;
2965 MultiFab u_save, v_save;
2966 const bool keep_perimeter = (cf_avgdown_perimeter != 0);
2967 if (keep_perimeter) {
2970 1, 0, MFInfo());
2972 1, 0, MFInfo());
2973 MultiFab::Copy(u_save, *xvel_new[crse_lev], 0, 0, 1, 0);
2974 MultiFab::Copy(v_save, *yvel_new[crse_lev], 0, 0, 1, 0);
2975 }
2976
2977 // See smooth_faces_along: ROMS averages the donor over nine fine faces, AMReX over the
2978 // three that tile the coarse face, so the normal direction is smoothed in first.
2979 MultiFab xv_sm, yv_sm;
2980 const MultiFab* xv_f = xvel_new[flev];
2981 const MultiFab* yv_f = yvel_new[flev];
2982 if (cf_avgdown_stencil) {
2984 1, xvel_new[flev]->nGrowVect());
2985 MultiFab::Copy(xv_sm, *xvel_new[flev], 0, 0, 1, xvel_new[flev]->nGrowVect());
2987 xv_f = &xv_sm;
2988
2990 1, yvel_new[flev]->nGrowVect());
2991 MultiFab::Copy(yv_sm, *yvel_new[flev], 0, 0, 1, yvel_new[flev]->nGrowVect());
2993 yv_f = &yv_sm;
2994 }
2995
2997 *vec_msku[flev], cmsku, 1, 0);
2999 *vec_mskv[flev], cmskv, 1, 1);
3000
3001 if (keep_perimeter) {
3004 }
3006 *vec_mskr[flev], cmskr, 1, 2);
3007
3009
3010 // Hand the child's 2D momentum back, as ROMS's fine2coarse does. The parent's next
3011 // advance_2d reads ubar(krhs) to form DUon, so dropping this moves Dogbone's x-velocity
3012 // by 9%. Subcycling only: timeStepML does not hand ubar/vbar back.
3013 if (do_substep && cf_avgdown_bar) {
3014 // Components 0 and 1 only. The three are leapfrog slots rotating per level, so the
3015 // two levels need not agree on which holds what -- but update_massflux_3d has just
3016 // set both of these to the same velocity, so averaging them cannot mix time levels.
3017 MultiFab ub_save, vb_save;
3018 if (keep_perimeter) {
3019 ub_save.define(vec_ubar[crse_lev]->boxArray(),
3021 vb_save.define(vec_vbar[crse_lev]->boxArray(),
3023 MultiFab::Copy(ub_save, *vec_ubar[crse_lev], 0, 0, 2, 0);
3024 MultiFab::Copy(vb_save, *vec_vbar[crse_lev], 0, 0, 2, 0);
3025 }
3026
3027 MultiFab ub_sm, vb_sm;
3028 const MultiFab* ub_src = vec_ubar[crse_lev+1].get();
3029 const MultiFab* vb_src = vec_vbar[crse_lev+1].get();
3030 if (cf_avgdown_stencil) {
3032 2, vec_ubar[flev]->nGrowVect());
3033 MultiFab::Copy(ub_sm, *vec_ubar[flev], 0, 0, 2, vec_ubar[flev]->nGrowVect());
3035 ub_src = &ub_sm;
3036
3038 2, vec_vbar[flev]->nGrowVect());
3039 MultiFab::Copy(vb_sm, *vec_vbar[flev], 0, 0, 2, vec_vbar[flev]->nGrowVect());
3041 vb_src = &vb_sm;
3042 }
3043
3044 for (int icomp = 0; icomp < 2; ++icomp) {
3045 MultiFab ubar_f(*ub_src, make_alias, icomp, 1);
3046 MultiFab ubar_c(*vec_ubar[crse_lev ], make_alias, icomp, 1);
3048
3049 MultiFab vbar_f(*vb_src, make_alias, icomp, 1);
3050 MultiFab vbar_c(*vec_vbar[crse_lev ], make_alias, icomp, 1);
3052 }
3053
3054 if (keep_perimeter) {
3057 }
3058
3059 // The averages above rewrote this level's valid cells under the patch but not the
3060 // ghosts imaging them across a periodic seam, and nothing else refreshes those before
3061 // the next barotropic step: FillPatch refills one component, fill_ghost_kcomps runs on
3062 // fine levels only. Where the patch abuts a periodic boundary the two copies of the
3063 // coarse periodic u-face then read different vbar for the same physical cell -- freshly
3064 // averaged against stale -- and the asymmetry grows step by step. On Channel_Test with
3065 // a half-width patch on the seam: 3.4e-4 by step 20 and NaN by ~300 without this,
3066 // exactly zero with it, and an interior patch unaffected either way.
3067 vec_ubar[crse_lev]->FillBoundary(geom[crse_lev].periodicity());
3068 vec_vbar[crse_lev]->FillBoundary(geom[crse_lev].periodicity());
3069
3070 // zeta is deliberately absent: set_zeta_to_Ztavg overwrites all three of its
3071 // components from Zt_avg1 next step and stretch_transform reads Zt_avg1 anyway, so
3072 // averaging it here would write something nothing reads.
3073 }
3074
3076}
3077
3078/**
3079 * Coarse-level cell mask: 1 where level crse_lev+1 covers the cell, 0 elsewhere, with one grow
3080 * cell so a face straddling the patch edge can see the cell outside it.
3081 *
3082 * @param[in ] crse_lev coarse level
3083 * @param[out ] covered mask on the coarse layout
3084 */
3085void
3087{
3088 // makeFineMask's (crse_value, fine_value) = (0, 1) puts a 1 exactly on the covered cells.
3090 ref_ratio[crse_lev], 0, 1);
3091
3092 covered.define(grids[crse_lev], dmap[crse_lev], 1, 1, MFInfo());
3093 covered.setVal(zero); // grow cells outside the patch read as uncovered
3094 const auto cma = covered.arrays();
3095 const auto ima = imask.const_arrays();
3096 ParallelFor(covered, [=] AMREX_GPU_DEVICE (int bno, int i, int j, int k) noexcept
3097 {
3098 cma[bno](i,j,k) = Real(ima[bno](i,j,k));
3099 });
3100 Gpu::streamSynchronize();
3101 covered.FillBoundary(geom[crse_lev].periodicity());
3102}
3103
3104/**
3105 * Put back the values a fine-to-coarse average just wrote onto the fine patch's perimeter
3106 * normal-velocity faces.
3107 *
3108 * ROMS's fine2coarse hands back every covered cell but excludes those faces: for a child
3109 * spanning coarse cells I_lo..I_hi it updates u-faces I_lo+1..I_hi only, so the face the
3110 * nested barotropic boundary condition writes is never returned to the parent. A face is
3111 * interior exactly when both of the cells it separates are covered, which is the test used
3112 * here.
3113 *
3114 * @param[in ] saved coarse field as it was before the average
3115 * @param[inout] mf coarse field to repair
3116 * @param[in ] covered cell mask from build_covered_mask
3117 * @param[in ] dir face normal direction
3118 * @param[in ] ncomp components to repair
3119 */
3120void
3121REMORA::restore_perimeter_faces (const MultiFab& saved, MultiFab& mf,
3122 const MultiFab& covered, int dir, int ncomp)
3123{
3124 const IntVect off = IntVect::TheDimensionVector(dir);
3125 for (MFIter mfi(mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
3126 {
3127 const Box& bx = mfi.tilebox();
3128 Array4<Real > const& a = mf.array(mfi);
3129 Array4<Real const> const& o = saved.const_array(mfi);
3130 Array4<Real const> const& c = covered.const_array(mfi);
3131 ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
3132 {
3133 const bool interior = (c(i,j,0) > Real(0.5)) &&
3134 (c(i-off[0], j-off[1], 0) > Real(0.5));
3135 if (!interior) { a(i,j,k,n) = o(i,j,k,n); }
3136 });
3137 }
3138 Gpu::streamSynchronize();
3139}
3140
3141/**
3142 * Average one field from crse_lev+1 onto crse_lev, weighting by the land/sea mask.
3143 *
3144 * Follows amrex::average_down's non-MFIter-safe branch, since coarsen(grids[flev]) does not
3145 * match grids[crse_lev] in general: compute onto a temporary on the coarsened-fine layout,
3146 * then ParallelCopy that onto the coarse level.
3147 *
3148 * @param[in ] crse_lev level to average down to
3149 * @param[in ] S_fine fine-level field
3150 * @param[out ] S_crse coarse-level field
3151 * @param[in ] msk_fine fine-level mask, on S_fine's layout and nodality
3152 * @param[in ] cmsk coarse-level mask, already on the coarsened-fine layout
3153 * @param[in ] ncomp number of components to average
3154 * @param[in ] face_dir face direction, or -1 for a cell-centered field
3155 * @param[in ] volume_weighted also weight a cell-centered field on grids[crse_lev+1] by
3156 * fine cell volume, Hz/(pm*pn)
3157 */
3158void
3159REMORA::average_down_masked (int crse_lev, const MultiFab& S_fine, MultiFab& S_crse,
3160 const MultiFab& msk_fine, const MultiFab& cmsk,
3161 int ncomp, int face_dir, bool volume_weighted)
3162{
3163 BL_PROFILE("REMORA::average_down_masked()");
3164 const IntVect ratio = refRatio(crse_lev);
3165
3166 BoxArray cba = amrex::coarsen(S_fine.boxArray(), ratio);
3167 MultiFab ctmp(cba, S_fine.DistributionMap(), ncomp, 0);
3168
3169 // One MFIter indexes all four arrays in the loop below, by local box index, so the masks
3170 // have to be distributed exactly as S_fine is. Equal DistributionMappings imply equal box
3171 // counts, since a ProcessorMap holds one entry per box.
3172 AMREX_ALWAYS_ASSERT(msk_fine.DistributionMap() == S_fine.DistributionMap());
3173 AMREX_ALWAYS_ASSERT(cmsk.DistributionMap() == S_fine.DistributionMap());
3174
3175 // Volume weighting reads the fine level's Hz, pm and pn by the same index, so S_fine has
3176 // to be on that level's grids. The weight is the same for every component: form it once.
3177 const bool weighted = (face_dir < 0 && volume_weighted);
3178 MultiFab wt;
3179 if (weighted) {
3180 const int flev = crse_lev + 1;
3181 AMREX_ALWAYS_ASSERT(S_fine.boxArray() == vec_Hz[flev]->boxArray());
3182 AMREX_ALWAYS_ASSERT(S_fine.DistributionMap() == vec_Hz[flev]->DistributionMap());
3183 AMREX_ALWAYS_ASSERT(S_fine.DistributionMap() == vec_pm[flev]->DistributionMap());
3184 AMREX_ALWAYS_ASSERT(S_fine.DistributionMap() == vec_pn[flev]->DistributionMap());
3185 wt.define(S_fine.boxArray(), S_fine.DistributionMap(), 1, 0);
3186 for (MFIter mfi(wt, TilingIfNotGPU()); mfi.isValid(); ++mfi)
3187 {
3188 Array4< Real> const& w = wt.array(mfi);
3189 Array4<const Real> const& fm = msk_fine.const_array(mfi);
3190 Array4<const Real> const& Hz = vec_Hz[flev]->const_array(mfi);
3191 Array4<const Real> const& pm = vec_pm[flev]->const_array(mfi);
3192 Array4<const Real> const& pn = vec_pn[flev]->const_array(mfi);
3193 ParallelFor(mfi.tilebox(), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
3194 {
3195 w(i,j,k) = amrex::min(Real(1.0), fm(i,j,0)) * Hz(i,j,k)
3196 / (pm(i,j,0) * pn(i,j,0));
3197 });
3198 }
3199 }
3200
3201 for (MFIter mfi(ctmp, TilingIfNotGPU()); mfi.isValid(); ++mfi)
3202 {
3203 const Box& bx = mfi.tilebox();
3204 Array4< Real> const& c = ctmp.array(mfi);
3205 Array4<const Real> const& f = S_fine.const_array(mfi);
3206 Array4<const Real> const& fm = msk_fine.const_array(mfi);
3207 Array4<const Real> const& cm = cmsk.const_array(mfi);
3208
3209 if (weighted) {
3210 const REMORAMaskedAvgDown::FieldWeight w{wt.const_array(mfi)};
3211 ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
3212 {
3214 });
3215 } else if (face_dir < 0) {
3216 ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
3217 {
3219 });
3220 } else {
3221 ParallelFor(bx, ncomp, [=] AMREX_GPU_DEVICE (int i, int j, int k, int n) noexcept
3222 {
3224 });
3225 }
3226 }
3227 Gpu::streamSynchronize();
3228
3229 // Periodicity arguments as amrex::average_down and average_down_faces pass them: the
3230 // cell-centered copy takes none, the face copy needs it so shared faces across a periodic
3231 // boundary agree.
3232 if (face_dir < 0) {
3233 S_crse.ParallelCopy(ctmp, 0, 0, ncomp);
3234 } else {
3235 S_crse.ParallelCopy(ctmp, 0, 0, ncomp, IntVect(0), IntVect(0),
3237 }
3238}
3239
3240/**
3241 * Inject the full-domain rho-mask from fine_lev-1 up onto fine_lev, grow cells included.
3242 *
3243 * Piecewise constant, which is the rule set_masks already uses for a level above the one the
3244 * mask was specified on. Nothing finer is known there, so refining cannot add coastline.
3245 *
3246 * @param[in ] fine_lev level to inject onto
3247 */
3248void
3250{
3251 auto const& finema = vec_mskr_full_domain[fine_lev]->arrays();
3252 auto const& crsema = vec_mskr_full_domain[fine_lev-1]->const_arrays();
3253 auto ratio = refRatio(fine_lev-1);
3256 [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k, int n) noexcept
3257 {
3258 // amrex::coarsen floors rather than truncating, which is what the negative indices of
3259 // the grow cells need.
3260 finema[box_no](i,j,k,n) = crsema[box_no](amrex::coarsen(i, ratio[0]),
3261 amrex::coarsen(j, ratio[1]), k, n);
3262 });
3263 Gpu::streamSynchronize();
3264}
3265
3266/**
3267 * Make sure the full-domain rho-mask exists on levels 0 through top_lev.
3268 *
3269 * Only fills what the hires_grid_level coarsening has not: levels above it, by injection, and
3270 * level 0 itself when there is no high-resolution grid and the mask was given per level.
3271 *
3272 * @param[in ] top_lev highest level that needs a mask
3273 */
3274void
3276{
3277 if (solverChoice.mask_type == MaskType::none || top_lev <= 0) { return; }
3278
3279 // Levels up to hires_grid_level were coarsened down from it already.
3280 const int have = (hires_grid_level > 0) ? hires_grid_level : 0;
3281 if (top_lev <= have) { return; }
3282
3283 BoxArray ba;
3284 ba.define(makeSlab(geom[0].Domain(),2,0));
3285 const DistributionMapping& dm = full_domain_dmap();
3286 auto mskr_growvect = vec_mskr[0]->nGrowVect();
3287
3288 if (hires_grid_level < 0) {
3289 // No high-resolution grid, so the specification lives on level 0. Seed from it.
3290 vec_mskr_full_domain[0].reset(new MultiFab(ba, dm, 1, IntVect(1,1,0)));
3291 vec_mskr_full_domain[0]->setVal(one);
3292 ParallelCopy(*vec_mskr_full_domain[0].get(), *vec_mskr[0].get(), 0, 0, 1,
3294 }
3295
3296 for (int lev = 1; lev <= top_lev; lev++) {
3297 ba = ba.refine(refRatio(lev-1));
3298 if (lev <= have) { continue; }
3299 vec_mskr_full_domain[lev].reset(new MultiFab(ba, dm, 1,
3301 vec_mskr_full_domain[lev]->setVal(one);
3303 }
3304}
3305
3306namespace {
3307/**
3308 * Build a face-centered mask from a rho-point one, following ROMS set_masks.F:
3309 * msku = mskr(i-1,j)*mskr(i,j) and mskv = mskr(i,j-1)*mskr(i,j).
3310 *
3311 * Derived where it is needed rather than stored. Only the full-domain average-down wants
3312 * these, at most once per level during initialization, so a stored pair would be two more
3313 * arrays to keep in step with the rho mask for no measurable saving.
3314 */
3315void derive_face_mask (const MultiFab& mskr, MultiFab& mskf, int idir)
3316{
3317 const IntVect ndir = (idir == 0) ? IntVect(1,0,0) : IntVect(0,1,0);
3318 // One ring narrower than the rho mask in the normal direction, where the stencil reaches.
3319 const IntVect ng = max(mskr.nGrowVect() - ndir, IntVect(0));
3320 mskf.define(convert(mskr.boxArray(), ndir), mskr.DistributionMap(), 1, ng);
3321 mskf.setVal(one);
3322
3323 for (MFIter mfi(mskf, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
3324 const Box& bx = mfi.growntilebox();
3325 Array4< Real> const& mf = mskf.array(mfi);
3326 Array4<const Real> const& mr = mskr.const_array(mfi);
3327 const int l_idir = idir;
3328 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
3329 {
3330 mf(i,j,k) = (l_idir == 0) ? mr(i-1,j,0) * mr(i,j,0)
3331 : mr(i,j-1,0) * mr(i,j,0);
3332 });
3333 }
3334}
3335} // namespace
3336
3337/**
3338 * Average a full-domain field from crse_lev+1 onto crse_lev, grow cells included.
3339 *
3340 * With use_mask this takes REMORAMaskedAvgDown's mask-weighted mean instead of the plain one,
3341 * so the initial state on a coarse cell only partly covered by water comes from that water.
3342 * It is the formula AverageDownTo applies every step, so the initial and the running state
3343 * agree on what a land point holds -- unless cf_avgdown_volume_weighted is set, which this
3344 * cascade ignores for want of a full-domain Hz. A two-way run then averages down once before
3345 * its first step, so only the coarse cells outside the fine grids keep the area mean. Leave
3346 * use_mask off for grid metrics: a cell size is well defined under land, and masking pm/pn
3347 * would corrupt it.
3348 *
3349 * @param[in ] crse_lev level to average data down to
3350 * @param[inout] vec_mf vector over levels of multifabs containing data to average
3351 * @param[in ] use_mask weight by the land/sea mask rather than averaging every fine cell
3352 */
3353void
3355 bool use_mask)
3356{
3357 auto const& crsema = vec_mf[crse_lev]->arrays();
3358 auto const& finema = vec_mf[crse_lev+1]->const_arrays();
3360 auto index_type = (vec_mf[crse_lev]->boxArray().ixType()).toIntVect();
3361 auto nghost_crse = cum_ref_ratios[crse_lev] - index_type;
3362
3364 if (masked) {
3365 // One box per level on a matching DistributionMapping, so an MFIter over one array
3366 // indexes the other. Assert it rather than assume it.
3372
3373 const int idir = (index_type[0]==1) ? 0 : ((index_type[1]==1) ? 1 : -1);
3374 MultiFab fmsk, cmsk;
3375 if (idir >= 0) {
3378 }
3379 auto const& fmskma = (idir >= 0) ? fmsk.const_arrays()
3380 : vec_mskr_full_domain[crse_lev+1]->const_arrays();
3381 auto const& cmskma = (idir >= 0) ? cmsk.const_arrays()
3382 : vec_mskr_full_domain[crse_lev]->const_arrays();
3384 [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k, int n) noexcept
3385 {
3386 if (idir < 0) {
3389 } else {
3392 }
3393 });
3394 Gpu::streamSynchronize();
3395 return;
3396 }
3397
3398 if (index_type[0]==0 and index_type[1]==0) {
3400 [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k, int n) noexcept
3401 {
3403 });
3404 } else if (index_type[0]==1 and index_type[1]==0) {
3406 [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k, int n) noexcept
3407 {
3409 });
3410 } else if (index_type[0]==0 and index_type[1]==1) {
3412 [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int k, int n) noexcept
3413 {
3415 });
3416 } else {
3417 amrex::Abort("Unexpected nodality in average_down_with_grow_cells");
3418 }
3419 Gpu::streamSynchronize();
3420}
3421
3422/**
3423 * @param[in ] lev level at which to get time
3424 */
3425amrex::Real REMORA::get_t_old(int lev) const
3426{
3427 return t_old[lev];
3428}
constexpr amrex::Real two
constexpr amrex::Real bogus_large_value
constexpr amrex::Real one
constexpr amrex::Real fourth
constexpr amrex::Real zero
PlotfileType
plotfile format
#define NAT
#define Tracer_comp
mf_h setVal(geomdata.ProbHi(2))
AMREX_ALWAYS_ASSERT(!NSPeriodic||!EWPeriodic)
bool QueryNetCDFHasVars(const std::string &fname, const amrex::Vector< std::string > &var_names)
Helper function for testing whether a file carries every named variable.
std::unique_ptr< ProblemBase > amrex_probinit(const amrex_real *problo, const amrex_real *probhi) AMREX_ATTRIBUTE_WEAK
Function to init the physical bounds of the domain and instantiate a Problem derived from ProblemBase...
A class to hold and interpolate time series data read from a NetCDF file.
static PlotfileType plotfile_type
Native or NetCDF plotfile output.
Definition REMORA.H:2070
std::string nc_grid_file_hires
Grid file for high resolution bathymetry.
Definition REMORA.H:2082
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr_full_domain
Land/sea mask at cell centers on the whole domain at each potential level. Specified at hires_grid_le...
Definition REMORA.H:421
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_EminusP
evaporation minus precipitation [kg/m^2/s], defined at rho-points
Definition REMORA.H:521
ProbCoords prob_coords(int lev) const
Coordinate arrays of level lev, for the prob functions.
amrex::Vector< std::string > nc_riv_file
NetCDF river file(s)
Definition REMORA.H:2099
void set_grid_vars_averaged_down(int lev)
Set pm/pn by averaging down from higher-resolution grid.
Definition REMORA.cpp:828
std::string riv_time_varname
Name of time field for river time.
Definition REMORA.H:2123
int foextrap_periodic_bc() const noexcept
Definition REMORA.H:1492
amrex::Vector< std::string > nc_clim_his_file
NetCDF climatology history file(s)
Definition REMORA.H:2102
int ncons
Number of conserved scalars in the state (temperature + salt + passive scalars + biology tracers)
Definition REMORA.H:1839
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr_crse_on_fine
the level's rho-, u- and v-masks copied onto the layout of the level above it coarsened,...
Definition REMORA.H:598
double stop_time
Time to stop, in seconds on the model clock (not since start_time)
Definition REMORA.H:1816
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_zeta_full_domain
high resolution initial free surface height (2D)
Definition REMORA.H:581
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rv2d
v velocity RHS (2D, includes horizontal and vertical advection)
Definition REMORA.H:436
std::string nc_init_file_hires
Init file for high resolution.
Definition REMORA.H:2089
int biology_debug_i
Target column i index for biology_debug = 1.
Definition REMORA.H:1856
static amrex::Real fixed_dt
User specified fixed baroclinic time step.
Definition REMORA.H:1866
amrex::Real last_plot_file_time
Simulation time when we last output a plotfile.
Definition REMORA.H:1801
int zvel_bc() const noexcept
Definition REMORA.H:1487
static bool plot_staggered_vels
Whether to write the staggered velocities (not averaged to cell centers)
Definition REMORA.H:2064
void init_bathymetry_from_netcdf(int lev)
Bathymetry data initialization from NetCDF file.
void init_bcs()
Read in boundary parameters from input file and set up data structures.
int xvel_bc() const noexcept
Definition REMORA.H:1485
void set_zeta_averaged_down(int lev)
Copy over zeta data that has been averaged down from high res.
Definition REMORA.cpp:902
std::unique_ptr< NCTimeSeries > qair_data_from_file
Data container for specific humidity read from file.
Definition REMORA.H:1632
static amrex::Real previousCPUTimeUsed
Accumulator variable for CPU time used thusfar.
Definition REMORA.H:2157
amrex::Vector< std::string > cons_names
Names of scalars for plotfile output.
Definition REMORA.H:2009
bool running_with_coupling_driver
True once REMORA has received forcing through the coupling driver.
Definition REMORA.H:526
amrex::Vector< std::unique_ptr< amrex::YAFluxRegister > > advflux_reg
array of flux registers for refluxing in multilevel
Definition REMORA.H:1757
std::unique_ptr< NCTimeSeries > sustr_data_from_file
Data container for u-component surface momentum flux read from file.
Definition REMORA.H:1622
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_fcor
coriolis factor (2D)
Definition REMORA.H:612
int cf_print_iface
print the imposed coarse-fine interface velocity at this baroclinic step (-1 = off)....
Definition REMORA.H:1951
void allocate_init_full_domain()
Allocate multifabs for storing full-domain high resolution initial data.
void init_gls_vmix(int lev, SolverChoice solver_choice)
Initialize GLS variables.
bool time_interp_flux
interpolate the parent's barotropic mass flux in time at the coarse-fine interface,...
Definition REMORA.H:1878
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xvel_full_domain
multilevel data container for high res initial x velocities (u in ROMS)
Definition REMORA.H:404
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_h
multilevel data container for current step's z velocities (largely unused; W stored separately)
Definition REMORA.H:413
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pm
horizontal scaling factor: 1 / dx (2D)
Definition REMORA.H:603
void set2DPlotVariables(const std::string &pp_plot_var_names_2d)
amrex::Vector< REMORAFillPatcher > FPr_v
Vector over levels of FillPatchers for v (3D)
Definition REMORA.H:1690
void init_biology_ic(int lev)
Initialize biology tracers from whichever source remora.biology_ic_type selects. Call after the physi...
void init_zeta_full_domain_from_netcdf()
Full-domain high res sea-surface height data initialization from NetCDF file.
amrex::Vector< amrex::MultiFab * > cons_new
multilevel data container for current step's scalar data: temperature, salinity, passive tracer
Definition REMORA.H:393
static bool write_history_file
Whether to output NetCDF files as a single history file with several time steps.
Definition REMORA.H:1619
void init_biology_ic_full_domain()
Full-domain counterpart of init_biology_ic, for the hires_init_level average-down path.
void stretch_transform(int lev)
Calculate vertical stretched coordinates.
std::unique_ptr< NCTimeSeries > rain_data_from_file
Data container for precipitation rate read from file.
Definition REMORA.H:1640
void define_flux_register(int lev)
build or rebuild the flux register between lev and lev-1
Definition REMORA.cpp:2799
void init_masks_full_domain_from_netcdf()
Full domain high-res mask data initialization from NetCDF file.
REMORABiology::BiologyModel biology_model
Active biology package.
Definition REMORA.H:1841
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_vwind
Wind in the v direction, defined at rho-points.
Definition REMORA.H:486
std::unique_ptr< ProblemBase > prob
Pointer to container of analytical functions for problem definition.
Definition REMORA.H:1723
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr
land/sea mask at cell centers (2D)
Definition REMORA.H:584
void check_mask_consistency()
Check every level pair's masks against each other, and every level's mask values, reporting according...
Definition REMORA.cpp:1154
void Construct_REMORAFillPatchers(int lev)
Construct FillPatchers.
Definition REMORA.cpp:586
void init_grid_vars_from_netcdf(int lev)
Grid variable initialization from NetCDF file.
static int sum_interval
Diagnostic sum output interval in number of steps.
Definition REMORA.H:2056
int history_count
Counter for which time index we are writing to in the netcdf history file.
Definition REMORA.H:1993
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rain
precipitation rate [kg/m^2/s]
Definition REMORA.H:515
int do_substep
Whether to substep fine levels in time.
Definition REMORA.H:1873
void Evolve()
Advance solution to final time.
Definition REMORA.cpp:272
void update_nodal_masks(int lev)
Rebuild the u-, v- and psi-point masks from vec_mskr and fill their ghosts.
std::string bdry_time_varname
Default name of time field for boundary data.
Definition REMORA.H:2107
amrex::Real plotfile_fill_value
fill value for masked arrays in amrex plotfiles
Definition REMORA.H:2023
void ReadCheckpointFile()
read checkpoint file from disk
int biology_debug
Biology diagnostic verbosity: 0 off, 1 target column, 2 all columns. See Source/Biology/Fortran/tag_m...
Definition REMORA.H:1854
bool chunk_history_file
Whether to split the netcdf history file into fixed-length chunks.
Definition REMORA.H:1989
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_sustr
Surface stress in the u direction.
Definition REMORA.H:479
amrex::Real get_t_old(int lev) const
Accessor method for t_old to expose to outside classes.
Definition REMORA.cpp:3425
int yvel_bc() const noexcept
Definition REMORA.H:1486
void build_covered_mask(int crse_lev, amrex::MultiFab &covered)
Average one field down a level, weighted by the land/sea mask.
Definition REMORA.cpp:3086
std::unique_ptr< NCTimeSeries > longwave_down_data_from_file
Data container for downward longwave radiation flux read from file.
Definition REMORA.H:1638
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_ru2d
u velocity RHS (2D, includes horizontal and vertical advection)
Definition REMORA.H:434
amrex::Vector< std::string > datalogname
Definition REMORA.H:2189
amrex::Vector< amrex::MultiFab * > zvel_new
multilevel data container for current step's z velocities (largely unused; W stored separately)
Definition REMORA.H:399
void reflux_to(int lev)
apply the lev/lev+1 tracer flux correction onto lev
Definition REMORA.cpp:391
void set_surface_state(int lev)
Initialize or calculate wind speed and other surface state vars from file or analytic.
Definition REMORA.cpp:1728
void check_cf_metrics(int crse_lev)
check that fine cell edges sum to the coarse edge across an interface
Definition REMORA.cpp:2714
void WriteAtIntermediateTime(int step, amrex::Real cur_time)
Write checkpoint and plotfiles at intermediate point of simulation, if needed.
Definition REMORA.cpp:361
double start_time
Time of the start of the simulation, in seconds on the model clock.
Definition REMORA.H:1822
void init_only(int lev, amrex::Real time)
Init (NOT restart or regrid)
Definition REMORA.cpp:1857
void init_set_vmix(int lev)
Initialize vertical mixing coefficients from file or analytic.
Definition REMORA.cpp:959
void average_down_masked(int crse_lev, const amrex::MultiFab &S_fine, amrex::MultiFab &S_crse, const amrex::MultiFab &msk_fine, const amrex::MultiFab &cmsk, int ncomp, int face_dir, bool volume_weighted=false)
Definition REMORA.cpp:3159
std::unique_ptr< NCTimeSeries > v_clim_data_from_file
Data container for v-velocity climatology data read from file.
Definition REMORA.H:1653
std::string clim_u_time_varname
Name of time field for u climatology data.
Definition REMORA.H:2116
void set_grid_scale(int lev)
Set pm and pn arrays and x/y coords on level lev.
void set_coriolis(int lev)
Initialize Coriolis factor from file or analytic.
Definition REMORA.cpp:930
int foextrap_bc() const noexcept
Definition REMORA.H:1493
amrex::Vector< REMORAFillPatcher > FPr_u
Vector over levels of FillPatchers for u (3D)
Definition REMORA.H:1688
void set_masks_averaged_down(int lev)
Copy this level's land-sea mask out of the full-domain mask that was coarsened down from hires_grid_l...
Definition REMORA.cpp:1100
amrex::Vector< amrex::Vector< amrex::Box > > boxes_at_level
the boxes specified at each level by tagging criteria
Definition REMORA.H:1730
amrex::Real check_plot_z_tol
abort when the coarse-fine mismatch at interior nodes exceeds this; negative (the default) only repor...
Definition REMORA.H:1969
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Hz
Width of cells in the vertical (z-) direction (3D, Hz in ROMS)
Definition REMORA.H:424
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Akt
Vertical diffusion coefficient (3D)
Definition REMORA.H:444
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_msku
land/sea mask at x-faces (2D)
Definition REMORA.H:586
std::unique_ptr< NCTimeSeriesRiver > river_source_transportbar
Data container for vertically integrated momentum transport in rivers.
Definition REMORA.H:1664
std::array< bool, AtmosState::NumTypes > driver_atmos_state_from_driver
provenance flags for driver-supplied atmospheric forcing lanes
Definition REMORA.H:524
void extrapolate_metric_to_physical_boundaries(amrex::MultiFab &mf, const amrex::Geometry &geom)
Extrapolate grid metrics to edge of MultiFab.
Definition REMORA.cpp:853
std::string clim_ubar_time_varname
Name of time field for ubar climatology data.
Definition REMORA.H:2112
std::unique_ptr< NCTimeSeries > u_clim_data_from_file
Data container for u-velocity climatology data read from file.
Definition REMORA.H:1651
std::string check_file
Checkpoint file prefix.
Definition REMORA.H:1982
static amrex::Real startCPUTime
Variable for CPU timing.
Definition REMORA.H:2155
int check_plot_z
check the nodal z a viewer rebuilds from the plotfile's Nu_nd set against z_phys_nd on each level,...
Definition REMORA.H:1965
int cf_flux_pc
distribute the parent's interface mass flux piecewise-constantly over the fine faces under each paren...
Definition REMORA.H:1930
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pm_full_domain
horizontal scaling factor: 1 / dx (2D) on whole domain
Definition REMORA.H:607
amrex::Vector< amrex::MultiFab * > xvel_old
multilevel data container for last step's x velocities (u in ROMS)
Definition REMORA.H:386
int cf_avgdown_perimeter
exclude the coarse-fine perimeter's normal-velocity faces from the fine-to-coarse average,...
Definition REMORA.H:1894
void ensure_full_domain_masks(int top_lev)
Make sure the full-domain rho-mask exists on levels 0 through top_lev.
Definition REMORA.cpp:3275
void init_data_from_netcdf(int lev)
Problem initialization from NetCDF file.
void init_masks_from_netcdf(int lev)
Mask data initialization from NetCDF file.
amrex::Vector< amrex::MultiFab * > yvel_new
multilevel data container for current step's y velocities (v in ROMS)
Definition REMORA.H:397
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_uwind
Wind in the u direction, defined at rho-points.
Definition REMORA.H:484
void refine_masks_with_grow_cells(int fine_lev)
Inject the full-domain rho-mask from fine_lev-1 onto fine_lev, piecewise constant,...
Definition REMORA.cpp:3249
static bool plot_nodal_data
Whether to write nodal data (Nu_nd) to plotfiles.
Definition REMORA.H:2067
int regrid_int
how often each level regrids the higher levels of refinement (after a level advances that many time s...
Definition REMORA.H:1972
amrex::Real check_int_time
Checkpoint output interval in seconds.
Definition REMORA.H:1986
DriverAtmosForcingMode driver_atmos_forcing_mode
Active atmosphere-to-ocean forcing contract on the most recent driver apply.
Definition REMORA.H:530
void init_scalar_metadata()
Build runtime scalar names after nscalar is known.
Definition REMORA.cpp:245
int zeta_bc() const noexcept
Definition REMORA.H:1490
void Define_REMORAFillPatchers(int lev)
Define FillPatchers.
Definition REMORA.cpp:642
amrex::Vector< amrex::IntVect > cum_ref_ratios
Cumulative refinement ratio between level 0 and level i.
Definition REMORA.H:2094
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_visc2_p
Harmonic viscosity defined on the psi points (corners of horizontal grid cells)
Definition REMORA.H:446
amrex::Real plot_int_time
Plotfile output interval in seconds.
Definition REMORA.H:1980
amrex::Vector< int > num_files_at_level
how many netcdf input files specified at each level
Definition REMORA.H:1728
amrex::Vector< REMORAFillPatcher > FPr_vbar
Vector over levels of FillPatchers for vbar (2D)
Definition REMORA.H:1698
void AverageDownTo(int crse_lev)
more flexible version of AverageDown() that lets you average down across multiple levels
Definition REMORA.cpp:2940
int steps_per_history_file
Time steps per netcdf history file. Must be > 0 if chunk_history_file.
Definition REMORA.H:1991
void post_timestep(int nstep, amrex::Real time, amrex::Real dt_lev)
Called after every level 0 timestep.
Definition REMORA.cpp:441
int max_step
maximum number of steps
Definition REMORA.H:1814
amrex::Vector< amrex::MultiFab * > zvel_old
multilevel data container for last step's z velocities (largely unused; W stored separately)
Definition REMORA.H:390
std::unique_ptr< NCTimeSeries > svstr_data_from_file
Data container for v-component surface momentum flux read from file.
Definition REMORA.H:1624
void coarsen_bathymetry_with_grow_cells(int crse_lev)
Coarsen the full-domain bathymetry from crse_lev+1 onto crse_lev, grow cells included,...
Definition REMORA.cpp:1379
amrex::Vector< std::string > nc_frc_file
NetCDF forcing file(s)
Definition REMORA.H:2097
amrex::Vector< int > num_boxes_at_level
how many boxes specified at each level by tagging criteria
Definition REMORA.H:1726
amrex::Vector< amrex::MultiFab * > xvel_new
multilevel data container for current step's x velocities (u in ROMS)
Definition REMORA.H:395
void refinement_criteria_setup()
Set refinement criteria.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskp
land/sea mask at cell corners (2D)
Definition REMORA.H:590
int Bio_comp
First cons component of the biology block, i.e. Tracer_comp + nscalar. The state is laid out as temp,...
Definition REMORA.H:1836
int last_check_file_step
Step when we last output a checkpoint file.
Definition REMORA.H:1804
int bdy_zeta() const noexcept
Definition REMORA.H:1503
void init_beta_plane_coriolis(int lev)
Calculate Coriolis parameters from beta plane parametrization.
std::string clim_vbar_time_varname
Name of time field for vbar climatology data.
Definition REMORA.H:2114
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskv
land/sea mask at y-faces (2D)
Definition REMORA.H:588
amrex::Vector< int > nsubsteps
How many substeps on each level?
Definition REMORA.H:1735
amrex::Vector< std::unique_ptr< REMORAPhysBCFunct > > physbcs
Vector (over level) of functors to apply physical boundary conditions.
Definition REMORA.H:1754
void ComputeDt()
a wrapper for estTimeStep()
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_msku_crse_on_fine
Definition REMORA.H:599
void fill_3d_masks(int lev)
Copy maskr to all z levels.
std::unique_ptr< NCTimeSeries > EminusP_data_from_file
Data container for evaporation minus precipitation read from file.
Definition REMORA.H:1644
void FillCoarsePatch(int lev, amrex::Real time, amrex::MultiFab *mf_fine, amrex::MultiFab *mf_crse, const int bccomp, const int bdy_var_type=BdyVars::null, const int icomp=0, const bool fill_all=true, const int n_not_fill=0, const int icomp_calc=0, const amrex::Real dt=zero, const amrex::MultiFab &mf_calc=amrex::MultiFab())
fill an entire multifab by interpolating from the coarser level
int plot_int
Plotfile output interval in iterations.
Definition REMORA.H:1978
std::unique_ptr< NCTimeSeries > cloud_data_from_file
Data container for cloud cover fraction read from file.
Definition REMORA.H:1642
amrex::YAFluxRegister * getAdvFluxReg(int lev)
flux register between lev and lev-1
Definition REMORA.H:1760
void inject_mask_outside_domain(int lev)
Give lev's rho-mask ghost cells beyond the domain the coarser level's values.
Definition REMORA.cpp:1060
int nbio
Number of biology tracers, set by the active biology model. Zero when no biology model is active.
Definition REMORA.H:1833
void WriteAtFinalTime()
Write checkpoint and plotfiles at end of simulation.
Definition REMORA.cpp:344
void InitData()
Initialize multilevel data.
Definition REMORA.cpp:466
void update_avgdown_masks(int crse_lev)
Make sure the coarse masks the average-down kernels read sit on the coarsened layout of level crse_le...
Definition REMORA.cpp:2866
void set3DPlotVariables(const std::string &pp_plot_var_names_3d)
amrex::Vector< int > istep
which step?
Definition REMORA.H:1733
void WriteCheckpointFile()
write checkpoint file to disk
std::string nc_clim_coeff_file
NetCDF climatology coefficient file.
Definition REMORA.H:2104
void setRecordDataInfo(int i, const std::string &filename)
Definition REMORA.H:2175
void set_analytic_vmix(int lev)
Set vertical mixing coefficients from analytic.
Definition REMORA.cpp:976
void coarsen_masks_with_grow_cells(int crse_lev)
Coarsen the full-domain rho-mask from crse_lev+1 onto crse_lev, grow cells included,...
Definition REMORA.cpp:1121
amrex::Vector< std::string > bdry_time_name_byvar
Name of time fields for boundary data.
Definition REMORA.H:2109
static int file_min_digits
Minimum number of digits in plotfile name or chunked history file.
Definition REMORA.H:2061
void init_riv_pos_from_netcdf(int lev)
static amrex::Vector< std::string > nc_bdry_file
NetCDF boundary data.
Definition REMORA.H:58
amrex::Vector< REMORAFillPatcher > FPr_Dubar
Vector over levels of FillPatchers for Dubar (2D)
Definition REMORA.H:1700
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_visc2_r
Harmonic viscosity defined on the rho points (centers)
Definition REMORA.H:448
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yvel_full_domain
multilevel data container for high res initial y velocities (v in ROMS)
Definition REMORA.H:406
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_svstr
Surface stress in the v direction.
Definition REMORA.H:481
std::unique_ptr< NCTimeSeries > srflx_data_from_file
Data container for shortwave radiation flux read from file.
Definition REMORA.H:1636
REMORA()
Definition REMORA.cpp:69
void set_zeta(int lev)
Initialize zeta from file or analytic.
Definition REMORA.cpp:715
static amrex::Real change_max
Fraction maximum change in subsequent time steps.
Definition REMORA.H:1864
int check_cf_metrics_flag
measure whether fine cell edges tile the coarse ones at a coarse-fine interface, which is what makes ...
Definition REMORA.H:1959
void init_zeta_from_netcdf(int lev)
Sea-surface height data initialization from NetCDF file.
void set_zeta_average(int lev)
Set Zt_avg1 to zeta.
void init_coriolis_from_netcdf(int lev)
Coriolis parameter data initialization from NetCDF file.
amrex::Vector< REMORAFillPatcher > FPr_Dvbar
Vector over levels of FillPatchers for Dvbar (2D)
Definition REMORA.H:1702
std::string pp_prefix
default prefix for input file parameters
Definition REMORA.H:381
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_h_full_domain
Bathymetry data on the whole domain at each potential level.
Definition REMORA.H:416
void set_bathymetry(int lev)
Initialize bathymetry from file or analytic.
Definition REMORA.cpp:754
amrex::Vector< amrex::MultiFab * > yvel_old
multilevel data container for last step's y velocities (v in ROMS)
Definition REMORA.H:388
void readTracersParams()
Read tracer and hydro particles parameters.
std::unique_ptr< NCTimeSeries > ubar_clim_data_from_file
Data container for ubar climatology data read from file.
Definition REMORA.H:1647
void init_full_domain_from_analytic()
Initialize high-resolution initial data, zeta included, from analytic functions.
void init_data_full_domain_from_netcdf()
High resolution roblem initialization from NetCDF file.
void init_ref_ratios()
Reject vertical refinement and accumulate the refinement ratios.
Definition REMORA.cpp:216
amrex::Vector< REMORAFillPatcher > FPr_c
Vector over levels of FillPatchers for scalars.
Definition REMORA.H:1686
int hires_init_level
Which level the high resolution initialization data is at.
Definition REMORA.H:2087
amrex::Vector< std::string > clim_cons_time_varname
Vector over cons components of the name of the time field for that tracer's climatology data.
Definition REMORA.H:2121
std::unique_ptr< NCTimeSeries > Tair_data_from_file
Data container for air temperature read from file.
Definition REMORA.H:1630
int cf_avgdown_bar
Definition REMORA.H:1911
int nscalar
Number of passive (dye) scalars carried in the state, beyond temperature and salinity....
Definition REMORA.H:1830
std::string clim_v_time_varname
Name of time field for v climatology data.
Definition REMORA.H:2118
amrex::Vector< REMORAFillPatcher > FPr_w
Vector over levels of FillPatchers for w.
Definition REMORA.H:1692
std::unique_ptr< NCTimeSeries > Uwind_data_from_file
Data container for u-direction wind read from file.
Definition REMORA.H:1626
std::unique_ptr< NCTimeSeries > Pair_data_from_file
Data container for air pressure read from file.
Definition REMORA.H:1634
int cf_fill_vel_after
re-fill the child's 3D velocity from the parent after the step, so the coarse-fine boundary carries t...
Definition REMORA.H:1902
amrex::Vector< amrex::Real > t_new
new time at each level, in seconds since start_time
Definition REMORA.H:1744
void init_stretch_coeffs()
initialize and calculate stretch coefficients
void init_bdry_from_netcdf(int lev)
Boundary data initialization from NetCDF file.
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:2017
void set_masks(int lev)
Initialize land-sea masks from file or analytic.
Definition REMORA.cpp:999
void set_zeta_to_Ztavg(int lev, bool apply_eminusp=true)
Set zeta components to be equal to time-averaged Zt_avg1.
bool driver_uses_two_way_coupling
Driver-level direction flag copied in before InitData.
Definition REMORA.H:528
amrex::Vector< amrex::Vector< std::unique_ptr< NCTimeSeriesBoundary > > > boundary_series
Vector over BdyVars of boundary series data containers.
Definition REMORA.H:1667
int cf_set_width
Width for fixing values at coarse-fine interface.
Definition REMORA.H:1683
int cf_avgdown_volume_weighted
average the fine tracers onto the coarse weighted by cell volume, Hz/(pm*pn), rather than by area as ...
Definition REMORA.H:1947
int biology_debug_j
Target column j index for biology_debug = 1.
Definition REMORA.H:1858
void ReadParameters()
read in some parameters from inputs file
Definition REMORA.cpp:2209
int do_reflux
correct the coarse tracer with the finer level's accumulated advective flux at their interface....
Definition REMORA.H:1889
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_ru
u velocity RHS (3D, includes horizontal and vertical advection)
Definition REMORA.H:430
void clear_avgdown_masks(int lev)
Drop the cached average-down masks of every level pair that involves lev.
Definition REMORA.cpp:2690
void sum_integrated_quantities(amrex::Real time)
Integrate conserved quantities for diagnostics.
static int total_nc_plot_file_step
Definition REMORA.H:1517
int cf_impose_flux
impose the parent's barotropic mass flux on DUon/DVom at the coarse-fine interface,...
Definition REMORA.H:1884
static amrex::Vector< amrex::Vector< std::string > > nc_grid_file
NetCDF grid file.
Definition REMORA.H:60
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_longwave_down
Downward longwave radiation.
Definition REMORA.H:499
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_zeta
free surface height (2D)
Definition REMORA.H:578
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_vbar
barotropic y velocity (2D)
Definition REMORA.H:576
void FillCoarsePatchPC(int lev, amrex::Real time, amrex::MultiFab *mf_fine, amrex::MultiFab *mf_crse, const int bccomp, const int bdy_var_type=BdyVars::null, const int icomp=0, const bool fill_all=true, const int n_not_fill=0, const int icomp_calc=0, const amrex::Real dt=zero, const amrex::MultiFab &mf_calc=amrex::MultiFab())
fill an entire multifab by interpolating from the coarser level using the piecewise constant interpol...
bool expand_plotvars_to_unif_rr
whether plotfile variables should be expanded to a uniform refinement ratio
Definition REMORA.H:2020
int plot_file_on_restart
Whether to output a plotfile on restart from checkpoint.
Definition REMORA.H:1808
void set_2darrays(int lev)
Set 2D momentum arrays from 3D momentum.
void init_analytic(int lev)
Initialize initial problem data from analytic functions.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_ubar
barotropic x velocity (2D)
Definition REMORA.H:574
void FillPatch(int lev, amrex::Real time, amrex::MultiFab &mf_to_be_filled, amrex::Vector< amrex::MultiFab * > const &mfs, const int bccomp, const int bdy_var_type=BdyVars::null, const int icomp=0, const bool fill_all=true, const bool fill_set=false, const int n_not_fill=0, const int icomp_calc=0, const amrex::Real dt=zero, const amrex::MultiFab &mf_calc=amrex::MultiFab(), amrex::Vector< amrex::MultiFab * > const &mfs_crse_old={}, amrex::Vector< amrex::MultiFab * > const &mfs_crse_new={})
Fill a new MultiFab by copying in phi from valid region and filling ghost cells.
void restore_perimeter_faces(const amrex::MultiFab &saved, amrex::MultiFab &mf, const amrex::MultiFab &covered, int dir, int ncomp)
put back the coarse normal-velocity faces on the fine patch's perimeter
Definition REMORA.cpp:3121
amrex::Vector< amrex::MultiFab * > cons_old
multilevel data container for last step's scalar data: temperature, salinity, passive tracer
Definition REMORA.H:384
std::string frc_time_varname
Name of time field for forcing data.
Definition REMORA.H:2125
amrex::Vector< REMORAFillPatcher > FPr_ubar
Vector over levels of FillPatchers for ubar (2D)
Definition REMORA.H:1696
bool is_it_time_for_action(int nstep, amrex::Real time, amrex::Real dt, int action_interval, amrex::Real action_per)
Decide if it is time to take an action.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_cons_full_domain
multilevel data container for high res initial data: temperature, salinity, passive tracer
Definition REMORA.H:402
std::unique_ptr< NCTimeSeries > Vwind_data_from_file
Data container for v-direction wind read from file.
Definition REMORA.H:1628
static constexpr bool DriverUsesStateForcing(DriverAtmosForcingMode mode) noexcept
Definition REMORA.H:100
static int ndtfast
User specified, number of barotropic steps per baroclinic step.
Definition REMORA.H:1868
void init_bathymetry_full_domain_from_netcdf()
Full domain high-res bathymetry data initialization from NetCDF file.
amrex::Vector< std::unique_ptr< NCTimeSeries > > cons_clim_data_from_file
Vector over cons components of climatology data read from file.
Definition REMORA.H:1657
void set_hmixcoef(int lev)
Initialize horizontal mixing coefficients.
Definition REMORA.cpp:1424
amrex::Vector< std::unique_ptr< NCTimeSeriesRiver > > river_source_cons
Vector of data containers for scalar data in rivers.
Definition REMORA.H:1660
void timeStep(int lev, amrex::Real time, int iteration)
advance a level by dt, includes a recursive call for finer levels
std::unique_ptr< NCTimeSeriesRiver > river_source_transport
Data container for momentum transport in rivers.
Definition REMORA.H:1662
int cf_time_interp_zeta
interpolate the parent in time onto the child's own sub-time, as put_refine2d does,...
Definition REMORA.H:1924
void init_grid_vars_full_domain_from_netcdf()
Full domain high-res grid variable initialization from NetCDF file.
void compute_wvel(int lev, amrex::MultiFab &mf_w)
"True" vertical velocity w (m/s) at w-points, as ROMS wvelocity
void AverageDown()
set covered coarse cells to be the average of overlying fine cells
Definition REMORA.cpp:2671
amrex::Real netcdf_fill_value
fill value for masked arrays in netcdf output
Definition REMORA.H:2025
void set_nsubsteps(int nlevs_max)
size nsubsteps and set how many steps each level takes per parent step
Definition REMORA.cpp:2824
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pn_full_domain
horizontal scaling factor: 1 / dy (2D) on whole domain
Definition REMORA.H:609
void timeStepML(amrex::Real time, int iteration)
advance all levels by dt, loops over finer levels
void print_timestep_hierarchy() const
report the per-level slow/fast timestep hierarchy; call after ComputeDt
const amrex::DistributionMapping & full_domain_dmap()
The shared full-domain DistributionMapping, built on first use.
int cf_avgdown_stencil
average the fine velocity onto the coarse over ROMS's nine-point stencil (fine2coarse2d,...
Definition REMORA.H:1939
int cf_set_2d_bcs
0 = never impose the 2D coarse-fine interface condition, 1 = every fast step, 2 = only the first fast...
Definition REMORA.H:1910
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pn
horizontal scaling factor: 1 / dy (2D)
Definition REMORA.H:605
amrex::Vector< std::unique_ptr< std::fstream > > datalog
Definition REMORA.H:2188
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Akv
Vertical viscosity coefficient (3D)
Definition REMORA.H:442
static amrex::Real cfl
CFL condition.
Definition REMORA.H:1862
void init_masks_full_domain_from_analytic()
Full domain land-sea mask initialization from analytic.
void append3DPlotVariables(const std::string &pp_plot_var_names_3d)
REMORABiology::FennelParameters fennel_params
Runtime parameters for the Fennel biology package.
Definition REMORA.H:1843
void allocate_bathymetry_grid_vars_full_domain()
Allocate multifabs for storing full-domain bathymetry and grid vars data.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskv_crse_on_fine
Definition REMORA.H:600
amrex::Real elapsed_time(double time) const noexcept
Elapsed time since start_time of a time on the model clock.
Definition REMORA.H:2216
void set_init_data_averaged_down(int lev)
Problem initialization from averaged-down high resolution data.
Definition REMORA.cpp:913
static int verbose
Verbosity level of output.
Definition REMORA.H:2053
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_cloud
cloud cover fraction [0-1], defined at rho-points
Definition REMORA.H:519
ParticleData particleData
Particle container with all particle species.
Definition REMORA.H:2030
std::string plot_file_name
Plotfile prefix.
Definition REMORA.H:1976
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Zt_avg1
Average of the free surface, zeta (2D)
Definition REMORA.H:476
int check_int
Checkpoint output interval in iterations.
Definition REMORA.H:1984
amrex::Real check_cf_tol
tolerance for the above
Definition REMORA.H:1961
void set_smflux(int lev)
Initialize or calculate surface momentum flux from file or analytic.
Definition REMORA.cpp:1709
void WritePlotFile(int istep)
main driver for writing AMReX plotfiles
std::string restart_chkfile
If set, restart from this checkpoint file.
Definition REMORA.H:1825
void average_down_with_grow_cells(int lev, amrex::Vector< std::unique_ptr< amrex::MultiFab > > &mf, bool use_mask=false)
Average down from level lev+1 to lev in mf, including grow cells.
Definition REMORA.cpp:3354
void init_clim_nudg_coeff(int lev)
Wrapper to initialize climatology nudging coefficient.
void init_bathymetry_full_domain_from_analytic()
Full domain bathymetry data initialization from analytic.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rv
v velocity RHS (3D, includes horizontal and vertical advection)
Definition REMORA.H:432
int cf_width
Nudging width at coarse-fine interface.
Definition REMORA.H:1681
static amrex::Vector< amrex::Vector< std::string > > nc_init_file
NetCDF initialization file.
Definition REMORA.H:59
int last_plot_file_step
Step when we last output a plotfile.
Definition REMORA.H:1799
int use_biology_cpp_answer
Select the native C++ biology kernel (1) or the ROMS Fortran bridge oracle (0). Only meaningful when ...
Definition REMORA.H:1851
amrex::Vector< amrex::Real > t_old
old time at each level, in seconds since start_time
Definition REMORA.H:1746
double model_time(amrex::Real elapsed) const noexcept
Time on the model clock, in seconds, of an elapsed time such as t_new.
Definition REMORA.H:2210
int cf_fill_all_kcomp
write every leapfrog record of the coarse-fine ghost band, as put_refine2d does when it sets zeta(:,...
Definition REMORA.H:1919
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_srflx
Shortwave radiation flux [W/m²], defined at rho-points.
Definition REMORA.H:495
amrex::Real last_check_file_time
Simulation time when we last output a checkpoint file.
Definition REMORA.H:1806
static amrex::Vector< REMORAErrorTag > ref_tags
Holds info for dynamically generated tagging criteria.
Definition REMORA.H:2134
void append2DPlotVariables(const std::string &pp_plot_var_names_2d)
void set_bathymetry_averaged_down(int lev)
Copy over bathymetry data that has been averaged down from high resolution input netcdf file.
Definition REMORA.cpp:813
amrex::Vector< amrex::Real > dt
time step at each level
Definition REMORA.H:1748
static amrex::Real sum_per
Diagnostic sum output interval in time.
Definition REMORA.H:2058
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Pair
Air pressure [mb], defined at rho-points.
Definition REMORA.H:492
virtual ~REMORA()
Definition REMORA.cpp:205
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_qair
Specific humidity [kg/kg], defined at rho-points.
Definition REMORA.H:490
int hires_grid_level
Which level the high resolution bathymetry is at.
Definition REMORA.H:2080
void restart()
Definition REMORA.cpp:698
int reflux_clamp
stop that correction driving a tracer negative, as ROMS does. On by default to match ROMS; costs exac...
Definition REMORA.H:1955
std::unique_ptr< NCTimeSeries > vbar_clim_data_from_file
Data container for vbar climatology data read from file.
Definition REMORA.H:1649
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Tair
Air temperature [°C], defined at rho-points.
Definition REMORA.H:488
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_diff2
Harmonic diffusivity for temperature / salinity.
Definition REMORA.H:450
REMORABiology::BiologyICType biology_ic_type
Source of the biology tracer initial condition, independent of remora.ic_type. Default follows ic_typ...
Definition REMORA.H:1846
@ Pair
atmospheric pressure [Pa from driver, mb in REMORA]
@ Vwind
10-m meridional wind [m/s]
@ Qair
specific humidity [kg/kg]
@ SWrad
downward shortwave radiation [W/m^2]
@ LWrad
downward longwave radiation [W/m^2]
@ Uwind
10-m zonal wind [m/s]
@ Rain
precipitation rate [kg/m^2/s]
@ Cloud
cloud fraction [0-1]
@ Tair
air temperature [K from driver, degC in REMORA]
static constexpr int cons_bc
static constexpr int Temp_bc_comp
static constexpr int t
cons component Temp_comp
static constexpr int u
int NumTypes(int ncons) noexcept
static constexpr int v
int vbar(int ncons) noexcept
int cons(int icomp) noexcept
static constexpr int null
int zeta(int ncons) noexcept
int ubar(int ncons) noexcept
@ Vwind
10-m meridional wind [m/s]
@ Pair
atmospheric pressure [mb]
@ Uwind
10-m zonal wind [m/s]
@ LWrad
longwave radiation [W/m^2]
@ Tair
air temperature [degC]
@ Qair
specific humidity or relative humidity [kg/kg or fraction]
@ Cloud
cloud fraction [0-1]
@ SWrad
downward shortwave radiation [W/m^2]
@ Rain
precipitation rate [kg/m^2/s]
@ EminusP
evaporation minus precipitation [m/s]
Vector< std::string > tracer_names(BiologyModel model, FennelParameters const &fennel_parameters)
std::string biology_ic_type_name(BiologyICType type)
bool has_biology(BiologyModel model) noexcept
BiologyICType parse_biology_ic_type(const std::string &name)
BiologyModel parse_biology_model(const std::string &name)
std::string biology_model_name(BiologyModel model)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void avgdown_faces_masked(int i, int j, int k, int n, amrex::Array4< amrex::Real > const &crse, amrex::Array4< amrex::Real const > const &fine, amrex::Array4< amrex::Real const > const &fmsk, amrex::Array4< amrex::Real const > const &cmsk, int ccomp, int fcomp, amrex::IntVect const &ratio, int idir) noexcept
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void avgdown_masked(int i, int j, int k, int n, amrex::Array4< amrex::Real > const &crse, amrex::Array4< amrex::Real const > const &fine, amrex::Array4< amrex::Real const > const &fmsk, amrex::Array4< amrex::Real const > const &cmsk, int ccomp, int fcomp, amrex::IntVect const &ratio) noexcept
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void avgdown_weighted(int i, int j, int k, int n, amrex::Array4< amrex::Real > const &crse, amrex::Array4< amrex::Real const > const &fine, Weight const &weight, amrex::Array4< amrex::Real const > const &cmsk, int ccomp, int fcomp, amrex::IntVect const &ratio) noexcept
const char * buildInfoGetGitHash(int i)
void init_params(const std::string &remora_prefix)
amrex::Vector< amrex::Real > Akt_bak
HorizMixingType horiz_mixing_type
amrex::Real Akv_bak
amrex::Vector< amrex::Real > tnu2
std::string longwave_netcdf_varname
amrex::Vector< int > do_rivers_cons
ScaledToGridAMRScaling scaled_to_grid_amr_scaling
MaskConsistency mask_consistency
void init_params(int ncons, int nscalar, const amrex::Vector< std::string > &cons_names)
read in and initialize parameters
SMFluxType smflux_type
VertMixingType vert_mixing_type
std::array< BulkForcingType, BulkFlux::NumTypes > bulk_flux_type
GridScaleType grid_scale_type
amrex::Vector< int > do_cons_clim_nudg
CouplingType coupling_type