REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_Plotfile.cpp
Go to the documentation of this file.
1#include <REMORA.H>
2#include "AMReX_Interp_3D_C.H"
3#include "AMReX_PlotFileUtil.H"
4
5using namespace amrex;
6
8
9template<typename V, typename T>
10bool containerHasElement(const V& iterable, const T& query) {
11 return std::find(iterable.begin(), iterable.end(), query) != iterable.end();
12}
13
14// The vertical part of the nodal displacement a viewer adds to the Cartesian node position it
15// builds from the Header: nu_z = z_phys_nd - (prob_lo_z + k*dz), with dz and prob_lo from the
16// Geometry the Header describes this level with. rz > 1 is the expand_plotvars_to_unif_rr case,
17// where the Header's dz is the native one over rz and mf_nd carries rz node layers per native
18// layer: z is interpolated linearly in k between native node layers, so node rz*k coincides with
19// native node k. Fills component 2 only; fill_nodal_xy_displacement fills components 0 and 1.
20static void
21fill_nodal_z_displacement (MultiFab& mf_nd, const MultiFab& z_phys_nd, const Geometry& g, int rz)
22{
23 const Real dz = g.CellSizeArray()[2];
24 const Real zlo = g.ProbLoArray()[2];
25#ifdef _OPENMP
26#pragma omp parallel if (Gpu::notInLaunchRegion())
27#endif
28 for (MFIter mfi(mf_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi)
29 {
30 const Box& bx = mfi.tilebox();
31 Array4<Real> const& nu = mf_nd.array(mfi);
32 Array4<Real const> const& zp = z_phys_nd.const_array(mfi);
33 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int kf) noexcept
34 {
35 const int k = kf / rz;
36 const int r = kf - k * rz;
37 Real z = zp(i,j,k);
38 // z_phys_nd has one z ghost but only k = -1 is filled, so k+1 is read only when
39 // its weight is nonzero, which keeps it at or below the top node.
40 if (r > 0) { z += (Real(r) / Real(rz)) * (zp(i,j,k+1) - zp(i,j,k)); }
41 nu(i,j,kf,2) = z - (zlo + Real(kf) * dz);
42 });
43 }
44}
45
46// The horizontal part of the nodal displacement: nu_x = x - (prob_lo_x + i*dx) and likewise in
47// y, from the physical psi-point coordinates mf_xy (component 0 x, 1 y) that plot_node_coords
48// builds on the nodal 2D grids of the same level. The horizontal index of mf_nd is the native
49// one on every path, since the expand path refines only in z. Fills components 0 and 1.
50static void
51fill_nodal_xy_displacement (MultiFab& mf_nd, const MultiFab& mf_xy, const Geometry& g)
52{
53 const auto dx = g.CellSizeArray();
54 const auto plo = g.ProbLoArray();
55#ifdef _OPENMP
56#pragma omp parallel if (Gpu::notInLaunchRegion())
57#endif
58 for (MFIter mfi(mf_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi)
59 {
60 const Box& bx = mfi.tilebox();
61 Array4<Real> const& nu = mf_nd.array(mfi);
62 Array4<Real const> const& xy = mf_xy.const_array(mfi);
63 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
64 {
65 nu(i,j,k,0) = xy(i,j,0,0) - (plo[0] + Real(i) * dx[0]);
66 nu(i,j,k,1) = xy(i,j,0,1) - (plo[1] + Real(j) * dx[1]);
67 });
68 }
69}
70
71// Refine a face- or cell-centred MultiFab in z by rz onto dst, for the same expand path:
72// piecewise constant where the data is cell-centred in z, linear between layers where it is
73// nodal in z (the w faces).
74static void
75refine_in_z (const MultiFab& src, MultiFab& dst, int rz)
76{
77 const bool nodal_z = src.boxArray().ixType().nodeCentered(2);
78#ifdef _OPENMP
79#pragma omp parallel if (Gpu::notInLaunchRegion())
80#endif
81 for (MFIter mfi(dst, TilingIfNotGPU()); mfi.isValid(); ++mfi)
82 {
83 const Box& bx = mfi.tilebox();
84 Array4<Real> const& d = dst.array(mfi);
85 Array4<Real const> const& s = src.const_array(mfi);
86 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int kf) noexcept
87 {
88 const int k = kf / rz;
89 if (nodal_z) {
90 const int r = kf - k * rz;
91 Real v = s(i,j,k);
92 if (r > 0) { v += (Real(r) / Real(rz)) * (s(i,j,k+1) - s(i,j,k)); }
93 d(i,j,kf) = v;
94 } else {
95 d(i,j,kf) = s(i,j,k);
96 }
97 });
98 }
99}
100
101// Write plotfile to disk
102void
104{
105#ifndef REMORA_USE_NETCDF
106 amrex::ignore_unused(istep_for_plot);
107#endif
108 Vector<std::string> varnames_3d;
109 varnames_3d.insert(varnames_3d.end(), plot_var_names_3d.begin(), plot_var_names_3d.end());
110
111 Vector<std::string> varnames_2d;
112 varnames_2d.insert(varnames_2d.end(), plot_var_names_2d.begin(), plot_var_names_2d.end());
113
114 Vector<std::string> varnames_2d_rho;
115 Vector<std::string> varnames_2d_u;
116 Vector<std::string> varnames_2d_v;
117
118 const int ncomp_mf_3d = varnames_3d.size();
119 const auto ngrow_vars = IntVect(NGROW-1,NGROW-1,0);
120
121 // These are the ncomp for the 2D cell-centered, x-face-based, y-face-based MultiFabs respectively
122 int ncomp_mf_2d_rho = 0;
123 int ncomp_mf_2d_u = 0;
124 int ncomp_mf_2d_v = 0;
125
126 // Check to see if we found all the requested variables
127 for (auto plot_name : varnames_2d) {
128 {
129 if (plot_name == "zeta" ) {varnames_2d_rho.push_back(plot_name); ncomp_mf_2d_rho++;}
130 if (plot_name == "h" ) {varnames_2d_rho.push_back(plot_name); ncomp_mf_2d_rho++;}
131 if (plot_name == "f" ) {varnames_2d_rho.push_back(plot_name); ncomp_mf_2d_rho++;}
132 if (plot_name == "visc2") {varnames_2d_rho.push_back(plot_name); ncomp_mf_2d_rho++;}
133 for (int n = 0; n < ncons; ++n) {
134 const std::string diff2_name = std::string("diff2_") + cons_names[n];
135 if (plot_name == diff2_name) {
136 varnames_2d_rho.push_back(plot_name);
138 }
139 }
140 for (int n = 0; n < ncons; ++n) {
141 const std::string stflux_name = std::string("stflux_") + cons_names[n];
142 if (plot_name == stflux_name) {
143 varnames_2d_rho.push_back(plot_name);
145 }
146 }
147 if (plot_name == "lrflux") {varnames_2d_rho.push_back(plot_name); ncomp_mf_2d_rho++;}
148 if (plot_name == "lhflux") {varnames_2d_rho.push_back(plot_name); ncomp_mf_2d_rho++;}
149 if (plot_name == "srflux") {varnames_2d_rho.push_back(plot_name); ncomp_mf_2d_rho++;}
150 if (plot_name == "shflux") {varnames_2d_rho.push_back(plot_name); ncomp_mf_2d_rho++;}
151 if (plot_name == "mask_rho") {varnames_2d_rho.push_back(plot_name); ncomp_mf_2d_rho++;}
152 if (plot_name == "ubar" ) {varnames_2d_u.push_back(plot_name); ncomp_mf_2d_u++;}
153 if (plot_name == "sustr") {varnames_2d_u.push_back(plot_name); ncomp_mf_2d_u++;}
154 if (plot_name == "bustr") {varnames_2d_u.push_back(plot_name); ncomp_mf_2d_u++;}
155 if (plot_name == "mask_u") {varnames_2d_u.push_back(plot_name); ncomp_mf_2d_u++;}
156 if (plot_name == "vbar" ) {varnames_2d_v.push_back(plot_name); ncomp_mf_2d_v++;}
157 if (plot_name == "svstr") {varnames_2d_v.push_back(plot_name); ncomp_mf_2d_v++;}
158 if (plot_name == "bvstr") {varnames_2d_v.push_back(plot_name); ncomp_mf_2d_v++;}
159 if (plot_name == "mask_v") {varnames_2d_v.push_back(plot_name); ncomp_mf_2d_v++;}
160 }
161 }
162
163 // We fillpatch here because some of the derived quantities require derivatives
164 // which require ghost cells to be filled. Don't fill the boundary, though.
165 for (int lev = 0; lev <= finest_level; ++lev) {
172 }
173
174 // omega and w are built from u and v, so they have to be computed before the land
175 // faces of u and v are overwritten with the fill value below
179 for (int lev = 0; lev <= finest_level; ++lev) {
180 if (plot_omega) {
181 nc_omega[lev].define(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,IntVect(1,1,0));
182 }
183 if (plot_wvel) {
184 nc_wvel[lev].define(convert(grids[lev],IntVect(0,0,1)),dmap[lev],1,IntVect(1,1,0));
185 }
187 plot_wvel ? &nc_wvel[lev] : nullptr);
188 }
189 }
190
192 for (int lev = 0; lev <= finest_level; ++lev) {
194 }
195 } else if (plotfile_type == PlotfileType::netcdf) {
196 for (int lev = 0; lev <= finest_level; ++lev) {
198 }
199 } else {
200 amrex::Abort("Don't know this plotfile type");
201 }
202
203 // Array of 3D MultiFabs to hold the plotfile data
205 for (int lev = 0; lev <= finest_level; ++lev) {
207 plotMF[lev].setVal(1.234e20);
208 }
209
210 // Array of 2D MultiFabs to hold the plotfile data
214 for (int lev = 0; lev <= finest_level; ++lev) {
215 BoxArray ba(grids[lev]);
216 BoxList bl2d = ba.boxList();
217 for (auto& b : bl2d) {
218 b.setRange(2,0);
219 }
220 BoxArray ba2d(std::move(bl2d));
221 mf_2d_rho[lev].define(ba2d, dmap[lev], ncomp_mf_2d_rho, IntVect(0,0,0));
222 mf_2d_u[lev].define(ba2d, dmap[lev], ncomp_mf_2d_u , IntVect(0,0,0));
223 mf_2d_v[lev].define(ba2d, dmap[lev], ncomp_mf_2d_v , IntVect(0,0,0));
224 }
225
226
227 // Physical (x, y) of the psi points, for the nodal displacement and the particle positions
230#ifdef REMORA_USE_PARTICLES
232#endif
234
235 // Array of MultiFabs for nodal data
237 if (plot_nodal_data) {
238 for (int lev = 0; lev <= finest_level; ++lev) {
239 BoxArray nodal_grids(grids[lev]); nodal_grids.surroundingNodes();
241 mf_nd[lev].setVal(zero);
242 }
243 }
244
245 // Vector of MultiFabs for face-centered velocity
250 for (int lev = 0; lev <= finest_level; ++lev) {
251 BoxArray grid_stag_u(grids[lev]); grid_stag_u.surroundingNodes(0);
252 BoxArray grid_stag_v(grids[lev]); grid_stag_v.surroundingNodes(1);
253 BoxArray grid_stag_w(grids[lev]); grid_stag_w.surroundingNodes(2);
254 mf_u[lev].define(grid_stag_u, dmap[lev], 1, 0);
255 mf_v[lev].define(grid_stag_v, dmap[lev], 1, 0);
256 mf_w[lev].define(grid_stag_w, dmap[lev], 1, 0);
257 MultiFab::Copy(mf_u[lev],*xvel_new[lev],0,0,1,0);
258 MultiFab::Copy(mf_v[lev],*yvel_new[lev],0,0,1,0);
259 MultiFab::Copy(mf_w[lev],*zvel_new[lev],0,0,1,0);
260 }
261 }
262
263 // Array of MultiFabs for cell-centered velocity
265
266 if (containerHasElement(plot_var_names_3d, "x_velocity") ||
267 containerHasElement(plot_var_names_3d, "y_velocity") ||
268 containerHasElement(plot_var_names_3d, "z_velocity") ||
269 containerHasElement(plot_var_names_3d, "vorticity") ) {
270
271 for (int lev = 0; lev <= finest_level; ++lev) {
272 mf_cc_vel[lev].define(grids[lev], dmap[lev], AMREX_SPACEDIM, IntVect(1,1,0));
273 mf_cc_vel[lev].setVal(zero); // FillBdyCCVels below leaves corners alone
276 mf_cc_vel[lev].FillBoundary(geom[lev].periodicity());
277 } // lev
278
279 // Fill level 0 before the interpolation below carries its ghost cells onto
280 // the finer levels. Matches the fill the vorticity tagging criterion uses.
282
283 // We need ghost cells if computing vorticity
284 amrex::Interpolater* mapper = &cell_cons_interp;
285 if ( containerHasElement(plot_var_names_3d, "vorticity") ) {
286 for (int lev = 1; lev <= finest_level; ++lev) {
291
292 MultiFab mf_to_fill;
293 amrex::FillPatchTwoLevels(mf_cc_vel[lev], mf_cc_vel[lev].nGrowVect(), IntVect(0,0,0),
294 t_new[lev], cmf, ctime, fmf, ftime,
295 0, 0, mf_cc_vel[lev].nComp(), geom[lev-1], geom[lev],
297
298 // Redo the reflections foextrap just overwrote at the domain boundary
300 } // lev
301 } // if
302 } // if
303
304 int icomp_rho = 0;
305 for (auto plot_name : varnames_2d_rho)
306 {
307 if (plot_name == "zeta" ) {
308 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_rho[lev],*vec_Zt_avg1[lev],0,icomp_rho,1,0); }
309 icomp_rho++;
310 }
311 if (plot_name == "h" ) {
312 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_rho[lev],*vec_h[lev],0,icomp_rho,1,0); }
313 icomp_rho++;
314 }
315 if (plot_name == "f" ) {
316 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_rho[lev],*vec_fcor[lev],0,icomp_rho,1,0); }
317 icomp_rho++;
318 }
319 if (plot_name == "visc2" ) {
320 for (int lev = 0; lev <= finest_level; ++lev) {
321 if (vec_visc2_r[lev]->contains_nan(0, 1, 0, true) || vec_visc2_r[lev]->contains_inf(0, 1, 0, true)) {
322 amrex::Abort("Found while writing output: visc2 contains nan or inf");
323 }
324 for (MFIter mfi(mf_2d_rho[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
325 const Box& bx = mfi.validbox();
326 const int K = mfi.index();
327 auto dst = mf_2d_rho[lev].array(mfi, icomp_rho);
328 auto src = vec_visc2_r[lev]->const_array(K);
329 ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept {
330 dst(i,j,0) = src(i,j,0);
331 });
332 }
333 }
334 icomp_rho++;
335 }
336 for (int n = 0; n < ncons; ++n) {
337 const std::string diff2_name = std::string("diff2_") + cons_names[n];
338 if (plot_name == diff2_name) {
339 for (int lev = 0; lev <= finest_level; ++lev) {
340 for (MFIter mfi(mf_2d_rho[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) {
341 const Box& bx = mfi.validbox();
342 const int K = mfi.index();
343 auto dst = mf_2d_rho[lev].array(mfi, icomp_rho);
344 auto src = vec_diff2[lev]->const_array(K);
345 ParallelFor(makeSlab(bx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept {
346 dst(i,j,0) = src(i,j,0,n);
347 });
348 }
349 }
350 icomp_rho++;
351 }
352 }
353 for (int n = 0; n < ncons; ++n) {
354 const std::string stflux_name = std::string("stflux_") + cons_names[n];
355 if (plot_name == stflux_name) {
356 for (int lev = 0; lev <= finest_level; ++lev) {
357 MultiFab::Copy(mf_2d_rho[lev],*vec_stflux[lev],n,icomp_rho,1,0);
358 }
359 icomp_rho++;
360 }
361 }
362 if (plot_name == "lrflux" ) {
364 amrex::Abort("Attempting to write longwave radiation flux to plotfile. Variable not allocated when bulk_fluxes turned off");
365 }
366 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_rho[lev],*vec_lrflx[lev],0,icomp_rho,1,0); }
367 icomp_rho++;
368 }
369 if (plot_name == "lhflux" ) {
371 amrex::Abort("Attempting to write latent heat flux to plotfile. Variable not allocated when bulk_fluxes turned off");
372 }
373 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_rho[lev],*vec_lhflx[lev],0,icomp_rho,1,0); }
374 icomp_rho++;
375 }
376 if (plot_name == "srflux" ) {
378 amrex::Abort("Attempting to write shortwave radiation flux to plotfile. Variable not allocated when bulk_fluxes turned off");
379 }
380 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_rho[lev],*vec_srflx[lev],0,icomp_rho,1,0); }
381 icomp_rho++;
382 }
383 if (plot_name == "shflux" ) {
385 amrex::Abort("Attempting to write sensible heat flux to plotfile. Variable not allocated when bulk_fluxes turned off");
386 }
387 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_rho[lev],*vec_shflx[lev],0,icomp_rho,1,0); }
388 icomp_rho++;
389 }
390 if (plot_name == "mask_rho" ) {
391 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_rho[lev],*vec_mskr[lev],0,icomp_rho,1,0); }
392 icomp_rho++;
393 }
394 }
395
396 int icomp_u = 0;
397 for (auto plot_name : varnames_2d_u)
398 {
399 if (plot_name == "ubar" ) {
400 for (int lev = 0; lev <= finest_level; ++lev) {
401 MultiFab::Copy(mf_2d_u[lev],*vec_ubar[lev],0,icomp_u,1,0);
402 }
403 icomp_u++;
404 }
405 if (plot_name == "sustr" ) {
406 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_u[lev],*vec_sustr[lev],0,icomp_u,1,0); }
407 icomp_u++;
408 }
409 if (plot_name == "bustr" ) {
410 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_u[lev],*vec_bustr[lev],0,icomp_u,1,0); }
411 icomp_u++;
412 }
413 if (plot_name == "mask_u" ) {
414 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_u[lev],*vec_msku[lev],0,icomp_u,1,0); }
415 icomp_u++;
416 }
417 }
418
419 int icomp_v = 0;
420 for (auto plot_name : varnames_2d_v)
421 {
422 if (plot_name == "vbar" ) {
423 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_v[lev],*vec_vbar[lev],0,icomp_v,1,0); }
424 icomp_v++;
425 }
426 if (plot_name == "svstr" ) {
427 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_v[lev],*vec_svstr[lev],0,icomp_v,1,0); }
428 icomp_v++;
429 }
430 if (plot_name == "bvstr" ) {
431 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_v[lev],*vec_bvstr[lev],0,icomp_v,1,0); }
432 icomp_v++;
433 }
434 if (plot_name == "mask_v" ) {
435 for (int lev = 0; lev <= finest_level; ++lev) { MultiFab::Copy(mf_2d_v[lev],*vec_mskv[lev],0,icomp_v,1,0); }
436 icomp_v++;
437 }
438 }
439
440 for (int lev = 0; lev <= finest_level; ++lev)
441 {
442 int mf_comp = 0;
443
445
446 // Check each tracer we are about to write, so the abort can name the offender.
447 // One pass per component, rather than an all-component scan repeated once for
448 // every variable in the plot list. nGrowVect() keeps the ghost-cell coverage the
449 // previous no-argument contains_nan() had.
450 for (int i = 0; i < ncons; ++i) {
452 const IntVect ng = cons_new[lev]->nGrowVect();
454 amrex::Abort("Found while writing output: " + cons_names[i] +
455 " contains nan or inf");
456 }
457 }
458
459 // First, copy any of the conserved state variables into the output plotfile
460 for (int i = 0; i < ncons; ++i) {
462 MultiFab::Copy(plotMF[lev],*cons_new[lev],i,mf_comp,1,ngrow_vars);
463 mf_comp++;
464 }
465 } // ncons
466
467 // Next, check for velocities
468 if (containerHasElement(plot_var_names_3d, "x_velocity")) {
469 if (mf_cc_vel[lev].contains_nan(0,1) || mf_cc_vel[lev].contains_inf(0,1)) {
470 amrex::Abort("Found while writing output: u velocity contains nan or inf");
471 }
472 MultiFab::Copy(plotMF[lev], mf_cc_vel[lev], 0, mf_comp, 1, 0);
473 mf_comp += 1;
474 }
475 if (containerHasElement(plot_var_names_3d, "y_velocity")) {
476 if (mf_cc_vel[lev].contains_nan(1,1) || mf_cc_vel[lev].contains_inf(1,1)) {
477 amrex::Abort("Found while writing output: v velocity contains nan or inf");
478 }
479 MultiFab::Copy(plotMF[lev], mf_cc_vel[lev], 1, mf_comp, 1, 0);
480 mf_comp += 1;
481 }
482 if (containerHasElement(plot_var_names_3d, "z_velocity")) {
483 if (mf_cc_vel[lev].contains_nan(2,1) || mf_cc_vel[lev].contains_inf(2,1)) {
484 amrex::Abort("Found while writing output: z velocity contains nan or inf");
485 }
486 MultiFab::Copy(plotMF[lev], mf_cc_vel[lev], 2, mf_comp, 1, 0);
487 mf_comp += 1;
488 }
489
490 // Fill cell-centered location
491 Real dx = Geom()[lev].CellSizeArray()[0];
492 Real dy = Geom()[lev].CellSizeArray()[1];
493
494 // Next, check for location names -- if we write one we write all
495 // Note: the locations must be filled before the derived variables, to match
496 // the order of the names built in set3DPlotVariables
500 {
502#ifdef _OPENMP
503#pragma omp parallel if (Gpu::notInLaunchRegion())
504#endif
505 for (MFIter mfi(dmf, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
506 const Box& bx = mfi.tilebox();
507 const Array4<Real> loc_arr = dmf.array(mfi);
508 const Array4<Real const> zp_arr = vec_z_phys_nd[lev]->const_array(mfi);
509
510 const Real xlo = Geom()[lev].ProbLoArray()[0];
511 const Real ylo = Geom()[lev].ProbLoArray()[1];
512 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
513 loc_arr(i,j,k,0) = xlo + (i+Real(0.5)) * dx;
514 loc_arr(i,j,k,1) = ylo + (j+Real(0.5)) * dy;
515 loc_arr(i,j,k,2) = Real(0.125) * (zp_arr(i,j ,k ) + zp_arr(i+1,j ,k ) +
516 zp_arr(i,j+1,k ) + zp_arr(i+1,j+1,k ) +
517 zp_arr(i,j ,k+1) + zp_arr(i+1,j ,k+1) +
518 zp_arr(i,j+1,k+1) + zp_arr(i+1,j+1,k+1) );
519 });
520 } // mfi
522 } // if containerHasElement
523
524 // Define standard process for calling the functions in Derive.cpp
525 auto calculate_derived = [&](const std::string& der_name,
527 {
529 MultiFab dmf(plotMF[lev], make_alias, mf_comp, 1);
530#ifdef _OPENMP
531#pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
532#endif
533 for (MFIter mfi(dmf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
534 {
535 const Box& bx = mfi.tilebox();
536 auto& dfab = dmf[mfi];
537
538 if (der_name == "vorticity") {
539 auto const& sfab = mf_cc_vel[lev][mfi];
541 } else {
542 auto const& sfab = (*cons_new[lev])[mfi];
544 }
545 }
546
547 mf_comp++;
548 }
549 };
550
551 // Note: All derived variables must be computed in order of "derived_names" defined in REMORA.H
553
554 // The NetCDF writer takes omega on w-points from nc_omega, so the cell-centered
555 // copy is only filled for AMReX plotfiles
558 MultiFab mf_omega(convert(grids[lev],IntVect(0,0,1)), dmap[lev], 1, IntVect(0));
560 MultiFab dmf(plotMF[lev], make_alias, mf_comp, 1);
561#ifdef _OPENMP
562#pragma omp parallel if (Gpu::notInLaunchRegion())
563#endif
564 for (MFIter mfi(dmf, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
565 const Box& bx = mfi.tilebox();
566 const Array4<Real > dst = dmf.array(mfi);
567 const Array4<Real const> om = mf_omega.const_array(mfi);
568 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
569 dst(i,j,k) = myhalf * (om(i,j,k) + om(i,j,k+1));
570 });
571 }
572 }
573 mf_comp++;
574 }
575
576#ifdef REMORA_USE_PARTICLES
578 for (ParticlesNamesVector::size_type i = 0; i < particles_namelist.size(); i++) {
579 if (containerHasElement(plot_var_names_3d, std::string(particles_namelist[i]+"_count")))
580 {
581 MultiFab temp_dat(plotMF[lev].boxArray(), plotMF[lev].DistributionMap(), 1, 0);
582 temp_dat.setVal(0);
584 MultiFab::Copy(plotMF[lev], temp_dat, 0, mf_comp, 1, 0);
585 mf_comp += 1;
586 }
587 }
588
589 Vector<std::string> particle_mesh_plot_names(0);
591 for (int i = 0; i < particle_mesh_plot_names.size(); i++) {
594 MultiFab temp_dat(plotMF[lev].boxArray(), plotMF[lev].DistributionMap(), 1, 1);
595 temp_dat.setVal(0);
597 MultiFab::Copy(plotMF[lev], temp_dat, 0, mf_comp, 1, 0);
598 mf_comp += 1;
599 }
600 }
601#endif
602
603 if (plot_nodal_data) {
606 }
607 } // lev
608
610 {
611
613
614 if (finest_level == 0)
615 {
617 amrex::Print() << "Writing plotfile " << plotfilename << "\n";
630 Geom(),
633
634#ifdef REMORA_USE_PARTICLES
636#endif
637
638 }
639
640 } else { // multilevel
642 amrex::Print() << "Writing plotfile " << plotfilename << "\n";
643 int lev0 = 0;
644 [[maybe_unused]] int desired_ratio = std::max(std::max(ref_ratio[lev0][0],ref_ratio[lev0][1]),ref_ratio[lev0][2]);
645 bool any_ratio_one = ( ( (ref_ratio[lev0][0] == 1) || (ref_ratio[lev0][1] == 1) ) ||
646 (ref_ratio[lev0][2] == 1) );
647 for (int lev = 1; lev < finest_level; lev++) {
649 ( ( (ref_ratio[lev][0] == 1) || (ref_ratio[lev][1] == 1) ) ||
650 (ref_ratio[lev][2] == 1) );
651 }
656
657 mf2[0].define(grids[0], dmap[0], ncomp_mf_3d, 0);
658
659 // Copy level 0 as is
660 MultiFab::Copy(mf2[0],plotMF[0],0,0,plotMF[0].nComp(),0);
661
662 // Define a new multi-level array of Geometry's so that we pass the new "domain" at lev > 0
664 {Geom()[0].isPeriodic(0),Geom()[0].isPeriodic(1),Geom()[0].isPeriodic(2)};
665 g2[0].define(Geom()[0].Domain(),&(Geom()[0].ProbDomain()),0,periodicity.data());
666
667 r2[0] = IntVect(1,1,ref_ratio[0][0]);
668 for (int lev = 1; lev <= finest_level; ++lev) {
669 if (lev > 1) {
670 r2[lev-1][0] = 1;
671 r2[lev-1][1] = 1;
672 r2[lev-1][2] = r2[lev-2][2] * ref_ratio[lev-1][0];
673 }
674
675 mf2[lev].define(refine(grids[lev],r2[lev-1]), dmap[lev], ncomp_mf_3d, 0);
676
677 // Set the new problem domain
678 Box d2(Geom()[lev].Domain());
679 d2.refine(r2[lev-1]);
680
681 g2[lev].define(d2,&(Geom()[lev].ProbDomain()),0,periodicity.data());
682 }
683
684 // Everything the Header describes with g2 has to live on g2's grid, not only the
685 // cell data: a viewer places level lev's nodes with g2[lev]'s dz, so the nodal
686 // displacement and the face velocities are refined in z by the same ratio.
693 for (int lev = 1; lev <= finest_level; ++lev) {
694 const int rz = r2[lev-1][2];
695 if (plot_nodal_data) {
696 BoxArray nodal2(mf2[lev].boxArray()); nodal2.surroundingNodes();
697 mf_nd2[lev].define(nodal2, dmap[lev], AMREX_SPACEDIM, 0);
698 mf_nd2[lev].setVal(zero);
701 nd2_ptrs[lev] = &mf_nd2[lev];
702 }
704 mf_u2[lev].define(refine(mf_u[lev].boxArray(), r2[lev-1]), dmap[lev], 1, 0);
705 mf_v2[lev].define(refine(mf_v[lev].boxArray(), r2[lev-1]), dmap[lev], 1, 0);
706 mf_w2[lev].define(refine(mf_w[lev].boxArray(), r2[lev-1]), dmap[lev], 1, 0);
710 u2_ptrs[lev] = &mf_u2[lev];
711 v2_ptrs[lev] = &mf_v2[lev];
712 w2_ptrs[lev] = &mf_w2[lev];
713 }
714 }
715
716 // Make a vector of BCRec with default values so we can use it here -- note the values
717 // aren't actually used because we do PCInterp
718 amrex::Vector<amrex::BCRec> null_dom_bcs;
719 null_dom_bcs.resize(mf2[0].nComp());
720 for (int n = 0; n < mf2[0].nComp(); n++) {
721 for (int dir = 0; dir < AMREX_SPACEDIM; dir++) {
724 }
725 }
726
727 // Do piecewise interpolation of mf into mf2
728 for (int lev = 1; lev <= finest_level; ++lev) {
731 0, 0, mf2[lev].nComp(),
732 geom[lev], g2[lev],
734 r2[lev-1], mapper_c, null_dom_bcs, 0);
735 }
736
737 // Define an effective ref_ratio which is isotropic to be passed into WriteMultiLevelPlotfile
739 for (int lev = 0; lev < finest_level; ++lev) {
740 rr[lev] = IntVect(ref_ratio[lev][0],ref_ratio[lev][1],ref_ratio[lev][0]);
741 }
742
752 g2,
753 model_time(t_new[0]), istep, rr);
755
756#ifdef REMORA_USE_PARTICLES
758#endif
759 } else {
772 Geom(),
775#ifdef REMORA_USE_PARTICLES
777#endif
778 }
779 }
780 } // end multi-level
781
782 }
783#ifdef REMORA_USE_NETCDF
785 {
786 // Each level goes to its own file: level 0 to _d01, a refined level to _d02 and up.
787 // A level with more than one box is not handled; only its first subdomain is written.
788 for (int lev = 0; lev <= finest_level; ++lev) {
789 plotMF[lev].FillBoundary(geom[lev].periodicity());
791 plot_omega ? &nc_omega[lev] : nullptr,
792 plot_wvel ? &nc_wvel[lev] : nullptr);
793 }
794 } // end if plotfile_type == netcdf
795#endif
797 for (int lev = 0; lev <= finest_level; ++lev) {
799 }
800 } else if (plotfile_type == PlotfileType::netcdf) {
801 for (int lev = 0; lev <= finest_level; ++lev) {
803 }
804 } else {
805 amrex::Abort("Don't know this plotfile type");
806 }
807}
808
809/**
810 * @param plotfilename name of plotfile to write to
811 * @param nlevels number of levels to write out
812 * @param mf MultiFab of data to write out
813 * @param mf_nd Multifab of nodal data to write out
814 * @param varnames_3d 3D variable names to write out
815 * @param varnames_2d_rho 2D cell-centered variable names to write out
816 * @param varnames_2d_u 2D x-face-based variable names to write out
817 * @param varnames_2d_v 2D y-face-based variable names to write out
818 * @param my_geom geometry to use for writing plotfile
819 * @param time time at which to output
820 * @param level_steps vector over level of iterations
821 * @param rr refinement ratio to use for writing plotfile
822 * @param versionName version string for VisIt
823 * @param levelPrefix string to prepend to level number
824 * @param mfPrefix subdirectory for multifab data
825 * @param extra_dirs additional subdirectories within plotfile
826 */
827 void
837 const Vector<std::string>& varnames_3d,
838 const Vector<std::string>& varnames_2d_rho,
839 const Vector<std::string>& varnames_2d_u,
840 const Vector<std::string>& varnames_2d_v,
842 double time,
843 const Vector<int>& level_steps,
844 const Vector<IntVect>& rr,
845 const std::string &versionName,
846 const std::string &levelPrefix,
847 const std::string &mfPrefix,
848 const Vector<std::string>& extra_dirs) const
849{
850 BL_PROFILE("WriteMultiLevelPlotfileWithBathymetry()");
851
852 AMREX_ASSERT(nlevels <= mf.size());
853 AMREX_ASSERT(nlevels <= ref_ratio.size()+1);
855
856 AMREX_ASSERT(mf[0]->nComp() == varnames_3d.size());
857
858 // Every extra set must live on the grid the Header describes with my_geom, or a viewer
859 // places it wrongly: the nodal set has one more node layer than the level has cells.
860 for (int level = 0; level < nlevels; ++level) {
861 if (plot_nodal_data) {
862 const Box nb = mf_nd[level]->boxArray().minimalBox();
865 nb.length(2) == my_geom[level].Domain().length(2) + 1,
866 "plotfile nodal set does not match the geometry written to the Header");
867 }
872 "plotfile face sets do not match the geometry written to the Header");
873 }
874 }
875
876 bool callBarrier(false);
878 if (!extra_dirs.empty()) {
879 for (const auto& d : extra_dirs) {
880 const std::string ed = plotfilename+"/"+d;
882 }
883 }
884 ParallelDescriptor::Barrier();
885
886 if (ParallelDescriptor::MyProc() == ParallelDescriptor::NProcs()-1) {
888 for(int level(0); level < boxArrays.size(); ++level) {
889 boxArrays[level] = mf[level]->boxArray();
890 }
891
892 auto f = [this, plotfilename, nlevels, boxArrays, varnames_3d,
895 VisMF::IO_Buffer io_buffer(VisMF::IO_Buffer_Size);
896 std::string HeaderFileName(plotfilename + "/Header");
897 std::ofstream HeaderFile;
898 HeaderFile.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size());
899 HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out |
900 std::ofstream::trunc |
901 std::ofstream::binary);
907 };
908
909 if (AsyncOut::UseAsyncOut()) {
910 AsyncOut::Submit(std::move(f));
911 } else {
912 f();
913 }
914 }
915
916 std::string mf_nodal_prefix = "Nu_nd";
917 std::string mf_uface_prefix = "UFace";
918 std::string mf_vface_prefix = "VFace";
919 std::string mf_wface_prefix = "WFace";
920 std::string mf_2d_rho_prefix = "rho2d";
921 std::string mf_2d_u_prefix = "u2d";
922 std::string mf_2d_v_prefix = "v2d";
923
924 for (int level = 0; level <= finest_level; ++level)
925 {
926 if (AsyncOut::UseAsyncOut()) {
927 VisMF::AsyncWrite(*mf[level],
929 true);
930 if (plot_nodal_data) {
931 VisMF::AsyncWrite(*mf_nd[level],
933 true);
934 }
936 VisMF::AsyncWrite(*mf_u[level],
938 true);
939 VisMF::AsyncWrite(*mf_v[level],
941 true);
942 VisMF::AsyncWrite(*mf_w[level],
944 true);
945 }
946 if (mf_2d_rho[level]->nComp() > 0) {
947 VisMF::AsyncWrite(*mf_2d_rho[level],
949 true);
950 }
951 if (mf_2d_u[level]->nComp() > 0) {
952 VisMF::AsyncWrite(*mf_2d_u[level],
954 true);
955 }
956 if (mf_2d_v[level]->nComp() > 0) {
957 VisMF::AsyncWrite(*mf_2d_v[level],
959 true);
960 }
961 } else {
962 const MultiFab* data;
963 std::unique_ptr<MultiFab> mf_tmp;
964 if (mf[level]->nGrowVect() != 0) {
965 mf_tmp = std::make_unique<MultiFab>(mf[level]->boxArray(),
967 mf[level]->nComp(), 0, MFInfo(),
968 mf[level]->Factory());
969 MultiFab::Copy(*mf_tmp, *mf[level], 0, 0, mf[level]->nComp(), 0);
970 data = mf_tmp.get();
971 } else {
972 data = mf[level];
973 }
975 if (plot_nodal_data) {
977 }
982 }
983 if (mf_2d_rho[level]->nComp() > 0) {
985 }
986 if (mf_2d_u[level]->nComp() > 0) {
988 }
989 if (mf_2d_v[level]->nComp() > 0) {
991 }
992 }
993 } // level
994}
995
996/**
997 * @param HeaderFile output stream for header
998 * @param nlevels number of levels to write out
999 * @param bArray vector over levels of BoxArrays
1000 * @param varnames_3d 3D variable names to write out
1001 * @param varnames_2d 2D variable names to write out
1002 * @param my_geom geometry to use for writing plotfile
1003 * @param time time at which to output
1004 * @param level_steps vector over level of iterations
1005 * @param my_ref_ratio refinement ratio to use for writing plotfile
1006 * @param versionName version string for VisIt
1007 * @param levelPrefix string to prepend to level number
1008 * @param mfPrefix subdirectory for multifab data
1009 */
1010void
1012 [[maybe_unused]] int nlevels,
1013 const Vector<BoxArray> &bArray,
1014 const Vector<std::string> &varnames_3d,
1015 const Vector<std::string> &varnames_2d_rho,
1016 const Vector<std::string> &varnames_2d_u,
1017 const Vector<std::string> &varnames_2d_v,
1019 double time,
1020 const Vector<int> &level_steps,
1022 const std::string &versionName,
1023 const std::string &levelPrefix,
1024 const std::string &mfPrefix) const
1025{
1026 AMREX_ASSERT(nlevels <= bArray.size());
1027 AMREX_ASSERT(nlevels <= ref_ratio.size()+1);
1028 AMREX_ASSERT(nlevels <= level_steps.size());
1029
1030 int num_extra_mfs = plot_nodal_data ? 1 : 0; // for nodal, if it is written
1031 if (plot_staggered_vels) {
1032 num_extra_mfs += 3; // for nodal, which is always on
1033 }
1034
1035 HeaderFile.precision(17);
1036
1037 // ---- this is the generic plot file type name
1038 HeaderFile << versionName << '\n';
1039
1040 HeaderFile << varnames_3d.size() << '\n';
1041
1042 for (int ivar = 0; ivar < varnames_3d.size(); ++ivar) {
1043 HeaderFile << varnames_3d[ivar] << "\n";
1044 }
1045 HeaderFile << AMREX_SPACEDIM << '\n';
1046 HeaderFile << time << '\n';
1047 HeaderFile << finest_level << '\n';
1048 for (int i = 0; i < AMREX_SPACEDIM; ++i) {
1049 HeaderFile << my_geom[0].ProbLo(i) << ' ';
1050 }
1051 HeaderFile << '\n';
1052 for (int i = 0; i < AMREX_SPACEDIM; ++i) {
1053 HeaderFile << my_geom[0].ProbHi(i) << ' ';
1054 }
1055 HeaderFile << '\n';
1056 for (int i = 0; i < finest_level; ++i) {
1057 HeaderFile << my_ref_ratio[i][0] << ' ';
1058 }
1059 HeaderFile << '\n';
1060 for (int i = 0; i <= finest_level; ++i) {
1061 HeaderFile << my_geom[i].Domain() << ' ';
1062 }
1063 HeaderFile << '\n';
1064 for (int i = 0; i <= finest_level; ++i) {
1065 HeaderFile << level_steps[i] << ' ';
1066 }
1067 HeaderFile << '\n';
1068 for (int i = 0; i <= finest_level; ++i) {
1069 for (int k = 0; k < AMREX_SPACEDIM; ++k) {
1070 HeaderFile << my_geom[i].CellSize()[k] << ' ';
1071 }
1072 HeaderFile << '\n';
1073 }
1074 HeaderFile << (int) my_geom[0].Coord() << '\n';
1075 HeaderFile << "0\n";
1076
1077 for (int level = 0; level <= finest_level; ++level) {
1078 HeaderFile << level << ' ' << bArray[level].size() << ' ' << time << '\n';
1079 HeaderFile << level_steps[level] << '\n';
1080
1081 const IntVect& domain_lo = my_geom[level].Domain().smallEnd();
1082 for (int i = 0; i < bArray[level].size(); ++i)
1083 {
1084 // Need to shift because the RealBox ctor we call takes the
1085 // physical location of index (0,0,0). This does not affect
1086 // the usual cases where the domain index starts with 0.
1087 const Box& b = shift(bArray[level][i], -domain_lo);
1088 RealBox loc = RealBox(b, my_geom[level].CellSize(), my_geom[level].ProbLo());
1089 for (int n = 0; n < AMREX_SPACEDIM; ++n) {
1090 HeaderFile << loc.lo(n) << ' ' << loc.hi(n) << '\n';
1091 }
1092 }
1093
1095 }
1096 HeaderFile << num_extra_mfs << "\n";
1097 if (plot_nodal_data) {
1098 HeaderFile << "3" << "\n";
1099 HeaderFile << "amrexvec_nu_x" << "\n";
1100 HeaderFile << "amrexvec_nu_y" << "\n";
1101 HeaderFile << "amrexvec_nu_z" << "\n";
1102 std::string mf_nodal_prefix = "Nu_nd";
1103 for (int level = 0; level <= finest_level; ++level) {
1105 }
1106 }
1107 if (plot_staggered_vels) {
1108 HeaderFile << "1" << "\n"; // number of components in the multifab
1109 HeaderFile << "u_vel" << "\n";
1110 std::string mf_uface_prefix = "UFace";
1111 for (int level = 0; level <= finest_level; ++level) {
1113 }
1114 HeaderFile << "1" << "\n";
1115 HeaderFile << "v_vel" << "\n";
1116 std::string mf_vface_prefix = "VFace";
1117 for (int level = 0; level <= finest_level; ++level) {
1119 }
1120 HeaderFile << "1" << "\n";
1121 HeaderFile << "w_vel" << "\n";
1122 std::string mf_wface_prefix = "WFace";
1123 for (int level = 0; level <= finest_level; ++level) {
1125 }
1126 }
1127
1128 if (varnames_2d_rho.size() > 0) {
1129 HeaderFile << varnames_2d_rho.size() << "\n"; // number of components in the 2D rho multifab
1130 for (int ivar = 0; ivar < varnames_2d_rho.size(); ++ivar) {
1131 HeaderFile << varnames_2d_rho[ivar] << "\n";
1132 }
1133 std::string mf_2d_rho_prefix = "rho2d";
1134 for (int level = 0; level <= finest_level; ++level) {
1136 }
1137 }
1138
1139 if (varnames_2d_u.size() > 0) {
1140 HeaderFile << varnames_2d_u.size() << "\n"; // number of components in the 2D rho multifab
1141 for (int ivar = 0; ivar < varnames_2d_u.size(); ++ivar) {
1142 HeaderFile << varnames_2d_u[ivar] << "\n";
1143 }
1144 std::string mf_2d_u_prefix = "u2d";
1145 for (int level = 0; level <= finest_level; ++level) {
1147 }
1148 }
1149
1150 if (varnames_2d_v.size() > 0) {
1151 HeaderFile << varnames_2d_v.size() << "\n"; // number of components in the 2D v multifab
1152 for (int ivar = 0; ivar < varnames_2d_v.size(); ++ivar) {
1153 HeaderFile << varnames_2d_v[ivar] << "\n";
1154 }
1155 std::string mf_2d_v_prefix = "v2d";
1156 for (int level = 0; level <= finest_level; ++level) {
1158 }
1159 }
1160}
1161
1162/**
1163 * Physical (x, y) of the psi points (cell corners) on every level, for the horizontal part of
1164 * the plotfile's nodal displacement and for particle positions. Level 0 takes the grid's own
1165 * x_psi and y_psi. Each finer level is interpolated bilinearly from the level below, so that
1166 * a fine node that coincides with a coarse node gets the coarse node's position: a fine
1167 * level's own x_psi is rebuilt from its pm and pn, which on a stretched grid does not match its
1168 * parent's, and on a NetCDF grid does not carry the file's origin.
1169 *
1170 * @param[out] mf_xy per level, a 2-component nodal 2D MultiFab: x, then y
1171 */
1172void
1174{
1175 mf_xy.resize(finest_level+1);
1176 for (int lev = 0; lev <= finest_level; ++lev) {
1177 mf_xy[lev].define(vec_xp[lev]->boxArray(), vec_xp[lev]->DistributionMap(), 2, 0);
1178 }
1179
1180 MultiFab::Copy(mf_xy[0], *vec_xp[0], 0, 0, 1, 0);
1181 MultiFab::Copy(mf_xy[0], *vec_yp[0], 0, 1, 1, 0);
1182
1184 for (int n = 0; n < 2; ++n) {
1185 for (int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
1188 }
1189 }
1190 for (int lev = 1; lev <= finest_level; ++lev) {
1191 const IntVect rr(ref_ratio[lev-1][0], ref_ratio[lev-1][1], 1);
1193 geom[lev-1], geom[lev],
1196 }
1197}
1198
1199/**
1200 * Check the nodal z a viewer rebuilds from what WritePlotFile is about to write.
1201 *
1202 * nd[lev] is the nodal displacement on the grid g[lev] describes -- the native geometry, or g2
1203 * on the expand_plotvars_to_unif_rr path -- so a viewer's node is z = ProbLo(2) + k*dz + nu_z.
1204 * Per level, the lowest and highest node layers must reproduce z_phys_nd's bottom and top to
1205 * rounding. Per level pair, at every fine node that coincides with a coarse node, |z_f - z_c|
1206 * is taken over the patch interior and its perimeter separately: a perimeter node averages
1207 * fine ghost columns the parent interpolated, so it is reported, and only the interior is held
1208 * to check_plot_z_tol.
1209 *
1210 * @param[in] nd nodal displacement per level, as passed to the writer
1211 * @param[in] g geometry per level, as passed to the writer
1212 */
1213void
1215{
1216 if (!plot_nodal_data) { return; }
1217
1219 for (int lev = 0; lev <= finest_level; ++lev)
1220 {
1221 const MultiFab& nu = *nd[lev];
1222 z[lev].define(nu.boxArray(), nu.DistributionMap(), 1, 0);
1223 const Real dz = g[lev].CellSizeArray()[2];
1224 const Real zlo = g[lev].ProbLoArray()[2];
1225 const int klo = g[lev].Domain().smallEnd(2);
1226 const int Nf = g[lev].Domain().length(2); // nodes 0..Nf on this grid
1227 const int N = Geom(lev).Domain().length(2); // nodes 0..N on the native one
1228
1231 using ReduceTuple = typename decltype(reduce_data)::Type;
1232 for (MFIter mfi(z[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi)
1233 {
1234 const Box& bx = mfi.tilebox();
1235 Array4<Real> const& za = z[lev].array(mfi);
1236 Array4<Real const> const& na = nu.const_array(mfi);
1237 Array4<Real const> const& zp = vec_z_phys_nd[lev]->const_array(mfi);
1238 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1239 {
1240 za(i,j,k) = zlo + Real(k - klo) * dz + na(i,j,k,2);
1241 });
1242 reduce_op.eval(bx, reduce_data, [=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple
1243 {
1244 const Real eb = (k == klo) ? std::abs(za(i,j,k) - zp(i,j,0)) : Real(0.0);
1245 const Real et = (k == klo + Nf) ? std::abs(za(i,j,k) - zp(i,j,N)) : Real(0.0);
1246 return {eb, et};
1247 });
1248 }
1250 Real err_bot = amrex::get<0>(hv);
1251 Real err_top = amrex::get<1>(hv);
1252 ParallelDescriptor::ReduceRealMax(err_bot);
1253 ParallelDescriptor::ReduceRealMax(err_top);
1254 amrex::Print() << "Plot nodal z, level " << lev << ": |z_bottom - z_phys| "
1255 << err_bot << ", |z_top - z_phys| " << err_top << "\n";
1256 const Real self_tol = Real(1.e-10) * std::max(Real(1.0), std::abs(zlo));
1257 if (err_bot > self_tol || err_top > self_tol) {
1258 amrex::Abort("check_plot_z: the plotfile's nodal z does not reproduce z_phys_nd");
1259 }
1260 }
1261
1262 for (int lev = 1; lev <= finest_level; ++lev)
1263 {
1264 IntVect r;
1265 for (int d = 0; d < AMREX_SPACEDIM; ++d) {
1266 r[d] = g[lev].Domain().length(d) / g[lev-1].Domain().length(d);
1267 }
1268
1269 // Sample the fine nodes that coincide with coarse nodes, then bring them onto the
1270 // coarse level's layout. A coarse node no fine box reaches keeps the sentinel.
1271 BoxArray cba = z[lev].boxArray(); cba.coarsen(r);
1272 MultiFab zfc(cba, z[lev].DistributionMap(), 1, 0);
1273 for (MFIter mfi(zfc, TilingIfNotGPU()); mfi.isValid(); ++mfi)
1274 {
1275 const Box& bx = mfi.tilebox();
1276 Array4<Real> const& c = zfc.array(mfi);
1277 Array4<Real const> const& f = z[lev].const_array(mfi);
1278 const int r0 = r[0], r1 = r[1], r2 = r[2];
1279 ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
1280 {
1281 c(i,j,k) = f(i*r0, j*r1, k*r2);
1282 });
1283 }
1284 const Real sentinel = Real(1.e30);
1285 MultiFab zcc(z[lev-1].boxArray(), z[lev-1].DistributionMap(), 1, 0);
1286 zcc.setVal(sentinel);
1287 zcc.ParallelCopy(zfc);
1288
1289 // A coarse node is interior to the patch when the fine level covers all four coarse
1290 // cells around it; refinement spans the whole depth, so one cell row answers for k.
1291 iMultiFab covered = makeFineMask(grids[lev-1], dmap[lev-1], IntVect(1,1,0), grids[lev],
1292 refRatio(lev-1), geom[lev-1].periodicity(), 0, 1);
1293 const int kcell = grids[lev-1].minimalBox().smallEnd(2);
1294
1297 using ReduceTuple = typename decltype(reduce_data)::Type;
1298 for (MFIter mfi(zcc, TilingIfNotGPU()); mfi.isValid(); ++mfi)
1299 {
1300 const Box& bx = mfi.tilebox();
1301 Array4<Real const> const& a = zcc.const_array(mfi);
1302 Array4<Real const> const& b = z[lev-1].const_array(mfi);
1303 Array4<int const> const& m = covered.const_array(mfi);
1304 reduce_op.eval(bx, reduce_data, [=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple
1305 {
1306 if (a(i,j,k) >= sentinel) { return {Real(0.0), Real(0.0), 0, 0}; }
1307 const bool interior = m(i-1,j-1,kcell) && m(i,j-1,kcell) &&
1308 m(i-1,j ,kcell) && m(i,j ,kcell);
1309 const Real d = std::abs(a(i,j,k) - b(i,j,k));
1310 return interior ? ReduceTuple{d, Real(0.0), 1, 0} : ReduceTuple{Real(0.0), d, 0, 1};
1311 });
1312 }
1314 Real d_int = amrex::get<0>(hv), d_per = amrex::get<1>(hv);
1315 int n_int = amrex::get<2>(hv), n_per = amrex::get<3>(hv);
1316 ParallelDescriptor::ReduceRealMax(d_int);
1317 ParallelDescriptor::ReduceRealMax(d_per);
1318 ParallelDescriptor::ReduceIntSum(n_int);
1319 ParallelDescriptor::ReduceIntSum(n_per);
1320 amrex::Print() << "Plot nodal z, levels " << lev-1 << "/" << lev
1321 << ": coarse-fine max |dz| interior " << d_int << " (" << n_int << " nodes)"
1322 << ", perimeter " << d_per << " (" << n_per << " nodes)\n";
1323 if (check_plot_z_tol >= Real(0.0) && d_int > check_plot_z_tol) {
1324 amrex::Abort("check_plot_z: coarse and fine nodal z disagree inside the patch");
1325 }
1326 }
1327}
1328
1329/**
1330 * @param lev level to mask
1331 * @param fill_value fill value to mask with
1332 * @param fill_where value at cells where we will apply the mask. This is necessary because rivers
1333 */
1334void
1336{
1337 for (MFIter mfi(*cons_new[lev],false); mfi.isValid(); ++mfi) {
1338 Box gbx1 = mfi.growntilebox(IntVect(NGROW+1,NGROW+1,0));
1339 Box gbx_coeff = mfi.growntilebox(IntVect(NGROW,NGROW,0));
1340 Box ubx = mfi.grownnodaltilebox(0,IntVect(NGROW,NGROW,0));
1341 Box vbx = mfi.grownnodaltilebox(1,IntVect(NGROW,NGROW,0));
1342
1343 Array4<Real> const& Zt_avg1 = vec_Zt_avg1[lev]->array(mfi);
1344 Array4<Real> const& ubar = vec_ubar[lev]->array(mfi);
1345 Array4<Real> const& vbar = vec_vbar[lev]->array(mfi);
1346 Array4<Real> const& xvel = xvel_new[lev]->array(mfi);
1347 Array4<Real> const& yvel = yvel_new[lev]->array(mfi);
1348 Array4<Real> const& visc2 = vec_visc2_r[lev]->array(mfi);
1349 Array4<Real> const& diff2 = vec_diff2[lev]->array(mfi);
1350 Array4<Real> const& temp = cons_new[lev]->array(mfi,Temp_comp);
1351 Array4<Real> const& salt = cons_new[lev]->array(mfi,Salt_comp);
1352
1353 Array4<Real const> const& mskr = vec_mskr[lev]->array(mfi);
1354 Array4<Real const> const& msku = vec_msku[lev]->array(mfi);
1355 Array4<Real const> const& mskv = vec_mskv[lev]->array(mfi);
1356 const int ncons_local = ncons;
1357
1358 ParallelFor(makeSlab(gbx1,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int )
1359 {
1360 if (mskr(i,j,0) == zero) { // Explicitly compare to 0.0
1361 Zt_avg1(i,j,0) = fill_value;
1362 }
1363 });
1364 ParallelFor(gbx1, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1365 {
1366 if (mskr(i,j,0) == zero) { // Explicitly compare to 0.0
1367 temp(i,j,k) = fill_value;
1368 salt(i,j,k) = fill_value;
1369 }
1370 });
1371 ParallelFor(makeSlab(gbx_coeff,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int )
1372 {
1373 if (mskr(i,j,0) == zero) { // Explicitly compare to 0.0
1374 visc2(i,j,0) = fill_value;
1375 for (int n = 0; n < ncons_local; ++n) {
1376 diff2(i,j,0,n) = fill_value;
1377 }
1378 }
1379 });
1380 ParallelFor(makeSlab(ubx,2,0), 3, [=] AMREX_GPU_DEVICE (int i, int j, int , int n)
1381 {
1382 if (msku(i,j,0) == zero && ubar(i,j,0)==fill_where) { // Explicitly compare to 0.0
1383 ubar(i,j,0,n) = fill_value;
1384 }
1385 });
1386 ParallelFor(makeSlab(vbx,2,0), 3, [=] AMREX_GPU_DEVICE (int i, int j, int , int n)
1387 {
1388 if (mskv(i,j,0) == zero && vbar(i,j,0)==fill_where) { // Explicitly compare to 0.0
1389 vbar(i,j,0,n) = fill_value;
1390 }
1391 });
1392 ParallelFor(ubx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1393 {
1394 if (msku(i,j,0) == zero && xvel(i,j,k)==fill_where) { // Explicitly compare to 0.0
1395 xvel(i,j,k) = fill_value;
1396 }
1397 });
1398 ParallelFor(vbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1399 {
1400 if (mskv(i,j,0) == zero && yvel(i,j,k)==fill_where) { // Explicitly compare to 0.0
1401 yvel(i,j,k) = fill_value;
1402 }
1403 });
1404 } // mfi
1405 Gpu::streamSynchronize();
1406}
1407
1408/**
1409 * omega and w for the NetCDF writer, on the s_w points ROMS writes them at.
1410 *
1411 * Both are diagnosed from the current velocities, as ROMS does just before it writes a
1412 * history record: omega by compute_diagnostic_omega and w by compute_wvel (the same w
1413 * that z_velocity holds after a step). Computing w here rather than reading zvel_new
1414 * keeps it consistent with omega on a coarse level whose velocities have since been
1415 * averaged down.
1416 *
1417 * Both come out with one ghost column. A ghost column at a non-periodic edge copies its
1418 * interior neighbor, as ROMS bc_w3d does, and masked columns hold the NetCDF fill value.
1419 * Call this before mask_arrays_for_write puts fill values into the land faces of u and v.
1420 *
1421 * @param[in ] lev level to diagnose
1422 * @param[out] mf_omega omega, or nullptr to skip it
1423 * @param[out] mf_wvel w, or nullptr to skip it
1424 */
1425void
1427{
1428 BL_PROFILE("REMORA::compute_omega_and_w()");
1429
1430 const Real fill_value = netcdf_fill_value;
1431
1432 if (mf_omega) {
1434 }
1435 if (mf_wvel) {
1436 // w is built on omega, so reuse it when it is being written too
1437 if (mf_omega) {
1439 } else {
1441 }
1442 }
1443
1444 // The NetCDF writer adds one ghost column around the subdomain it writes, which on a
1445 // refined level is the first of boxes_at_level. A side of it is copied from the
1446 // interior unless it is a periodic domain edge, which FillBoundary has filled.
1447 const Box& dom = Geom(lev).Domain();
1448 const Box sub = (lev == 0) ? dom : boxes_at_level[lev][0];
1449 const IntVect slo = sub.smallEnd();
1450 const IntVect shi = sub.bigEnd();
1452 for (int dir = 0; dir < 2; ++dir) {
1453 copy_lo[dir] = !(Geom(lev).isPeriodic(dir) && slo[dir] == dom.smallEnd(dir));
1454 copy_hi[dir] = !(Geom(lev).isPeriodic(dir) && shi[dir] == dom.bigEnd(dir));
1455 }
1456
1457 for (MultiFab* mf : {mf_omega, mf_wvel}) {
1458 if (mf == nullptr) { continue; }
1459
1460 mf->FillBoundary(geom[lev].periodicity());
1461
1462#ifdef _OPENMP
1463#pragma omp parallel if (Gpu::notInLaunchRegion())
1464#endif
1465 for (MFIter mfi(*mf, TilingIfNotGPU()); mfi.isValid(); ++mfi)
1466 {
1467 // The writer skips grids outside the subdomain, and the clamp below would
1468 // reach outside their arrays
1469 if (!sub.contains(enclosedCells(mfi.validbox(),2))) { continue; }
1470
1471 Box gbx = mfi.growntilebox(IntVect(1,1,0));
1472 Array4<Real> const& a = mf->array(mfi);
1473 Array4<Real const> const& mskr = vec_mskr[lev]->const_array(mfi);
1474
1475 // The cell read is always inside the subdomain, so never one this loop writes
1476 ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1477 {
1478 int ii = i;
1479 int jj = j;
1480 if (i < slo[0] && copy_lo[0]) { ii = slo[0]; }
1481 if (i > shi[0] && copy_hi[0]) { ii = shi[0]; }
1482 if (j < slo[1] && copy_lo[1]) { jj = slo[1]; }
1483 if (j > shi[1] && copy_hi[1]) { jj = shi[1]; }
1484 if (ii != i || jj != j) {
1485 a(i,j,k) = a(ii,jj,k);
1486 }
1487 });
1488 ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
1489 {
1490 if (mskr(i,j,0) == zero) { // Explicitly compare to 0.0
1491 a(i,j,k) = fill_value;
1492 }
1493 });
1494 }
1495 }
1496 Gpu::streamSynchronize();
1497}
constexpr amrex::Real zero
constexpr amrex::Real myhalf
Coord
Coordinates.
#define NGROW
#define Temp_comp
#define Salt_comp
mf_h setVal(geomdata.ProbHi(2))
AMREX_ALWAYS_ASSERT(!NSPeriodic||!EWPeriodic)
pp_prob query("traditional", traditional)
bool containerHasElement(const V &iterable, const T &query)
static void fill_nodal_z_displacement(MultiFab &mf_nd, const MultiFab &z_phys_nd, const Geometry &g, int rz)
static void fill_nodal_xy_displacement(MultiFab &mf_nd, const MultiFab &mf_xy, const Geometry &g)
static void refine_in_z(const MultiFab &src, MultiFab &dst, int rz)
static PhysBCFunctNoOp null_bc_for_fill
void GetMeshPlotVar(const std::string &a_var_name, amrex::MultiFab &a_mf, const int a_lev)
bool isEmpty() const
void writePlotFile(const std::string &a_fname, const amrex::Vector< amrex::MultiFab > &a_xy) const
const ParticlesNamesVector & getNames() const
void GetMeshPlotVarNames(amrex::Vector< std::string > &a_names) const
static PlotfileType plotfile_type
Native or NetCDF plotfile output.
Definition REMORA.H:2070
int ncons
Number of conserved scalars in the state (temperature + salt + passive scalars + biology tracers)
Definition REMORA.H:1839
static bool plot_staggered_vels
Whether to write the staggered velocities (not averaged to cell centers)
Definition REMORA.H:2064
amrex::Vector< amrex::BCRec > domain_bcs_type
vector (over BCVars) of BCRecs
Definition REMORA.H:1772
amrex::Vector< std::string > cons_names
Names of scalars for plotfile output.
Definition REMORA.H:2009
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_fcor
coriolis factor (2D)
Definition REMORA.H:612
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< std::unique_ptr< amrex::MultiFab > > vec_lrflx
longwave radiation
Definition REMORA.H:497
void compute_omega_and_w(int lev, amrex::MultiFab *mf_omega, amrex::MultiFab *mf_wvel)
omega and w on the s_w points of the NetCDF output, ghost columns filled and masked
void WriteNCPlotFile(int istep, amrex::MultiFab const *plotMF, int lev=0, amrex::MultiFab const *mf_omega=nullptr, amrex::MultiFab const *mf_wvel=nullptr)
Write plotfile using NetCDF (wrapper)
amrex::Vector< amrex::MultiFab * > cons_new
multilevel data container for current step's scalar data: temperature, salinity, passive tracer
Definition REMORA.H:393
bool plot_omega
Whether to write omega, the S-coordinate vertical velocity, to NetCDF output.
Definition REMORA.H:2005
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr
land/sea mask at cell centers (2D)
Definition REMORA.H:584
void compute_diagnostic_omega(int lev, amrex::MultiFab &mf_omega)
omega (m/s) as ROMS writes it: from mass fluxes of the current velocities
amrex::Real plotfile_fill_value
fill value for masked arrays in amrex plotfiles
Definition REMORA.H:2023
void writeJobInfo(const std::string &dir) const
Write job info to stdout.
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_sustr
Surface stress in the u direction.
Definition REMORA.H:479
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yp
y_grid on psi-points (2D)
Definition REMORA.H:632
amrex::Vector< amrex::MultiFab * > zvel_new
multilevel data container for current step's z velocities (largely unused; W stored separately)
Definition REMORA.H:399
int foextrap_bc() const noexcept
Definition REMORA.H:1493
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_msku
land/sea mask at x-faces (2D)
Definition REMORA.H:586
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
bool plot_wvel
Whether to write z_velocity, the true vertical velocity, to NetCDF output as w.
Definition REMORA.H:2007
amrex::Vector< amrex::MultiFab * > yvel_new
multilevel data container for current step's y velocities (v in ROMS)
Definition REMORA.H:397
static bool plot_nodal_data
Whether to write nodal data (Nu_nd) to plotfiles.
Definition REMORA.H:2067
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_shflx
sensible heat flux
Definition REMORA.H:503
amrex::Vector< amrex::MultiFab * > xvel_new
multilevel data container for current step's x velocities (u in ROMS)
Definition REMORA.H:395
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_lhflx
latent heat flux
Definition REMORA.H:501
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskv
land/sea mask at y-faces (2D)
Definition REMORA.H:588
amrex::Vector< int > istep
which step?
Definition REMORA.H:1733
void mask_arrays_for_write(int lev, amrex::Real fill_value, amrex::Real fill_where)
Mask data arrays before writing output.
static int file_min_digits
Minimum number of digits in plotfile name or chunked history file.
Definition REMORA.H:2061
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_svstr
Surface stress in the v direction.
Definition REMORA.H:481
void FillBdyCCVels(int lev, amrex::MultiFab &mf_cc_vel)
Fill the physical boundary conditions for cell-centered velocity (diagnostic only)
void plot_node_coords(amrex::Vector< amrex::MultiFab > &mf_xy)
Physical (x, y) of the psi points on every level, for plotfile output.
amrex::Vector< amrex::Real > t_new
new time at each level, in seconds since start_time
Definition REMORA.H:1744
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:2017
void WriteGenericPlotfileHeaderWithBathymetry(std::ostream &HeaderFile, int nlevels, const amrex::Vector< amrex::BoxArray > &bArray, const amrex::Vector< std::string > &varnames_3d, const amrex::Vector< std::string > &varnames_2d_rho, const amrex::Vector< std::string > &varnames_2d_u, const amrex::Vector< std::string > &varnames_2d_v, const amrex::Vector< amrex::Geometry > &my_geom, double time, const amrex::Vector< int > &level_steps, const amrex::Vector< amrex::IntVect > &rr, const std::string &versionName, const std::string &levelPrefix, const std::string &mfPrefix) const
write out header data for an AMReX plotfile
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xp
x_grid on psi-points (2D)
Definition REMORA.H:630
void FillPatchNoBC(int lev, amrex::Real time, amrex::MultiFab &mf_to_be_filled, amrex::Vector< amrex::MultiFab * > const &mfs, const int bdy_var_type=BdyVars::null, const int icomp=0, const bool fill_all=true, const bool fill_set=false, 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 without applying boun...
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_vbar
barotropic y velocity (2D)
Definition REMORA.H:576
bool expand_plotvars_to_unif_rr
whether plotfile variables should be expanded to a uniform refinement ratio
Definition REMORA.H:2020
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_ubar
barotropic x velocity (2D)
Definition REMORA.H:574
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_bustr
Bottom stress in the u direction.
Definition REMORA.H:540
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_bvstr
Bottom stress in the v direction.
Definition REMORA.H:542
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_z_phys_nd
z coordinates at psi points (cell nodes)
Definition REMORA.H:473
void compute_wvel(int lev, amrex::MultiFab &mf_w)
"True" vertical velocity w (m/s) at w-points, as ROMS wvelocity
amrex::Real netcdf_fill_value
fill value for masked arrays in netcdf output
Definition REMORA.H:2025
void check_plot_nodal_z(const amrex::Vector< const amrex::MultiFab * > &nd, const amrex::Vector< amrex::Geometry > &g)
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pn
horizontal scaling factor: 1 / dy (2D)
Definition REMORA.H:605
amrex::Vector< std::string > plot_var_names_3d
Names of 3D variables to output to AMReX plotfile.
Definition REMORA.H:2001
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_stflux
Surface tracer flux; input arrays.
Definition REMORA.H:508
void WriteMultiLevelPlotfileWithBathymetry(const std::string &plotfilename, int nlevels, const amrex::Vector< const amrex::MultiFab * > &mf, const amrex::Vector< const amrex::MultiFab * > &mf_nd, const amrex::Vector< const amrex::MultiFab * > &mf_u, const amrex::Vector< const amrex::MultiFab * > &mf_v, const amrex::Vector< const amrex::MultiFab * > &mf_w, const amrex::Vector< const amrex::MultiFab * > &mf_2d_rho, const amrex::Vector< const amrex::MultiFab * > &mf_2d_u, const amrex::Vector< const amrex::MultiFab * > &mf_2d_v, const amrex::Vector< std::string > &varnames_3d, const amrex::Vector< std::string > &varnames_2d_rho, const amrex::Vector< std::string > &varnames_2d_u, const amrex::Vector< std::string > &varnames_2d_v, const amrex::Vector< amrex::Geometry > &my_geom, double time, const amrex::Vector< int > &level_steps, const amrex::Vector< amrex::IntVect > &rr, const std::string &versionName="HyperCLaw-V1.1", const std::string &levelPrefix="Level_", const std::string &mfPrefix="Cell", const amrex::Vector< std::string > &extra_dirs=amrex::Vector< std::string >()) const
write out particular data to an AMReX plotfile
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
void WritePlotFile(int istep)
main driver for writing AMReX plotfiles
amrex::Vector< std::string > plot_var_names_2d
Names of 2D variables to output to AMReX plotfile.
Definition REMORA.H:2003
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
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_srflx
Shortwave radiation flux [W/m²], defined at rho-points.
Definition REMORA.H:495
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_diff2
Harmonic diffusivity for temperature / salinity.
Definition REMORA.H:450
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)
void remora_dernull(const amrex::Box &, amrex::FArrayBox &, int, int, const amrex::FArrayBox &, const amrex::Array4< const amrex::Real > &, const amrex::Array4< const amrex::Real > &, const amrex::Array4< const amrex::Real > &, const amrex::Geometry &, amrex::Real, const int *, const int)