REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_Tagging.cpp
Go to the documentation of this file.
1#include <REMORA.H>
2#include <REMORA_Derive.H>
3
4using namespace amrex;
5
6namespace {
7/**
8 * Copy mf's topmost and bottommost valid z-planes into its z-ghost planes.
9 *
10 * Several of the fields ErrorEst tags on live in MultiFabs with no ghost cells in z
11 * (zvel_new, vec_mskr3d), and the vorticity branch only ever computes valid cells, so
12 * mf's k = klo-1 and k = khi+1 planes would otherwise be read uninitialized by the GRAD
13 * (adjacent_difference_greater) test, which unconditionally differences k+-1. There is
14 * no data below k=0 or above k=N to copy, so impose a zero vertical gradient: GRAD then
15 * sees no vertical difference at the surface and bottom cells instead of garbage.
16 *
17 * @param[inout] mf single-component MultiFab with at least one ghost cell in z
18 */
19void
20fill_z_ghost_planes (MultiFab& mf)
21{
22 AMREX_ALWAYS_ASSERT(mf.nComp() == 1 && mf.nGrowVect()[2] >= 1);
23
24 // Not tiled: each iteration writes the two z-planes of a whole column, so tiling in
25 // z would have several tiles writing the same cell.
26#ifdef _OPENMP
27#pragma omp parallel if (Gpu::notInLaunchRegion())
28#endif
29 for (MFIter mfi(mf); mfi.isValid(); ++mfi)
30 {
31 const Box& vbx = mfi.validbox();
32 const int klo = vbx.smallEnd(2);
33 const int khi = vbx.bigEnd(2);
34
35 // Laterally grown as well, so the ghost columns get their z-planes too --
36 // GRAD reads i+-1 and j+-1 in those columns as well as k+-1.
37 const Box& gbx = mfi.fabbox();
38 auto arr = mf.array(mfi);
39
40 ParallelFor(makeSlab(gbx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept
41 {
42 arr(i,j,klo-1) = arr(i,j,klo);
43 arr(i,j,khi+1) = arr(i,j,khi);
44 });
45 }
46}
47
48/**
49 * Fill mf's lateral ghost cells from the nearest valid cell of the same box.
50 *
51 * Call this *before* FillBoundary: FillBoundary reads only valid regions, so it
52 * overwrites every ghost cell backed by a real same-level or periodic neighbor and
53 * leaves only the physical-boundary and coarse/fine ghosts holding the zero-gradient
54 * value written here. That keeps the GRAD test's i+-1 / j+-1 reads defined and
55 * deterministic everywhere without tagging on a jump we invented.
56 *
57 * @param[inout] mf single-component MultiFab
58 */
59void
61{
62 AMREX_ALWAYS_ASSERT(mf.nComp() == 1);
63 const IntVect ng = mf.nGrowVect();
64
65#ifdef _OPENMP
66#pragma omp parallel if (Gpu::notInLaunchRegion())
67#endif
68 for (MFIter mfi(mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
69 {
70 const Box& vbx = mfi.validbox();
71 const Box gbx = mfi.growntilebox(IntVect(ng[0],ng[1],0));
72 auto arr = mf.array(mfi);
73
74 const int ilo = vbx.smallEnd(0); const int ihi = vbx.bigEnd(0);
75 const int jlo = vbx.smallEnd(1); const int jhi = vbx.bigEnd(1);
76
77 ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
78 {
80 arr(i,j,k) = arr(amrex::min(amrex::max(i,ilo),ihi),
81 amrex::min(amrex::max(j,jlo),jhi), k);
82 }
83 });
84 }
85}
86} // namespace
87
88/**
89 * Apply this criterion to tba, skipping values the land/sea mask makes meaningless.
90 * The parameters are documented on the declaration in REMORA_ErrorTag.H.
91 *
92 * The one thing to know when reading the body: a cell this declines to test is left exactly
93 * as it was found, not cleared. That is what lets a static box keep the tags it set over a
94 * coast, and it is the whole difference from the derefine criteria this replaced.
95 */
96void
98 const MultiFab* mf,
99 const MultiFab* mskr3d,
100 char clearval,
101 char tagval,
102 Real time,
103 int level,
104 const Geometry& geom,
105 const IntVect& mask_lo,
106 const IntVect& mask_hi) const
107{
108 BL_PROFILE("REMORAErrorTag::operator()");
109
110 // The tests that read a field cell by cell, and so have something for the mask to guard.
111 // RELGRAD and VORT do too, and are absent only because refinement_criteria_setup cannot
112 // build them -- so assert rather than let a future one slip silently past the guard.
113 const bool masked_test = (m_test == GRAD || m_test == LESS || m_test == GREATER);
114
116 (m_test != RELGRAD && m_test != VORT),
117 "REMORAErrorTag: RELGRAD and VORT have no mask guard; "
118 "give them one before building them from inputs");
119
120 if (mskr3d == nullptr || !masked_test) {
121 amrex::AMRErrorTag::operator()(tba, mf, clearval, tagval, time, level, geom);
122 return;
123 }
124
125 AMREX_ALWAYS_ASSERT(mf != nullptr);
126
127 // One box index runs the tags, the field and the mask below, so all three have to be
128 // distributed the same way over the same boxes.
129 AMREX_ALWAYS_ASSERT(mskr3d->boxArray() == mf->boxArray() &&
130 mskr3d->DistributionMap() == mf->DistributionMap());
131
132 AMREX_ALWAYS_ASSERT(mask_lo[0] <= 0 && mask_lo[1] <= 0 &&
133 mask_hi[0] >= 0 && mask_hi[1] >= 0);
134
135 // Rejected rather than silently ignored: the mask is column-constant, so a vertical offset
136 // could not mean anything, and for the same reason nothing below reads the mask at k+-1 --
137 // which is why mskr3d needs no vertical ghost cells.
138 AMREX_ALWAYS_ASSERT(mask_lo[2] == 0 && mask_hi[2] == 0);
139
140 // Furthest the loop below reaches laterally: the field's own dependence on mskr, plus one
141 // for GRAD, which asks the same of the neighbor it differences against.
142 const int grad_reach = (m_test == GRAD) ? 1 : 0;
143 for (int d = 0; d < 2; ++d) {
144 AMREX_ALWAYS_ASSERT(mskr3d->nGrowVect()[d] >=
145 amrex::max(-mask_lo[d], mask_hi[d]) + grad_reach);
146 }
147
148 if ( (level < 0 ) || (level >= m_info.m_max_level) ||
149 (time < m_info.m_min_time ) || (time > m_info.m_max_time ) ) {
150 return;
151 }
152
153 auto const& tagma = tba.arrays();
154 auto const& datma = mf->const_arrays();
155 auto const& mskma = mskr3d->const_arrays();
156
157 auto const nvalues = std::ssize(m_value);
159 "Threshold values not properly set in REMORAErrorTag");
160 auto const threshold = m_value[(level < nvalues) ? level : nvalues-1];
161 auto const tag_update = m_info.m_derefine ? clearval : tagval;
162 auto const volume_weighting = m_info.m_volume_weighting;
163 auto const& geomdata = geom.data();
164 auto const test = m_test;
165 auto const mlo = mask_lo;
166 auto const mhi = mask_hi;
167
168 ParallelFor(tba, [=] AMREX_GPU_DEVICE (int bi, int i, int j, int k) noexcept
169 {
170 auto const& msk = mskma[bi];
171
172 // Whether the value at index (ii,jj,k) was computed from water alone. A value that read
173 // the land side reports on the coast whichever cell index it is filed under, so this
174 // asks about every rho-cell the value depends on, not just its own.
175 auto value_is_clean = [=] (int ii, int jj) noexcept
176 {
177 for (int jo = mlo[1]; jo <= mhi[1]; ++jo) {
178 for (int io = mlo[0]; io <= mhi[0]; ++io) {
179 if (msk(ii+io,jj+jo,k) <= Real(0.5)) { return false; }
180 }
181 }
182 return true;
183 };
184
185 if (!value_is_clean(i,j)) { return; }
186
187 auto const& dat = datma[bi];
188 bool tag_it = false;
189
190 if (test == GRAD)
191 {
192 // A difference only between two clean values, as AMReX's EB form of this test
193 // takes one only across a connected face.
194 Real ax = Real(0.0);
195 if (value_is_clean(i+1,j)) {
196 ax = amrex::max(ax, std::abs(dat(i+1,j,k) - dat(i,j,k)));
197 }
198 if (value_is_clean(i-1,j)) {
199 ax = amrex::max(ax, std::abs(dat(i,j,k) - dat(i-1,j,k)));
200 }
201
202 Real ay = Real(0.0);
203 if (value_is_clean(i,j+1)) {
204 ay = amrex::max(ay, std::abs(dat(i,j+1,k) - dat(i,j,k)));
205 }
206 if (value_is_clean(i,j-1)) {
207 ay = amrex::max(ay, std::abs(dat(i,j,k) - dat(i,j-1,k)));
208 }
209
210 // The mask is constant down a column, so both vertical neighbors of a water cell
211 // are water and neither face needs a guard.
212 Real az = std::abs(dat(i,j,k+1) - dat(i,j,k));
213 az = amrex::max(az, std::abs(dat(i,j,k) - dat(i,j,k-1)));
214
215 tag_it = (amrex::max(ax,ay,az) >= threshold);
216 }
217 else
218 {
219 const Real vol = volume_weighting
220 ? Geometry::Volume(IntVect{AMREX_D_DECL(i,j,k)}, geomdata)
221 : Real(1.0);
222 tag_it = (test == LESS) ? (dat(i,j,k) * vol <= threshold)
223 : (dat(i,j,k) * vol >= threshold);
224 }
225
226 if (tag_it) { tagma[bi](i,j,k) = tag_update; }
227 });
228 Gpu::streamSynchronize();
229}
230
231/**
232 * Function to tag cells for refinement -- this overrides the pure virtual function in AmrCore
233 *
234 * @param[in] levc level of refinement (0 is coarsest level)
235 * @param[out] tags array of tagged cells
236 * @param[in] time current time
237 * @param[in] ngrow number of grow cells
238*/
239void
240REMORA::ErrorEst (int levc, TagBoxArray& tags, Real time, int /*ngrow*/)
241{
242 const int clearval = TagBox::CLEAR;
243 const int tagval = TagBox::SET;
244
245 //
246 // This mf must have ghost cells because we may take differences between adjacent values
247 //
248 std::unique_ptr<MultiFab> mf = std::make_unique<MultiFab>(grids[levc], dmap[levc], 1, 1);
249
250 // Any cell-centered tracer may drive refinement, named as it is elsewhere: "temp",
251 // "salt", "tracer", "tracer_1", or a biology tracer such as "NO3".
252 auto cons_comp_for_field = [this] (const std::string& field) {
253 for (int icomp = 0; icomp < ncons; ++icomp) {
254 if (cons_names[icomp] == field) { return icomp; }
255 }
256 return -1;
257 };
258
259 // Which tracers exist depends on runtime input -- "tracer" only when remora.nscalar > 0,
260 // the biology names only with a biology model -- so "use a tracer name" sends a reader
261 // hunting for a typo that is not there. Name the ones this run actually has.
262 auto valid_field_names = [this] () {
263 std::string names;
264 for (int icomp = 0; icomp < ncons; ++icomp) { names += cons_names[icomp] + ", "; }
265 names += "x_velocity, y_velocity, z_velocity, vorticity, mask";
266#ifdef REMORA_USE_PARTICLES
267 names += ", <particle>_count";
268#endif
269 return names;
270 };
271
272 for (int j=0; j < ref_tags.size(); ++j)
273 {
275
276 // Which rho-cells the value this criterion reads depends on, relative to the index it
277 // is stored at. Set alongside the fill rather than in a second switch on the field
278 // name, so adding a field cannot leave it filled but unguarded. Zero means the value
279 // is its own cell and nothing else.
280 IntVect mask_lo = IntVect::TheZeroVector();
281 IntVect mask_hi = IntVect::TheZeroVector();
282
283 if (cons_comp >= 0) {
285 0,true,false);
286 }
287 // This allows dynamic refinement based on the value of a tracer
288 if (cons_comp >= 0)
289 {
290 // A tracer is masked in place by advance_3d_ml, so its own cell is the whole story.
291 MultiFab::Copy(*mf,*cons_new[levc],cons_comp,0,1,1);
292 } else if (ref_tags[j].Field() == "x_velocity") {
294 MultiFab::Copy(*mf,*xvel_new[levc],0,0,1,1);
295 // u is stored at a cell index but lives on that cell's low-x face, and vert_mean_3d
296 // multiplies it by msku(i,j) = mskr(i-1,j)*mskr(i,j). So u at a water cell whose
297 // i-1 neighbor is land is an exact zero: a mask artifact, not slack water.
298 mask_lo = IntVect(-1,0,0);
299 } else if (ref_tags[j].Field() == "y_velocity") {
301 MultiFab::Copy(*mf,*yvel_new[levc],0,0,1,1);
302 // As for u, with mskv(i,j) = mskr(i,j-1)*mskr(i,j).
303 mask_lo = IntVect(0,-1,0);
304 } else if (ref_tags[j].Field() == "z_velocity") {
306 // zvel_new has no ghost cells in z, so we can only ask the copy for lateral ones
307 MultiFab::Copy(*mf,*zvel_new[levc],0,0,1,IntVect(1,1,0));
309 // zvel_new is identically zero: nothing masks it, and nothing puts a computed
310 // value in it -- the vertical velocity the model solves for lives in a scratch
311 // array inside advance_3d. So its own cell is as good an answer as any. If it is
312 // ever wired up, W is built from Huon and Hvom at i+1 and j+1, making its real
313 // dependence the five-point cross.
314 } else if (ref_tags[j].Field() == "vorticity") {
315 // Fill the ghost cells of the face-based velocities -- including at
316 // coarse/fine boundaries, which is what FillPatch's FillPatchTwoLevels
317 // interpolates -- since the cell-centered velocities in those ghost cells
318 // are read when computing vorticity below
322
323 // Vorticity needs no vertical neighbor, and zvel_new has no z ghost cells
324 // to average from, so only grow laterally
325 MultiFab mf_cc_vel(grids[levc],dmap[levc],3,IntVect(1,1,0));
328 yvel_new[levc],
329 zvel_new[levc]},
330 IntVect(1,1,0));
331 // Impose bc's at domain boundaries at all levels
333
334#ifdef _OPENMP
335#pragma omp parallel if (Gpu::notInLaunchRegion())
336#endif
337 for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
338 {
339 const Box& bx = mfi.tilebox();
340 auto& dfab = (*mf)[mfi];
341 auto& sfab = mf_cc_vel[mfi];
342 auto pm = vec_pm[levc]->const_array(mfi);
343 auto pn = vec_pn[levc]->const_array(mfi);
344 auto maskr = vec_mskr[levc]->const_array(mfi);
345 derived::remora_dervort(bx, dfab, 0, 1, sfab, pm, pn, maskr, Geom(levc), time, nullptr, levc);
346 } // mfi
347
348 // remora_dervort only writes valid cells, so fill mf's own ghosts before the
349 // tagging criteria difference across them. The zero-gradient pass goes first;
350 // FillBoundary then replaces every ghost that has a real same-level or periodic
351 // neighbor, leaving only the physical-boundary and coarse/fine ghosts extrapolated.
353 mf->FillBoundary(geom[levc].periodicity());
355
356 // remora_dervort differences the cell-centered velocities at i+-1 and j+-1 without
357 // masking them (see the TODO there), and each is an average of two faces, so this
358 // value depends on the whole 3x3 block around it. Narrow once the derive is masked.
359 //
360 // Untested, unlike the face velocities that DogboneAnalytic_MLdryface covers: the
361 // only masked problem available is near-irrotational, so any assertion on it would
362 // be pinned to roundoff. Covering it wants a masked case with shear along a coast.
363 mask_lo = IntVect(-1,-1,0);
364 mask_hi = IntVect( 1, 1,0);
365
366 } else if (ref_tags[j].Field() == "mask") {
367 // vec_mskr3d has no z ghost cells, so this copy leaves mf's top and bottom
368 // ghost planes unwritten while the GRAD test differences in z. The mask is
369 // constant down a column, so the zero vertical gradient imposed here is exact.
370 MultiFab::Copy(*mf,*vec_mskr3d[levc],0,0,1,IntVect(1,1,0));
372 } else if (ref_tags[j].Field() == "h") {
373 // Bathymetry is 2D, so spread it down each column of mf. One lateral
374 // ghost ring is enough for the GRAD test; vec_h holds NGROW+1. The value
375 // is constant down a column, so the zero vertical gradient imposed on
376 // the z ghost planes is exact.
377 for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
378 {
379 const Box bx = mfi.growntilebox(IntVect(1,1,0));
380 Array4<const Real> const& h = vec_h[levc]->const_array(mfi);
381 Array4< Real> const& arr = mf->array(mfi);
382 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
383 {
384 arr(i,j,k) = h(i,j,0);
385 });
386 }
388#ifdef REMORA_USE_PARTICLES
389 } else {
390 //
391 // This allows dynamic refinement based on the number of particles per cell
392 //
393 // Note that we must count all the particles in levels both at and above the current,
394 // since otherwise, e.g., if the particles are all at level 1, counting particles at
395 // level 0 will not trigger refinement when regridding so level 1 will disappear,
396 // then come back at the next regridding
397 //
398 const auto& particles_namelist( particleData.getNames() );
399 mf->setVal(zero);
400 bool matched_particle_count = false;
401 for (ParticlesNamesVector::size_type i = 0; i < particles_namelist.size(); i++)
402 {
403 std::string tmp_string(particles_namelist[i]+"_count");
404 IntVect rr = IntVect::TheUnitVector();
405 if (ref_tags[j].Field() == tmp_string) {
407 for (int lev = levc; lev <= finest_level; lev++)
408 {
409 MultiFab temp_dat(grids[lev], dmap[lev], 1, 0); temp_dat.setVal(0);
410 particleData[particles_namelist[i]]->IncrementWithTotal(temp_dat, lev);
411
412 MultiFab temp_dat_crse(grids[levc], dmap[levc], 1, 0); temp_dat_crse.setVal(0);
413
414 if (lev == levc) {
415 MultiFab::Copy(*mf, temp_dat, 0, 0, 1, 0);
416 } else {
417 for (int d = 0; d < AMREX_SPACEDIM; d++) {
418 rr[d] *= ref_ratio[levc][d];
419 }
421 MultiFab::Add(*mf, temp_dat_crse, 0, 0, 1, 0);
422 }
423 }
424 }
425 }
426
427 // A box-only indicator carries no field name and tags geometrically, so it
428 // never reads mf; only a named field that matched nothing is an error.
430 amrex::Abort("Unknown refinement field '" + ref_tags[j].Field() +
431 "'. This run has: " + valid_field_names() + ".");
432 }
433#else
434 } else if (!ref_tags[j].Field().empty()) {
435 // mf is uninitialized until something writes it, so an unrecognized named
436 // field would otherwise tag on garbage. A box-only indicator has no field
437 // name, tags geometrically, and never reads mf.
438 amrex::Abort("Unknown refinement field '" + ref_tags[j].Field() +
439 "'. This run has: " + valid_field_names() + ".");
440#endif
441 }
442
443 // Two criteria want the unguarded test. One keyed on the mask is asking where the
444 // coast is: guarding it would leave no water-water face across which the mask varies,
445 // so it would never tag. And with no mask every cell is water, so the guard could not
446 // fire -- skipping it keeps an unmasked run on exactly AMReX's own code path.
447 const bool unguarded = (ref_tags[j].Field() == "mask") ||
449 const MultiFab* mskr3d_for_tag = unguarded ? nullptr : vec_mskr3d[levc].get();
450
451 // time counts from remora.start_time, but the window is on the model clock. Checked
452 // here, after the field is filled, so a misnamed field still aborts outside it.
453 if (!ref_tags[j].ActiveAt(model_time(time))) {
454 continue;
455 }
456
459 }
460
461 // Promote any tagged cell to a full local z-column.
462 for (MFIter mfi(tags, TilingIfNotGPU()); mfi.isValid(); ++mfi)
463 {
464 const Box& bx = mfi.validbox();
465 auto const& tag = tags.array(mfi);
466
467 const int klo = bx.smallEnd(2);
468 const int khi = bx.bigEnd(2);
469
470 amrex::ParallelFor(makeSlab(bx, 2, 0),
471 [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept
472 {
473 bool refine_col = false;
474 for (int k = klo; k <= khi; ++k) {
475 refine_col = refine_col || (tag(i,j,k) != TagBox::CLEAR);
476 }
477
478 if (refine_col) {
479 for (int k = klo; k <= khi; ++k) {
480 tag(i,j,k) = TagBox::SET;
481 }
482 }
483 });
484 }
485}
486
487/**
488 * Function to define the refinement criteria based on user input
489*/
490void
492{
493 if (max_level > 0)
494 {
496 Vector<std::string> refinement_indicators;
497 pp.queryarr("refinement_indicators",refinement_indicators,0,pp.countval("refinement_indicators"));
498 for (int i=0; i<refinement_indicators.size(); ++i)
499 {
500 std::string ref_prefix = pp_prefix + "." + refinement_indicators[i];
501
504 int lev_for_box;
505
506 int num_real_lo = ppr.countval("in_box_lo");
507 int num_indx_lo = ppr.countval("in_box_lo_indices");
508 int num_indx_lo_crse = ppr.countval("in_box_lo_indices_crse");
509
510 int num_real_hi = ppr.countval("in_box_hi");
511 int num_indx_hi = ppr.countval("in_box_hi_indices");
512 int num_indx_hi_crse = ppr.countval("in_box_hi_indices_crse");
513
517
518 // Problem low and high (in real not index space) are the same at all levels
519 if ( !((num_real_lo >= AMREX_SPACEDIM-1 && num_indx_lo == 0 && num_indx_lo_crse == 0) ||
521 (num_indx_lo == 0 && num_real_lo == 0 && num_indx_lo_crse == 0) ||
523 ) )
524 {
525 amrex::Abort("Must only specify box for refinement using real OR index space with fine/coarse grid indices");
526 }
527
528 if (num_real_lo > 0) {
529 std::vector<Real> box_lo(3), box_hi(3);
530 ppr.get("max_level",lev_for_box);
531 if (lev_for_box > 0 && lev_for_box <= max_level)
532 {
533 ppr.getarr("in_box_lo",box_lo,0,2);
534 ppr.getarr("in_box_hi",box_hi,0,2);
535 box_lo[2] = geom[0].ProbLo(2);
536 box_hi[2] = geom[0].ProbHi(2);
537 realbox = RealBox(&(box_lo[0]),&(box_hi[0]));
538
539 amrex::Print() << "Reading " << realbox << " at level " << lev_for_box << std::endl;
541
542 const auto* dx = geom[lev_for_box].CellSize();
543 const Real* plo = geom[lev_for_box].ProbLo();
544
545 int ilo = static_cast<int>((box_lo[0] - plo[0])/dx[0]);
546 int jlo = static_cast<int>((box_lo[1] - plo[1])/dx[1]);
547 int klo = static_cast<int>((box_lo[2] - plo[2])/dx[2]);
548 int ihi = static_cast<int>((box_hi[0] - plo[0])/dx[0]-1);
549 int jhi = static_cast<int>((box_hi[1] - plo[1])/dx[1]-1);
550 int khi = static_cast<int>((box_hi[2] - plo[2])/dx[2]-1);
551
552 Box bx_old(IntVect(ilo,jlo,klo),IntVect(ihi,jhi,khi));
553
554 int mod_ilo = ilo%ref_ratio[lev_for_box-1][0];
555 int mod_jlo = jlo%ref_ratio[lev_for_box-1][1];
556
557 int mod_ihi = (ihi+1)%ref_ratio[lev_for_box-1][0];
558 int mod_jhi = (jhi+1)%ref_ratio[lev_for_box-1][1];
559
560 if (mod_ilo != 0) {
561 ilo -= mod_ilo;
562 }
563 if (mod_jlo != 0) {
564 jlo -= mod_jlo;
565 }
566 if (mod_ihi != 0) {
567 ihi += ref_ratio[lev_for_box-1][0] - mod_ihi;
568 }
569 if (mod_jhi != 0) {
570 jhi += ref_ratio[lev_for_box-1][1] - mod_jhi;
571 }
572 Box bx(IntVect(ilo,jlo,klo),IntVect(ihi,jhi,khi));
573 if (mod_ilo !=0 || mod_jlo !=0 || mod_ihi != 0 || mod_jhi != 0) {
574 amrex::Print() << "Fine box on level " << lev_for_box << " adjusted from " << bx_old << " to " << bx << " to make it valid for refinement." << std::endl;
575 }
576 boxes_at_level[lev_for_box].push_back(bx);
577 amrex::Print() << "Saving in 'boxes at level' as " << bx << std::endl;
578 } // lev
579
580 } else if (num_indx_lo > 0) {
581
582 std::vector<int> box_lo(3), box_hi(3);
583 ppr.get("max_level",lev_for_box);
584 if (lev_for_box > 0 && lev_for_box <= max_level)
585 {
586 if (n_error_buf[0] != IntVect::TheZeroVector()) {
587 amrex::Abort("Don't use n_error_buf > 0 when setting the box explicitly");
588 }
589
590 ppr.getarr("in_box_lo_indices",box_lo,0,num_indx_lo);
591 ppr.getarr("in_box_hi_indices",box_hi,0,num_indx_hi);
592
594 box_lo[2] = geom[lev_for_box].Domain().smallEnd(2);
595 box_hi[2] = geom[lev_for_box].Domain().bigEnd(2);
596 }
597
598 Box bx(IntVect(box_lo[0],box_lo[1],box_lo[2]),IntVect(box_hi[0],box_hi[1],box_hi[2]));
599 const Box& domain = geom[lev_for_box].Domain();
600
601 if (!domain.contains(bx)) {
602 amrex::Print() << "\n";
603 amrex::Print() << "Box specified is " << bx << std::endl;
604 amrex::Print() << "But domain at level is " << domain << std::endl;
605 amrex::Error("Specified box doesn't fit in the domain");
606 }
607
608 const auto* dx = geom[lev_for_box].CellSize();
609 const Real* plo = geom[lev_for_box].ProbLo();
610 realbox = RealBox(plo[0]+ box_lo[0] *dx[0], plo[1]+ box_lo[1] *dx[1], plo[2]+ box_lo[2] *dx[2],
611 plo[0]+(box_hi[0]+1)*dx[0], plo[1]+(box_hi[1]+1)*dx[1], plo[2]+(box_hi[2]+1)*dx[2]);
612
613 Print() << "Reading " << bx << " at level " << lev_for_box << std::endl;
615
616 if(box_lo[0]%ref_ratio[lev_for_box-1][0] != 0){
617 amrex::Print()<< "Requested ilo in x-direction : " << box_lo[0] << std::endl;
618 amrex::Print() << "ilo = " << box_lo[0] << " is not divisible by ref_ratio in x direction = " <<
619 ref_ratio[lev_for_box-1][0] << std::endl;
620 amrex::Error("Adjust in_box_lo_indices in x-direction to be divisible by ref_ratio and try again");
621 }
622 if((box_hi[0]+1)%ref_ratio[lev_for_box-1][0] != 0){
623 amrex::Print()<< "Requested ihi in x-direction : " << box_hi[0] << std::endl;
624 amrex::Print() << "ihi+1 = " << box_hi[0]+1 << " is not divisible by ref_ratio in x direction = " <<
625 ref_ratio[lev_for_box-1][0] << std::endl;
626 amrex::Error("Adjust in_box_hi_indices in x-direction to be divisible by ref_ratio and try again");
627 }
628 if(box_lo[1]%ref_ratio[lev_for_box-1][1] != 0){
629 amrex::Print()<< "Requested jlo in y-direction : " << box_lo[1] << std::endl;
630 amrex::Print() << "jlo = " << box_lo[1] << " is not divisible by ref_ratio in y direction = " <<
631 ref_ratio[lev_for_box-1][1] << std::endl;
632 amrex::Error("Adjust in_box_lo_indices in y-direction to be divisible by ref_ratio and try again");
633 }
634 if((box_hi[1]+1)%ref_ratio[lev_for_box-1][1] != 0){
635 amrex::Print()<< "Requested jhi in y-direction : " << box_hi[1] << std::endl;
636 amrex::Print() << "jhi+1 = " << box_hi[1]+1 << " is not divisible by ref_ratio in y direction = " <<
637 ref_ratio[lev_for_box-1][1] << std::endl;
638 amrex::Error("Adjust in_box_hi_indices in y-direction to be divisible by ref_ratio and try again");
639 }
640 if(box_lo[2]%ref_ratio[lev_for_box-1][2] != 0){
641 amrex::Print()<< "Requested klo in z-direction : " << box_lo[2] << std::endl;
642 amrex::Print() << "klo = " << box_lo[2] << " is not divisible by ref_ratio in z direction = " <<
643 ref_ratio[lev_for_box-1][2] << std::endl;
644 amrex::Error("Adjust in_box_lo_indices in z-direction to be divisible by ref_ratio and try again");
645 }
646 if((box_hi[2]+1)%ref_ratio[lev_for_box-1][2] != 0){
647 amrex::Print()<< "Requested khi in z-direction : " << box_hi[2] << std::endl;
648 amrex::Print() << "khi+1 = " << box_hi[2]+1 << " is not divisible by ref_ratio in z direction = " <<
649 ref_ratio[lev_for_box-1][2] << std::endl;
650 amrex::Error("Adjust in_box_hi_indices in z-direction to be divisible by ref_ratio and try again");
651 }
652
653 boxes_at_level[lev_for_box].push_back(bx);
654 Print() << "Saving in 'boxes at level' as " << bx << std::endl;
655 } // lev
656
657 } else if (num_indx_lo_crse > 0) {
658
659 std::vector<int> box_lo(3), box_hi(3);
660 ppr.get("max_level",lev_for_box);
661 if (lev_for_box > 0 && lev_for_box <= max_level)
662 {
663 if (n_error_buf[0] != IntVect::TheZeroVector()) {
664 amrex::Abort("Don't use n_error_buf > 0 when setting the box explicitly");
665 }
666
667 ppr.getarr("in_box_lo_indices_crse",box_lo,0,num_indx_lo_crse);
668 ppr.getarr("in_box_hi_indices_crse",box_hi,0,num_indx_hi_crse);
669
671 box_lo[2] = geom[lev_for_box-1].Domain().smallEnd(2);
672 box_hi[2] = geom[lev_for_box-1].Domain().bigEnd(2);
673 }
674
675 Box bx(IntVect(box_lo[0],box_lo[1],box_lo[2]),IntVect(box_hi[0],box_hi[1],box_hi[2]));
676
677 if (!geom[lev_for_box-1].Domain().contains(bx)) {
678 amrex::Print() << "\n";
679 amrex::Print() << "(Coarse) Box specified is " << bx << std::endl;
680 amrex::Print() << "But (coarse) domain at level is " << geom[lev_for_box-1].Domain() << std::endl;
681 amrex::Error("Specified box doesn't fit in the domain");
682 }
683
684 bx.refine(ref_ratio[lev_for_box-1]);
685
686 const auto* dx = geom[lev_for_box-1].CellSize();
687
688 const Real* plo = geom[lev_for_box].ProbLo();
689 realbox = RealBox(plo[0]+ box_lo[0] *dx[0], plo[1]+ box_lo[1] *dx[1], plo[2]+ box_lo[2] *dx[2],
690 plo[0]+(box_hi[0]+1)*dx[0], plo[1]+(box_hi[1]+1)*dx[1], plo[2]+(box_hi[2]+1)*dx[2]);
691
692 Print() << "Reading " << bx << " at level " << lev_for_box << std::endl;
694
695 boxes_at_level[lev_for_box].push_back(bx);
696 Print() << "Saving in 'boxes at level' as " << bx << std::endl;
697 } // lev
698 }
699
701
702 if (realbox.ok()) {
703 info.SetRealBox(realbox);
704 }
705 // The window is on the model clock. It stays out of info, whose times are Real
706 // and are compared against elapsed time; ErrorEst applies it instead.
707 double ref_min_time = std::numeric_limits<double>::lowest();
708 double ref_max_time = std::numeric_limits<double>::max();
709 ppr.query("start_time",ref_min_time);
710 ppr.query("end_time",ref_max_time);
711 if (ppr.countval("max_level") > 0) {
712 int ref_max_level; ppr.get("max_level",ref_max_level);
713 info.SetMaxLevel(ref_max_level);
714 }
715
716 if (ppr.countval("value_greater")) {
717 int num_val = ppr.countval("value_greater");
719 ppr.getarr("value_greater",value,0,num_val);
720 std::string field; ppr.get("field_name",field);
721 ref_tags.push_back(REMORAErrorTag(value,AMRErrorTag::GREATER,field,info));
722 }
723 else if (ppr.countval("value_less")) {
724 int num_val = ppr.countval("value_less");
726 ppr.getarr("value_less",value,0,num_val);
727 std::string field; ppr.get("field_name",field);
728 ref_tags.push_back(REMORAErrorTag(value,AMRErrorTag::LESS,field,info));
729 }
730 else if (ppr.countval("adjacent_difference_greater")) {
731 int num_val = ppr.countval("adjacent_difference_greater");
733 ppr.getarr("adjacent_difference_greater",value,0,num_val);
734 std::string field; ppr.get("field_name",field);
735 ref_tags.push_back(REMORAErrorTag(value,AMRErrorTag::GRAD,field,info));
736 }
737 else if (realbox.ok())
738 {
739 ref_tags.push_back(REMORAErrorTag(info));
740 } else {
741 Abort(std::string("Unrecognized refinement indicator for " + refinement_indicators[i]).c_str());
742 }
743 ref_tags.back().SetModelTimeWindow(ref_min_time, ref_max_time);
744 } // loop over criteria
745 } // if max_level > 0
746}
constexpr amrex::Real zero
mf_h setVal(geomdata.ProbHi(2))
AMREX_ALWAYS_ASSERT(!NSPeriodic||!EWPeriodic)
void operator()(amrex::TagBoxArray &tba, const amrex::MultiFab *mf, const amrex::MultiFab *mskr3d, char clearval, char tagval, amrex::Real time, int level, const amrex::Geometry &geom, const amrex::IntVect &mask_lo=amrex::IntVect::TheZeroVector(), const amrex::IntVect &mask_hi=amrex::IntVect::TheZeroVector()) const
int ncons
Number of conserved scalars in the state (temperature + salt + passive scalars + biology tracers)
Definition REMORA.H:1797
int zvel_bc() const noexcept
Definition REMORA.H:1450
int xvel_bc() const noexcept
Definition REMORA.H:1448
amrex::Vector< std::string > cons_names
Names of scalars for plotfile output.
Definition REMORA.H:1941
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
amrex::Vector< amrex::MultiFab * > cons_new
multilevel data container for current step's scalar data: temperature, salinity, passive tracer
Definition REMORA.H:393
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr
land/sea mask at cell centers (2D)
Definition REMORA.H:584
int yvel_bc() const noexcept
Definition REMORA.H:1449
amrex::Vector< amrex::MultiFab * > zvel_new
multilevel data container for current step's z velocities (largely unused; W stored separately)
Definition REMORA.H:399
amrex::Vector< amrex::Vector< amrex::Box > > boxes_at_level
the boxes specified at each level by tagging criteria
Definition REMORA.H:1686
amrex::Vector< amrex::MultiFab * > yvel_new
multilevel data container for current step's y velocities (v in ROMS)
Definition REMORA.H:397
amrex::Vector< int > num_boxes_at_level
how many boxes specified at each level by tagging criteria
Definition REMORA.H:1682
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.
virtual void ErrorEst(int lev, amrex::TagBoxArray &tags, amrex::Real time, int ngrow) override
Tag cells for refinement.
void FillBdyCCVels(int lev, amrex::MultiFab &mf_cc_vel)
Fill the physical boundary conditions for cell-centered velocity (diagnostic only)
std::string pp_prefix
default prefix for input file parameters
Definition REMORA.H:381
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:1949
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.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pn
horizontal scaling factor: 1 / dy (2D)
Definition REMORA.H:605
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:2170
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr3d
land/sea mask at cell centers, copied to all z levels (3D)
Definition REMORA.H:592
static amrex::Vector< REMORAErrorTag > ref_tags
Holds info for dynamically generated tagging criteria.
Definition REMORA.H:2066
static constexpr int cons_bc
static constexpr int t
cons component Temp_comp
static constexpr int u
static constexpr int v
static constexpr int null
void remora_dervort(const amrex::Box &bx, amrex::FArrayBox &derfab, int dcomp, int ncomp, const amrex::FArrayBox &datfab, const amrex::Array4< const amrex::Real > &pm, const amrex::Array4< const amrex::Real > &pn, const amrex::Array4< const amrex::Real > &, const amrex::Geometry &, amrex::Real, const int *, const int)