REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_NCPlotFile.cpp
Go to the documentation of this file.
1#include <iomanip>
2#include <iostream>
3#include <map>
4#include <string>
5#include <vector>
6#include <ctime>
7
8#ifdef _OPENMP
9#include <omp.h>
10#endif
11
12#include <AMReX_Utility.H>
13#include <AMReX_buildInfo.H>
14#include <AMReX_ParmParse.H>
15
16#include "REMORA.H"
17#include "REMORA_NCInterface.H"
18#include "REMORA_NCPlotFile.H"
19#include "REMORA_IndexDefines.H"
20
21using namespace amrex;
22
23namespace {
24/**
25 * \brief Was this 2D field named in remora.plot_vars_2d?
26 *
27 * Mirrors the name test the AMReX plotfile writer does when it walks plot_var_names_2d,
28 * so both writers answer to the same input key.
29 */
30bool
31plot_2d_var_requested (const amrex::Vector<std::string>& names, const std::string& nm)
32{
33 for (int i = 0; i < names.size(); ++i) {
34 if (names[i] == nm) { return true; }
35 }
36 return false;
37}
38
39/**
40 * \brief Accumulates each rank's hyperslabs and writes them with collective I/O.
41 *
42 * PnetCDF's collective APIs have to be called by every rank that opened the file,
43 * the same number of times and in the same order. REMORA's write loops are MFIter
44 * loops, so the number of hyperslabs is per-rank -- a plain put_all() inside those
45 * loops would mismatch and hang. ncmpi_put_varn_*_all() takes all of a rank's
46 * subarrays for one variable in a single call and tolerates counts that differ
47 * between ranks, including zero, which is exactly the shape we need.
48 *
49 * So the loops hand their hyperslabs here instead of writing, and flush() issues
50 * one collective call per variable at the end. Data is copied into m_entries on
51 * add(), so callers may reuse or destroy their staging buffer immediately -- unlike
52 * the nonblocking iput() path, where PnetCDF aliases buffers larger than 4 KB and
53 * only reads them at wait_all().
54 */
55class VarnCollector
56{
57public:
58 //! Stage one hyperslab of `varid`. `start`/`count` are in NetCDF dimension
59 //! order and `dptr` holds product(count) values.
60 void add (int varid,
61 const std::vector<MPI_Offset>& start,
62 const std::vector<MPI_Offset>& count,
63 const amrex::Real* dptr)
64 {
65 AMREX_ASSERT(start.size() == count.size());
66 MPI_Offset nelems = 1;
67 for (auto c : count) { nelems *= c; }
68
69 Entry& e = m_entries[varid];
70 e.starts.push_back(start);
71 e.counts.push_back(count);
72 e.data.insert(e.data.end(), dptr, dptr + nelems);
73 }
74
75 /**
76 * \brief Write everything staged so far. Collective: all ranks must call it.
77 *
78 * Ranks own different boxes, so they stage different variables -- a rank with
79 * no boxes stages nothing at all. The set of variables to write therefore has
80 * to be agreed on before any collective call, which is what the reduction over
81 * the touched-variable mask below does. Iterating that agreed set in ascending
82 * varid order gives every rank the same call sequence.
83 */
84 void flush (const ncutils::NCFile& ncf)
85 {
86 const int nvars = ncf.num_variables();
87 if (nvars <= 0) { return; }
88
89 std::vector<int> touched(nvars, 0);
90 for (const auto& kv : m_entries) {
91 if (kv.first >= 0 && kv.first < nvars) { touched[kv.first] = 1; }
92 }
93#ifdef AMREX_USE_MPI
95 amrex::ParallelContext::CommunicatorSub());
96#endif
97
98 for (int varid = 0; varid < nvars; ++varid) {
99 if (!touched[varid]) { continue; }
100
101 auto it = m_entries.find(varid);
102 if (it == m_entries.end()) {
103 // This rank has nothing for this variable, but the call is
104 // collective, so it still has to take part with zero subarrays.
105 ncutils::NCVar { ncf.ncid, varid }.put_varn_all(
106 0, nullptr, nullptr, static_cast<const amrex::Real*>(nullptr));
107 continue;
108 }
109
110 Entry& e = it->second;
111 const int num = static_cast<int>(e.starts.size());
112
113 // ncmpi_put_varn_* wants arrays of pointers into the start/count rows.
114 std::vector<MPI_Offset*> start_ptrs(num);
115 std::vector<MPI_Offset*> count_ptrs(num);
116 for (int i = 0; i < num; ++i) {
117 start_ptrs[i] = e.starts[i].data();
118 count_ptrs[i] = e.counts[i].data();
119 }
120
121 ncutils::NCVar { ncf.ncid, varid }.put_varn_all(
122 num, start_ptrs.data(), count_ptrs.data(), e.data.data());
123 }
124
125 m_entries.clear();
126 }
127
128 /**
129 * \brief Handle with the same put() shape as ncutils::NCVar, staging instead
130 * of writing, so the write loops read the same as they always did.
131 */
132 class StagedVar
133 {
134 public:
135 StagedVar (VarnCollector& coll, int varid) : m_coll(coll), m_varid(varid) {}
136
137 void put (const amrex::Real* dptr,
138 const std::vector<MPI_Offset>& start,
139 const std::vector<MPI_Offset>& count) const
140 {
141 m_coll.add(m_varid, start, count, dptr);
142 }
143
144 private:
145 VarnCollector& m_coll;
146 int m_varid;
147 };
148
149 //! Handle for staging writes to the named variable.
150 StagedVar var (const ncutils::NCFile& ncf, const std::string& name)
151 {
152 return StagedVar(*this, ncf.var(name).varid);
153 }
154
155private:
156 struct Entry {
157 std::vector<std::vector<MPI_Offset>> starts;
158 std::vector<std::vector<MPI_Offset>> counts;
159 std::vector<amrex::Real> data;
160 };
161
162 //! Keyed by varid and held in a std::map so iteration is in varid order.
163 std::map<int, Entry> m_entries;
164};
165} // namespace
166
167/**
168 * @param which_step current step for output
169 */
170void REMORA::WriteNCPlotFile(int which_step, MultiFab const* plotMF) {
172 // For right now we assume single level -- we will generalize this later to multilevel
173 int lev = 0;
174 int which_subdomain = 0;
175 int which_step_in_chunk = -1;
176
177 // Create filename
178 std::string plt_string;
179 std::string plotfilename;
181 plotfilename = plot_file_name + "_his";
182 } else {
184 }
185 // If chunking, concatenate with which file we're in
190 }
191
192 // Set the full IO path for NetCDF output
193 std::string FullPath = plotfilename;
194 if (lev == 0) {
195 const std::string &extension = amrex::Concatenate("_d", lev + 1, 2);
196 FullPath += extension + ".nc";
197 } else {
198 const std::string &extension = amrex::Concatenate("_d", lev + 1 + which_subdomain, 2);
199 FullPath += extension + ".nc";
200 }
201
202 //
203 // Check if this file/directory already exists and if so,
204 // have the IOProcessor move the existing
205 // file/directory to filename.old
206 //
207 if ((!REMORA::write_history_file) || (which_step == 0) || (which_step_in_chunk == 0)) {
208 if (amrex::ParallelDescriptor::IOProcessor()) {
209 if (amrex::FileExists(FullPath)) {
210 std::string newoldname(FullPath + ".old." + amrex::UniqueString());
211 amrex::Print() << "WriteNCPlotFile: " << FullPath << " exists. Renaming to: " << newoldname << std::endl;
212 if (std::rename(FullPath.c_str(), newoldname.c_str())) {
213 amrex::Abort("WriteNCPlotfile:: std::rename failed");
214 }
215 }
216 }
217 ParallelDescriptor::Barrier();
218 }
219
220 bool is_history;
221
223 is_history = true;
224 bool write_header = !(amrex::FileExists(FullPath));
225
226 auto ncf =
228 ncutils::NCFile::create(FullPath, NC_CLOBBER|NC_64BIT_DATA, amrex::ParallelContext::CommunicatorSub(), MPI_INFO_NULL) :
229 ncutils::NCFile::open(FullPath, NC_WRITE, amrex::ParallelContext::CommunicatorSub(), MPI_INFO_NULL);
230
231 amrex::Print() << "Writing into level " << lev << " NetCDF history file " << FullPath << std::endl;
232
234
235 } else {
236
237 is_history = false;
238 bool write_header = true;
239
240 // Open new netcdf file to write data
241 auto ncf = ncutils::NCFile::create(FullPath, NC_CLOBBER|NC_64BIT_DATA, amrex::ParallelContext::CommunicatorSub(), MPI_INFO_NULL);
242 amrex::Print() << "Writing level " << lev << " NetCDF plot file " << FullPath << std::endl;
243
245 }
246}
247
248/**
249 * @param lev level of data to output
250 * @param which_subdomain index of subdomain if lev != 0
251 * @param write_header whether to write a header
252 * @param ncf netcdf file object
253 * @param is_history whether the file being written is a history file
254 */
257{
258 // Number of cells in this "domain" at this level
259 std::vector<int> n_cells;
260
261 // We only do single-level writes when using NetCDF format
262 int flev = 1; //max_level;
263
264 Box subdomain;
265 if (lev == 0) {
266 subdomain = geom[lev].Domain();
267 } else {
269 }
270
271 int nx = subdomain.length(0);
272 int ny = subdomain.length(1);
273 int nz = subdomain.length(2);
274
275 n_cells.push_back(nx);
276 n_cells.push_back(ny);
277 n_cells.push_back(nz);
278
279 const std::string nt_name = "ocean_time";
280 const std::string ndim_name = "num_geo_dimensions";
281
282 const std::string flev_name = "FINEST_LEVEL";
283
284 const std::string nx_name = "NX";
285 const std::string ny_name = "NY";
286 const std::string nz_name = "NZ";
287
288 const std::string nx_r_name = "xi_rho";
289 const std::string ny_r_name = "eta_rho";
290 const std::string nz_r_name = "s_rho";
291
292 const std::string nx_u_name = "xi_u";
293 const std::string ny_u_name = "eta_u";
294
295 const std::string nx_v_name = "xi_v";
296 const std::string ny_v_name = "eta_v";
297
298 const std::string nx_p_name = "xi_psi";
299 const std::string ny_p_name = "eta_psi";
300 const std::string nz_w_name = "s_w";
301
302 if (write_header) {
303 ncf.enter_def_mode();
304 ncf.put_attr("title", "REMORA data ");
305 // The time dimension is unlimited so the record count reflects what was
306 // actually written rather than an up-front estimate from max_step/plot_int.
307 // Note PnetCDF does not prefill record variables, so every element of a
308 // time-varying variable has to be written explicitly (see the loops below).
309 ncf.def_dim(nt_name, NC_UNLIMITED);
310 ncf.def_dim(ndim_name, AMREX_SPACEDIM);
311
312 ncf.def_dim(nx_r_name, nx + 2);
313 ncf.def_dim(ny_r_name, ny + 2);
314 ncf.def_dim(nz_r_name, nz);
315
316 ncf.def_dim(nx_u_name, nx + 1);
317 ncf.def_dim(ny_u_name, ny + 2);
318
319 ncf.def_dim(nx_v_name, nx + 2);
320 ncf.def_dim(ny_v_name, ny + 1);
321
322 ncf.def_dim(nx_p_name, nx + 1);
323 ncf.def_dim(ny_p_name, ny + 1);
324
325 ncf.def_dim(nz_w_name, nz + 1);
326
327 ncf.def_dim(flev_name, flev);
328
329 ncf.def_dim(nx_name, n_cells[0]);
330 ncf.def_dim(ny_name, n_cells[1]);
331 ncf.def_dim(nz_name, n_cells[2]);
332
333 ncf.def_var("probLo", ncutils::NCDType::Real, { ndim_name });
334 ncf.var("probLo").put_attr("long_name","Low side of problem domain in internal AMReX grid");
335 ncf.var("probLo").put_attr("units","meter");
336 ncf.def_var("probHi", ncutils::NCDType::Real, { ndim_name });
337 ncf.var("probHi").put_attr("long_name","High side of problem domain in internal AMReX grid");
338 ncf.var("probHi").put_attr("units","meter");
339
340 ncf.def_var("Geom.smallend", NC_INT, { flev_name, ndim_name });
341 ncf.var("Geom.smallend").put_attr("long_name","Low side of problem domain in index space");
342 ncf.def_var("Geom.bigend", NC_INT, { flev_name, ndim_name });
343 ncf.var("Geom.bigend").put_attr("long_name","High side of problem domain in index space");
344 ncf.def_var("CellSize", ncutils::NCDType::Real, { flev_name, ndim_name });
345 ncf.var("CellSize").put_attr("long_name","Cell size on internal AMReX grid");
346 ncf.var("CellSize").put_attr("units","meter");
347
348 ncf.def_var("theta_s",ncutils::NCDType::Real,{});
349 ncf.var("theta_s").put_attr("long_name","S-coordinate surface control parameter");
350 ncf.def_var("theta_b",ncutils::NCDType::Real,{});
351 ncf.var("theta_b").put_attr("long_name","S-coordinate bottom control parameter");
352 ncf.def_var("hc",ncutils::NCDType::Real,{});
353 ncf.var("hc").put_attr("long_name","S-coordinate parameter, critical depth");
354 ncf.var("hc").put_attr("units","meter");
355
356 ncf.def_var("grid",NC_INT, {});
357 ncf.var("grid").put_attr("cf_role","grid_topology");
358 ncf.var("grid").put_attr("topology_dimension",std::vector({2}));
359 ncf.var("grid").put_attr("node_dimensions", "xi_psi eta_psi");
360 ncf.var("grid").put_attr("face_dimensions", "xi_rho: xi_psi (padding: both) eta_rho: eta_psi (padding: both)");
361 ncf.var("grid").put_attr("edge1_dimensions", "xi_u: xi_psi eta_u: eta_psi (padding: both)");
362 ncf.var("grid").put_attr("edge2_dimensions", "xi_v: xi_psi (padding: both) eta_v: eta_psi");
363 ncf.var("grid").put_attr("node_coordinates", "x_psi y_psi");
364 ncf.var("grid").put_attr("face_coordinates", "x_rho y_rho");
365 ncf.var("grid").put_attr("edge1_coordinates", "x_u y_u");
366 ncf.var("grid").put_attr("edge2_coordinates", "x_v y_v");
367 ncf.var("grid").put_attr("vertical_dimensions", "s_rho: s_w (padding: none)");
368
369 ncf.def_var("s_rho",ncutils::NCDType::Real, {nz_r_name});
370 ncf.var("s_rho").put_attr("long_name","S-coordinate at RHO-points");
371 ncf.var("s_rho").put_attr("field","s_rho, scalar");
372
373 ncf.def_var("s_w",ncutils::NCDType::Real, {nz_w_name});
374 ncf.var("s_w").put_attr("long_name","S-coordinate at W-points");
375 ncf.var("s_w").put_attr("field","s_w, scalar");
376
378 ncf.var("pm").put_attr("long_name","curvilinear coordinate metric in XI");
379 ncf.var("pm").put_attr("units","meter-1");
380 ncf.var("pm").put_attr("grid","grid");
381 ncf.var("pm").put_attr("location","face");
382 ncf.var("pm").put_attr("coordinates","x_rho y_rho");
383 ncf.var("pm").put_attr("field","pm, scalar");
384
386 ncf.var("pn").put_attr("long_name","curvilinear coordinate metric in ETA");
387 ncf.var("pn").put_attr("units","meter-1");
388 ncf.var("pn").put_attr("grid","grid");
389 ncf.var("pn").put_attr("location","face");
390 ncf.var("pn").put_attr("coordinates","x_rho y_rho");
391 ncf.var("pn").put_attr("field","pn, scalar");
392
394 ncf.var("f").put_attr("long_name","Coriolis parameter at RHO-points");
395 ncf.var("f").put_attr("units","second-1");
396 ncf.var("f").put_attr("grid","grid");
397 ncf.var("f").put_attr("location","face");
398 ncf.var("f").put_attr("coordinates","x_rho y_rho");
399 ncf.var("f").put_attr("field","coriolis, scalar");
400
401 ncf.def_var("x_rho",ncutils::NCDType::Real, {ny_r_name, nx_r_name});
402 ncf.var("x_rho").put_attr("long_name","x-locations of RHO-points");
403 ncf.var("x_rho").put_attr("units","meter");
404 ncf.var("x_rho").put_attr("field","x_rho, scalar");
405
406 ncf.def_var("y_rho",ncutils::NCDType::Real, {ny_r_name, nx_r_name});
407 ncf.var("y_rho").put_attr("long_name","y-locations of RHO-points");
408 ncf.var("y_rho").put_attr("units","meter");
409 ncf.var("y_rho").put_attr("field","y_rho, scalar");
410
412 ncf.var("x_u").put_attr("long_name","x-locations of U-points");
413 ncf.var("x_u").put_attr("units","meter");
414 ncf.var("x_u").put_attr("field","x_u, scalar");
415
417 ncf.var("y_u").put_attr("long_name","y-locations of U-points");
418 ncf.var("y_u").put_attr("units","meter");
419 ncf.var("y_u").put_attr("field","y_u, scalar");
420
422 ncf.var("x_v").put_attr("long_name","x-locations of V-points");
423 ncf.var("x_v").put_attr("units","meter");
424 ncf.var("x_v").put_attr("field","x_v, scalar");
425
427 ncf.var("y_v").put_attr("long_name","y-locations of V-points");
428 ncf.var("y_v").put_attr("units","meter");
429 ncf.var("y_v").put_attr("field","y_v, scalar");
430
431 ncf.def_var("x_psi",ncutils::NCDType::Real, {ny_p_name, nx_p_name});
432 ncf.var("x_psi").put_attr("long_name","x-locations of PSI-points");
433 ncf.var("x_psi").put_attr("units","meter");
434 ncf.var("x_psi").put_attr("field","x_psi, scalar");
435
436 ncf.def_var("y_psi",ncutils::NCDType::Real, {ny_p_name, nx_p_name});
437 ncf.var("y_psi").put_attr("long_name","y-locations of PSI-points");
438 ncf.var("y_psi").put_attr("units","meter");
439 ncf.var("y_psi").put_attr("field","y_psi, scalar");
440
441 ncf.def_var("ocean_time", ncutils::NCDType::Real, { nt_name });
442 ncf.var("ocean_time").put_attr("long_name","time since initialization");
443 ncf.var("ocean_time").put_attr("units","seconds since 0001-01-01 00:00:00");
444 ncf.var("ocean_time").put_attr("field","time, scalar, series");
445
446 ncf.def_var("Cs_r", ncutils::NCDType::Real, {nz_r_name});
447 ncf.var("Cs_r").put_attr("long_name", "S-coordinate stretching curves at RHO points");
448 ncf.var("Cs_r").put_attr("valid_min",std::vector({-one}));
449 ncf.var("Cs_r").put_attr("valid_max",std::vector({zero}));
450 ncf.var("Cs_r").put_attr("field","Cs_r, scalar");
451
452 ncf.def_var("Cs_w", ncutils::NCDType::Real, {nz_w_name});
453 ncf.var("Cs_w").put_attr("long_name", "S-coordinate stretching curves at W points");
454 ncf.var("Cs_w").put_attr("valid_min",std::vector({-one}));
455 ncf.var("Cs_w").put_attr("valid_max",std::vector({zero}));
456 ncf.var("Cs_w").put_attr("field","Cs_w, scalar");
457
458 ncf.def_var("h", ncutils::NCDType::Real, { ny_r_name, nx_r_name });
459 ncf.var("h").put_attr("long_name","bathymetry at RHO-points");
460 ncf.var("h").put_attr("units","meter");
461 ncf.var("h").put_attr("grid","grid");
462 ncf.var("h").put_attr("location","face");
463 ncf.var("h").put_attr("coordinates","x_rho y_rho");
464 ncf.var("h").put_attr("field","bath, scalar");
465
467 ncf.var("zeta").put_attr("long_name","free-surface");
468 ncf.var("zeta").put_attr("units","meter");
469 ncf.var("zeta").put_attr("time","ocean_time");
470 ncf.var("zeta").put_attr("grid","grid");
471 ncf.var("zeta").put_attr("location","face");
472 ncf.var("zeta").put_attr("coordinates","x_rho y_rho ocean_time");
473 ncf.var("zeta").put_attr("field","free-surface, scalar, series");
474
475 for (int n = 0; n < ncons; ++n) {
476 int comp = -1;
477 for (int i = 0; i < plot_var_names_3d.size(); i++) {
478 if (plot_var_names_3d[i] == cons_names[n]) comp = i;
479 }
480 if (comp >= 0) {
481 const std::string& nm = cons_names[n];
483 if (n == Temp_comp) {
484 ncf.var(nm).put_attr("long_name", "potential temperature");
485 ncf.var(nm).put_attr("units", "Celsius");
486 ncf.var(nm).put_attr("field", "temperature, scalar, series");
487 } else if (n == Salt_comp) {
488 ncf.var(nm).put_attr("long_name", "salinity");
489 ncf.var(nm).put_attr("field", "salinity, scalar, series");
490 } else {
491 ncf.var(nm).put_attr("long_name", nm);
492 ncf.var(nm).put_attr("field", nm + ", scalar, series");
493 }
494 ncf.var(nm).put_attr("time", "ocean_time");
495 ncf.var(nm).put_attr("grid", "grid");
496 ncf.var(nm).put_attr("location", "face");
497 ncf.var(nm).put_attr("coordinates", "x_rho y_rho s_rho ocean_time");
498 }
499 }
500
501 {
502 int comp = -1;
503 for (int i = 0; i < plot_var_names_3d.size(); i++) {
504 if (plot_var_names_3d[i] == "vorticity") comp = i;
505 }
506 if (comp >= 0) {
508 ncf.var("vorticity").put_attr("long_name","vorticity");
509 ncf.var("vorticity").put_attr("time","ocean_time");
510 ncf.var("vorticity").put_attr("grid","grid");
511 ncf.var("vorticity").put_attr("location","face");
512 ncf.var("vorticity").put_attr("coordinates","x_rho y_rho s_rho ocean_time");
513 ncf.var("vorticity").put_attr("field","vorticity, scalar, series");
514 }
515 } // end vorticity
516
517 // Output 2D horizontal mixing coefficients if using scaled_to_grid option
519 ncf.def_var_fill("visc2", ncutils::NCDType::Real, { ny_r_name, nx_r_name }, &netcdf_fill_value);
520 ncf.var("visc2").put_attr("long_name","horizontal harmonic viscosity coefficient at RHO-points");
521 ncf.var("visc2").put_attr("units","meter2 second-1");
522 ncf.var("visc2").put_attr("grid","grid");
523 ncf.var("visc2").put_attr("location","face");
524 ncf.var("visc2").put_attr("coordinates","x_rho y_rho");
525 ncf.var("visc2").put_attr("field","visc2, scalar");
526
527 for (int n = 0; n < ncons; ++n) {
528 const std::string nm = std::string("diff2_") + cons_names[n];
530 ncf.var(nm).put_attr("long_name", std::string("horizontal harmonic diffusivity coefficient for ") + cons_names[n] + " at RHO-points");
531 ncf.var(nm).put_attr("units","meter2 second-1");
532 ncf.var(nm).put_attr("grid","grid");
533 ncf.var(nm).put_attr("location","face");
534 ncf.var(nm).put_attr("coordinates","x_rho y_rho");
535 ncf.var(nm).put_attr("field", nm + ", scalar");
536 }
537 }
538
540 ncf.var("u").put_attr("long_name","u-momentum component");
541 ncf.var("u").put_attr("units","meter second-1");
542 ncf.var("u").put_attr("time","ocean_time");
543 ncf.var("u").put_attr("grid","grid");
544 ncf.var("u").put_attr("location","edge1");
545 ncf.var("u").put_attr("coordinates","x_u y_u s_rho ocean_time");
546 ncf.var("u").put_attr("field","u-velocity, scalar, series");
547
549 ncf.var("v").put_attr("long_name","v-momentum component");
550 ncf.var("v").put_attr("units","meter second-1");
551 ncf.var("v").put_attr("time","ocean_time");
552 ncf.var("v").put_attr("grid","grid");
553 ncf.var("v").put_attr("location","edge2");
554 ncf.var("v").put_attr("coordinates","x_v y_v s_rho ocean_time");
555 ncf.var("v").put_attr("field","v-velocity, scalar, series");
556
558 ncf.var("ubar").put_attr("long_name","vertically integrated u-momentum component");
559 ncf.var("ubar").put_attr("units","meter second-1");
560 ncf.var("ubar").put_attr("time","ocean_time");
561 ncf.var("ubar").put_attr("grid","grid");
562 ncf.var("ubar").put_attr("location","edge1");
563 ncf.var("ubar").put_attr("coordinates","x_u y_u ocean_time");
564 ncf.var("ubar").put_attr("field","ubar-velocity, scalar, series");
565
567 ncf.var("vbar").put_attr("long_name","vertically integrated v-momentum component");
568 ncf.var("vbar").put_attr("units","meter second-1");
569 ncf.var("vbar").put_attr("time","ocean_time");
570 ncf.var("vbar").put_attr("grid","grid");
571 ncf.var("vbar").put_attr("location","edge2");
572 ncf.var("vbar").put_attr("coordinates","x_v y_v ocean_time");
573 ncf.var("vbar").put_attr("field","vbar-velocity, scalar, series");
574
575 ncf.def_var("sustr", ncutils::NCDType::Real, { nt_name, ny_u_name, nx_u_name });
576 ncf.var("sustr").put_attr("long_name","surface u-momentum stress");
577 ncf.var("sustr").put_attr("units","newton meter-2");
578 ncf.var("sustr").put_attr("time","ocean_time");
579 ncf.var("sustr").put_attr("grid","grid");
580 ncf.var("sustr").put_attr("location","edge1");
581 ncf.var("sustr").put_attr("coordinates","x_u y_u ocean_time");
582 ncf.var("sustr").put_attr("field","surface u-momentum stress, scalar, series");
583
584 ncf.def_var("svstr", ncutils::NCDType::Real, { nt_name, ny_v_name, nx_v_name });
585 ncf.var("svstr").put_attr("long_name","surface v-momentum stress");
586 ncf.var("svstr").put_attr("units","newton meter-2");
587 ncf.var("svstr").put_attr("time","ocean_time");
588 ncf.var("svstr").put_attr("grid","grid");
589 ncf.var("svstr").put_attr("location","edge2");
590 ncf.var("svstr").put_attr("coordinates","x_v y_v ocean_time");
591 ncf.var("svstr").put_attr("field","surface v-momentum stress, scalar, series");
592
593 ncf.def_var("mask_rho", ncutils::NCDType::Real, { nt_name, ny_r_name, nx_r_name });
594 ncf.var("mask_rho").put_attr("long_name","mask on RHO-points");
595 ncf.var("mask_rho").put_attr("time","ocean_time");
596 ncf.var("mask_rho").put_attr("flag_values",std::vector({Real(0.0),Real(1.0)}));
597 ncf.var("mask_rho").put_attr("flag_meanings","land water");
598
599 ncf.def_var("mask_u", ncutils::NCDType::Real, { nt_name, ny_u_name, nx_u_name });
600 ncf.var("mask_u").put_attr("long_name","mask on U-points");
601 ncf.var("mask_u").put_attr("time","ocean_time");
602 ncf.var("mask_u").put_attr("flag_values",std::vector({Real(0.0),Real(1.0)}));
603 ncf.var("mask_u").put_attr("flag_meanings","land water");
604
605 ncf.def_var("mask_v", ncutils::NCDType::Real, { nt_name, ny_v_name, nx_v_name });
606 ncf.var("mask_v").put_attr("long_name","mask on V-points");
607 ncf.var("mask_v").put_attr("time","ocean_time");
608 ncf.var("mask_v").put_attr("flag_values",std::vector({Real(0.0),Real(1.0)}));
609 ncf.var("mask_v").put_attr("flag_meanings","land water");
610
611 // Surface tracer fluxes, one per cell-centered tracer, matching what the AMReX
612 // plotfile writer already offers and requested through the same remora.plot_vars_2d
613 // key. vec_stflux is ncons wide and unconditionally allocated, so any tracer can be
614 // named. Components past temp and salt read zero unless something fills them --
615 // Fennel's air-sea gas exchange acts on the tracer directly rather than through this
616 // array -- so they show what the solver actually applied, which may be nothing.
617 for (int n = 0; n < ncons; ++n) {
618 const std::string nm = std::string("stflux_") + cons_names[n];
619 if (!plot_2d_var_requested(plot_var_names_2d, nm)) { continue; }
621 ncf.var(nm).put_attr("long_name", std::string("surface flux of ") + cons_names[n]);
622 // Kinematic, as ssflux is: the tracer's own units times meter second-1.
623 ncf.var(nm).put_attr("units", (n == Temp_comp) ? "Celsius meter second-1"
624 : "meter second-1");
625 ncf.var(nm).put_attr("time","ocean_time");
626 ncf.var(nm).put_attr("grid","grid");
627 ncf.var(nm).put_attr("location","face");
628 ncf.var(nm).put_attr("coordinates","x_rho y_rho ocean_time");
629 ncf.var(nm).put_attr("field", nm + ", scalar, series");
630 }
631
633 // Surface air temperature (Celsius)
635 ncf.var("Tair").put_attr("long_name","surface air temperature");
636 ncf.var("Tair").put_attr("units","Celsius");
637 ncf.var("Tair").put_attr("time","ocean_time");
638 ncf.var("Tair").put_attr("grid","grid");
639 ncf.var("Tair").put_attr("location","face");
640 ncf.var("Tair").put_attr("coordinates","x_rho y_rho ocean_time");
641 ncf.var("Tair").put_attr("field","Tair, scalar, series");
642
643 // Surface air pressure (Pascal)
645 ncf.var("Pair").put_attr("long_name","surface air pressure");
646 ncf.var("Pair").put_attr("units","Pascal");
647 ncf.var("Pair").put_attr("time","ocean_time");
648 ncf.var("Pair").put_attr("grid","grid");
649 ncf.var("Pair").put_attr("location","face");
650 ncf.var("Pair").put_attr("coordinates","x_rho y_rho ocean_time");
651 ncf.var("Pair").put_attr("field","Pair, scalar, series");
652
653 // Surface net heat flux (W/m2)
655 ncf.var("qnet").put_attr("long_name","surface net heat flux");
656 ncf.var("qnet").put_attr("units","watt meter-2");
657 ncf.var("qnet").put_attr("time","ocean_time");
658 ncf.var("qnet").put_attr("grid","grid");
659 ncf.var("qnet").put_attr("location","face");
660 ncf.var("qnet").put_attr("coordinates","x_rho y_rho ocean_time");
661 ncf.var("qnet").put_attr("field","surface heat flux, scalar, series");
662
663 // Surface net salt flux (kinematic)
664 ncf.def_var("ssflux", ncutils::NCDType::Real, {nt_name, ny_r_name, nx_r_name });
665 ncf.var("ssflux").put_attr("long_name","kinematic surface net salt flux, SALT*(E-P)/rhow");
666 ncf.var("ssflux").put_attr("units","meter second-1");
667 ncf.var("ssflux").put_attr("time","ocean_time");
668 ncf.var("ssflux").put_attr("grid","grid");
669 ncf.var("ssflux").put_attr("location","face");
670 ncf.var("ssflux").put_attr("coordinates","x_rho y_rho ocean_time");
671 ncf.var("ssflux").put_attr("field","surface net salt flux, scalar, series");
672
673 // Latent heat flux (W/m2)
674 ncf.def_var("latent", ncutils::NCDType::Real, {nt_name, ny_r_name, nx_r_name });
675 ncf.var("latent").put_attr("long_name","net latent heat flux");
676 ncf.var("latent").put_attr("units","watt meter-2");
677 ncf.var("latent").put_attr("time","ocean_time");
678 ncf.var("latent").put_attr("grid","grid");
679 ncf.var("latent").put_attr("location","face");
680 ncf.var("latent").put_attr("coordinates","x_rho y_rho ocean_time");
681 ncf.var("latent").put_attr("field","latent heat flux, scalar, series");
682
683 // Sensible heat flux (W/m2)
684 ncf.def_var("sensible", ncutils::NCDType::Real, {nt_name, ny_r_name, nx_r_name });
685 ncf.var("sensible").put_attr("long_name","net sensible heat flux");
686 ncf.var("sensible").put_attr("units","watt meter-2");
687 ncf.var("sensible").put_attr("time","ocean_time");
688 ncf.var("sensible").put_attr("grid","grid");
689 ncf.var("sensible").put_attr("location","face");
690 ncf.var("sensible").put_attr("coordinates","x_rho y_rho ocean_time");
691 ncf.var("sensible").put_attr("field","sensible heat flux, scalar, series");
692
693 // Longwave radiation (W/m2)
694 ncf.def_var("lwrad", ncutils::NCDType::Real, {nt_name, ny_r_name, nx_r_name });
695 ncf.var("lwrad").put_attr("long_name","net longwave radiation flux");
696 ncf.var("lwrad").put_attr("units","watt meter-2");
697 ncf.var("lwrad").put_attr("time","ocean_time");
698 ncf.var("lwrad").put_attr("grid","grid");
699 ncf.var("lwrad").put_attr("location","face");
700 ncf.var("lwrad").put_attr("coordinates","x_rho y_rho ocean_time");
701 ncf.var("lwrad").put_attr("field","longwave radiation, scalar, series");
702
703 // Shortwave radiation (W/m2)
704 ncf.def_var("swrad", ncutils::NCDType::Real, {nt_name, ny_r_name, nx_r_name });
705 ncf.var("swrad").put_attr("long_name","solar shortwave radiation flux");
706 ncf.var("swrad").put_attr("units","watt meter-2");
707 ncf.var("swrad").put_attr("time","ocean_time");
708 ncf.var("swrad").put_attr("grid","grid");
709 ncf.var("swrad").put_attr("location","face");
710 ncf.var("swrad").put_attr("coordinates","x_rho y_rho ocean_time");
711 ncf.var("swrad").put_attr("field","shortwave radiation, scalar, series");
712
713 // Evaporation rate (kg m-2 s-1)
714 ncf.def_var("evaporation", ncutils::NCDType::Real, {nt_name, ny_r_name, nx_r_name });
715 ncf.var("evaporation").put_attr("long_name","evaporation rate");
716 ncf.var("evaporation").put_attr("units","kilogram meter-2 second-1");
717 ncf.var("evaporation").put_attr("time","ocean_time");
718 ncf.var("evaporation").put_attr("grid","grid");
719 ncf.var("evaporation").put_attr("location","face");
720 ncf.var("evaporation").put_attr("coordinates","x_rho y_rho ocean_time");
721 ncf.var("evaporation").put_attr("field","evaporation, scalar, series");
722
723 // Rain rate (kg m-2 s-1)
725 ncf.var("rain").put_attr("long_name","rain fall rate");
726 ncf.var("rain").put_attr("units","kilogram meter-2 second-1");
727 ncf.var("rain").put_attr("time","ocean_time");
728 ncf.var("rain").put_attr("grid","grid");
729 ncf.var("rain").put_attr("location","face");
730 ncf.var("rain").put_attr("coordinates","x_rho y_rho ocean_time");
731 ncf.var("rain").put_attr("field","rain, scalar, series");
732 }
733 // Right now this is hard-wired to {temp, salt, tracer, u, v}
734 ncf.put_attr("space_dimension", std::vector<int> { AMREX_SPACEDIM });
735// ncf.put_attr("current_time", std::vector<double> { time });
736 ncf.put_attr("start_time", std::vector<double> { start_bdy_time });
737 ncf.put_attr("CurrentLevel", std::vector<int> { flev });
738 ncf.put_attr("DefaultGeometry", std::vector<int> { amrex::DefaultGeometry().Coord() });
739
740 ncf.exit_def_mode();
741
742 // We are doing single-level writes but it doesn't have to be level 0
743 //
744 // Write out the header information.
745 //
746
747 Real dx[AMREX_SPACEDIM];
748 for (int i = 0; i < AMREX_SPACEDIM; i++) {
749 dx[i] = geom[lev].CellSize()[i];
750 }
751 const auto *base = geom[lev].ProbLo();
753
754 amrex::Vector<Real> probLo;
755 amrex::Vector<Real> probHi;
756 for (int i = 0; i < AMREX_SPACEDIM; i++) {
757 probLo.push_back(rb.lo(i));
758 probHi.push_back(rb.hi(i));
759 }
760
761 //nc_probLo.par_access(NC_COLLECTIVE);
762 // small variable data written by just the master proc
764 if (amrex::ParallelDescriptor::IOProcessor()) // only master proc
765 {
766 auto nc_probLo = ncf.var("probLo");
767
768 nc_probLo.put(probLo.data(), { 0 }, { AMREX_SPACEDIM });
769
770 auto nc_probHi = ncf.var("probHi");
771 //nc_probHi.par_access(NC_COLLECTIVE);
772 nc_probHi.put(probHi.data(), { 0 }, { AMREX_SPACEDIM });
773
774 amrex::Vector<int> smallend;
775 amrex::Vector<int> bigend;
776 for (int i = lev; i < flev; i++) {
777 smallend.clear();
778 bigend.clear();
779 for (int j = 0; j < AMREX_SPACEDIM; j++) {
780 smallend.push_back(subdomain.smallEnd(j));
781 bigend.push_back(subdomain.bigEnd(j));
782 }
783 auto nc_Geom_smallend = ncf.var("Geom.smallend");
784 //nc_Geom_smallend.par_access(NC_COLLECTIVE);
785 nc_Geom_smallend.put(smallend.data(), { static_cast<long long int>(i - lev), 0 }, { 1,
786 AMREX_SPACEDIM });
787
788 auto nc_Geom_bigend = ncf.var("Geom.bigend");
789 //nc_Geom_bigend.par_access(NC_COLLECTIVE);
790 nc_Geom_bigend.put(bigend.data(), { static_cast<long long int>(i - lev), 0 }, { 1,
791 AMREX_SPACEDIM });
792 }
793
794 amrex::Vector<Real> CellSize;
795 for (int i = lev; i < flev; i++) {
796 CellSize.clear();
797 for (Real &j : dx) {
798 CellSize.push_back(amrex::Real(j));
799 }
800 auto nc_CellSize = ncf.var("CellSize");
801 //nc_CellSize.par_access(NC_COLLECTIVE);
802 nc_CellSize.put(CellSize.data(), { static_cast<long long int>(i - lev), 0 }, { 1,
804 }
805 Real hc = solverChoice.tcline;
806 Real theta_s = solverChoice.theta_s;
807 Real theta_b = solverChoice.theta_b;
808 ncf.var("hc").put(&hc);
809 ncf.var("theta_s").put(&theta_s);
810 ncf.var("theta_b").put(&theta_b);
811
812 }
814
815 } // end if write_header
816
817 // Past this point every write is collective. The loops below stage their
818 // hyperslabs into the collector rather than writing them, because the number
819 // of hyperslabs a rank contributes depends on how many boxes it owns; the
820 // single flush() at the end turns each variable into one ncmpi_put_varn_*_all.
821 VarnCollector collector;
822
823 //
824 // We compute the offsets based on location of the box within the domain
825 //
827 long long local_start_nt = (is_history ? static_cast<long long>(adjusted_history_count) : static_cast<long long>(0));
828 long long local_nt = 1; // We write data for only one time
829
830 if (amrex::ParallelDescriptor::IOProcessor()) // only master proc
831 {
832 auto nc_plot_var = collector.var(ncf, "ocean_time");
833 //nc_plot_var.par_access(NC_COLLECTIVE);
835 }
836
837 // Check whether there are any nans or infs in variables that we will write out
838 if (vec_Zt_avg1[lev]->contains_nan() || vec_Zt_avg1[lev]->contains_inf()) {
839 amrex::Abort("Found while writing output: zeta contains nan or inf");
840 }
841 // Check every cell-centered tracer that is actually being written: temperature,
842 // salinity, the passive scalars and the biology tracers. plotMF is indexed by
843 // position in plot_var_names_3d, not by cons component, so look the name up the
844 // same way the write loops below do.
845 for (int n = 0; n < ncons; ++n) {
846 int comp = -1;
847 for (int i = 0; i < plot_var_names_3d.size(); i++) {
848 if (plot_var_names_3d[i] == cons_names[n]) comp = i;
849 }
850 if (comp < 0) { continue; }
851 if (plotMF->contains_nan(comp,1) || plotMF->contains_inf(comp,1)) {
852 amrex::Abort("Found while writing output: " + cons_names[n] +
853 " contains nan or inf");
854 }
855 }
857 amrex::Abort("Found while writing output: velocity u contains nan or inf");
858 }
859 if (vec_ubar[lev]->contains_nan(0,1) || vec_ubar[lev]->contains_inf(0,1)) {
860 amrex::Abort("Found while writing output: velocity ubar contains nan or inf");
861 }
863 amrex::Abort("Found while writing output: velocity v contains nan or inf");
864 }
865 if (vec_vbar[lev]->contains_nan(0,1) || vec_vbar[lev]->contains_inf(0,1)) {
866 amrex::Abort("Found while writing output: velocity vbar contains nan or inf");
867 }
868
869 for (MFIter mfi(*plotMF, false); mfi.isValid(); ++mfi) {
870 auto bx = mfi.validbox();
871 if (subdomain.contains(bx)) {
872 //
873 // We only include one grow cell at subdomain boundaries, not internal grid boundaries
874 //
875 Box tmp_bx(bx);
876 if (tmp_bx.smallEnd()[0] == subdomain.smallEnd()[0])
877 tmp_bx.growLo(0, 1);
878 if (tmp_bx.smallEnd()[1] == subdomain.smallEnd()[1])
879 tmp_bx.growLo(1, 1);
880 if (tmp_bx.bigEnd()[0] == subdomain.bigEnd()[0])
881 tmp_bx.growHi(0, 1);
882 if (tmp_bx.bigEnd()[1] == subdomain.bigEnd()[1])
883 tmp_bx.growHi(1, 1);
884 // amrex::Print() << " BX " << bx << std::endl;
885 // amrex::Print() << "TMP_BX " << tmp_bx << std::endl;
886
887 Box tmp_bx_2d(tmp_bx);
888 tmp_bx_2d.makeSlab(2, 0);
889
890 Box tmp_bx_1d(tmp_bx);
891 tmp_bx_1d.makeSlab(0, 0);
892 tmp_bx_1d.makeSlab(1, 0);
893
894 //
895 // These are the dimensions of the data we write for only this box
896 //
897 long long local_nx = tmp_bx.length()[0];
898 long long local_ny = tmp_bx.length()[1];
899 long long local_nz = tmp_bx.length()[2];
900
901 // We do the "+1" because the offset needs to start at 0
902 long long local_start_x = static_cast<long long>(tmp_bx.smallEnd()[0] + 1);
903 long long local_start_y = static_cast<long long>(tmp_bx.smallEnd()[1] + 1);
904 long long local_start_z = static_cast<long long>(tmp_bx.smallEnd()[2]);
905
906 if (write_header) {
907 // Only write out s_rho and s_w at x=0,y=0 to avoid NaNs
908 if (bx.contains(IntVect(0,0,0)))
909 {
910 {
911 amrex::Vector<amrex::Real> tmp_srho(local_nz);
912
913#ifdef AMREX_USE_GPU
914 Gpu::dtoh_memcpy(tmp_srho.data(), s_r.data(), sizeof(amrex::Real)*local_nz);
915#else
916 std::memcpy(tmp_srho.data(), s_r.data(), sizeof(amrex::Real)*local_nz);
917#endif
918 Gpu::streamSynchronize();
919
920 auto nc_plot_var = collector.var(ncf, "s_rho");
921 //nc_plot_var.par_access(NC_INDEPENDENT);
922 nc_plot_var.put(tmp_srho.data(), { local_start_z }, { local_nz });
923 }
924 {
925 amrex::Vector<amrex::Real> tmp_sw(local_nz+1);
926
927#ifdef AMREX_USE_GPU
928 Gpu::dtoh_memcpy(tmp_sw.data(), s_w.data(), sizeof(amrex::Real)*(local_nz+1));
929#else
930 std::memcpy(tmp_sw.data(), s_w.data(), sizeof(amrex::Real)*(local_nz+1));
931#endif
932 Gpu::streamSynchronize();
933
934 auto nc_plot_var = collector.var(ncf, "s_w");
935 //nc_plot_var.par_access(NC_INDEPENDENT);
936 nc_plot_var.put(tmp_sw.data(), { local_start_z }, { local_nz + 1});
937 }
938 {
939 amrex::Vector<amrex::Real> tmp_Csrho(local_nz);
940
941#ifdef AMREX_USE_GPU
942 Gpu::dtoh_memcpy(tmp_Csrho.data(), Cs_r.data(), sizeof(amrex::Real)*(local_nz));
943#else
944 std::memcpy(tmp_Csrho.data(), Cs_r.data(), sizeof(amrex::Real)*(local_nz));
945#endif
946 Gpu::streamSynchronize();
947
948 auto nc_plot_var = collector.var(ncf, "Cs_r");
949 //nc_plot_var.par_access(NC_INDEPENDENT);
950 nc_plot_var.put(tmp_Csrho.data(), { local_start_z }, { local_nz });
951 }
952 {
953 amrex::Vector<amrex::Real> tmp_Csw(local_nz+1);
954
955#ifdef AMREX_USE_GPU
956 Gpu::dtoh_memcpy(tmp_Csw.data(), Cs_w.data(), sizeof(amrex::Real)*(local_nz+1));
957#else
958 std::memcpy(tmp_Csw.data(), Cs_w.data(), sizeof(amrex::Real)*(local_nz+1));
959#endif
960
961 Gpu::streamSynchronize();
962
963 auto nc_plot_var = collector.var(ncf, "Cs_w");
964 //nc_plot_var.par_access(NC_INDEPENDENT);
965 nc_plot_var.put(tmp_Csw.data(), { local_start_z }, { local_nz + 1});
966 }
967 }
968
969 {
970 FArrayBox tmp_bathy;
971 tmp_bathy.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
972
973 tmp_bathy.template copy<RunOn::Device>((*vec_h[lev])[mfi.index()], 0, 0, 1);
974 Gpu::streamSynchronize();
975
976 auto nc_plot_var = collector.var(ncf, "h");
977 //nc_plot_var.par_access(NC_INDEPENDENT);
978 nc_plot_var.put(tmp_bathy.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
979 }
980
981 {
982 FArrayBox tmp_pm;
983 tmp_pm.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
984
985 tmp_pm.template copy<RunOn::Device>((*vec_pm[lev])[mfi.index()], 0, 0, 1);
986 Gpu::streamSynchronize();
987
988 auto nc_plot_var = collector.var(ncf, "pm");
989 //nc_plot_var.par_access(NC_INDEPENDENT);
990
991 nc_plot_var.put(tmp_pm.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
992 }
993
994 {
995 FArrayBox tmp_pn;
996 tmp_pn.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
997
998 tmp_pn.template copy<RunOn::Device>((*vec_pn[lev])[mfi.index()], 0, 0, 1);
999 Gpu::streamSynchronize();
1000
1001 auto nc_plot_var = collector.var(ncf, "pn");
1002 //nc_plot_var.par_access(NC_INDEPENDENT);
1003 nc_plot_var.put(tmp_pn.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1004 }
1005
1006 {
1007 FArrayBox tmp_f;
1008 tmp_f.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1009
1010 tmp_f.template copy<RunOn::Device>((*vec_fcor[lev])[mfi.index()], 0, 0, 1);
1011 Gpu::streamSynchronize();
1012
1013 auto nc_plot_var = collector.var(ncf, "f");
1014 //nc_plot_var.par_access(NC_INDEPENDENT);
1015 nc_plot_var.put(tmp_f.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1016 }
1017
1018 {
1019 FArrayBox tmp_xr;
1020 tmp_xr.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1021
1022 tmp_xr.template copy<RunOn::Device>((*vec_xr[lev])[mfi.index()], 0, 0, 1);
1023 Gpu::streamSynchronize();
1024
1025 auto nc_plot_var = collector.var(ncf, "x_rho");
1026 //nc_plot_var.par_access(NC_INDEPENDENT);
1027
1028 nc_plot_var.put(tmp_xr.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1029 }
1030
1031 {
1032 FArrayBox tmp_yr;
1033 tmp_yr.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1034
1035 tmp_yr.template copy<RunOn::Device>((*vec_yr[lev])[mfi.index()], 0, 0, 1);
1036 Gpu::streamSynchronize();
1037
1038 auto nc_plot_var = collector.var(ncf, "y_rho");
1039 //nc_plot_var.par_access(NC_INDEPENDENT);
1040 nc_plot_var.put(tmp_yr.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1041 }
1042 }
1043
1044 {
1045 FArrayBox tmp_zeta;
1046 tmp_zeta.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1047 tmp_zeta.template copy<RunOn::Device>((*vec_Zt_avg1[lev])[mfi.index()], 0, 0, 1);
1048 Gpu::streamSynchronize();
1049
1050 auto nc_plot_var = collector.var(ncf, "zeta");
1051 nc_plot_var.put(tmp_zeta.dataPtr(), { local_start_nt, local_start_y, local_start_x }, { local_nt, local_ny,
1052 local_nx });
1053 }
1054
1055 {
1056 FArrayBox tmp_mask_rho;
1057 tmp_mask_rho.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1058 tmp_mask_rho.template copy<RunOn::Device>((*vec_mskr[lev])[mfi.index()], 0, 0, 1);
1059 Gpu::streamSynchronize();
1060
1061 auto nc_plot_var = collector.var(ncf, "mask_rho");
1062 nc_plot_var.put(tmp_mask_rho.dataPtr(), { local_start_nt, local_start_y, local_start_x }, { local_nt, local_ny,
1063 local_nx });
1064 }
1065
1066 // stflux_*, one per tracer. Defined above under the same condition.
1067 for (int n = 0; n < ncons; ++n) {
1068 const std::string nm = std::string("stflux_") + cons_names[n];
1069 if (!plot_2d_var_requested(plot_var_names_2d, nm)) { continue; }
1070 FArrayBox tmp;
1071 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1072 tmp.template copy<RunOn::Device>((*vec_stflux[lev])[mfi.index()], n, 0, 1);
1073 Gpu::streamSynchronize();
1074
1075 auto nc_var = collector.var(ncf, nm);
1076 nc_var.put(tmp.dataPtr(), { local_start_nt, local_start_y, local_start_x },
1077 { local_nt, local_ny, local_nx });
1078 }
1079
1081 {
1082 const Real Hscale = solverChoice.rho0 * Cp;
1083 // The copy and the mult below are both async on the same stream, so the
1084 // sync has to follow the last of them and precede the host-side put().
1085 // Tair
1086 {
1087 FArrayBox tmp_Tair;
1088 tmp_Tair.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1089 tmp_Tair.template copy<RunOn::Device>((*vec_Tair[lev])[mfi.index()], 0, 0, 1);
1090 Gpu::streamSynchronize();
1091
1092 auto nc_plot_var = collector.var(ncf, "Tair");
1093 nc_plot_var.put(tmp_Tair.dataPtr(), { local_start_nt, local_start_y, local_start_x }, { local_nt, local_ny, local_nx });
1094 }
1095 // Pair
1096 {
1097 FArrayBox tmp_Pair;
1098 tmp_Pair.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1099 tmp_Pair.template copy<RunOn::Device>((*vec_Pair[lev])[mfi.index()], 0, 0, 1);
1100 Gpu::streamSynchronize();
1101
1102 auto nc_plot_var = collector.var(ncf, "Pair");
1103 nc_plot_var.put(tmp_Pair.dataPtr(), { local_start_nt, local_start_y, local_start_x }, { local_nt, local_ny, local_nx });
1104 }
1105 // qnet (stored °C m/s → write W/m²)
1106 {
1107 FArrayBox tmp;
1108 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1109
1110 // Copy stflux Temp component
1111 tmp.template copy<RunOn::Device>(
1112 (*vec_stflux[lev])[mfi.index()],
1113 Temp_comp, // source component
1114 0, // dest component
1115 1 // number of comps
1116 );
1117
1118 // Convert °C·m/s → W/m²
1119 tmp.mult<RunOn::Device>(Hscale);
1120
1121 Gpu::streamSynchronize();
1122
1123 auto nc_var = collector.var(ncf, "qnet");
1124 nc_var.put(tmp.dataPtr(),
1125 { local_start_nt, local_start_y, local_start_x },
1126 { local_nt, local_ny, local_nx });
1127 }
1128 // ssflux = surface net freshwater flux (kg/m²/s converted to m/s)
1129 {
1130 FArrayBox tmp;
1131 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1132
1133 // Copy stflux Salt component
1134 tmp.template copy<RunOn::Device>(
1135 (*vec_stflux[lev])[mfi.index()],
1136 Salt_comp, // source component
1137 0, // destination component
1138 1 // number of components
1139 );
1140
1141 Gpu::streamSynchronize();
1142
1143 auto nc_var = collector.var(ncf, "ssflux");
1144 nc_var.put(tmp.dataPtr(),
1145 { local_start_nt, local_start_y, local_start_x },
1146 { local_nt, local_ny, local_nx });
1147 }
1148 // latent (stored °C m/s → write W/m²)
1149 {
1150 FArrayBox tmp;
1151 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1152 tmp.template copy<RunOn::Device>((*vec_lhflx[lev])[mfi.index()], 0, 0, 1);
1153
1154 // Convert °C·m/s → W/m²
1155 tmp.mult<RunOn::Device>(Hscale);
1156
1157 Gpu::streamSynchronize();
1158
1159 auto nc_var = collector.var(ncf, "latent");
1160 nc_var.put(tmp.dataPtr(),
1161 { local_start_nt, local_start_y, local_start_x },
1162 { local_nt, local_ny, local_nx });
1163 }
1164 // sensible (stored °C m/s → write W/m²)
1165 {
1166 FArrayBox tmp;
1167 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1168 tmp.template copy<RunOn::Device>((*vec_shflx[lev])[mfi.index()], 0, 0, 1);
1169
1170 // Convert °C·m/s → W/m²
1171 tmp.mult<RunOn::Device>(Hscale);
1172
1173 Gpu::streamSynchronize();
1174
1175 auto nc_var = collector.var(ncf, "sensible");
1176 nc_var.put(tmp.dataPtr(),
1177 { local_start_nt, local_start_y, local_start_x },
1178 { local_nt, local_ny, local_nx });
1179 }
1180 // lwrad (stored °C m/s → write W/m²)
1181 {
1182 FArrayBox tmp;
1183 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1184 tmp.template copy<RunOn::Device>((*vec_lrflx[lev])[mfi.index()], 0, 0, 1);
1185
1186 // Convert °C·m/s → W/m²
1187 tmp.mult<RunOn::Device>(Hscale);
1188
1189 Gpu::streamSynchronize();
1190
1191 auto nc_var = collector.var(ncf, "lwrad");
1192 nc_var.put(tmp.dataPtr(),
1193 { local_start_nt, local_start_y, local_start_x },
1194 { local_nt, local_ny, local_nx });
1195 }
1196 // swrad, note this is stored explicitly as W/m², not degC m/s in REMORA.bulk_flux.cpp
1197 {
1198 FArrayBox tmp;
1199 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1200 tmp.template copy<RunOn::Device>((*vec_srflx[lev])[mfi.index()], 0, 0, 1);
1201 Gpu::streamSynchronize();
1202
1203 auto nc_var = collector.var(ncf, "swrad");
1204 nc_var.put(tmp.dataPtr(),
1205 { local_start_nt, local_start_y, local_start_x },
1206 { local_nt, local_ny, local_nx });
1207 }
1208 // evaporation
1209 {
1210 FArrayBox tmp;
1211 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1212 tmp.template copy<RunOn::Device>((*vec_evap[lev])[mfi.index()], 0, 0, 1);
1213 Gpu::streamSynchronize();
1214
1215 auto nc_var = collector.var(ncf, "evaporation");
1216 nc_var.put(tmp.dataPtr(),
1217 { local_start_nt, local_start_y, local_start_x },
1218 { local_nt, local_ny, local_nx });
1219 }
1220 // rain
1221 {
1222 FArrayBox tmp;
1223 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1224 tmp.template copy<RunOn::Device>((*vec_rain[lev])[mfi.index()], 0, 0, 1);
1225 Gpu::streamSynchronize();
1226
1227 auto nc_var = collector.var(ncf, "rain");
1228 nc_var.put(tmp.dataPtr(),
1229 { local_start_nt, local_start_y, local_start_x },
1230 { local_nt, local_ny, local_nx });
1231 }
1232 } // end output forcing
1233
1234 // **************************************************************************
1235 for (int n = 0; n < ncons; ++n) {
1236 int comp = -1;
1237 for (int i = 0; i < plot_var_names_3d.size(); i++) {
1238 if (plot_var_names_3d[i] == cons_names[n]) comp = i;
1239 }
1240 if (comp >= 0) {
1241 FArrayBox tmp;
1242 tmp.resize(tmp_bx, 1, amrex::The_Pinned_Arena());
1243 tmp.template copy<RunOn::Device>((*plotMF)[mfi.index()], comp, 0, 1);
1244 Gpu::streamSynchronize();
1245
1246 auto nc_plot_var = collector.var(ncf, cons_names[n]);
1247 nc_plot_var.put(tmp.dataPtr(), { local_start_nt, local_start_z, local_start_y, local_start_x }, { local_nt,
1248 local_nz, local_ny, local_nx });
1249 }
1250 }
1251 // **************************************************************************
1252
1253 // **************************************************************************
1254 { // Vorticity
1255 int comp = -1;
1256 for (int i = 0; i < plot_var_names_3d.size(); i++) {
1257 if (plot_var_names_3d[i] == "vorticity") comp = i;
1258 }
1259 if (comp >= 0) {
1260 FArrayBox tmp;
1261 tmp.resize(tmp_bx, 1, amrex::The_Pinned_Arena());
1262 tmp.template copy<RunOn::Device>((*plotMF)[mfi.index()], comp, 0, 1);
1263 Gpu::streamSynchronize();
1264
1266 nc_plot_var.put(tmp.dataPtr(), { local_start_nt, local_start_z, local_start_y, local_start_x }, { local_nt,
1267 local_nz, local_ny, local_nx });
1268 } // if vorticity exists in plotMF
1269 } // end vorticity
1270 // **************************************************************************
1271
1272 // **************************************************************************
1273 // Horizontal mixing coefficients (scaled_to_grid):
1274 // vertically homogeneous and time-invariant -> write static 2D fields once.
1275 // **************************************************************************
1277 { // visc2
1278 FArrayBox tmp;
1279 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1280 tmp.template copy<RunOn::Device>((*vec_visc2_r[lev])[mfi.index()], 0, 0, 1);
1281 Gpu::streamSynchronize();
1282
1283 auto nc_var = collector.var(ncf, "visc2");
1284 nc_var.put(tmp.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1285 }
1286
1287 // diff2_*
1288 for (int n = 0; n < ncons; ++n) {
1289 const std::string nm = std::string("diff2_") + cons_names[n];
1290 FArrayBox tmp;
1291 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1292 tmp.template copy<RunOn::Device>((*vec_diff2[lev])[mfi.index()], n, 0, 1);
1293 Gpu::streamSynchronize();
1294
1295 auto nc_var = collector.var(ncf, nm);
1296 nc_var.put(tmp.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1297 }
1298 }
1299
1300 } // subdomain
1301 } // mfi
1302
1303 // Flush at each loop boundary rather than once at the end, so the collector
1304 // only ever holds one loop's worth of staged data. All ranks reach these
1305 // points, which is what the collective call requires.
1306 collector.flush(ncf);
1307
1308 // Writing u (we loop over cons to get cell-centered box)
1309 for (MFIter mfi(*plotMF, false); mfi.isValid(); ++mfi) {
1310 Box bx = mfi.validbox();
1311
1312 if (subdomain.contains(bx)) {
1313 //
1314 // We only include one grow cell at subdomain boundaries, not internal grid boundaries
1315 //
1316 Box tmp_bx(bx);
1317 tmp_bx.surroundingNodes(0);
1318 if (tmp_bx.smallEnd()[1] == subdomain.smallEnd()[1])
1319 tmp_bx.growLo(1, 1);
1320 if (tmp_bx.bigEnd()[1] == subdomain.bigEnd()[1])
1321 tmp_bx.growHi(1, 1);
1322 Box tmp_bx_2d(tmp_bx);
1323 tmp_bx_2d.makeSlab(2, 0);
1324
1325 //
1326 // These are the dimensions of the data we write for only this box
1327 //
1328 long long local_nx = tmp_bx.length()[0];
1329 long long local_ny = tmp_bx.length()[1];
1330 long long local_nz = tmp_bx.length()[2];
1331
1332 // We do the "+1" because the offset needs to start at 0
1333 long long local_start_x = static_cast<long long>(tmp_bx.smallEnd()[0]);
1334 long long local_start_y = static_cast<long long>(tmp_bx.smallEnd()[1] + 1);
1335 long long local_start_z = static_cast<long long>(tmp_bx.smallEnd()[2]);
1336
1337 if (write_header) {
1338 {
1339 FArrayBox tmp;
1340 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1341 tmp.template copy<RunOn::Device>((*vec_xu[lev])[mfi.index()], 0, 0, 1);
1342 Gpu::streamSynchronize();
1343
1344 auto nc_plot_var = collector.var(ncf, "x_u");
1345 //nc_plot_var.par_access(NC_INDEPENDENT);
1346 nc_plot_var.put(tmp.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1347 }
1348 {
1349 FArrayBox tmp;
1350 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1351 tmp.template copy<RunOn::Device>((*vec_yu[lev])[mfi.index()], 0, 0, 1);
1352 Gpu::streamSynchronize();
1353
1354 auto nc_plot_var = collector.var(ncf, "y_u");
1355 //nc_plot_var.par_access(NC_INDEPENDENT);
1356 nc_plot_var.put(tmp.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1357 }
1358 }
1359
1360 {
1361 FArrayBox tmp;
1362 tmp.resize(tmp_bx, 1, amrex::The_Pinned_Arena());
1363 tmp.template copy<RunOn::Device>((*xvel_new[lev])[mfi.index()], 0, 0, 1);
1364 Gpu::streamSynchronize();
1365
1366 auto nc_plot_var = collector.var(ncf, "u");
1367 nc_plot_var.put(tmp.dataPtr(), { local_start_nt, local_start_z, local_start_y, local_start_x }, { local_nt,
1368 local_nz, local_ny, local_nx });
1369 }
1370
1371 {
1372 FArrayBox tmp;
1373 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1374 tmp.template copy<RunOn::Device>((*vec_ubar[lev])[mfi.index()], 0, 0, 1);
1375 Gpu::streamSynchronize();
1376
1377 auto nc_plot_var = collector.var(ncf, "ubar");
1378 nc_plot_var.put(tmp.dataPtr(), { local_start_nt, local_start_y, local_start_x }, { local_nt, local_ny, local_nx });
1379 }
1380 {
1381 FArrayBox tmp;
1382 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1383 tmp.template copy<RunOn::Device>((*vec_sustr[lev])[mfi.index()], 0, 0, 1);
1384 Gpu::streamSynchronize();
1385
1386 auto nc_plot_var = collector.var(ncf, "sustr");
1387 nc_plot_var.put(tmp.dataPtr(), { local_start_nt, local_start_y, local_start_x }, { local_nt, local_ny, local_nx });
1388 }
1389 {
1390 FArrayBox tmp;
1391 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1392 tmp.template copy<RunOn::Device>((*vec_msku[lev])[mfi.index()], 0, 0, 1);
1393 Gpu::streamSynchronize();
1394
1395 auto nc_plot_var = collector.var(ncf, "mask_u");
1396 nc_plot_var.put(tmp.dataPtr(), { local_start_nt, local_start_y, local_start_x }, { local_nt, local_ny, local_nx });
1397 }
1398 } // in subdomain
1399 } // mfi
1400
1401 collector.flush(ncf);
1402
1403 // Writing v (we loop over cons to get cell-centered box)
1404 for (MFIter mfi(*plotMF, false); mfi.isValid(); ++mfi) {
1405 Box bx = mfi.validbox();
1406
1407 if (subdomain.contains(bx)) {
1408 //
1409 // We only include one grow cell at subdomain boundaries, not internal grid boundaries
1410 //
1411 Box tmp_bx(bx);
1412 tmp_bx.surroundingNodes(1);
1413 if (tmp_bx.smallEnd()[0] == subdomain.smallEnd()[0])
1414 tmp_bx.growLo(0, 1);
1415 if (tmp_bx.bigEnd()[0] == subdomain.bigEnd()[0])
1416 tmp_bx.growHi(0, 1);
1417 // amrex::Print() << " BX " << bx << std::endl;
1418 // amrex::Print() << "TMP_BX " << tmp_bx << std::endl;
1419
1420 Box tmp_bx_2d(tmp_bx);
1421 tmp_bx_2d.makeSlab(2, 0);
1422
1423 //
1424 // These are the dimensions of the data we write for only this box
1425 //
1426 long long local_nx = tmp_bx.length()[0];
1427 long long local_ny = tmp_bx.length()[1];
1428 long long local_nz = tmp_bx.length()[2];
1429
1430 // We do the "+1" because the offset needs to start at 0
1431 long long local_start_x = static_cast<long long>(tmp_bx.smallEnd()[0] + 1);
1432 long long local_start_y = static_cast<long long>(tmp_bx.smallEnd()[1]);
1433 long long local_start_z = static_cast<long long>(tmp_bx.smallEnd()[2]);
1434
1435 if (write_header) {
1436 {
1437 FArrayBox tmp;
1438 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1439 tmp.template copy<RunOn::Device>((*vec_xv[lev])[mfi.index()], 0, 0, 1);
1440 Gpu::streamSynchronize();
1441
1442 auto nc_plot_var = collector.var(ncf, "x_v");
1443 //nc_plot_var.par_access(NC_INDEPENDENT);
1444 nc_plot_var.put(tmp.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1445 }
1446 {
1447 FArrayBox tmp;
1448 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1449 tmp.template copy<RunOn::Device>((*vec_yv[lev])[mfi.index()], 0, 0, 1);
1450 Gpu::streamSynchronize();
1451
1452 auto nc_plot_var = collector.var(ncf, "y_v");
1453 //nc_plot_var.par_access(NC_INDEPENDENT);
1454 nc_plot_var.put(tmp.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1455 }
1456 }
1457
1458 {
1459 FArrayBox tmp;
1460 tmp.resize(tmp_bx, 1, amrex::The_Pinned_Arena());
1461 tmp.template copy<RunOn::Device>((*yvel_new[lev])[mfi.index()], 0, 0, 1);
1462 Gpu::streamSynchronize();
1463
1464 auto nc_plot_var = collector.var(ncf, "v");
1465 nc_plot_var.put(tmp.dataPtr(), { local_start_nt, local_start_z, local_start_y, local_start_x }, { local_nt,
1466 local_nz, local_ny, local_nx });
1467 }
1468
1469 {
1470 FArrayBox tmp;
1471 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1472 tmp.template copy<RunOn::Device>((*vec_vbar[lev])[mfi.index()], 0, 0, 1);
1473 Gpu::streamSynchronize();
1474
1475 auto nc_plot_var = collector.var(ncf, "vbar");
1476 nc_plot_var.put(tmp.dataPtr(), { local_start_nt, local_start_y, local_start_x }, { local_nt, local_ny, local_nx });
1477 }
1478
1479 {
1480 FArrayBox tmp;
1481 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1482 tmp.template copy<RunOn::Device>((*vec_svstr[lev])[mfi.index()], 0, 0, 1);
1483 Gpu::streamSynchronize();
1484
1485 auto nc_plot_var = collector.var(ncf, "svstr");
1486 nc_plot_var.put(tmp.dataPtr(), { local_start_nt, local_start_y, local_start_x }, { local_nt, local_ny, local_nx });
1487 }
1488 {
1489 FArrayBox tmp;
1490 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1491 tmp.template copy<RunOn::Device>((*vec_mskv[lev])[mfi.index()], 0, 0, 1);
1492 Gpu::streamSynchronize();
1493
1494 auto nc_plot_var = collector.var(ncf, "mask_v");
1495 nc_plot_var.put(tmp.dataPtr(), { local_start_nt, local_start_y, local_start_x }, { local_nt, local_ny, local_nx });
1496 }
1497
1498 } // in subdomain
1499 } // mfi
1500
1501 collector.flush(ncf);
1502
1503 for (MFIter mfi(*plotMF, false); mfi.isValid(); ++mfi) {
1504 Box bx = mfi.validbox();
1505
1506 if (subdomain.contains(bx)) {
1507 //
1508 // We only include one grow cell at subdomain boundaries, not internal grid boundaries
1509 //
1510 Box tmp_bx(bx);
1511 tmp_bx.surroundingNodes(0);
1512 tmp_bx.surroundingNodes(1);
1513
1514 Box tmp_bx_2d(tmp_bx);
1515 tmp_bx_2d.makeSlab(2, 0);
1516
1517 //
1518 // These are the dimensions of the data we write for only this box
1519 //
1520 long long local_nx = tmp_bx.length()[0];
1521 long long local_ny = tmp_bx.length()[1];
1522
1523 // We do the "+1" because the offset needs to start at 0
1524 long long local_start_x = static_cast<long long>(tmp_bx.smallEnd()[0]);
1525 long long local_start_y = static_cast<long long>(tmp_bx.smallEnd()[1]);
1526
1527 if (write_header) {
1528 {
1529 FArrayBox tmp;
1530 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1531 tmp.template copy<RunOn::Device>((*vec_xp[lev])[mfi.index()], 0, 0, 1);
1532 Gpu::streamSynchronize();
1533
1534 auto nc_plot_var = collector.var(ncf, "x_psi");
1535 //nc_plot_var.par_access(NC_INDEPENDENT);
1536 nc_plot_var.put(tmp.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1537 }
1538 {
1539 FArrayBox tmp;
1540 tmp.resize(tmp_bx_2d, 1, amrex::The_Pinned_Arena());
1541 tmp.template copy<RunOn::Device>((*vec_yp[lev])[mfi.index()], 0, 0, 1);
1542 Gpu::streamSynchronize();
1543
1544 auto nc_plot_var = collector.var(ncf, "y_psi");
1545 //nc_plot_var.par_access(NC_INDEPENDENT);
1546 nc_plot_var.put(tmp.dataPtr(), { local_start_y, local_start_x }, { local_ny, local_nx });
1547 }
1548
1549 } // header
1550 } // in subdomain
1551 } // mfi
1552
1553 // One collective ncmpi_put_varn_*_all per variable. Collective writes keep
1554 // numrecs synced in the file header as they go, so the separate
1555 // ncmpi_end_indep_data() sync the independent path needed is no longer
1556 // required here.
1557 collector.flush(ncf);
1558
1559 ncf.close();
1560
1562}
constexpr amrex::Real one
constexpr amrex::Real zero
constexpr amrex::Real Cp
#define Temp_comp
#define Salt_comp
mf_h setVal(geomdata.ProbHi(2))
int ncons
Number of conserved scalars in the state (temperature + salt + passive scalars + biology tracers)
Definition REMORA.H:1644
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_evap
evaporation rate [kg/m^2/s]
Definition REMORA.H:505
amrex::Vector< std::string > cons_names
Names of scalars for plotfile output.
Definition REMORA.H:1709
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_fcor
coriolis factor (2D)
Definition REMORA.H:577
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:406
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pm
horizontal scaling factor: 1 / dx (2D)
Definition REMORA.H:568
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_lrflx
longwave radiation
Definition REMORA.H:485
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yv
y_grid on v-points (2D)
Definition REMORA.H:592
static bool write_history_file
Whether to output NetCDF files as a single history file with several time steps.
Definition REMORA.H:1442
amrex::Gpu::DeviceVector< amrex::Real > s_w
Scaled vertical coordinate (range [0,1]) that transforms to z, defined at w-points (cell faces)
Definition REMORA.H:448
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskr
land/sea mask at cell centers (2D)
Definition REMORA.H:557
int history_count
Counter for which time index we are writing to in the netcdf history file.
Definition REMORA.H:1702
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_rain
precipitation rate [kg/m^2/s]
Definition REMORA.H:503
bool chunk_history_file
Whether to split the netcdf history file into fixed-length chunks.
Definition REMORA.H:1698
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_sustr
Surface stress in the u direction.
Definition REMORA.H:467
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yp
y_grid on psi-points (2D)
Definition REMORA.H:597
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xr
x_grid on rho points (2D)
Definition REMORA.H:580
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xv
x_grid on v-points (2D)
Definition REMORA.H:590
amrex::Vector< amrex::Vector< amrex::Box > > boxes_at_level
the boxes specified at each level by tagging criteria
Definition REMORA.H:1549
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_msku
land/sea mask at x-faces (2D)
Definition REMORA.H:559
amrex::Vector< amrex::MultiFab * > yvel_new
multilevel data container for current step's y velocities (v in ROMS)
Definition REMORA.H:390
amrex::Real start_bdy_time
Start time in the time series of boundary data.
Definition REMORA.H:1437
amrex::Gpu::DeviceVector< amrex::Real > s_r
Scaled vertical coordinate (range [0,1]) that transforms to z, defined at rho points (cell centers)
Definition REMORA.H:446
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_shflx
sensible heat flux
Definition REMORA.H:491
int steps_per_history_file
Time steps per netcdf history file. Must be > 0 if chunk_history_file.
Definition REMORA.H:1700
amrex::Vector< amrex::MultiFab * > xvel_new
multilevel data container for current step's x velocities (u in ROMS)
Definition REMORA.H:388
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_lhflx
latent heat flux
Definition REMORA.H:489
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_mskv
land/sea mask at y-faces (2D)
Definition REMORA.H:561
static int file_min_digits
Minimum number of digits in plotfile name or chunked history file.
Definition REMORA.H:1761
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_visc2_r
Harmonic viscosity defined on the rho points (centers)
Definition REMORA.H:436
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_svstr
Surface stress in the v direction.
Definition REMORA.H:469
void WriteNCPlotFile(int istep, amrex::MultiFab const *plotMF)
Write plotfile using NetCDF (wrapper)
amrex::Gpu::DeviceVector< amrex::Real > Cs_r
Stretching coefficients at rho points.
Definition REMORA.H:456
amrex::Vector< amrex::Real > t_new
new time at each level
Definition REMORA.H:1556
static SolverChoice solverChoice
Container for algorithmic choices.
Definition REMORA.H:1717
static int total_nc_plot_file_step
Definition REMORA.H:1345
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xp
x_grid on psi-points (2D)
Definition REMORA.H:595
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_vbar
barotropic y velocity (2D)
Definition REMORA.H:549
amrex::Gpu::DeviceVector< amrex::Real > Cs_w
Stretching coefficients at w points.
Definition REMORA.H:458
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_ubar
barotropic x velocity (2D)
Definition REMORA.H:547
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yu
y_grid on u-points (2D)
Definition REMORA.H:587
void WriteNCPlotFile_which(int lev, int which_subdomain, amrex::MultiFab const *plotMF, bool write_header, ncutils::NCFile &ncf, bool is_history)
Write a particular NetCDF plotfile.
amrex::Real netcdf_fill_value
fill value for masked arrays in netcdf output
Definition REMORA.H:1725
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_xu
x_grid on u-points (2D)
Definition REMORA.H:585
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_pn
horizontal scaling factor: 1 / dy (2D)
Definition REMORA.H:570
amrex::Vector< std::string > plot_var_names_3d
Names of 3D variables to output to AMReX plotfile.
Definition REMORA.H:1705
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_stflux
Surface tracer flux; input arrays.
Definition REMORA.H:496
std::string plot_file_name
Plotfile prefix.
Definition REMORA.H:1685
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Zt_avg1
Average of the free surface, zeta (2D)
Definition REMORA.H:464
amrex::Vector< std::string > plot_var_names_2d
Names of 2D variables to output to AMReX plotfile.
Definition REMORA.H:1707
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_srflx
Shortwave radiation flux [W/m²], defined at rho-points.
Definition REMORA.H:483
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Pair
Air pressure [mb], defined at rho-points.
Definition REMORA.H:480
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_Tair
Air temperature [°C], defined at rho-points.
Definition REMORA.H:476
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_yr
y_grid on rho points (2D)
Definition REMORA.H:582
amrex::Vector< std::unique_ptr< amrex::MultiFab > > vec_diff2
Harmonic diffusivity for temperature / salinity.
Definition REMORA.H:438
static NCFile create(const std::string &name, const int cmode=NC_CLOBBER|NC_64BIT_DATA, MPI_Comm comm=MPI_COMM_WORLD, MPI_Info info=MPI_INFO_NULL)
Create a file. Defaults to CDF-5; classic CDF-1 has a 2GB limit.
static NCFile open(const std::string &name, const int cmode=NC_NOWRITE, MPI_Comm comm=MPI_COMM_WORLD, MPI_Info info=MPI_INFO_NULL)
Open an existing file.
@ data
annual climatology of Laurent et al. (2017)
HorizMixingType horiz_mixing_type
amrex::Real theta_b
amrex::Real theta_s
amrex::Real tcline
static constexpr nc_type Real
Representation of a NetCDF variable.
const int ncid
File/Group identifier.