REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_NCTimeSeriesBoundary.cpp
Go to the documentation of this file.
2#include "REMORA_NCFile.H"
3
4#include "AMReX_ParallelDescriptor.H"
5
6#include <string>
7
8#ifdef REMORA_USE_NETCDF
9/**
10 * @param[in ] a_lev level at which we will store the data
11 * @param[in ] a_geom Vector of Geometry objects at all levels
12 * @param[in ] a_file_name vector of file name(s) to read from
13 * @param[in ] a_field_name name of field to read in
14 * @param[in ] a_time_name name of time variable in NetCDF file
15 * @param[in ] a_index_type nodality of data field
16 * @param[in ] a_var_need_data array over boundaries of flags that indicate whether we need data for the variable
17 * @param[in ] a_is2d Whether the variable we're working with is 2D
18 * @param[in ] a_rx refinement ratio in x relative to level 0
19 * @param[in ] a_ry refinement ratio in y relative to level 0
20 * @param[in ] a_time_ref remora.time_ref, the reference date of the model clock
21 */
22NCTimeSeriesBoundary::NCTimeSeriesBoundary (int a_lev, const amrex::Vector<amrex::Geometry> a_geom,
23 const amrex::Vector<std::string>& a_file_names, const std::string a_field_name,
24 const std::string a_time_name,
25 const amrex::IntVect a_index_type,
26 const amrex::GpuArray<bool, AMREX_SPACEDIM*2>& a_var_need_data,
27 bool a_is2d, int a_rx, int a_ry, double a_time_ref)
28{
30 m_lev = a_lev;
31 m_geom = a_geom;
32 file_names.assign(a_file_names.begin(), a_file_names.end());
35 domain = a_geom[a_lev].Domain();
37 // Copied, not aliased: the caller's array (REMORA::phys_bc_need_data) is an
38 // amrex::Vector element, whose address is not stable across a resize.
40 is2d = a_is2d;
41 m_rx = a_rx;
42 m_ry = a_ry;
43}
44
46{
47 // Initialize Fabs
48 amrex::Arena* Arena_Used = amrex::The_Arena();
49#ifdef AMREX_USE_GPU
50 Arena_Used = amrex::The_Pinned_Arena();
51#endif
52
53 // open file
54 // Only announce the variables that will really be read. A series is constructed for
55 // every boundary variable, but most of them typically sit on a local boundary
56 // condition and never touch the file.
57 bool any_side_needs_data = false;
58 for (int ori = 0; ori < AMREX_SPACEDIM*2; ++ori) {
60 }
62 amrex::Print() << "Setting up boundary data for " << field_name << " coming from NetCDF file " << std::endl;
63 }
64
65 // The time field can have any number of names, depending on the field.
66 // If not specified in input file (time_name.empty()) then set it by default
67 if (time_name.empty())
68 {
69 time_name = "ocean_time";
70 }
71
72 int ioproc = amrex::ParallelDescriptor::IOProcessorNumber();
73 for (int ifile = 0; ifile < file_names.size(); ifile++) {
74 std::string file_name = file_names[ifile];
75 // The time stamps must count days, if they carry units at all; without
76 // units they are assumed to be days on the model clock. A reference date
77 // in the units is honored: this is the shift that puts them on the model
78 // clock. Only announced for a series that will be read.
80 false, any_side_needs_data);
81 // get times and put in array
82 amrex::Vector<NDArray<double>> array_ts(1);
83 ReadNetCDFFile(file_name, {time_name}, array_ts); // filled only on proc 0
84 if (amrex::ParallelDescriptor::IOProcessor())
85 {
86 int ntimes_io = array_ts[0].get_vshape()[0];
87 for (int nt(0); nt < ntimes_io; nt++)
88 {
89 // Onto the model clock, then from days to seconds
90 bry_times.push_back((*(array_ts[0].get_data() + nt) + offset_days) * 60.0 * 60.0 * 24.0);
91 file_for_time.push_back(ifile);
92 file_itime_offset.push_back(nt);
93 }
94 }
95 }
96
97 int ntimes = bry_times.size();
98 // Only do checks on IO processor since bry_times isn't populated on other ranks yet
99 if (amrex::ParallelDescriptor::IOProcessor()) {
100 AMREX_ASSERT(std::is_sorted(bry_times.begin(), bry_times.end()));
101 if (ntimes <= 1) {
102 amrex::Error("Time series of boundary data must be given at at least two times");
103 }
104 }
105 amrex::ParallelDescriptor::Bcast(&ntimes,1,ioproc);
106 if (!(amrex::ParallelDescriptor::IOProcessor())) {
107 bry_times.resize(ntimes);
108 file_for_time.resize(ntimes);
110 }
111 amrex::ParallelDescriptor::Bcast(bry_times.data(), bry_times.size(), ioproc);
112 amrex::ParallelDescriptor::Bcast(file_for_time.data(), file_for_time.size(), ioproc);
113 amrex::ParallelDescriptor::Bcast(file_itime_offset.data(), file_itime_offset.size(), ioproc);
114
115 const auto& lo = domain.loVect();
116 const auto& hi = domain.hiVect();
117
118 amrex::Box xlo_bx(amrex::IntVect(lo[0]+index_type[0]-1, lo[1]+index_type[1]-1, lo[2]),
119 amrex::IntVect(lo[0]+index_type[0]-1, hi[1]+1 , hi[2]), index_type);
120 amrex::Box xhi_bx(amrex::IntVect(hi[0]+1 , lo[1]+index_type[1]-1, lo[2]),
121 amrex::IntVect(hi[0]+1 , hi[1]+1 , hi[2]), index_type);
122 amrex::Box ylo_bx(amrex::IntVect(lo[0]+index_type[0]-1, lo[1]+index_type[1]-1, lo[2]),
123 amrex::IntVect(hi[0]+1 , lo[1]+index_type[1]-1, hi[2]), index_type);
124 amrex::Box yhi_bx(amrex::IntVect(lo[0]+index_type[0]-1, hi[1]+1 , lo[2]),
125 amrex::IntVect(hi[0]+1 , hi[1]+1 , hi[2]), index_type);
126 if (is2d) {
127 xlo_bx.makeSlab(2,0);
128 xhi_bx.makeSlab(2,0);
129 ylo_bx.makeSlab(2,0);
130 yhi_bx.makeSlab(2,0);
131 }
132
133 // amrex::Print() << xlo_bx << " " << xhi_bx << " " << ylo_bx << " " << yhi_bx << std::endl;
134
135 xlo_dat_before = amrex::FArrayBox(xlo_bx, 1, Arena_Used);
136 xhi_dat_before = amrex::FArrayBox(xhi_bx, 1, Arena_Used);
137 ylo_dat_before = amrex::FArrayBox(ylo_bx, 1, Arena_Used);
138 yhi_dat_before = amrex::FArrayBox(yhi_bx, 1, Arena_Used);
139
140 xlo_dat_after = amrex::FArrayBox(xlo_bx, 1, Arena_Used);
141 xhi_dat_after = amrex::FArrayBox(xhi_bx, 1, Arena_Used);
142 ylo_dat_after = amrex::FArrayBox(ylo_bx, 1, Arena_Used);
143 yhi_dat_after = amrex::FArrayBox(yhi_bx, 1, Arena_Used);
144
145 xlo_dat_interp = amrex::FArrayBox(xlo_bx, 1, Arena_Used);
146 xhi_dat_interp = amrex::FArrayBox(xhi_bx, 1, Arena_Used);
147 ylo_dat_interp = amrex::FArrayBox(ylo_bx, 1, Arena_Used);
148 yhi_dat_interp = amrex::FArrayBox(yhi_bx, 1, Arena_Used);
149
150 // dummy initialization
151 i_time_before = -100;
152
153 if (var_need_data[amrex::Orientation(amrex::Direction::x,amrex::Orientation::low)] == true) {
154 nc_var_names.push_back(field_name + "_west");
155 }
156 if (var_need_data[amrex::Orientation(amrex::Direction::x,amrex::Orientation::high)] == true) {
157 nc_var_names.push_back(field_name + "_east");
158 }
159 if (var_need_data[amrex::Orientation(amrex::Direction::y,amrex::Orientation::low)] == true) {
160 nc_var_names.push_back(field_name + "_south");
161 }
162 if (var_need_data[amrex::Orientation(amrex::Direction::y,amrex::Orientation::high)] == true) {
163 nc_var_names.push_back(field_name + "_north");
164 }
165
166 // Nothing to check for a variable that reads no boundary data at all, and skipping
167 // keeps a run with many tracers from opening the file once per inert tracer
168 if (nc_var_names.empty()) {
169 return;
170 }
171
172 // Fail early and by name. read_in_at_time dereferences each variable unconditionally,
173 // so a file that is missing e.g. NO3_west would otherwise fail deep inside the reader
174 // with nothing to say which variable, which file, or which input asked for it.
175 for (const auto& file_name : file_names) {
177 std::string msg = "Boundary file " + file_name + " does not contain all of:";
178 for (const auto& var_name : nc_var_names) {
179 msg += " " + var_name;
180 }
181 msg += ". The boundary condition requested for " + field_name +
182 " reads from file (clamped, chapman, flather, or orlanski_rad_nudg)."
183 " Either add the missing variable(s) to the file, or give " + field_name +
184 " a boundary condition that needs no file data, such as outflow.";
185 amrex::Abort(msg);
186 }
187 }
188}
189
190/**
191 * @param time time to interpolate to
192 */
194{
195 // Nothing in the file for this variable, so there is nothing to read or
196 // interpolate. Returning here also keeps the reader from announcing that it is
197 // reading a variable the file does not contain: the caller updates every tracer
198 // in one sweep, and most of them are typically on a local boundary condition.
199 if (nc_var_names.empty()) {
200 return;
201 }
202
203 // Initialize Fabs
204 amrex::Arena* Arena_Used = amrex::The_Arena();
205#ifdef AMREX_USE_GPU
206 Arena_Used = amrex::The_Pinned_Arena();
207#endif
208
209 // Figure out time index:
211 int i_time_new = -1;
212 for (int nt=0; nt < bry_times.size()-1; nt++) {
213 if ((bry_times[nt] <= time) and (bry_times[nt+1] >= time)) {
214 i_time_new = nt;
215 break;
216 }
217 }
218 // Bracketing must succeed: falling through would read the series at a stale or
219 // uninitialized index. A runtime check rather than an assert, because the failure
220 // is a mismatch between the file and the run.
221 if (i_time_new < 0) {
222 amrex::Abort("Time " + std::to_string(time) + " is not spanned by the boundary time"
223 + " series for '" + field_name + "', which covers ["
224 + std::to_string(bry_times[0]) + ", "
225 + std::to_string(bry_times[bry_times.size()-1])
226 + "]. Extend the boundary file to cover the run. The file's times were"
227 + " placed on the model clock from the reference date in their units"
228 + " attribute (the mapping is printed when the series is loaded), so"
229 + " also check remora.time_ref and remora.start_time.");
230 }
234
235 int i_time_after = i_time_before + 1;
236
237 amrex::FArrayBox crse_xlo_dat;
238 amrex::FArrayBox crse_xhi_dat;
239 amrex::FArrayBox crse_ylo_dat;
240 amrex::FArrayBox crse_yhi_dat;
241
242 if (m_lev > 0) {
243 const auto& crse_lo = m_geom[0].Domain().loVect();
244 const auto& crse_hi = m_geom[0].Domain().hiVect();
245
246 amrex::Box crse_xlo_bx(amrex::IntVect(crse_lo[0]+index_type[0]-1, crse_lo[1]+index_type[1]-1, crse_lo[2]),
247 amrex::IntVect(crse_lo[0]+index_type[0]-1, crse_hi[1]+1 , crse_hi[2]), index_type);
248 amrex::Box crse_xhi_bx(amrex::IntVect(crse_hi[0]+1 , crse_lo[1]+index_type[1]-1, crse_lo[2]),
249 amrex::IntVect(crse_hi[0]+1 , crse_hi[1]+1 , crse_hi[2]), index_type);
250 amrex::Box crse_ylo_bx(amrex::IntVect(crse_lo[0]+index_type[0]-1, crse_lo[1]+index_type[1]-1, crse_lo[2]),
251 amrex::IntVect(crse_hi[0]+1 , crse_lo[1]+index_type[1]-1, crse_hi[2]), index_type);
252 amrex::Box crse_yhi_bx(amrex::IntVect(crse_lo[0]+index_type[0]-1, crse_hi[1]+1 , crse_lo[2]),
253 amrex::IntVect(crse_hi[0]+1 , crse_hi[1]+1 , crse_hi[2]), index_type);
254 if (is2d) {
255 crse_xlo_bx.makeSlab(2,0);
256 crse_xhi_bx.makeSlab(2,0);
257 crse_ylo_bx.makeSlab(2,0);
258 crse_yhi_bx.makeSlab(2,0);
259 }
260
261 crse_xlo_dat = amrex::FArrayBox(crse_xlo_bx, 1, Arena_Used);
262 crse_xhi_dat = amrex::FArrayBox(crse_xhi_bx, 1, Arena_Used);
263 crse_ylo_dat = amrex::FArrayBox(crse_ylo_bx, 1, Arena_Used);
264 crse_yhi_dat = amrex::FArrayBox(crse_yhi_bx, 1, Arena_Used);
265 }
266
267 if (i_time_before_old + 1 == i_time_before) {
268 // swap multifabs so we only have to read in one MultiFab
269 std::swap(xlo_dat_before, xlo_dat_after);
270 std::swap(xhi_dat_before, xhi_dat_after);
271 std::swap(ylo_dat_before, ylo_dat_after);
272 std::swap(yhi_dat_before, yhi_dat_after);
273
274 amrex::Print() << "Reading in " << field_name << " at (after) time " << i_time_after << std::endl;
275
276 if (m_lev == 0) {
278 } else {
279
281
286
287 // The_Pinned_Arena is not stream-ordered, so wait for interp_fab's
288 // kernels before these fabs are freed at return
289 amrex::Gpu::streamSynchronize();
290
291 }
292
293 } else if (i_time_before_old != i_time_before) {
294
295
296 if (m_lev == 0) {
297 amrex::Print() << "Reading in " << field_name << " at (before) time " << i_time_before << std::endl;
299 amrex::Print() << "Reading in " << field_name << " at (after) time " << i_time_after << std::endl;
301 } else {
302
303 amrex::Print() << "Reading in " << field_name << " at (before) time " << i_time_before << std::endl;
304
306
311
312 amrex::Print() << "Reading in " << field_name << " at time " << i_time_after << std::endl;
313
315
320
321 // The_Pinned_Arena is not stream-ordered, so wait for interp_fab's
322 // kernels before these fabs are freed at return
323 amrex::Gpu::streamSynchronize();
324 } // lev
325 } // i_time
326
327 // Both differences are small, so Real holds them even when the times do not.
328 const amrex::Real dt = static_cast<amrex::Real>(time_after - time_before);
329 const amrex::Real time_since_before = static_cast<amrex::Real>(time - time_before);
330
331 amrex::Array4<amrex::Real> xlo_interp_arr = xlo_dat_interp.array();
332 amrex::Array4<amrex::Real> xhi_interp_arr = xhi_dat_interp.array();
333 amrex::Array4<amrex::Real> ylo_interp_arr = ylo_dat_interp.array();
334 amrex::Array4<amrex::Real> yhi_interp_arr = yhi_dat_interp.array();
335
336 amrex::Array4<amrex::Real> xlo_before_arr = xlo_dat_before.array();
337 amrex::Array4<amrex::Real> xhi_before_arr = xhi_dat_before.array();
338 amrex::Array4<amrex::Real> ylo_before_arr = ylo_dat_before.array();
339 amrex::Array4<amrex::Real> yhi_before_arr = yhi_dat_before.array();
340
341 amrex::Array4<amrex::Real> xlo_after_arr = xlo_dat_after.array();
342 amrex::Array4<amrex::Real> xhi_after_arr = xhi_dat_after.array();
343 amrex::Array4<amrex::Real> ylo_after_arr = ylo_dat_after.array();
344 amrex::Array4<amrex::Real> yhi_after_arr = yhi_dat_after.array();
345
346 if (var_need_data[amrex::Orientation(amrex::Direction::x,amrex::Orientation::low)] == true) {
347 amrex::ParallelFor(xlo_dat_interp.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
348 {
350 });
351 }
352
353 if (var_need_data[amrex::Orientation(amrex::Direction::x,amrex::Orientation::high)] == true) {
354 amrex::ParallelFor(xhi_dat_interp.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
355 {
357 });
358 }
359
360 if (var_need_data[amrex::Orientation(amrex::Direction::y,amrex::Orientation::low)] == true) {
361 amrex::ParallelFor(ylo_dat_interp.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
362 {
364 });
365 }
366
367 if (var_need_data[amrex::Orientation(amrex::Direction::y,amrex::Orientation::high)] == true) {
368 amrex::ParallelFor(yhi_dat_interp.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
369 {
371 });
372 }
373}
374
375/**
376 * @param[inout] fab_xlo fab to store xlo boundary data into
377 * @param[inout] fab_xhi fab to store xhi boundary data into
378 * @param[inout] fab_ylo fab to store ylo boundary data into
379 * @param[inout] fab_yhi fab to store yhi boundary data into
380 * @param[in ] itime index of time step to read from file
381 */
383 amrex::FArrayBox& fab_xhi,
384 amrex::FArrayBox& fab_ylo,
385 amrex::FArrayBox& fab_yhi,
386 int itime) {
387 // The host writes below land in pinned fabs that queued kernels may still be
388 // reading in place, so drain the stream first
389 amrex::Gpu::streamSynchronize();
390
392 amrex::Vector<RARRAY> arrays(nc_var_names.size());
393
394 amrex::Print() << "Actually reading in " << field_name << " at time " << bry_times[itime] << std::endl;
395 std::string nc_bdry_file = file_names[file_for_time[itime]];
397 ReadNetCDFFile(nc_bdry_file, nc_var_names, arrays, true, itime_offset); // does work on proc 0 only
398
399 for (int iv=0; iv < nc_var_names.size(); iv++) {
400 if (amrex::ParallelDescriptor::IOProcessor())
401 {
402 std::string last4 = nc_var_names[iv].substr(nc_var_names[iv].size()-4, 4);
403 std::string last5 = nc_var_names[iv].substr(nc_var_names[iv].size()-5, 5);
404 int nx, ny, nz, n_plane;
405 int i, j, k, ioff, joff;
406 if (last4 == "west") {
407 amrex::Box my_box = fab_xlo.box();
408 if (is2d) {
409 nz = 1;
410 ny = arrays[iv].get_vshape()[1];
411 } else {
412 nz = arrays[iv].get_vshape()[1];
413 ny = arrays[iv].get_vshape()[2];
414 }
415 n_plane = ny * nz;
417
418 i = my_box.smallEnd()[0];
419 joff = my_box.smallEnd()[1];
420
421 amrex::Array4<amrex::Real> fab_arr = fab_xlo.array();
422
423 for (int n(0); n < n_plane; n++) {
424 k = n / ny;
425 j = n - (k * ny);
426 fab_arr(i, j+joff, k, 0) = static_cast<amrex::Real>(*(arrays[iv].get_data() + n));
427 }
428 } else if (last4 == "east") {
429 amrex::Box my_box = fab_xhi.box();
430 if (is2d) {
431 nz = 1;
432 ny = arrays[iv].get_vshape()[1];
433 } else {
434 nz = arrays[iv].get_vshape()[1];
435 ny = arrays[iv].get_vshape()[2];
436 }
437 n_plane = ny * nz;
439
440 i = my_box.smallEnd()[0];
441 joff = my_box.smallEnd()[1];
442
443 amrex::Array4<amrex::Real> fab_arr = fab_xhi.array();
444 for (int n(0); n < n_plane; n++) {
445 k = n / ny;
446 j = n - (k * ny);
447 fab_arr(i, j+joff, k, 0) = static_cast<amrex::Real>(*(arrays[iv].get_data() + n));
448 }
449 } else if (last5 == "south") {
450 amrex::Box my_box = fab_ylo.box();
451 if (is2d) {
452 nz = 1;
453 nx = arrays[iv].get_vshape()[1];
454 } else {
455 nz = arrays[iv].get_vshape()[1];
456 nx = arrays[iv].get_vshape()[2];
457 }
458 n_plane = nx * nz;
460
461 j = my_box.smallEnd()[1];
462 ioff = my_box.smallEnd()[0];
463
464 amrex::Array4<amrex::Real> fab_arr = fab_ylo.array();
465 for (int n(0); n < n_plane; n++) {
466 k = n / nx;
467 i = n - (k * nx);
468 fab_arr(i+ioff, j, k, 0) = static_cast<amrex::Real>(*(arrays[iv].get_data() + n));
469 }
470 } else if (last5 == "north") {
471 amrex::Box my_box = fab_yhi.box();
472 if (is2d) {
473 nz = 1;
474 nx = arrays[iv].get_vshape()[1];
475 } else {
476 nz = arrays[iv].get_vshape()[1];
477 nx = arrays[iv].get_vshape()[2];
478 }
479 n_plane = nx * nz;
481
482 j = my_box.smallEnd()[1];
483 ioff = my_box.smallEnd()[0];
484
485 amrex::Array4<amrex::Real> fab_arr = fab_yhi.array();
486 for (int n(0); n < n_plane; n++) {
487 k = n / nx;
488 i = n - (k * nx);
489 fab_arr(i+ioff, j, k, 0) = static_cast<amrex::Real>(*(arrays[iv].get_data() + n));
490 }
491 }
492 }
493 }
494
495 amrex::ParallelDescriptor::Barrier();
496
497 // When an FArrayBox is built, space is allocated on every rank. However, we only
498 // filled the data in these FABs on the IOProcessor. So here we broadcast
499 // the data to every rank.
500 int ioproc = amrex::ParallelDescriptor::IOProcessorNumber(); // I/O rank
501 amrex::ParallelDescriptor::Bcast(fab_xlo.dataPtr(),fab_xlo.box().numPts(),ioproc);
502 amrex::ParallelDescriptor::Bcast(fab_xhi.dataPtr(),fab_xhi.box().numPts(),ioproc);
503 amrex::ParallelDescriptor::Bcast(fab_ylo.dataPtr(),fab_ylo.box().numPts(),ioproc);
504 amrex::ParallelDescriptor::Bcast(fab_yhi.dataPtr(),fab_yhi.box().numPts(),ioproc);
505
506 amrex::Print() << "DONE reading in " << field_name << " at time " << bry_times[itime] << std::endl;
507}
508
509/**
510 * @param[in ] dat_crse fab of coarse data to interpolate from
511 * @param[ out] dat_fine fab of fine data to interpoalte to
512 */
513void NCTimeSeriesBoundary::interp_fab(amrex::FArrayBox& dat_crse, amrex::FArrayBox& dat_fine)
514{
515 amrex::Array4<amrex::Real> crse_arr = dat_crse.array();
516 amrex::Array4<amrex::Real> fine_arr = dat_fine.array();
517
518 const auto& bhi = ubound(dat_crse.box());
519
520 int rx = m_rx;
521 int ry = m_ry;
522
523 amrex::Real xfac = one / static_cast<amrex::Real>(rx);
524 amrex::Real yfac = one / static_cast<amrex::Real>(ry);
525
526 // Doing box on x-face
527 if (dat_crse.box().length(0) == 1) {
528 // amrex::Print() << "DOING INTERP ON XFACE " << dat_crse.box() << " " << dat_fine.box() << std::endl;
529 if (dat_crse.box().ixType()[1] == 0) {
530 amrex::ParallelFor(dat_crse.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
531 {
532 int i_f = (i == -1) ? -1 : rx*i;
533 if (j == -1) {
534 fine_arr(i_f,j,k) = crse_arr(i,j,k);
535 } else {
536 fine_arr(i_f,ry*j ,k) = crse_arr(i,j,k);
537 if (j < bhi.y) {
538 for (int n = 1; n < ry; n++) {
539 fine_arr(i_f,ry*j+n,k) = crse_arr(i,j,k) + n * yfac * (crse_arr(i,j+1,k) - crse_arr(i,j,k));
540 }
541 }
542 }
543 });
544 } else {
545 amrex::ParallelFor(dat_crse.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
546 {
547 int i_f = (i == -1) ? -1 : rx*i;
548 if (j == -1) {
549 fine_arr(i_f,j,k) = crse_arr(i,j,k);
550 } else {
551 fine_arr(i_f,ry*j,k) = crse_arr(i,j,k);
552 if (j < bhi.y) {
553 for (int n = 1; n < ry; n++) {
554 fine_arr(i_f,ry*j+n,k) = crse_arr(i,j,k) + n * yfac * (crse_arr(i,j+1,k) - crse_arr(i,j,k));
555 }
556 }
557 }
558 });
559 }
560 } else if (dat_crse.box().length(1) == 1) {
561 // Doing box on y-face
562 // amrex::Print() << "DOING INTERP ON YFACE " << dat_crse.box() << " " << dat_fine.box() << std::endl;
563 if (dat_crse.box().ixType()[0] == 0) {
564 amrex::ParallelFor(dat_crse.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
565 {
566 int j_f = (j == -1) ? -1 : ry*j;
567 if (i == -1) {
568 fine_arr(i,j_f,k) = crse_arr(i,j,k);
569 } else {
570 fine_arr(rx*i ,j_f,k) = crse_arr(i,j,k);
571 if (i < bhi.x) {
572 for (int n = 1; n < rx; n++) {
573 fine_arr(rx*i+n,j_f,k) = crse_arr(i,j,k) + n * xfac * (crse_arr(i+1,j,k) - crse_arr(i,j,k));
574 }
575 }
576 }
577 });
578 } else {
579 amrex::ParallelFor(dat_crse.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
580 {
581 int j_f = (j == -1) ? -1 : ry*j;
582 if (i == -1) {
583 fine_arr(i,j_f,k) = crse_arr(i,j,k);
584 } else {
585 fine_arr(rx*i,j_f,k) = crse_arr(i,j,k);
586 if (i < bhi.x) {
587 for (int n = 1; n < rx; n++) {
588 fine_arr(rx*i+n,j_f,k) = crse_arr(i,j,k) + n * xfac * (crse_arr(i+1,j,k) - crse_arr(i,j,k));
589 }
590 }
591 }
592 });
593 }
594 } else {
595 amrex::Abort(" What am I doing here??");
596 }
597}
598
599#endif // REMORA_USE_NETCDF
constexpr amrex::Real one
mf_h setVal(geomdata.ProbHi(2))
AMREX_ALWAYS_ASSERT(!NSPeriodic||!EWPeriodic)
void ReadNetCDFFile(const std::string &fname, amrex::Vector< std::string > names, amrex::Vector< NDArray< DType > > &arrays, bool one_time=false, int fill_time=0)
Read in data from netcdf file and save to data arrays.
bool QueryNetCDFHasVars(const std::string &fname, const amrex::Vector< std::string > &var_names)
Helper function for testing whether a file carries every named variable.
double ReadNetCDFTimeOffsetDays(const std::string &fname, const std::string &time_name, double time_ref, bool units_required, bool announce)
Offset, in days, that puts a file's time variable onto the model clock.
amrex::FArrayBox yhi_dat_before
FArrayBox to store data at time_before at yhi boundary.
amrex::FArrayBox yhi_dat_after
FArrayBox to store data at time_after at yhi boundary.
amrex::GpuArray< bool, AMREX_SPACEDIM *2 > var_need_data
Array over boundaries indicating whether we need physical data for this variable.
void interp_fab(amrex::FArrayBox &dat_crse, amrex::FArrayBox &dat_fine)
spatially interpolate boundary fabs from level 0 data (read in) to current level
amrex::FArrayBox ylo_dat_before
FArrayBox to store data at time_before at ylo boundary.
amrex::FArrayBox xlo_dat_after
FArrayBox to store data at time_after at xlo boundary.
amrex::Vector< std::string > nc_var_names
Variable names that will be read from file.
amrex::FArrayBox ylo_dat_after
FArrayBox to store data at time_after at ylo boundary.
amrex::Vector< double > bry_times
Time points in netcdf file, in seconds on the model clock.
double time_before
Time in ocean_times immediately before the last time interpolated to.
void update_interpolated_to_time(double time)
Calculate interpolated values at time, in seconds on the model clock, reading in data as necessary.
void Initialize()
Read in time array from file and allocate data arrays.
amrex::FArrayBox yhi_dat_interp
FArrayBox to store data at inteprolated time at yhi boundary *‍/.
amrex::FArrayBox xlo_dat_interp
FArrayBox to store data at inteprolated time at xlo boundary *‍/.
std::string time_name
Field name for time series in netcdf file.
amrex::FArrayBox xhi_dat_interp
FArrayBox to store data at inteprolated time at xhi boundary *‍/.
amrex::IntVect index_type
Index type for field to fill.
double time_after
Time in ocean_times immediately after the last time interpolated to.
int i_time_before
Time index immediately before the last time interpolated to.
std::string field_name
Field name in netcdf file.
amrex::FArrayBox ylo_dat_interp
FArrayBox to store data at inteprolated time at ylo boundary *‍/.
NCTimeSeriesBoundary(int a_lev, const amrex::Vector< amrex::Geometry > a_geom, const amrex::Vector< std::string > &a_file_name, const std::string a_field_name, const std::string a_time_name, const amrex::IntVect a_index_type, const amrex::GpuArray< bool, AMREX_SPACEDIM *2 > &a_var_need_data, bool a_is2d, int rx, int ry, double a_time_ref)
Constructor.
amrex::Vector< amrex::Geometry > m_geom
Geometry at all levels.
amrex::FArrayBox xhi_dat_after
FArrayBox to store data at time_after at xhi boundary.
void read_in_at_time(amrex::FArrayBox &fab_xlo, amrex::FArrayBox &fab_xhi, amrex::FArrayBox &fab_ylo, amrex::FArrayBox &fab_yhi, int itime)
Read in data from file at time index itime and fill into mf.
amrex::Vector< std::string > file_names
File name to read from.
amrex::Vector< int > file_itime_offset
Offset to access a particular time within its file.
amrex::FArrayBox xhi_dat_before
FArrayBox to store data at time_before at xhi boundary.
bool is2d
Whether the field we're reading in is 2d.
int m_lev
Level at which we are holding the boundary data.
int m_rx
Refinement ratios relative to level 0.
amrex::FArrayBox xlo_dat_before
FArrayBox to store data at time_before at xlo boundary.
amrex::Vector< int > file_for_time
File index to access a particular time.
NDArray is the datatype designed to hold any data, including scalars, multidimensional arrays,...