REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_NCTimeSeries.cpp
Go to the documentation of this file.
2#include "REMORA_NCFile.H"
3
4#include "AMReX_FillPatchUtil.H"
5#include "AMReX_Interpolater.H"
6#include "AMReX_ParallelDescriptor.H"
7
8#include <string>
9
10#ifdef REMORA_USE_NETCDF
11/**
12 * @param[in ] a_file_names 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_domain simulation domain
16 * @param[inout] a_mf_var MultiFab of data to either store into or reference for dimensions
17 * @param[in ] a_is2d Whether the variable we're working with is 2D
18 * @param[in ] a_save_interpolated Whether the interpolated value should be saved internally
19 */
20NCTimeSeries::NCTimeSeries (const amrex::Vector<std::string>& a_file_names, const std::string a_field_name,
21 const std::string a_time_name,
22 const amrex::Box& a_domain,
23 amrex::MultiFab* a_mf_var, bool a_is2d, bool a_save_interpolated) {
24 file_names.assign(a_file_names.begin(), a_file_names.end());
25 time_name = a_time_name;
26 field_name = a_field_name;
27 domain = a_domain;
28 mf_var = a_mf_var;
29 is2d = a_is2d;
30 save_interpolated = a_save_interpolated;
31}
32
34 // open file
35 amrex::Print() << "Loading " << field_name << " from NetCDF file(s)" << std::endl;
36
37 // The time field can have any number of names, depending on the field.
38 // If not specified in input file (time_name.empty()) then set it by default
39 if (time_name.empty())
40 {
41 if (field_name.find("wind") != std::string::npos) {
42 time_name = "wind_time";
43 } else if ((field_name.find("str") != std::string::npos) and (field_name[0] == 's')) {
44 time_name = "sms_time";
45 } else if ((field_name.find("str") != std::string::npos) and (field_name[0] == 'b')) {
46 time_name = "bms_time";
47 } else {
48 time_name = "ocean_time";
49 }
50 }
51
52 for (int ifile = 0; ifile < file_names.size(); ++ifile) {
53 const std::string& file_name = file_names[ifile];
54
55 // Check units of time stamps; should be days
56 std::string unit_str = ReadNetCDFVarAttrStr(file_name, time_name, "units"); // works on proc 0
57 if (amrex::ParallelDescriptor::IOProcessor())
58 {
59 if (unit_str.find("days") == std::string::npos) {
60 amrex::Print() << "Units of ocean_time given as: " << unit_str << std::endl;
61 amrex::Abort("Units must be in days.");
62 }
63 }
64
65 // get times and put in array
66 using RARRAY = NDArray<amrex::Real>;
67 amrex::Vector<RARRAY> array_ts(1);
68 ReadNetCDFFile(file_name, {time_name}, array_ts); // filled only on proc 0
69 if (amrex::ParallelDescriptor::IOProcessor())
70 {
71 int ntimes_io = array_ts[0].get_vshape()[0];
72 for (int nt(0); nt < ntimes_io; nt++)
73 {
74 // Convert ocean time from days to seconds
75 ocean_times.push_back((*(array_ts[0].get_data() + nt)) * amrex::Real(60.0) * amrex::Real(60.0) * amrex::Real(24.0));
76 file_for_time.push_back(ifile);
77 file_itime_offset.push_back(nt);
78 }
79 }
80 }
81 int ntimes = ocean_times.size();
82 // Only do checks on IO processor since ocean_times isn't populated on other ranks yet
83 if (amrex::ParallelDescriptor::IOProcessor()) {
84 AMREX_ASSERT(std::is_sorted(ocean_times.begin(), ocean_times.end()));
85 if (ntimes <= 1) {
86 amrex::Error("Time series data must be given at at least two times");
87 }
88 }
89 int ioproc = amrex::ParallelDescriptor::IOProcessorNumber();
90 amrex::ParallelDescriptor::Bcast(&ntimes,1,ioproc);
91 if (!(amrex::ParallelDescriptor::IOProcessor())) {
92 ocean_times.resize(ntimes);
93 file_for_time.resize(ntimes);
94 file_itime_offset.resize(ntimes);
95 }
96 amrex::ParallelDescriptor::Bcast(ocean_times.data(), ocean_times.size(), ioproc);
97 amrex::ParallelDescriptor::Bcast(file_for_time.data(), file_for_time.size(), ioproc);
98 amrex::ParallelDescriptor::Bcast(file_itime_offset.data(), file_itime_offset.size(), ioproc);
99
100 // Initialize MultiFabs
101 // NetCDF data is always read and temporally interpolated on level 0.
102 mf_before = new amrex::MultiFab(mf_var->boxArray(), mf_var->DistributionMap(), 1, mf_var->nGrowVect());
103 mf_after = new amrex::MultiFab(mf_var->boxArray(), mf_var->DistributionMap(), 1, mf_var->nGrowVect());
104 mf_interp_lev0 = new amrex::MultiFab(mf_var->boxArray(), mf_var->DistributionMap(), 1, mf_var->nGrowVect());
105
106 // dummy initialization
107 i_time_before = -100;
108}
109
110/**
111 * @param time time to interpolate to
112 */
113void NCTimeSeries::update_interpolated_to_time (amrex::Real time, int lev,
114 amrex::MultiFab* mf_lev,
115 const amrex::Vector<amrex::Geometry>& geom,
116 const amrex::Vector<amrex::IntVect>& ref_ratio) {
117 // Figure out time index:
118 AMREX_ASSERT(time >= ocean_times[0]);
119 AMREX_ASSERT(time <= ocean_times[ocean_times.size()-1]);
120 int i_time_before_old = i_time_before;
121 for (int nt=0; nt < ocean_times.size()-1; nt++) {
122 if ((ocean_times[nt] <= time) and (ocean_times[nt+1] >= time)) {
123 i_time_before = nt;
125 time_after = ocean_times[nt+1];
126 break;
127 }
128 }
129
130 int i_time_after = i_time_before + 1;
131 if (i_time_before_old + 1 == i_time_before) {
132 // swap multifabs so we only have to read in one MultiFab
133 std::swap(mf_before, mf_after);
134 read_in_at_time(mf_after, i_time_after);
135 } else if (i_time_before_old != i_time_before) {
136 read_in_at_time(mf_after, i_time_after);
138 }
139
140 amrex::Real dt = time_after - time_before;
141
142 auto nodality = mf_interp_lev0->ixType();
143
144#ifdef AMREX_USE_OMP
145#pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
146#endif
147 for (amrex::MFIter mfi(*mf_interp_lev0,true); mfi.isValid(); ++mfi) {
148 // Adjust box to match ROMS grid
149 amrex::Box bx = mfi.growntilebox(amrex::IntVect(1-nodality[0],1-nodality[1],0));
150
151 amrex::Real time_before_copy = time_before;
152
153 // Temporal interpolation is done once on level 0.
154 amrex::MultiFab* mf_to_fill = mf_interp_lev0;
155 amrex::Array4<amrex::Real> to_fill = mf_to_fill->array(mfi);
156 amrex::Array4<const amrex::Real> before = mf_before->const_array(mfi);
157 amrex::Array4<const amrex::Real> after = mf_after->const_array(mfi);
158 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
159 {
160 to_fill(i,j,k) = before(i,j,k) + (time - time_before_copy) * (after(i,j,k) - before(i,j,k)) / dt;
161 });
162 }
163
164 amrex::MultiFab* mf_to_fill_lev = mf_lev;
165 if (save_interpolated) {
166 if (mf_interpolated_lev.size() <= static_cast<std::size_t>(lev)) {
167 mf_interpolated_lev.resize(lev+1);
168 }
169 if (!mf_interpolated_lev[lev] ||
170 mf_interpolated_lev[lev]->boxArray() != mf_lev->boxArray() ||
171 mf_interpolated_lev[lev]->nGrowVect() != mf_lev->nGrowVect()) {
172 mf_interpolated_lev[lev] = std::make_unique<amrex::MultiFab>(
173 mf_lev->boxArray(), mf_lev->DistributionMap(), 1, mf_lev->nGrowVect());
174 }
175 mf_to_fill_lev = mf_interpolated_lev[lev].get();
176 }
177
178 if (lev == 0) {
179 amrex::MultiFab::Copy(*mf_to_fill_lev, *mf_interp_lev0, 0, 0, 1, mf_to_fill_lev->nGrowVect());
180 return;
181 }
182
183 amrex::PhysBCFunctNoOp null_bc_for_fill;
184 amrex::Vector<amrex::BCRec> null_dom_bcs(1);
185 for (int dir = 0; dir < AMREX_SPACEDIM; ++dir) {
186 null_dom_bcs[0].setLo(dir, amrex::BCType::int_dir);
187 null_dom_bcs[0].setHi(dir, amrex::BCType::int_dir);
188 }
189
190 amrex::Interpolater* mapper = nullptr;
191 const auto& idx_type = mf_to_fill_lev->ixType();
192 if (idx_type == amrex::IndexType(amrex::IntVect(0,0,0))) {
193 mapper = &amrex::cell_cons_interp;
194 } else {
195 mapper = &amrex::face_cons_linear_interp;
196 }
197
198 amrex::InterpFromCoarseLevel(*mf_to_fill_lev, amrex::Real(0.0), *mf_interp_lev0,
199 0, 0, 1,
200 geom[0], geom[lev],
202 cumulative_ref_ratio(lev, ref_ratio),
203 mapper, null_dom_bcs, 0);
204
205 mf_to_fill_lev->FillBoundary(geom[lev].periodicity());
206}
207
208amrex::IntVect
210 const amrex::Vector<amrex::IntVect>& ref_ratio) const {
211 amrex::IntVect rr(1,1,1);
212 for (int l = 0; l < lev; ++l) {
213 rr[0] *= ref_ratio[l][0];
214 rr[1] *= ref_ratio[l][1];
215 rr[2] *= ref_ratio[l][2];
216 }
217 return rr;
218}
219
220const amrex::MultiFab*
222 AMREX_ALWAYS_ASSERT(save_interpolated);
223 AMREX_ALWAYS_ASSERT(lev < static_cast<int>(mf_interpolated_lev.size()));
224 AMREX_ALWAYS_ASSERT(mf_interpolated_lev[lev]);
225 return mf_interpolated_lev[lev].get();
226}
227
228/**
229 * @param[inout] mf multifab to store time step data into
230 * @param[in ] itime index of time step to read from file
231 */
232void NCTimeSeries::read_in_at_time (amrex::MultiFab* mf, int itime) {
233 // This all assumes that we're on level 0 with only one boxes_at_level
234 amrex::FArrayBox NC_fab;
235 amrex::Vector<amrex::FArrayBox*> NC_fabs;
236 amrex::Vector<std::string> NC_names;
237 amrex::Vector<enum NC_Data_Dims_Type> NC_dim_types;
238
239 const std::string& file_name = file_names[file_for_time[itime]];
240 const int itime_offset = file_itime_offset[itime];
241
242 amrex::Print() << "Reading in " << field_name << " at time index " << itime
243 << " from " << file_name << std::endl;
244
245 NC_fabs.push_back(&NC_fab) ; NC_names.push_back(field_name);
246
247 if (is2d) {
248 NC_dim_types.push_back(NC_Data_Dims_Type::Time_SN_WE);
249 } else {
250 NC_dim_types.push_back(NC_Data_Dims_Type::Time_BT_SN_WE);
251 }
252
253 BuildFABsFromNetCDFFile<amrex::FArrayBox,amrex::Real>(domain, file_name, NC_names, NC_dim_types,
254 NC_fabs, true, itime_offset);
255
256#ifdef _OPENMP
257#pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
258#endif
259 {
260 // Don't tile this since we are operating on full FABs in this routine
261 for ( amrex::MFIter mfi(*mf, false); mfi.isValid(); ++mfi )
262 {
263 amrex::FArrayBox &fab = (*mf)[mfi];
264
265 //
266 // FArrayBox to FArrayBox copy does "copy on intersection"
267 // This only works here because we have broadcast the FArrayBox of data from the netcdf file to all ranks
268
269 fab.template copy<amrex::RunOn::Device>(NC_fab);
270 } // mf
271 } // omp
272}
273#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.
PhysBCFunctNoOp null_bc_for_fill
amrex::Vector< std::unique_ptr< amrex::MultiFab > > mf_interpolated_lev
Interpolated data on each requested AMR level if save_interpolated=true.
bool is2d
Whether the field we're reading in is 2d.
void update_interpolated_to_time(amrex::Real time, int lev, amrex::MultiFab *mf_lev, const amrex::Vector< amrex::Geometry > &geom, const amrex::Vector< amrex::IntVect > &ref_ratio)
Calculate interpolated values at time and fill data for level lev.
void read_in_at_time(amrex::MultiFab *mf, int itime)
Read in data from file at time index itime and fill into mf.
amrex::Vector< int > file_itime_offset
Offset to access a particular time within its file.
amrex::MultiFab * mf_interp_lev0
Multifab storing temporally interpolated data on level 0.
amrex::Real time_after
Time in ocean_times immediately after the last time interpolated to.
amrex::Real time_before
Time in ocean_times immediately before the last time interpolated to.
amrex::Box domain
Domain.
void Initialize()
Read in time array from file and allocate data arrays.
amrex::MultiFab * mf_before
Multifab to store data at time_before.
int i_time_before
Time index immediately before the last time interpolated to.
std::string field_name
Field name in netcdf file.
amrex::IntVect cumulative_ref_ratio(int lev, const amrex::Vector< amrex::IntVect > &ref_ratio) const
Build cumulative refinement ratio from level 0 to lev.
NCTimeSeries(const amrex::Vector< std::string > &a_file_names, const std::string a_field_name, const std::string a_time_name, const amrex::Box &a_domain, amrex::MultiFab *a_mf_var, bool a_is2d, bool a_save_interpolated)
Constructor.
amrex::Vector< std::string > file_names
File names to read from.
amrex::MultiFab * mf_after
Multifab to store data at time_after.
std::string time_name
Field name for time series in netcdf file.
amrex::Vector< int > file_for_time
File index to access a particular time.
const amrex::MultiFab * get_interpolated_mf(int lev) const
Access interpolated data saved for a specific level.
amrex::Vector< amrex::Real > ocean_times
Time points in netcdf file.
amrex::MultiFab * mf_var
NDArray is the datatype designed to hold any data, including scalars, multidimensional arrays,...