REMORA
Regional Modeling of Oceans Refined Adaptively
Loading...
Searching...
No Matches
REMORA_NCFile.H
Go to the documentation of this file.
1#ifndef _REMORA_NCFILE_H_
2#define _REMORA_NCFILE_H_
3
4#include <sstream>
5#include <string>
6#include <ctime>
7#include <memory>
8
9#include "AMReX_FArrayBox.H"
10#include "AMReX_IArrayBox.H"
11// this includes pnetcdf.h, and mpi.h
12#include "REMORA_NCInterface.H"
13
14using PlaneVector = amrex::Vector<amrex::FArrayBox>;
15
20 SN_WE,
23 BT_Riv,
24};
25
26/**
27 * \brief NDArray is the datatype designed to hold any data, including scalars, multidimensional
28 * arrays, that read from the NetCDF file.
29 *
30 * The data read from NetCDF file are stored in a continuous memory, and the data layout is described
31 * by using a vector (shape). AMRex Box can be constructed using the data shape information, and MultiFab
32 * data array can be setup using the data that stored in the NDArray.
33 */
34template <typename DataType>
35struct NDArray
36{
37 using DType = typename std::remove_const<DataType>::type;
38
39 /** \brief constructor */
40 explicit NDArray (const std::string vname, const std::vector<MPI_Offset>& vshape)
42 data{std::shared_ptr<DType[]>(new DType[this->ndim()],
43 std::default_delete<DType[]>())} {}
44
45 /** \brief default constructor */
46 NDArray () : name{"null"}, data{nullptr} {}
47
48 /** \brief get the data pointer */
49 decltype(auto) get_data () {
50 return data.get();
51 }
52
53 /** \brief get the variable name */
54 std::string get_vname () {
55 return name;
56 }
57
58 /** \brief get the variable data shape */
59 std::vector<MPI_Offset> get_vshape () {
60 return shape;
61 }
62
63 /** \brief return the total number of data */
64 size_t ndim () {
65 size_t num = 1;
66 int isize = static_cast<int>(shape.size());
67 for (auto i=0; i<isize; ++i) num *= shape[i];
68 return num;
69 }
70
71 /** \brief set the data shape information */
72 void set_vshape (std::vector<MPI_Offset> vshape) {
73 shape = vshape;
74 }
75
76 private:
77 std::string name;
78 std::vector<MPI_Offset> shape;
79 std::shared_ptr<DType[]> data;
80};
81
82/** \brief Read in data from netcdf file and save to data arrays
83 *
84 * @param[in ] fname file name to read from
85 * @param[in ] names field names to read
86 * @param[inout] arrays vector of data arrays to fill
87 * @param[in ] one_time whether to read in a single time step of data
88 * @param[in ] fill_time if reading a single time, what time step to read from the file?
89 */
90template<typename DType>
91void ReadNetCDFFile (const std::string& fname, amrex::Vector<std::string> names,
92 amrex::Vector<NDArray<DType> >& arrays, bool one_time=false, int fill_time=0)
93{
94 AMREX_ASSERT(arrays.size() == names.size());
95
98 if (amrex::ParallelDescriptor::IOProcessor())
99 {
100
101 /*
102 // get the dimension information
103 int Time = static_cast<int>(ncf.dim("Time").len());
104 int DateStrLen = static_cast<int>(ncf.dim("DateStrLen").len());
105 int west_east = static_cast<int>(ncf.dim("west_east").len());
106 int south_north = static_cast<int>(ncf.dim("south_north").len());
107 int bottom_top = static_cast<int>(ncf.dim("bottom_top").len());
108 int bottom_top_stag = static_cast<int>(ncf.dim("bottom_top_stag").len());
109 int west_east_stag = static_cast<int>(ncf.dim("west_east_stag").len());
110 int south_north_stag = static_cast<int>(ncf.dim("south_north_stag").len());
111 */
112
113// amrex::Print() << "Reading the dimensions from the netcdf file " << "\n";
114
115 for (auto n=0; n<arrays.size(); ++n) {
116 // read the data from NetCDF file
117 std::string vname_to_write = names[n];
118 std::string vname_to_read = names[n];
119
120 if (!ncf.has_var(vname_to_read)) {
121 amrex::Print() << "Variable name " << vname_to_read << " not found!" << std::endl;
122 }
123
124 // amrex::AllPrint() << "About to read " << vname_to_read
125 // << " while filling the array for " << vname_to_write << std::endl;
126
127 std::vector<MPI_Offset> shape = ncf.var(vname_to_read).shape();
128 std::vector<MPI_Offset> start(shape.size(), 0);
129 // If only reading one time step, adjust start and shape accordingly
130 if (one_time) {
132 "Cannot read one time step from a scalar NetCDF variable");
133 start[0] = fill_time;
134 shape[0] = 1;
135 }
136
138 DType* dataPtr = arrays[n].get_data();
139
140 // auto numPts = arrays[n].ndim();
141 // amrex::Print() << "NetCDF Variable name = " << vname_to_read << std::endl;
142 // amrex::Print() << "numPts read from NetCDF file/var = " << numPts << std::endl;
143 // amrex::Print() << "Dims of the variable = (";
144 // for (auto &dim:shape)
145 // amrex::Print() << dim << ", " ;
146 // amrex::Print() << ")" << std::endl;
147
148 ncf.var(vname_to_read).get(dataPtr, start, shape);
149 }
150 }
151 ncf.close();
152}
153
154/**
155 * \brief Helper function for reading a single variable attribute
156 */
157std::string ReadNetCDFVarAttrStr (const std::string& fname,
158 const std::string& var_name,
159 const std::string& attr_name);
160
161/**
162 * \brief Helper function for testing for the presence of a single variable attribute
163 */
164bool QueryNetCDFVarAttrStr (const std::string& fname,
165 const std::string& var_name,
166 const std::string& attr_name);
167
168/**
169 * \brief Offset, in days, that puts a file's time variable onto the model clock.
170 *
171 * Reads the `units` attribute of \p time_name, which must count days, and its
172 * `calendar` attribute if it has one. A `days since <date>` reference date is
173 * converted to days since the date `remora.time_ref` names, so a file whose
174 * epoch differs from the run's is still read at the right time; the offset is
175 * zero when the two dates agree. A time variable with no reference date is
176 * taken to be on the model clock already. A calendar that disagrees with
177 * \p time_ref is an error, since the two would count days differently.
178 *
179 * Collective: the attributes are read on the I/O processor and the result is
180 * broadcast. The mapping applied is printed, so a reader of the log can see it.
181 *
182 * @param[in] fname NetCDF file
183 * @param[in] time_name name of the time variable
184 * @param[in] time_ref remora.time_ref
185 * @param[in] units_required abort when the time variable has no `units`
186 * attribute; otherwise warn and assume days on the
187 * model clock
188 * @param[in] announce print the mapping; false for a series that is set
189 * up but will never be read
190 */
191double ReadNetCDFTimeOffsetDays (const std::string& fname,
192 const std::string& time_name,
193 double time_ref,
194 bool units_required,
195 bool announce);
196
197/**
198 * \brief Helper function for testing whether a file carries every named variable.
199 *
200 * Call this before reading optional variables: ReadNetCDFFile() dereferences
201 * ncf.var(name) unconditionally, so a missing name is a hard failure.
202 */
203bool QueryNetCDFHasVars (const std::string& fname,
204 const amrex::Vector<std::string>& var_names);
205/**
206 * \brief Helper function for reading data from NetCDF file into a
207 * provided FAB.
208 *
209 * @param iv Index for which variable we are going to fill
210 * @param nc_arrays Arrays of data from NetCDF file
211 * @param var_name Variable name
212 * @param NC_dim_type Dimension type for the variable as stored in the NetCDF file
213 * @param temp FAB where we store the variable data from the NetCDF Arrays
214 * @param custom_ngrow Custom number of grow cells for the FAB
215 */
216template<class FAB,typename DType>
217void
219 amrex::Vector<NDArray<amrex::Real>>& nc_arrays,
220 const std::string& var_name,
222 FAB& temp, amrex::IntVect custom_ngrow)
223{
224 int nsx, nsy, nsz;
226 nsz = 1;
227 nsy = nc_arrays[iv].get_vshape()[0];
228 nsx = nc_arrays[iv].get_vshape()[1];
229 // amrex::Print() << "TYPE SN WE " << nsy << " " << nsx << std::endl;
231 nsz = nc_arrays[iv].get_vshape()[0];
232 nsy = nc_arrays[iv].get_vshape()[1];
233 nsx = nc_arrays[iv].get_vshape()[2];
234 // amrex::Print() << "TYPE BT SN WE " << nz << " " << nsy << " " << nsx << std::endl;
236 nsz = 1;
237 nsy = nc_arrays[iv].get_vshape()[1];
238 nsx = nc_arrays[iv].get_vshape()[2];
239 // amrex::Print() << "TYPE TIME SN WE " << nsy << " " << nsx << std::endl;
241 nsz = nc_arrays[iv].get_vshape()[1];
242 nsy = nc_arrays[iv].get_vshape()[2];
243 nsx = nc_arrays[iv].get_vshape()[3];
244 // amrex::Print() << "TYPE TIME BT SN WE " << nsx << " " << nsy << " " << nsz << std::endl;
246 nsz = nc_arrays[iv].get_vshape()[1]; // number of z-coords
247 nsy = 1; // dummy
248 nsx = nc_arrays[iv].get_vshape()[2]; // number of rivers
250 nsz = 1; // number of z-coords
251 nsy = 1; // dummy
252 nsx = nc_arrays[iv].get_vshape()[1]; // number of rivers
254 nsz = nc_arrays[iv].get_vshape()[0]; // number of z-coords
255 nsy = 1; // dummy
256 nsx = nc_arrays[iv].get_vshape()[1]; // number of rivers
257 } else {
258 amrex::Abort("Dont know this NC_Data_Dims_Type");
259 }
260
261 amrex::Box my_box;
262
263 if (var_name == "u" || var_name == "ubar" || var_name == "mask_u" || var_name == "x_u" ||
264 var_name == "y_u" || var_name == "sustr")
265 {
266 my_box.setSmall(amrex::IntVect(0,-1,0));
267 my_box.setBig(amrex::IntVect(nsx-1,nsy-2,nsz-1));
268 my_box.setType(amrex::IndexType(amrex::IntVect(1,0,0)));
269 }
270 else if (var_name == "v" || var_name == "vbar" || var_name == "mask_v" || var_name == "x_v" ||
271 var_name == "y_v" || var_name == "svstr")
272 {
273 my_box.setSmall(amrex::IntVect(-1,0,0));
274 my_box.setBig(amrex::IntVect(nsx-2,nsy-1,nsz-1));
275 my_box.setType(amrex::IndexType(amrex::IntVect(0,1,0)));
276 }
277 else if (var_name == "mask_psi" || var_name == "x_psi" || var_name == "y_psi" ||
278 var_name == "lon_psi" || var_name == "lat_psi")
279 {
280 my_box.setSmall(amrex::IntVect(0,0,0));
281 my_box.setBig(amrex::IntVect(nsx-1,nsy-1,nsz-1));
282 my_box.setType(amrex::IndexType(amrex::IntVect(1,1,0)));
283 // River data has no ghost cells
287 my_box.setSmall(amrex::IntVect(0,0,0));
288 my_box.setBig(amrex::IntVect(nsx-1,nsy-1,nsz-1));
289 }
290 else // everything cell-centered -- these have one ghost cell
291 {
292 my_box.setSmall(amrex::IntVect(-1,-1,0));
293 my_box.setBig(amrex::IntVect(nsx-2,nsy-2,nsz-1));
294 }
295 // Adjust box size if a non-standard number of grow cells
296 my_box.grow(custom_ngrow - amrex::IntVect(1,1,0));
297
298 // amrex::Print() <<" NAME and BOX for this VAR " << var_name << " " << my_box << std::endl;
299
300#ifdef AMREX_USE_GPU
301 // Make sure temp lives on CPU since nc_arrays lives on CPU only
302 temp.resize(my_box,1,amrex::The_Pinned_Arena());
303#else
304 temp.resize(my_box,1);
305#endif
306 amrex::Array4<DType> fab_arr = temp.array();
307
308 int ioff = temp.box().smallEnd()[0];
309 int joff = temp.box().smallEnd()[1];
310
311 auto num_pts_in_box = my_box.numPts();
312 auto num_pts_in_src = nsx*nsy*nsz;
313 // amrex::Print() <<" NUM PTS IN BOX " << num_pts_in_box << std::endl;
314 // amrex::Print() <<" NUM PTS IN SRC " << num_pts_in_src << std::endl;
316
317 for (int n(0); n < num_pts_in_src; ++n) {
318 int nplane = nsx*nsy;
319 int k = n / nplane;
320 int j = (n - k*nplane) / nsx + joff;
321 int i = n - k*nplane - (j-joff) * nsx + ioff;
322 fab_arr(i,j,k,0) = static_cast<DType>(*(nc_arrays[iv].get_data()+n));
323 }
324}
325
326/**
327 * \brief Function to read NetCDF variables and fill the corresponding Array4's
328 *
329 * @param fname Name of the NetCDF file to be read
330 * @param nc_var_names Variable names in the NetCDF file
331 * @param NC_dim_types NetCDF data dimension types
332 * @param fab_vars Fab data we are to fill
333 * @param one_time Whether to read in a single time step from file
334 * @param fill_time What time step to read from file, if only reading one step
335 * @param custom_ngrow Number of grow cells to read in the fab; default is based off of one cell in each of x and y directions
336 */
337template<class FAB,typename DType>
338void
339BuildFABsFromNetCDFFile (const amrex::Box& domain,
340 const std::string &fname,
341 amrex::Vector<std::string> nc_var_names,
342 amrex::Vector<enum NC_Data_Dims_Type> NC_dim_types,
343 amrex::Vector<FAB*> fab_vars,
344 bool one_time=false, int fill_time=0,
345 amrex::IntVect custom_ngrow=amrex::IntVect(1,1,0))
346{
347 int ioproc = amrex::ParallelDescriptor::IOProcessorNumber(); // I/O rank
348
349 amrex::Vector<NDArray<amrex::Real>> nc_arrays(nc_var_names.size());
350
351
352 ReadNetCDFFile(fname, nc_var_names, nc_arrays, one_time, fill_time); // this is filled only on proc 0
353
354 for (int iv = 0; iv < nc_var_names.size(); iv++)
355 {
356 FAB tmp;
357 if (amrex::ParallelDescriptor::IOProcessor()) {
359 }
360
361 int ncomp = tmp.nComp();
362 amrex::Box box = tmp.box();
363
364 amrex::ParallelDescriptor::Bcast(&box, 1, ioproc);
365 amrex::ParallelDescriptor::Bcast(&ncomp, 1, ioproc);
366
367 if (!amrex::ParallelDescriptor::IOProcessor()) {
368#ifdef AMREX_USE_GPU
369 tmp.resize(box,ncomp,amrex::The_Pinned_Arena());
370#else
371 tmp.resize(box,ncomp);
372#endif
373 }
374
375 amrex::ParallelDescriptor::Bcast(tmp.dataPtr(), tmp.size(), ioproc);
376
377 // Shift box by the domain lower corner
378 amrex::Box fab_bx = tmp.box();
379 amrex::Dim3 dom_lb = lbound(domain);
380 fab_bx += amrex::IntVect(dom_lb.x,dom_lb.y,dom_lb.z);
381 // fab_vars points to data on device
382 fab_vars[iv]->resize(fab_bx,1);
383#ifdef AMREX_USE_GPU
384 amrex::Gpu::copy(amrex::Gpu::hostToDevice,
385 tmp.dataPtr(), tmp.dataPtr() + tmp.size(),
386 fab_vars[iv]->dataPtr());
387#else
388 // Provided by BaseFab inheritance through FArrayBox
389 fab_vars[iv]->copy(tmp,tmp.box(),0,fab_bx,0,1);
390#endif
391 }
392}
393
394#endif
mf_h setVal(geomdata.ProbHi(2))
AMREX_ALWAYS_ASSERT(!NSPeriodic||!EWPeriodic)
NC_Data_Dims_Type
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.
void BuildFABsFromNetCDFFile(const amrex::Box &domain, const std::string &fname, amrex::Vector< std::string > nc_var_names, amrex::Vector< enum NC_Data_Dims_Type > NC_dim_types, amrex::Vector< FAB * > fab_vars, bool one_time=false, int fill_time=0, amrex::IntVect custom_ngrow=amrex::IntVect(1, 1, 0))
Function to read NetCDF variables and fill the corresponding Array4's.
bool QueryNetCDFHasVars(const std::string &fname, const amrex::Vector< std::string > &var_names)
Helper function for testing whether a file carries every named variable.
bool QueryNetCDFVarAttrStr(const std::string &fname, const std::string &var_name, const std::string &attr_name)
Helper function for testing for the presence of a single variable attribute.
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.
void fill_fab_from_arrays(int iv, amrex::Vector< NDArray< amrex::Real > > &nc_arrays, const std::string &var_name, NC_Data_Dims_Type &NC_dim_type, FAB &temp, amrex::IntVect custom_ngrow)
Helper function for reading data from NetCDF file into a provided FAB.
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::Vector< amrex::FArrayBox > PlaneVector
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.
NDArray is the datatype designed to hold any data, including scalars, multidimensional arrays,...
std::string name
std::shared_ptr< DType[]> data
size_t ndim()
return the total number of data
NDArray()
default constructor
std::string get_vname()
get the variable name
std::vector< MPI_Offset > shape
NDArray(const std::string vname, const std::vector< MPI_Offset > &vshape)
constructor
typename std::remove_const< DataType >::type DType
decltype(auto) get_data()
get the data pointer
void set_vshape(std::vector< MPI_Offset > vshape)
set the data shape information
std::vector< MPI_Offset > get_vshape()
get the variable data shape