REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_land_layout.cpp
Go to the documentation of this file.
1/**
2 * \file REMORA_land_layout.cpp
3 *
4 * How a level's boxes are laid out over land: the wet-cell weights the distribution mapping
5 * can balance (remora.load_balance) and the all-land boxes level 0 can leave out
6 * (remora.drop_dry_boxes). Both need the land mask of a layout before the level on it exists,
7 * which probe_rho_mask provides.
8 */
9
10#include <REMORA.H>
11#include <REMORA_prob_common.H>
12
13#include <AMReX_ParallelReduce.H>
14
15#include <algorithm>
16#include <limits>
17#include <map>
18#include <vector>
19
20using namespace amrex;
21
22namespace {
23BoxArray slab_boxes (const BoxArray& ba)
24{
25 BoxList bl2d = ba.boxList();
26 for (auto& b : bl2d) {
27 b.setRange(2,0);
28 }
29 return BoxArray(std::move(bl2d));
30}
31}
32
33/**
34 * Fill mskr, a rho-point mask on any layout of level lev, with the mask set_masks would give
35 * a level on that layout, before such a level exists. On level 0 every ghost cell the level's
36 * own mask carries is set, as set_masks sets it; on a finer level only the valid cells are,
37 * from the parent's mask (piecewise constant, as set_masks injects it), which is all a weight
38 * needs.
39 *
40 * @param[in ] lev level the layout belongs to
41 * @param[inout] mskr mask to fill, on 2D slab boxes, initially anything
42 * @return false when the mask cannot be known before the level is made: level 0 with
43 * remora.hires_grid_level >= 0 at initialization, whose mask is coarsened from
44 * full-domain data that init_only builds
45 */
46bool
47REMORA::probe_rho_mask (int lev, MultiFab& mskr)
48{
49 mskr.setVal(one);
51 return true;
52 }
53
54 if (lev <= hires_grid_level) {
55 if (lev >= vec_mskr_full_domain.size() || !vec_mskr_full_domain[lev]) {
56 return false;
57 }
58 // As set_masks_averaged_down
59 mskr.ParallelCopy(*vec_mskr_full_domain[lev], 0, 0, 1,
60 vec_mskr_full_domain[lev]->nGrowVect(), mskr.nGrowVect());
61 mskr.FillBoundary(geom[lev].periodicity());
62 return true;
63 }
64
65 if (lev == 0) {
67 prob->init_analytic_masks(lev, geom[lev], solverChoice, *this, mskr);
68 mskr.FillBoundary(geom[lev].periodicity());
69 return true;
70 }
71#ifdef REMORA_USE_NETCDF
73 return true;
74#else
75 return false;
76#endif
77 }
78
79 // A finer level's valid cells take the parent's mask, piecewise constant
80 AMREX_ALWAYS_ASSERT(lev-1 < vec_mskr.size() && vec_mskr[lev-1]);
81 const IntVect rr = refRatio(lev-1);
82 MultiFab ctmp(amrex::coarsen(mskr.boxArray(), rr), mskr.DistributionMap(), 1, 0);
83 ctmp.setVal(one);
84 ctmp.ParallelCopy(*vec_mskr[lev-1], 0, 0, 1);
85 const int rx = rr[0], ry = rr[1];
86#ifdef AMREX_USE_OMP
87#pragma omp parallel if (Gpu::notInLaunchRegion())
88#endif
89 for (MFIter mfi(mskr, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
90 Array4< Real> const& f = mskr.array(mfi);
91 Array4<const Real> const& c = ctmp.const_array(mfi);
92 ParallelFor(mfi.tilebox(), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
93 {
94 f(i,j,k) = c(amrex::coarsen(i, rx), amrex::coarsen(j, ry), 0);
95 });
96 }
97 Gpu::streamSynchronize();
98 return true;
99}
100
101/**
102 * Wet rho cells in each box of a prospective layout of level lev: the box's wet columns times
103 * its vertical extent. The same on every rank.
104 *
105 * @param[in ] lev level the layout belongs to
106 * @param[in ] ba the layout's (3D) boxes
107 * @param[ out] wet wet cells per box of ba
108 * @return false when the mask cannot be known yet (see probe_rho_mask)
109 */
110bool
111REMORA::wet_cells_per_box (int lev, const BoxArray& ba, Vector<Long>& wet)
112{
113 const BoxArray ba2d = slab_boxes(ba);
114 MultiFab mskr(ba2d, DistributionMapping(ba2d), 1, IntVect(NGROW+1,NGROW+1,0));
115 if (!probe_rho_mask(lev, mskr)) { return false; }
116
117 wet.assign(ba.size(), 0);
118#ifdef AMREX_USE_OMP
119#pragma omp parallel if (Gpu::notInLaunchRegion())
120#endif
121 for (MFIter mfi(mskr); mfi.isValid(); ++mfi) {
122 // The mask is exactly 0 or 1, so the sum counts wet columns exactly
123 const Real ncol = mskr[mfi].sum<RunOn::Device>(mfi.validbox(), 0);
124 wet[mfi.index()] = static_cast<Long>(ncol) * ba[mfi.index()].length(2);
125 }
126 ParallelAllReduce::Sum(wet.data(), static_cast<int>(wet.size()),
127 ParallelContext::CommunicatorSub());
128 return true;
129}
130
131/**
132 * The distribution mapping of a new BoxArray on level lev (overrides AmrMesh). Called by AMReX
133 * for every new layout -- initialization, regrid, a level made from coarse -- and by the
134 * checkpoint reader. remora.load_balance picks the strategy; each box weighs its wet cells
135 * plus remora.load_balance_land_weight times its land cells. Where every box weighs its cell
136 * count (no land, or a land weight of one) the SFC strategy is AMReX's own mapping, exactly as
137 * remora.load_balance = amrex gives it. Ownership never changes the answer.
138 *
139 * @param[in ] lev level of the BoxArray
140 * @param[in ] ba the BoxArray to distribute
141 */
142DistributionMapping
143REMORA::MakeDistributionMap (int lev, BoxArray const& ba)
144{
145 BL_PROFILE("REMORA::MakeDistributionMap()");
147 return AmrCore::MakeDistributionMap(lev, ba);
148 }
149
150 // A land cell weighing as much as a wet one makes every weight the cell count, so the mask
151 // is not needed (nor read from a grid file again).
152 Vector<Long> wet;
153 const bool known = (load_balance_land_weight != one) && wet_cells_per_box(lev, ba, wet);
154
155 // Weights in units of 1/scale of a wet cell, so a fractional land weight keeps its value
156 // to 1/scale (2^20, about 1e-6); a box of 1e7 cells weighs 1e13 at most, far inside Long.
157 constexpr Long scale = Long(1) << 20;
158 const Long land_w = std::llround(Real(scale) * load_balance_land_weight);
159 std::vector<Long> wgts(ba.size());
160 Long total_cells = 0, total_wet = 0;
161 bool cell_count_weights = true;
162 for (int b = 0; b < ba.size(); ++b) {
163 const Long npts = ba[b].numPts();
164 const Long nwet = known ? wet[b] : npts;
165 wgts[b] = scale * nwet + land_w * (npts - nwet);
166 cell_count_weights = cell_count_weights && (wgts[b] == scale * npts);
167 total_cells += npts;
168 total_wet += nwet;
169 }
170
171 if (!known && load_balance_land_weight != one) {
172 amrex::Print() << "Level " << lev << ": the land mask is not known before the level is"
173 << " made (remora.hires_grid_level >= " << lev << "), so its boxes are"
174 << " weighted by cell count for remora.load_balance.\n";
175 }
176
177 if (cell_count_weights && load_balance == LoadBalance::sfc) {
178 return AmrCore::MakeDistributionMap(lev, ba);
179 }
180
181 const int nprocs = ParallelContext::NProcsSub();
182 DistributionMapping dm;
184 dm.SFCProcessorMap(ba, wgts, nprocs);
185 } else if (load_balance == LoadBalance::knapsack) {
186 dm.KnapSackProcessorMap(wgts, nprocs);
187 } else {
188 dm.RoundRobinProcessorMap(wgts, nprocs);
189 }
190
191 // How even the result is, by the weights balanced and by cell count
192 std::vector<Long> rank_w(nprocs, 0), rank_c(nprocs, 0), rank_n(nprocs, 0);
193 for (int b = 0; b < ba.size(); ++b) {
194 const int r = ParallelContext::global_to_local_rank(dm[b]);
195 rank_w[r] += wgts[b];
196 rank_c[r] += ba[b].numPts();
197 rank_n[r] += 1;
198 }
199 auto max_over_mean = [nprocs] (const std::vector<Long>& v) {
200 Long tot = 0, mx = 0;
201 for (auto x : v) { tot += x; mx = std::max(mx, x); }
202 return (tot > 0) ? Real(mx) * nprocs / Real(tot) : one;
203 };
204 amrex::Print() << "Level " << lev << " load balance (" << load_balance_name()
205 << ", land weight " << load_balance_land_weight << "): " << ba.size()
206 << " boxes, " << total_wet << " of " << total_cells
207 << (known ? " cells wet;" : " cells (mask unknown, all weighed);")
208 << " max/mean rank load " << max_over_mean(rank_w)
209 << " by weight, " << max_over_mean(rank_c) << " by cell count; boxes per"
210 << " rank " << *std::min_element(rank_n.begin(), rank_n.end()) << " to "
211 << *std::max_element(rank_n.begin(), rank_n.end()) << "\n";
212 return dm;
213}
214
215/**
216 * @return the remora.load_balance spelling of the current strategy
217 */
218std::string
220{
221 switch (load_balance) {
222 case LoadBalance::amrex: return "amrex";
223 case LoadBalance::sfc: return "sfc";
224 case LoadBalance::knapsack: return "knapsack";
225 case LoadBalance::roundrobin: return "roundrobin";
226 }
227 return "unknown";
228}
229
230/**
231 * The level-0 cells whose boxes a finer level needs kept: every refinement indicator's region,
232 * in level-0 index space, as AMReX's grid generation sees it. AMReX buffers the tagged cells by
233 * amr.n_error_buf, rounds them to blocks of amr.blocking_factor (of level 1) over the refinement
234 * ratio, and clears every tag within amr.n_proper such blocks of a cell no level-0 box covers.
235 * So a dropped box that close to an indicator would clip the fine grids the full-domain run
236 * makes. The region is therefore the indicator's cells plus the buffer, rounded out to whole
237 * blocks, grown by n_proper + 1 blocks, and then by the coarse stencil of the coarse-fine fills
238 * and the flux register. A box touching it is never dropped, so the fine grids, and the coarse
239 * data under and around them, are those of the full-domain run. Level 1 only: drop_dry_level0_boxes
240 * drops nothing when amr.max_level > 1, since a level-2 indicator grows level 1 by its own
241 * buffer and proper nesting, which this does not follow.
242 *
243 * @param[ out] unbounded true when an indicator has no region, so a finer level may go anywhere
244 */
245BoxList
246REMORA::level0_refine_footprint (bool& unbounded) const
247{
248 BoxList bl;
249 unbounded = false;
250 if (max_level < 1 || ref_tags.empty()) { return bl; }
251 unbounded = refine_region_unbounded;
252 if (unbounded) { return bl; }
253
254 const IntVect rr = ref_ratio[0];
255 IntVect bf(1), stencil(0);
256 for (int d = 0; d < 2; ++d) {
257 bf[d] = std::max(1, blocking_factor[1][d] / rr[d]);
258 stencil[d] = (NGROW + 2 + rr[d] - 1) / rr[d] + 1;
259 }
260 const Box& dom = geom[0].Domain();
261 const Real* plo = geom[0].ProbLo();
262 const Real* dx = geom[0].CellSize();
263 for (const auto& rb : refine_regions) {
264 IntVect lo = dom.smallEnd(), hi = dom.bigEnd();
265 for (int d = 0; d < 2; ++d) {
266 lo[d] = static_cast<int>(std::floor((rb.lo(d) - plo[d]) / dx[d])) - n_error_buf[0][d];
267 hi[d] = static_cast<int>(std::floor((rb.hi(d) - plo[d]) / dx[d])) + n_error_buf[0][d];
268 }
269 Box b(lo, hi);
270 // Whole blocks, then n_proper + 1 more of them, then the coarse stencil
271 b.coarsen(bf);
272 b.grow(IntVect(n_proper + 1, n_proper + 1, 0));
273 b.refine(bf);
274 b.grow(stencil);
275 b &= dom;
276 if (b.ok()) { bl.push_back(b); }
277 }
278 return bl;
279}
280
281/**
282 * The boxes of a level-0 BoxArray that remora.drop_dry_boxes keeps: every box with a wet rho
283 * cell in its footprint -- its valid cells and every ghost cell the mask carries, up to one
284 * cell beyond a non-periodic domain edge -- and every
285 * box a finer level may need (level0_refine_footprint). The rest are land over all of that, so
286 * no wet cell's stencil reaches them; partly wet boxes are kept whole.
287 *
288 * @param[in ] ba candidate level-0 BoxArray
289 * @return the kept boxes, in the order of ba; ba itself when nothing is dropped
290 */
291BoxArray
293{
294 BL_PROFILE("REMORA::drop_dry_level0_boxes()");
296 amrex::Print() << "remora.drop_dry_boxes: no land mask (remora.mask_type = none), so no"
297 << " box is dropped.\n";
298 return ba;
299 }
300
301#ifdef REMORA_USE_PARTICLES
302 constexpr bool with_particles = true;
303#else
304 constexpr bool with_particles = false;
305#endif
306#ifdef REMORA_USE_MOAB
307 constexpr bool with_moab = true;
308#else
309 constexpr bool with_moab = false;
310#endif
311 if (with_particles) {
312 // Tracer particles are seeded over the whole domain, and a particle in a dropped box
313 // would belong to no box. Untested, so not allowed.
314 amrex::Print() << "remora.drop_dry_boxes: built with particles, so no level-0 box is"
315 << " dropped.\n";
316 return ba;
317 }
318 if (with_moab) {
319 // InitMOABMesh builds its mesh from level 0 and takes it to cover the domain
320 amrex::Print() << "remora.drop_dry_boxes: built with MOAB, so no level-0 box is dropped.\n";
321 return ba;
322 }
323 if (max_level > 1) {
324 // A level-2 indicator makes level 1 grow by its own buffer and proper nesting, and
325 // AMReX would clear the level-1 tags near a hole without a word; the footprint below
326 // covers level 1 only.
327 amrex::Print() << "remora.drop_dry_boxes: amr.max_level > 1, so no level-0 box is"
328 << " dropped.\n";
329 return ba;
330 }
332 // The driver builds its remap from level 0's layout, which it takes to cover the domain
333 amrex::Print() << "remora.drop_dry_boxes: coupled to an atmosphere driver, so no level-0"
334 << " box is dropped.\n";
335 return ba;
336 }
337
338 bool unbounded = false;
339 const BoxList protect = level0_refine_footprint(unbounded);
340 if (unbounded) {
341 amrex::Print() << "remora.drop_dry_boxes: a refinement indicator has no box, so a finer"
342 << " level may cover any land; no level-0 box is dropped.\n";
343 return ba;
344 }
345
346 const BoxArray ba2d = slab_boxes(ba);
347 MultiFab mskr(ba2d, DistributionMapping(ba2d), 1, IntVect(NGROW+1,NGROW+1,0));
348 if (!probe_rho_mask(0, mskr)) {
349 amrex::Print() << "remora.drop_dry_boxes: the level-0 mask comes from remora.hires_grid_level"
350 << " and is not known before the level is made; no box is dropped.\n";
351 return ba;
352 }
353
354 // The footprint stops one cell beyond a non-periodic domain edge: a grid file holds the
355 // mask there and no further, and the rings beyond it are physical-boundary ghost cells,
356 // filled from the inside rather than exchanged, so they keep the all-water placeholder.
357 Box reach = makeSlab(geom[0].Domain(), 2, 0);
358 for (int d = 0; d < 2; ++d) {
359 reach.grow(d, geom[0].isPeriodic(d) ? NGROW+1 : 1);
360 }
361 Vector<int> keep(ba.size(), 0);
362#ifdef AMREX_USE_OMP
363#pragma omp parallel if (Gpu::notInLaunchRegion())
364#endif
365 for (MFIter mfi(mskr); mfi.isValid(); ++mfi) {
366 const Real wet = mskr[mfi].max<RunOn::Device>(mfi.fabbox() & reach, 0);
367 keep[mfi.index()] = (wet > zero) ? 1 : 0;
368 }
369 ParallelAllReduce::Max(keep.data(), static_cast<int>(keep.size()),
370 ParallelContext::CommunicatorSub());
371
372 BoxList kept;
373 Long ndrop = 0, cells_drop = 0;
374 for (int b = 0; b < ba.size(); ++b) {
375 bool k = keep[b] != 0;
376 if (!k) {
377 for (Box p : protect) {
378 p.setRange(2,0);
379 if (p.intersects(ba2d[b])) { k = true; break; }
380 }
381 }
382 if (k) {
383 kept.push_back(ba[b]);
384 } else {
385 ++ndrop;
386 cells_drop += ba[b].numPts();
387 }
388 }
389 if (kept.isEmpty()) {
390 amrex::Abort("remora.drop_dry_boxes: every level-0 box is land; there is no water to model");
391 }
392
393 amrex::Print() << "remora.drop_dry_boxes: level 0 keeps " << kept.size() << " of " << ba.size()
394 << " boxes; dropped " << ndrop << " all-land boxes, " << cells_drop << " of "
395 << ba.numPts() << " cells (" << Real(100.0)*Real(cells_drop)/Real(ba.numPts())
396 << "%)\n";
397 if (ndrop == 0) { return ba; }
398 return BoxArray(std::move(kept));
399}
400
401/**
402 * Whether a level's BoxArray leaves part of its domain out, as remora.drop_dry_boxes does to
403 * level 0. Only meaningful for level 0, whose boxes are disjoint and should cover the domain.
404 */
405bool
407{
408 if (grids.empty() || grids[0].empty()) { return false; }
409 return grids[0].numPts() != geom[0].Domain().numPts();
410}
411
412/**
413 * Abort if level 1, or the coarse cells its fills and averaging use, reaches a dropped level-0
414 * box. level0_refine_footprint keeps those boxes when the run starts; this catches a grid that
415 * outgrew that footprint at a regrid.
416 */
417void
419{
420 if (finest_level < 1 || !level0_has_holes()) { return; }
421 const IntVect rr = ref_ratio[0];
422 IntVect ng(0);
423 for (int d = 0; d < 2; ++d) { ng[d] = (NGROW + 2 + rr[d] - 1) / rr[d] + 1; }
424 BoxArray need = amrex::coarsen(grids[1], rr);
425 need.grow(ng);
426 BoxList bl;
427 for (int b = 0; b < need.size(); ++b) {
428 Box bx = need[b] & geom[0].Domain();
429 if (bx.ok()) { bl.push_back(bx); }
430 }
431 if (!grids[0].contains(BoxArray(std::move(bl)))) {
432 amrex::Abort("remora.drop_dry_boxes: level 1, or the level-0 cells it needs, reaches a"
433 " dropped level-0 box. Set remora.drop_dry_boxes = false for this run.");
434 }
435}
436
437/**
438 * Regrid (overrides AmrCore), then make sure level 1 has not grown into a dropped level-0 box.
439 */
440void
441REMORA::regrid (int lbase, Real time, bool initial)
442{
443 AmrCore::regrid(lbase, time, initial);
445}
446
447/**
448 * Level 0's mask, as set_masks built it on the kept boxes, has to be the mask the boxes were
449 * dropped on; otherwise the decision rested on a different coastline. Compared everywhere the
450 * mask is stored, ghost cells included.
451 */
452void
454{
455 const MultiFab& mskr = *vec_mskr[0];
456 MultiFab probe(mskr.boxArray(), mskr.DistributionMap(), 1, mskr.nGrowVect());
458 MultiFab::Subtract(probe, mskr, 0, 0, 1, mskr.nGrowVect());
459 if (probe.norminf(0, 1, mskr.nGrowVect()) != zero) {
460 amrex::Abort("remora.drop_dry_boxes: level 0's mask differs from the one the boxes were"
461 " dropped on");
462 }
463}
464
465namespace {
466void add_if (Vector<MultiFab*>& v, MultiFab* mf) { if (mf) { v.push_back(mf); } }
467void add_if (Vector<MultiFab*>& v, const std::unique_ptr<MultiFab>& mf) { if (mf) { v.push_back(mf.get()); } }
468
469/**
470 * Per layout (BoxArray and ghost width), the cells of each box's footprint that lie inside the
471 * domain but in no box of the layout, computed once per box index. Level 0 is never regridded,
472 * so a run has few such layouts: one per index type, 2D or 3D, and ghost width.
473 */
474struct DryGhostHoles {
475 BoxArray ba;
476 IntVect ng;
477 std::map<int, Vector<Box>> holes;
478};
479std::vector<DryGhostHoles>& dry_ghost_cache () { static std::vector<DryGhostHoles> c; return c; }
480}
481
482/**
483 * The level-0 fields: those the time step evolves or rebuilds from them, and, with all, the
484 * grid, metrics, bathymetry, masks, forcing and coefficients as well.
485 */
486Vector<MultiFab*>
488{
489 const int lev = 0;
490 Vector<MultiFab*> f;
491 for (auto* m : {cons_new[lev], cons_old[lev], xvel_new[lev], xvel_old[lev],
492 yvel_new[lev], yvel_old[lev], zvel_new[lev], zvel_old[lev]}) { add_if(f, m); }
493 for (auto* v : {&vec_ubar, &vec_vbar, &vec_zeta, &vec_Zt_avg1, &vec_Huon, &vec_Hvom,
500 &vec_Lscale, &vec_Akk, &vec_Akp}) {
501 if (lev < v->size()) { add_if(f, (*v)[lev]); }
502 }
503#ifdef REMORA_USE_NETCDF
504 for (auto* v : {&vec_sustr_rho, &vec_svstr_rho}) {
505 if (lev < v->size()) { add_if(f, (*v)[lev]); }
506 }
507#endif
508 if (all) {
509 for (auto* v : {&vec_h, &vec_Hz, &vec_z_w, &vec_z_r, &vec_z_phys_nd, &vec_visc2_p,
518 if (lev < v->size()) { add_if(f, (*v)[lev]); }
519 }
520 for (auto& nv : vec_nudg_coeff) {
521 if (lev < nv.size()) { add_if(f, nv[lev]); }
522 }
523 }
524 return f;
525}
526
527/**
528 * Write val into every cell of mf, a field on the level-0 layout, that lies inside the domain
529 * but in no box: the ghost cells of kept boxes inside dropped ones, which no exchange fills. No
530 * op when level 0 covers the domain.
531 *
532 * @return the number of values written on this rank
533 */
534Long
535REMORA::set_dry_ghost_cells (MultiFab& mf, Real val)
536{
537 if (!level0_has_holes()) { return 0; }
538 const BoxArray& ba = mf.boxArray();
539 const IntVect ng = mf.nGrowVect();
540 auto& cache = dry_ghost_cache();
541 DryGhostHoles* entry = nullptr;
542 for (auto& e : cache) {
543 if (e.ng == ng && e.ba == ba) { entry = &e; break; }
544 }
545 if (!entry) {
546 cache.push_back(DryGhostHoles{ba, ng, {}});
547 entry = &cache.back();
548 }
549 // The domain in the field's index type; a 2D field lives on slabs at k = 0
550 Box dom = amrex::convert(geom[0].Domain(), mf.ixType());
551 const Box mb = ba.minimalBox();
552 dom.setRange(2, mb.smallEnd(2), mb.length(2));
553
554 // The hole boxes of every local box first, serially, since this fills the cache
555 for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
556 if (entry->holes.count(mfi.index()) == 0) {
557 Vector<Box> hv;
558 const Box fab = mfi.fabbox() & dom;
559 if (fab.ok()) {
560 for (const Box& h : ba.complementIn(fab)) { hv.push_back(h); }
561 }
562 entry->holes.emplace(mfi.index(), std::move(hv));
563 }
564 }
565
566 Long n = 0;
567 const int nc = mf.nComp();
568 const auto& holes = entry->holes;
569#ifdef AMREX_USE_OMP
570#pragma omp parallel if (Gpu::notInLaunchRegion()) reduction(+:n)
571#endif
572 for (MFIter mfi(mf); mfi.isValid(); ++mfi) {
573 Array4<Real> const& a = mf.array(mfi);
574 for (const Box& h : holes.at(mfi.index())) {
575 n += h.numPts() * nc;
576 ParallelFor(h, nc, [=] AMREX_GPU_DEVICE (int i, int j, int k, int c) noexcept
577 {
578 a(i,j,k,c) = val;
579 });
580 }
581 }
582 Gpu::streamSynchronize();
583 return n;
584}
585
586/**
587 * With level-0 boxes dropped, the ghost cells of a kept box that lie in a dropped box are never
588 * filled by an exchange, and the land stencils of the kept box read them. What those stencils
589 * compute stays on land only while the values there are finite: a wet cell is at least NGROW+2
590 * land cells away and the masks multiply the land values out (the regression tests write 7
591 * there and compare), but 0 * NaN is NaN, so a NaN there spreads into the water within a few
592 * steps. A fresh FArrayBox holds NaN in a debug build (amrex.init_snan), so these cells are
593 * zeroed in every level-0 field as soon as the fields exist, before anything reads them.
594 * Fields made later on the level-0 layout (lon/lat/angle from the grid file, the rho-point
595 * surface stress) zero theirs when they are allocated.
596 *
597 * The scratch MultiFabs the step allocates on the level-0 layout (for example in setup_step,
598 * gls, bulk_fluxes and the prestep) are not zeroed there. Nothing that the step reads from
599 * them in a dropped region reaches a kept cell's result: the Debug CI build (amrex.init_snan)
600 * and the dropped-vs-full feature checks with signalling NaNs and the invalid, zero and
601 * overflow traps on all complete with bitwise water points. A new kernel that reads such
602 * scratch in its ghost cells and lets the value reach a wet cell would break that; zero the
603 * scratch with set_dry_ghost_cells then.
604 */
605void
607{
608 if (!level0_has_holes()) { return; }
609 for (MultiFab* mf : level0_fields(true)) {
611 }
612}
613
614/**
615 * remora.dry_ghost_test (debug). After initialization, overwrite the ghost cells of the kept
616 * level-0 boxes that lie in dropped boxes, in every evolving field (remora.dry_ghost_fields =
617 * dynamic) or every field (all): 1 writes a signalling NaN, which with amrex.fpe_trap_invalid
618 * = 1 stops the run at the first arithmetic read of one; 2 writes remora.dry_ghost_value
619 * (default 1e30), so a plotted field that still matches the full-domain run does not depend on
620 * those cells while they hold that finite value.
621 */
622void
624{
625 if (dry_ghost_test == 0 || !level0_has_holes()) { return; }
626 std::string which = "dynamic";
627 ParmParse pp("remora");
628 pp.query("dry_ghost_fields", which);
629 if (which != "dynamic" && which != "all") {
630 amrex::Abort("remora.dry_ghost_fields must be dynamic or all, not " + which);
631 }
632 Real big = Real(1.e30);
633 pp.query("dry_ghost_value", big);
634 const Real val = (dry_ghost_test == 1) ? std::numeric_limits<Real>::signaling_NaN() : big;
635 const Vector<MultiFab*> f = level0_fields(which == "all");
636 Long npoison = 0;
637 for (MultiFab* mf : f) {
638 npoison += set_dry_ghost_cells(*mf, val);
639 }
640 ParallelDescriptor::ReduceLongSum(npoison);
641 amrex::Print() << "remora.dry_ghost_test = " << dry_ghost_test << ": overwrote " << npoison
642 << " uncovered ghost values in " << f.size() << " level-0 fields\n";
643}
constexpr amrex::Real one
constexpr amrex::Real zero
#define NGROW
AMREX_ALWAYS_ASSERT(!NSPeriodic||!EWPeriodic)
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_lonr
longitude on rho-points (2D, degrees east); only filled when the grid NetCDF file carries lon_rho
Definition REMORA.H:744
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:495
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_EminusP
evaporation minus precipitation [kg/m^2/s], defined at rho-points
Definition REMORA.H:595
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rv2d
v velocity RHS (2D, includes horizontal and vertical advection)
Definition REMORA.H:510
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_evap
evaporation rate [kg/m^2/s]
Definition REMORA.H:591
bool running_with_coupling_driver
True once REMORA has received forcing through the coupling driver.
Definition REMORA.H:600
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_fcor
coriolis factor (2D)
Definition REMORA.H:713
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_metric_u
metric products at u points, components MetricU (2D)
Definition REMORA.H:706
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_btflux
Bottom tracer flux; input arrays.
Definition REMORA.H:586
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rubar
barotropic x velocity for the RHS (2D)
Definition REMORA.H:659
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:487
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pm
horizontal scaling factor: 1 / dx (2D)
Definition REMORA.H:694
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_ZoBot
Bottom roughness length [m], defined at rho points.
Definition REMORA.H:611
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_DU_avg2
correct time average of barotropic x velocity flux for coupling (2D)
Definition REMORA.H:634
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_lrflx
longwave radiation
Definition REMORA.H:571
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yv
y_grid on v-points (2D)
Definition REMORA.H:728
amrex::Vector< amrex::MultiFab * > cons_new
multilevel data container for current step's scalar data: temperature, salinity, passive tracer
Definition REMORA.H:467
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_sustr_rho
Surface stress from a lat-lon file, east and north components on rho points before it is rotated onto...
Definition REMORA.H:1804
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_vwind
Wind in the v direction, defined at rho-points.
Definition REMORA.H:560
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_svstr_rho
Definition REMORA.H:1805
std::unique_ptr< ProblemBase > prob
Pointer to container of analytical functions for problem definition.
Definition REMORA.H:1906
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr
land/sea mask at cell centers (2D)
Definition REMORA.H:675
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_latr
latitude on rho-points (2D, degrees north); only filled when the grid NetCDF file carries lat_rho
Definition REMORA.H:747
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rain
precipitation rate [kg/m^2/s]
Definition REMORA.H:589
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_scratch_2d
Scratch for the barotropic step: the n_scratch_2d fields one pass of advance_2d hands the next (new d...
Definition REMORA.H:625
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_tke
Turbulent kinetic energy.
Definition REMORA.H:786
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_stflx
Surface tracer flux; working arrays.
Definition REMORA.H:580
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_gls
Turbulent generic length scale.
Definition REMORA.H:788
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_sustr
Surface stress in the u direction.
Definition REMORA.H:553
amrex::Vector< amrex::RealBox > refine_regions
Physical-space region of each refinement indicator, for level0_refine_footprint.
Definition REMORA.H:359
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yp
y_grid on psi-points (2D)
Definition REMORA.H:733
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xr
x_grid on rho points (2D)
Definition REMORA.H:716
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Dvbar_new
see vec_Dvbar_old (2D)
Definition REMORA.H:647
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_ru2d
u velocity RHS (2D, includes horizontal and vertical advection)
Definition REMORA.H:508
amrex::Vector< amrex::MultiFab * > zvel_new
multilevel data container for current step's z velocities (largely unused; W stored separately)
Definition REMORA.H:473
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_sstore
additional scratch space for calculations on temp, salt, etc
Definition REMORA.H:766
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xv
x_grid on v-points (2D)
Definition REMORA.H:726
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Lscale
Vertical mixing turbulent length scale.
Definition REMORA.H:790
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Hz
Width of cells in the vertical (z-) direction (3D, Hz in ROMS)
Definition REMORA.H:498
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Akt
Vertical diffusion coefficient (3D)
Definition REMORA.H:518
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_msku
land/sea mask at x-faces (2D)
Definition REMORA.H:677
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rvfrc
v velocity RHS, integrated, including advection and bottom/surface stresses (2D)
Definition REMORA.H:514
bool level0_has_holes() const
Whether level 0's BoxArray leaves part of the domain out.
bool refine_region_unbounded
Whether some refinement indicator has no region, so it may refine anywhere.
Definition REMORA.H:361
amrex::Vector< amrex::MultiFab * > xvel_old
multilevel data container for last step's x velocities (u in ROMS)
Definition REMORA.H:460
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Dubar_old
DU_avg2 per unit cell edge length, at the start and end of this level's step. What a subcycled finer ...
Definition REMORA.H:641
amrex::Vector< amrex::MultiFab * > yvel_new
multilevel data container for current step's y velocities (v in ROMS)
Definition REMORA.H:471
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_uwind
Wind in the u direction, defined at rho-points.
Definition REMORA.H:558
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rufrc
u velocity RHS, integrated, including advection and bottom/surface stresses (2D)
Definition REMORA.H:512
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_metric_p
metric products at psi points, components MetricP (2D)
Definition REMORA.H:710
std::string load_balance_name() const
The remora.load_balance spelling of the current strategy.
bool wet_cells_per_box(int lev, const amrex::BoxArray &ba, amrex::Vector< amrex::Long > &wet)
Wet cells in each box of a prospective layout of level lev; false if unknown.
void check_dropped_level0_mask()
Check level 0's mask against the mask the dropping decision was made on.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_shflx
sensible heat flux
Definition REMORA.H:577
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:520
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_dmde
d(1/m)/d(eta)
Definition REMORA.H:763
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rvbar
barotropic y velocity for the RHS (2D)
Definition REMORA.H:661
amrex::Vector< amrex::MultiFab * > zvel_old
multilevel data container for last step's z velocities (largely unused; W stored separately)
Definition REMORA.H:464
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_r
z coordinates at rho points (cell centers)
Definition REMORA.H:527
amrex::Vector< amrex::MultiFab * > xvel_new
multilevel data container for current step's x velocities (u in ROMS)
Definition REMORA.H:469
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_lhflx
latent heat flux
Definition REMORA.H:575
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskp
land/sea mask at cell corners (2D)
Definition REMORA.H:681
virtual void regrid(int lbase, amrex::Real time, bool initial=false) override
Regrid (overrides AmrCore), then check that level 1 stays clear of dropped level-0 boxes.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_bvf
Brunt-Vaisala frequency (3D)
Definition REMORA.H:773
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_lonp
longitude on psi-points (2D, degrees east); only filled when the grid NetCDF file carries lon_psi
Definition REMORA.H:737
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskv
land/sea mask at y-faces (2D)
Definition REMORA.H:679
amrex::BoxArray drop_dry_level0_boxes(const amrex::BoxArray &ba)
The kept boxes of a level-0 BoxArray under remora.drop_dry_boxes.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rhoS
density perturbation
Definition REMORA.H:769
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_visc2_r
Harmonic viscosity defined on the rho points (centers)
Definition REMORA.H:522
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_metric_r
metric products of pm and pn at rho points, components MetricR (2D). Recomputed by set_derived_metric...
Definition REMORA.H:704
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_svstr
Surface stress in the v direction.
Definition REMORA.H:555
void poison_dry_ghost_cells()
remora.dry_ghost_test: overwrite the ghost cells of kept level-0 boxes that lie in dropped boxes (1: ...
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Huon
u-volume flux (3D)
Definition REMORA.H:500
amrex::Vector< amrex::MultiFab * > yvel_old
multilevel data container for last step's y velocities (v in ROMS)
Definition REMORA.H:462
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rhoA
vertically-averaged density
Definition REMORA.H:771
amrex::BoxList level0_refine_footprint(bool &unbounded) const
Level-0 index region a finer level may need; unbounded if it can go anywhere.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_DV_avg1
time average of barotropic y velocity flux
Definition REMORA.H:636
bool probe_rho_mask(int lev, amrex::MultiFab &mskr)
The rho-point mask a level lev on mskr's layout would get, before that level exists; false if it cann...
void check_dry_box_nesting() const
Abort if level 1 or its coarse stencil reaches a dropped level-0 box.
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:2218
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Akk
Turbulent kinetic energy vertical diffusion coefficient.
Definition REMORA.H:792
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rdrag2
Quadratic drag coefficient [unitless], defined at rho points.
Definition REMORA.H:609
amrex::Vector< amrex::MultiFab * > level0_fields(bool all)
The level-0 fields: the evolving ones, and with all the static ones too.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_ru
u velocity RHS (3D, includes horizontal and vertical advection)
Definition REMORA.H:504
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xp
x_grid on psi-points (2D)
Definition REMORA.H:731
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_longwave_down
Downward longwave radiation.
Definition REMORA.H:573
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_zeta
free surface height (2D)
Definition REMORA.H:669
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_vbar
barotropic y velocity (2D)
Definition REMORA.H:667
int dry_ghost_test
Debug: see poison_dry_ghost_cells.
Definition REMORA.H:357
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_DU_avg1
time average of barotropic x velocity flux (2D)
Definition REMORA.H:632
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_alpha
Thermal expansion coefficient (3D)
Definition REMORA.H:775
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_ubar
barotropic x velocity (2D)
Definition REMORA.H:665
void fill_rho_mask_from_netcdf(int lev, amrex::MultiFab &mskr)
fill a rho-point mask on any layout of level lev from its NetCDF grid file(s)
amrex::Vector< amrex::MultiFab * > cons_old
multilevel data container for last step's scalar data: temperature, salinity, passive tracer
Definition REMORA.H:458
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_bustr
Bottom stress in the u direction.
Definition REMORA.H:614
void zero_level0_dry_ghost_cells()
Zero those cells in every level-0 field (see set_dry_ghost_cells)
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_DV_avg2
correct time average of barotropic y velocity flux for coupling (2D)
Definition REMORA.H:638
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yu
y_grid on u-points (2D)
Definition REMORA.H:723
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_dndx
d(1/n)/d(xi)
Definition REMORA.H:761
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_bvstr
Bottom stress in the v direction.
Definition REMORA.H:616
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rzeta
free surface height for the RHS (2D)
Definition REMORA.H:663
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_angler
angle from east to the local xi axis on rho-points (2D, radians, positive counterclockwise: ROMS's an...
Definition REMORA.H:751
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_phys_nd
z coordinates at psi points (cell nodes)
Definition REMORA.H:547
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Dvbar_old
DV_avg2 per unit cell edge length, see vec_Dubar_old (2D)
Definition REMORA.H:645
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xu
x_grid on u-points (2D)
Definition REMORA.H:721
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > vec_nudg_coeff
Climatology nudging coefficients.
Definition REMORA.H:797
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pn
horizontal scaling factor: 1 / dy (2D)
Definition REMORA.H:696
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Akv
Vertical viscosity coefficient (3D)
Definition REMORA.H:516
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_stflux
Surface tracer flux; input arrays.
Definition REMORA.H:582
amrex::Real load_balance_land_weight
cost of a land cell relative to a wet one in the load-balance weights (remora.load_balance_land_weigh...
Definition REMORA.H:368
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Dubar_new
see vec_Dubar_old (2D)
Definition REMORA.H:643
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rdrag
Linear drag coefficient [m/s], defined at rho points.
Definition REMORA.H:607
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_cloud
cloud cover fraction [0-1], defined at rho-points
Definition REMORA.H:593
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Zt_avg1
Average of the free surface, zeta (2D)
Definition REMORA.H:550
amrex::Long set_dry_ghost_cells(amrex::MultiFab &mf, amrex::Real val)
Write val into the cells of a level-0 field inside the domain but in no box.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rv
v velocity RHS (3D, includes horizontal and vertical advection)
Definition REMORA.H:506
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_btflx
Bottom tracer flux; working arrays.
Definition REMORA.H:584
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_beta
Saline contraction coefficient (3D)
Definition REMORA.H:777
virtual amrex::DistributionMapping MakeDistributionMap(int lev, amrex::BoxArray const &ba) override
Distribution mapping for a new BoxArray on level lev, by remora.load_balance: each box weighs its wet...
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr3d
land/sea mask at cell centers, copied to all z levels (3D)
Definition REMORA.H:683
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_srflx
Shortwave radiation flux [W/m²], defined at rho-points.
Definition REMORA.H:569
static amrex::Vector< REMORAErrorTag > ref_tags
Holds info for dynamically generated tagging criteria.
Definition REMORA.H:2351
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_metric_v
metric products at v points, components MetricV (2D)
Definition REMORA.H:708
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_DUon
Scratch for the barotropic step, kept per level rather than allocated on every fast step: the x and y...
Definition REMORA.H:620
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Pair
Air pressure [mb], defined at rho-points.
Definition REMORA.H:566
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_DVom
Definition REMORA.H:621
LoadBalance load_balance
Definition REMORA.H:365
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_qair
Specific humidity [kg/kg], defined at rho-points.
Definition REMORA.H:564
int hires_grid_level
Which level the high resolution bathymetry is at.
Definition REMORA.H:2293
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_w
z coordinates at w points (faces between z-cells)
Definition REMORA.H:530
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Tair
Air temperature [°C], defined at rho-points.
Definition REMORA.H:562
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yr
y_grid on rho points (2D)
Definition REMORA.H:718
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Hvom
v-volume flux (3D)
Definition REMORA.H:502
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Akp
Turbulent length scale vertical diffusion coefficient.
Definition REMORA.H:794
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_diff2
Harmonic diffusivity for temperature / salinity.
Definition REMORA.H:524
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_latp
latitude on psi-points (2D, degrees north); only filled when the grid NetCDF file carries lat_psi
Definition REMORA.H:740