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_file_name file name to read from
11 * @param[in ] a_field_name name of field to read in
12 * @param[in ] a_time_name name of time variable in NetCDF file
13 * @param[in ] a_domain simulation domain
14 * @param[in ] a_is2d Whether the variable we're working with is 2D
15 */
16NCTimeSeriesBoundary::NCTimeSeriesBoundary (const amrex::Vector<std::string>& a_file_names, const std::string a_field_name,
17 const std::string a_time_name,
18 const amrex::Box& a_domain,
19 const amrex::IntVect a_index_type,
20 const amrex::GpuArray<bool, AMREX_SPACEDIM*2>* a_var_need_data,
21 bool a_is2d) {
22 file_names.assign(a_file_names.begin(), a_file_names.end());
23 time_name = a_time_name;
24 field_name = a_field_name;
25 domain = a_domain;
26 index_type = a_index_type;
27 var_need_data = *a_var_need_data;
28 is2d = a_is2d;
29}
30
32 // open file
33 amrex::Print() << "Loading boundary data for " << field_name << " from NetCDF file " << std::endl;
34
35 // The time field can have any number of names, depending on the field.
36 // If not specified in input file (time_name.empty()) then set it by default
37 if (time_name.empty())
38 {
39 time_name = "ocean_time";
40 }
41
42 for (int ifile = 0; ifile < file_names.size(); ifile++) {
43 std::string file_name = file_names[ifile];
44 // Check units of time stamps; should be days
45 std::string unit_str = ReadNetCDFVarAttrStr(file_name, time_name, "units"); // works on proc 0
46 if (amrex::ParallelDescriptor::IOProcessor())
47 {
48 if (unit_str.find("days") == std::string::npos) {
49 amrex::Print() << "Units of ocean_time given as: " << unit_str << std::endl;
50 amrex::Abort("Units must be in days.");
51 }
52 }
53 // get times and put in array
54 using RARRAY = NDArray<amrex::Real>;
55 amrex::Vector<RARRAY> array_ts(1);
56 ReadNetCDFFile(file_name, {time_name}, array_ts); // filled only on proc 0
57 if (amrex::ParallelDescriptor::IOProcessor())
58 {
59 int ntimes_io = array_ts[0].get_vshape()[0];
60 for (int nt(0); nt < ntimes_io; nt++)
61 {
62 // Convert ocean time from days to seconds
63 bry_times.push_back((*(array_ts[0].get_data() + nt)) * amrex::Real(60.0) * amrex::Real(60.0) * amrex::Real(24.0));
64 file_for_time.push_back(ifile);
65 file_itime_offset.push_back(nt);
66 // amrex::Print() << "TIMES " << bry_times[nt] << std::endl;
67 }
68 }
69 }
70
71 AMREX_ASSERT(std::is_sorted(bry_times.begin(), bry_times.end()));
72
73 int ntimes = bry_times.size();
74 int ioproc = amrex::ParallelDescriptor::IOProcessorNumber();
75 amrex::ParallelDescriptor::Bcast(&ntimes,1,ioproc);
76 if (!(amrex::ParallelDescriptor::IOProcessor())) {
77 bry_times.resize(ntimes);
78 file_for_time.resize(ntimes);
79 file_itime_offset.resize(ntimes);
80 }
81 amrex::ParallelDescriptor::Bcast(bry_times.data(), bry_times.size(), ioproc);
82 amrex::ParallelDescriptor::Bcast(file_for_time.data(), file_for_time.size(), ioproc);
83 amrex::ParallelDescriptor::Bcast(file_itime_offset.data(), file_itime_offset.size(), ioproc);
84
85 // Initialize Fabs
86 amrex::Arena* Arena_Used = amrex::The_Arena();
87#ifdef AMREX_USE_GPU
88 Arena_Used = amrex::The_Pinned_Arena();
89#endif
90 const auto& lo = domain.loVect();
91 const auto& hi = domain.hiVect();
92
93 amrex::Box xlo_bx(amrex::IntVect(lo[0]+index_type[0]-1, lo[1]+index_type[1]-1, lo[2]),
94 amrex::IntVect(lo[0]+index_type[0]-1, hi[1]+1 , hi[2]), index_type);
95 amrex::Box xhi_bx(amrex::IntVect(hi[0]+1 , lo[1]+index_type[1]-1, lo[2]),
96 amrex::IntVect(hi[0]+1 , hi[1]+1 , hi[2]), index_type);
97 amrex::Box ylo_bx(amrex::IntVect(lo[0]+index_type[0]-1, lo[1]+index_type[1]-1, lo[2]),
98 amrex::IntVect(hi[0]+1 , lo[1]+index_type[1]-1, hi[2]), index_type);
99 amrex::Box yhi_bx(amrex::IntVect(lo[0]+index_type[0]-1, hi[1]+1 , lo[2]),
100 amrex::IntVect(hi[0]+1 , hi[1]+1 , hi[2]), index_type);
101 if (is2d) {
102 xlo_bx.makeSlab(2,0);
103 xhi_bx.makeSlab(2,0);
104 ylo_bx.makeSlab(2,0);
105 yhi_bx.makeSlab(2,0);
106 }
107
108 amrex::Print() << xlo_bx << " " << xhi_bx << " " << ylo_bx << " " << yhi_bx << std::endl;
109
110 xlo_dat_before = amrex::FArrayBox(xlo_bx, 1, Arena_Used);
111 xhi_dat_before = amrex::FArrayBox(xhi_bx, 1, Arena_Used);
112 ylo_dat_before = amrex::FArrayBox(ylo_bx, 1, Arena_Used);
113 yhi_dat_before = amrex::FArrayBox(yhi_bx, 1, Arena_Used);
114
115 xlo_dat_after = amrex::FArrayBox(xlo_bx, 1, Arena_Used);
116 xhi_dat_after = amrex::FArrayBox(xhi_bx, 1, Arena_Used);
117 ylo_dat_after = amrex::FArrayBox(ylo_bx, 1, Arena_Used);
118 yhi_dat_after = amrex::FArrayBox(yhi_bx, 1, Arena_Used);
119
120 xlo_dat_interp = amrex::FArrayBox(xlo_bx, 1, Arena_Used);
121 xhi_dat_interp = amrex::FArrayBox(xhi_bx, 1, Arena_Used);
122 ylo_dat_interp = amrex::FArrayBox(ylo_bx, 1, Arena_Used);
123 yhi_dat_interp = amrex::FArrayBox(yhi_bx, 1, Arena_Used);
124
125 // dummy initialization
126 i_time_before = -100;
127
128 if (var_need_data[amrex::Orientation(amrex::Direction::x,amrex::Orientation::low)] == true) {
129 nc_var_names.push_back(field_name + "_west");
130 }
131 if (var_need_data[amrex::Orientation(amrex::Direction::x,amrex::Orientation::high)] == true) {
132 nc_var_names.push_back(field_name + "_east");
133 }
134 if (var_need_data[amrex::Orientation(amrex::Direction::y,amrex::Orientation::low)] == true) {
135 nc_var_names.push_back(field_name + "_south");
136 }
137 if (var_need_data[amrex::Orientation(amrex::Direction::y,amrex::Orientation::high)] == true) {
138 nc_var_names.push_back(field_name + "_north");
139 }
140}
141
142/**
143 * @param time time to interpolate to
144 */
146 // Figure out time index:
147 AMREX_ASSERT(time >= bry_times[0]);
148 AMREX_ASSERT(time <= bry_times[bry_times.size()-1]);
149 int i_time_before_old = i_time_before;
150 for (int nt=0; nt < bry_times.size()-1; nt++) {
151 if ((bry_times[nt] <= time) and (bry_times[nt+1] >= time)) {
152 i_time_before = nt;
154 time_after = bry_times[nt+1];
155 break;
156 }
157 }
158
159 int i_time_after = i_time_before + 1;
160 if (i_time_before_old + 1 == i_time_before) {
161 // swap multifabs so we only have to read in one MultiFab
162 std::swap(xlo_dat_before, xlo_dat_after);
163 std::swap(xhi_dat_before, xhi_dat_after);
164 std::swap(ylo_dat_before, ylo_dat_after);
165 std::swap(yhi_dat_before, yhi_dat_after);
167 } else if (i_time_before_old != i_time_before) {
170 }
171
172 amrex::Real dt = time_after - time_before;
173
174 amrex::Real time_before_copy = time_before;
175
176 amrex::Array4<amrex::Real> xlo_interp_arr = xlo_dat_interp.array();
177 amrex::Array4<amrex::Real> xhi_interp_arr = xhi_dat_interp.array();
178 amrex::Array4<amrex::Real> ylo_interp_arr = ylo_dat_interp.array();
179 amrex::Array4<amrex::Real> yhi_interp_arr = yhi_dat_interp.array();
180
181 amrex::Array4<amrex::Real> xlo_before_arr = xlo_dat_before.array();
182 amrex::Array4<amrex::Real> xhi_before_arr = xhi_dat_before.array();
183 amrex::Array4<amrex::Real> ylo_before_arr = ylo_dat_before.array();
184 amrex::Array4<amrex::Real> yhi_before_arr = yhi_dat_before.array();
185
186 amrex::Array4<amrex::Real> xlo_after_arr = xlo_dat_after.array();
187 amrex::Array4<amrex::Real> xhi_after_arr = xhi_dat_after.array();
188 amrex::Array4<amrex::Real> ylo_after_arr = ylo_dat_after.array();
189 amrex::Array4<amrex::Real> yhi_after_arr = yhi_dat_after.array();
190
191 if (var_need_data[amrex::Orientation(amrex::Direction::x,amrex::Orientation::low)] == true) {
192 amrex::ParallelFor(xlo_dat_interp.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
193 {
194 xlo_interp_arr(i,j,k) = xlo_before_arr(i,j,k) + (time - time_before_copy) * (xlo_after_arr(i,j,k) - xlo_before_arr(i,j,k)) / dt;
195 });
196 }
197
198 if (var_need_data[amrex::Orientation(amrex::Direction::x,amrex::Orientation::high)] == true) {
199 amrex::ParallelFor(xhi_dat_interp.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
200 {
201 xhi_interp_arr(i,j,k) = xhi_before_arr(i,j,k) + (time - time_before_copy) * (xhi_after_arr(i,j,k) - xhi_before_arr(i,j,k)) / dt;
202 });
203 }
204
205 if (var_need_data[amrex::Orientation(amrex::Direction::y,amrex::Orientation::low)] == true) {
206 amrex::ParallelFor(ylo_dat_interp.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
207 {
208 ylo_interp_arr(i,j,k) = ylo_before_arr(i,j,k) + (time - time_before_copy) * (ylo_after_arr(i,j,k) - ylo_before_arr(i,j,k)) / dt;
209 });
210 }
211
212 if (var_need_data[amrex::Orientation(amrex::Direction::y,amrex::Orientation::high)] == true) {
213 amrex::ParallelFor(yhi_dat_interp.box(), [=] AMREX_GPU_DEVICE (int i, int j, int k)
214 {
215 yhi_interp_arr(i,j,k) = yhi_before_arr(i,j,k) + (time - time_before_copy) * (yhi_after_arr(i,j,k) - yhi_before_arr(i,j,k)) / dt;
216 });
217 }
218}
219
220/**
221 * @param[inout] fab_xlo fab to store xlo boundary data into
222 * @param[inout] fab_xhi fab to store xhi boundary data into
223 * @param[inout] fab_ylo fab to store ylo boundary data into
224 * @param[inout] fab_yhi fab to store yhi boundary data into
225 * @param[in ] itime index of time step to read from file
226 */
227void NCTimeSeriesBoundary::read_in_at_time (amrex::FArrayBox& fab_xlo,
228 amrex::FArrayBox& fab_xhi,
229 amrex::FArrayBox& fab_ylo,
230 amrex::FArrayBox& fab_yhi,
231 int itime) {
232 using RARRAY = NDArray<amrex::Real>;
233 amrex::Vector<RARRAY> arrays(nc_var_names.size());
234
235 // The width of the boundary region we need to read is 1
236 int width = 1;
237
238 amrex::Print() << "Reading in " << field_name << " at time " << bry_times[itime] << std::endl;
239 std::string nc_bdry_file = file_names[file_for_time[itime]];
240 int itime_offset = file_itime_offset[itime];
241 ReadNetCDFFile(nc_bdry_file, nc_var_names, arrays, true, itime_offset); // does work on proc 0 only
242
243 for (int iv=0; iv < nc_var_names.size(); iv++) {
244 if (amrex::ParallelDescriptor::IOProcessor())
245 {
246 std::string last4 = nc_var_names[iv].substr(nc_var_names[iv].size()-4, 4);
247 std::string last5 = nc_var_names[iv].substr(nc_var_names[iv].size()-5, 5);
248 int nx, ny, nz, n_plane;
249 int i, j, k, ioff, joff;
250 if (last4 == "west") {
251 amrex::Box my_box = fab_xlo.box();
252 if (is2d) {
253 nz = 1;
254 ny = arrays[iv].get_vshape()[1];
255 } else {
256 nz = arrays[iv].get_vshape()[1];
257 ny = arrays[iv].get_vshape()[2];
258 }
259 n_plane = ny * nz;
260 AMREX_ALWAYS_ASSERT(my_box.numPts() == n_plane);
261
262 i = my_box.smallEnd()[0];
263 joff = my_box.smallEnd()[1];
264
265 amrex::Array4<amrex::Real> fab_arr = fab_xlo.array();
266 for (int n(0); n < n_plane; n++) {
267 k = n / ny;
268 j = n - (k * ny);
269 fab_arr(i, j+joff, k, 0) = static_cast<amrex::Real>(*(arrays[iv].get_data() + n));
270 }
271 } else if (last4 == "east") {
272 amrex::Box my_box = fab_xhi.box();
273 if (is2d) {
274 nz = 1;
275 ny = arrays[iv].get_vshape()[1];
276 } else {
277 nz = arrays[iv].get_vshape()[1];
278 ny = arrays[iv].get_vshape()[2];
279 }
280 n_plane = ny * nz;
281 AMREX_ALWAYS_ASSERT(my_box.numPts() == n_plane);
282
283 i = my_box.smallEnd()[0];
284 joff = my_box.smallEnd()[1];
285
286 amrex::Array4<amrex::Real> fab_arr = fab_xhi.array();
287 for (int n(0); n < n_plane; n++) {
288 k = n / ny;
289 j = n - (k * ny);
290 fab_arr(i, j+joff, k, 0) = static_cast<amrex::Real>(*(arrays[iv].get_data() + n));
291 }
292 } else if (last5 == "south") {
293 amrex::Box my_box = fab_ylo.box();
294 if (is2d) {
295 nz = 1;
296 nx = arrays[iv].get_vshape()[1];
297 } else {
298 nz = arrays[iv].get_vshape()[1];
299 nx = arrays[iv].get_vshape()[2];
300 }
301 n_plane = nx * nz;
302 AMREX_ALWAYS_ASSERT(my_box.numPts() == n_plane);
303
304 j = my_box.smallEnd()[1];
305 ioff = my_box.smallEnd()[0];
306
307 amrex::Array4<amrex::Real> fab_arr = fab_ylo.array();
308 for (int n(0); n < n_plane; n++) {
309 k = n / nx;
310 i = n - (k * nx);
311 fab_arr(i+ioff, j, k, 0) = static_cast<amrex::Real>(*(arrays[iv].get_data() + n));
312 }
313 } else if (last5 == "north") {
314 amrex::Box my_box = fab_yhi.box();
315 if (is2d) {
316 nz = 1;
317 nx = arrays[iv].get_vshape()[1];
318 } else {
319 nz = arrays[iv].get_vshape()[1];
320 nx = arrays[iv].get_vshape()[2];
321 }
322 n_plane = nx * nz;
323 AMREX_ALWAYS_ASSERT(my_box.numPts() == n_plane);
324
325 j = my_box.smallEnd()[1];
326 ioff = my_box.smallEnd()[0];
327
328 amrex::Array4<amrex::Real> fab_arr = fab_yhi.array();
329 for (int n(0); n < n_plane; n++) {
330 k = n / nx;
331 i = n - (k * nx);
332 fab_arr(i+ioff, j, k, 0) = static_cast<amrex::Real>(*(arrays[iv].get_data() + n));
333 }
334 }
335 }
336 }
337
338 amrex::ParallelDescriptor::Barrier();
339
340 // When an FArrayBox is built, space is allocated on every rank. However, we only
341 // filled the data in these FABs on the IOProcessor. So here we broadcast
342 // the data to every rank.
343 int ioproc = amrex::ParallelDescriptor::IOProcessorNumber(); // I/O rank
344 amrex::ParallelDescriptor::Bcast(fab_xlo.dataPtr(),fab_xlo.box().numPts(),ioproc);
345 amrex::ParallelDescriptor::Bcast(fab_xhi.dataPtr(),fab_xhi.box().numPts(),ioproc);
346 amrex::ParallelDescriptor::Bcast(fab_ylo.dataPtr(),fab_ylo.box().numPts(),ioproc);
347 amrex::ParallelDescriptor::Bcast(fab_yhi.dataPtr(),fab_yhi.box().numPts(),ioproc);
348
349}
350#endif // REMORA_USE_NETCDF
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.
std::string ReadNetCDFVarAttrStr(const std::string &fname, const std::string &var_name, const std::string &attr_name)
Helper function for reading a single variable attribute.
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.
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::Real time_before
Time in ocean_times immediately before the last time interpolated to.
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 *‍/.
amrex::Vector< amrex::Real > bry_times
Time points in netcdf file.
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.
NCTimeSeriesBoundary(const amrex::Vector< std::string > &a_file_name, const std::string a_field_name, const std::string a_time_name, const amrex::Box &a_domain, const amrex::IntVect a_index_type, const amrex::GpuArray< bool, AMREX_SPACEDIM *2 > *a_var_need_data, bool a_is2d)
Constructor.
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 *‍/.
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.
void update_interpolated_to_time(amrex::Real time)
Calculate interpolated values at time, reading in data as necessary.
amrex::FArrayBox xlo_dat_before
FArrayBox to store data at time_before at xlo boundary.
amrex::Real time_after
Time in ocean_times immediately after the last time interpolated to.
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,...