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 <atomic>
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)
41 : name{vname}, shape{vshape}, ref_counted{1}, owned{true} {
42 data = new DType [this->ndim()];
43 }
44
45 /** \brief default constructor */
46 NDArray () : name{"null"}, ref_counted{1}, owned{false}, data{nullptr} {}
47
48 /** \brief copy constructor */
49 NDArray (const NDArray& array) {
50 name = array.name;
51 shape = array.shape;
52 data = array.data;
53 owned = false;
54 ref_counted.fetch_add(1, std::memory_order_relaxed);
55 }
56
57 /** \brief copy assignment */
58 NDArray& operator=(const NDArray& array) {
59 name = array.name;
60 shape = array.shape;
61 data = array.data;
62 owned = false;
63 ref_counted.fetch_add(1, std::memory_order_relaxed);
64 return *this;
65 }
66
67 /** \brief destructor */
69 ref_counted.fetch_sub(1, std::memory_order_acq_rel);
70 if (ref_counted == 1 && owned) delete[] data;
71 }
72
73 /** \brief get the data pointer */
74 decltype(auto) get_data () {
75 ref_counted.fetch_add(1, std::memory_order_relaxed);
76 return data;
77 }
78
79 /** \brief get the variable name */
80 std::string get_vname () {
81 return name;
82 }
83
84 /** \brief get the variable data shape */
85 std::vector<MPI_Offset> get_vshape () {
86 return shape;
87 }
88
89 /** \brief return the total number of data */
90 size_t ndim () {
91 size_t num = 1;
92 int isize = static_cast<int>(shape.size());
93 for (auto i=0; i<isize; ++i) num *= shape[i];
94 return num;
95 }
96
97 /** \brief set the data shape information */
98 void set_vshape (std::vector<MPI_Offset> vshape) {
99 shape = vshape;
100 }
101
102 private:
103 std::string name;
104 std::vector<MPI_Offset> shape;
105 mutable std::atomic<size_t> ref_counted;
106 bool owned;
108};
109
110/** \brief Read in data from netcdf file and save to data arrays
111 *
112 * @param[in ] fname file name to read from
113 * @param[in ] names field names to read
114 * @param[inout] arrays vector of data arrays to fill
115 * @param[in ] one_time whether to read in a single time step of data
116 * @param[in ] fill_time if reading a single time, what time step to read from the file?
117 */
118template<typename DType>
119void ReadNetCDFFile (const std::string& fname, amrex::Vector<std::string> names,
120 amrex::Vector<NDArray<DType> >& arrays, bool one_time=false, int fill_time=0)
121{
122 AMREX_ASSERT(arrays.size() == names.size());
123
124 auto ncf = ncutils::NCFile::open(fname, NC_NOCLOBBER );
125 ncmpi_begin_indep_data(ncf.ncid);
126 if (amrex::ParallelDescriptor::IOProcessor())
127 {
128
129 /*
130 // get the dimension information
131 int Time = static_cast<int>(ncf.dim("Time").len());
132 int DateStrLen = static_cast<int>(ncf.dim("DateStrLen").len());
133 int west_east = static_cast<int>(ncf.dim("west_east").len());
134 int south_north = static_cast<int>(ncf.dim("south_north").len());
135 int bottom_top = static_cast<int>(ncf.dim("bottom_top").len());
136 int bottom_top_stag = static_cast<int>(ncf.dim("bottom_top_stag").len());
137 int west_east_stag = static_cast<int>(ncf.dim("west_east_stag").len());
138 int south_north_stag = static_cast<int>(ncf.dim("south_north_stag").len());
139 */
140
141// amrex::Print() << "Reading the dimensions from the netcdf file " << "\n";
142
143 for (auto n=0; n<arrays.size(); ++n) {
144 // read the data from NetCDF file
145 std::string vname_to_write = names[n];
146 std::string vname_to_read = names[n];
147
148 // amrex::AllPrint() << "About to read " << vname_to_read
149 // << " while filling the array for " << vname_to_write << std::endl;
150
151 std::vector<MPI_Offset> shape = ncf.var(vname_to_read).shape();
152 arrays[n] = NDArray<DType>(vname_to_read,shape);
153 DType* dataPtr = arrays[n].get_data();
154
155 std::vector<MPI_Offset> start(shape.size(), 0);
156 // If only reading one time step, adjust start and shape accordingly
157 if (one_time) {
158 start[0] = fill_time;
159 shape[0] = 1;
160 }
161
162 // auto numPts = arrays[n].ndim();
163 // amrex::Print() << "NetCDF Variable name = " << vname_to_read << std::endl;
164 // amrex::Print() << "numPts read from NetCDF file/var = " << numPts << std::endl;
165 // amrex::Print() << "Dims of the variable = (";
166 // for (auto &dim:shape)
167 // amrex::Print() << dim << ", " ;
168 // amrex::Print() << ")" << std::endl;
169
170 ncf.var(vname_to_read).get(dataPtr, start, shape);
171 }
172 }
173 ncf.close();
174}
175
176/**
177 * \brief Helper function for reading a single variable attribute
178 */
179std::string ReadNetCDFVarAttrStr (const std::string& fname,
180 const std::string& var_name,
181 const std::string& attr_name);
182
183/**
184 * \brief Helper function for reading data from NetCDF file into a
185 * provided FAB.
186 *
187 * @param iv Index for which variable we are going to fill
188 * @param nc_arrays Arrays of data from NetCDF file
189 * @param var_name Variable name
190 * @param NC_dim_type Dimension type for the variable as stored in the NetCDF file
191 * @param temp FAB where we store the variable data from the NetCDF Arrays
192 */
193template<class FAB,typename DType>
194void
196 amrex::Vector<NDArray<amrex::Real>>& nc_arrays,
197 const std::string& var_name,
198 NC_Data_Dims_Type& NC_dim_type,
199 FAB& temp)
200{
201 int nsx, nsy, nsz;
202 if (NC_dim_type == NC_Data_Dims_Type::SN_WE) {
203 nsz = 1;
204 nsy = nc_arrays[iv].get_vshape()[0];
205 nsx = nc_arrays[iv].get_vshape()[1];
206 // amrex::Print() << "TYPE SN WE " << nsy << " " << nsx << std::endl;
207 } else if (NC_dim_type == NC_Data_Dims_Type::BT_SN_WE) {
208 nsz = nc_arrays[iv].get_vshape()[0];
209 nsy = nc_arrays[iv].get_vshape()[1];
210 nsx = nc_arrays[iv].get_vshape()[2];
211 // amrex::Print() << "TYPE BT SN WE " << nz << " " << nsy << " " << nsx << std::endl;
212 } else if (NC_dim_type == NC_Data_Dims_Type::Time_SN_WE) {
213 nsz = 1;
214 nsy = nc_arrays[iv].get_vshape()[1];
215 nsx = nc_arrays[iv].get_vshape()[2];
216 // amrex::Print() << "TYPE TIME SN WE " << nsy << " " << nsx << std::endl;
217 } else if (NC_dim_type == NC_Data_Dims_Type::Time_BT_SN_WE) {
218 nsz = nc_arrays[iv].get_vshape()[1];
219 nsy = nc_arrays[iv].get_vshape()[2];
220 nsx = nc_arrays[iv].get_vshape()[3];
221 // amrex::Print() << "TYPE TIME BT SN WE " << nsx << " " << nsy << " " << nsz << std::endl;
222 } else if (NC_dim_type == NC_Data_Dims_Type::Time_BT_Riv) {
223 nsz = nc_arrays[iv].get_vshape()[1]; // number of z-coords
224 nsy = 1; // dummy
225 nsx = nc_arrays[iv].get_vshape()[2]; // number of rivers
226 } else if (NC_dim_type == NC_Data_Dims_Type::Time_Riv) {
227 nsz = 1; // number of z-coords
228 nsy = 1; // dummy
229 nsx = nc_arrays[iv].get_vshape()[1]; // number of rivers
230 } else if (NC_dim_type == NC_Data_Dims_Type::BT_Riv) {
231 nsz = nc_arrays[iv].get_vshape()[0]; // number of z-coords
232 nsy = 1; // dummy
233 nsx = nc_arrays[iv].get_vshape()[1]; // number of rivers
234 } else {
235 amrex::Abort("Dont know this NC_Data_Dims_Type");
236 }
237
238 amrex::Box my_box;
239
240 if (var_name == "u" || var_name == "ubar" || var_name == "mask_u" || var_name == "x_u" ||
241 var_name == "y_u" || var_name == "sustr")
242 {
243 my_box.setSmall(amrex::IntVect(0,-1,0));
244 my_box.setBig(amrex::IntVect(nsx-1,nsy-2,nsz-1));
245 my_box.setType(amrex::IndexType(amrex::IntVect(1,0,0)));
246 }
247 else if (var_name == "v" || var_name == "vbar" || var_name == "mask_v" || var_name == "x_v" ||
248 var_name == "y_v" || var_name == "svstr")
249 {
250 my_box.setSmall(amrex::IntVect(-1,0,0));
251 my_box.setBig(amrex::IntVect(nsx-2,nsy-1,nsz-1));
252 my_box.setType(amrex::IndexType(amrex::IntVect(0,1,0)));
253 }
254 else if (var_name == "mask_psi" || var_name == "x_psi" || var_name == "y_psi")
255 {
256 my_box.setSmall(amrex::IntVect(0,0,0));
257 my_box.setBig(amrex::IntVect(nsx-1,nsy-1,nsz-1));
258 my_box.setType(amrex::IndexType(amrex::IntVect(1,1,0)));
259 // River data has no ghost cells
260 } else if (NC_dim_type == NC_Data_Dims_Type::Time_BT_Riv ||
261 NC_dim_type == NC_Data_Dims_Type::Time_Riv ||
262 NC_dim_type == NC_Data_Dims_Type::BT_Riv) {
263 my_box.setSmall(amrex::IntVect(0,0,0));
264 my_box.setBig(amrex::IntVect(nsx-1,nsy-1,nsz-1));
265 }
266 else // everything cell-centered -- these have one ghost cell
267 {
268 my_box.setSmall(amrex::IntVect(-1,-1,0));
269 my_box.setBig(amrex::IntVect(nsx-2,nsy-2,nsz-1));
270 }
271
272 // amrex::Print() <<" NAME and BOX for this VAR " << var_name << " " << my_box << std::endl;
273
274#ifdef AMREX_USE_GPU
275 // Make sure temp lives on CPU since nc_arrays lives on CPU only
276 temp.resize(my_box,1,amrex::The_Pinned_Arena());
277#else
278 temp.resize(my_box,1);
279#endif
280 amrex::Array4<DType> fab_arr = temp.array();
281
282 int ioff = temp.box().smallEnd()[0];
283 int joff = temp.box().smallEnd()[1];
284
285 auto num_pts_in_box = my_box.numPts();
286 auto num_pts_in_src = nsx*nsy*nsz;
287 // amrex::Print() <<" NUM PTS IN BOX " << num_pts_in_box << std::endl;
288 // amrex::Print() <<" NUM PTS IN SRC " << num_pts_in_src << std::endl;
289 AMREX_ALWAYS_ASSERT(num_pts_in_box >= num_pts_in_src);
290
291 for (int n(0); n < num_pts_in_src; ++n) {
292 int nplane = nsx*nsy;
293 int k = n / nplane;
294 int j = (n - k*nplane) / nsx + joff;
295 int i = n - k*nplane - (j-joff) * nsx + ioff;
296 fab_arr(i,j,k,0) = static_cast<DType>(*(nc_arrays[iv].get_data()+n));
297 }
298}
299
300/**
301 * \brief Function to read NetCDF variables and fill the corresponding Array4's
302 *
303 * @param fname Name of the NetCDF file to be read
304 * @param nc_var_names Variable names in the NetCDF file
305 * @param NC_dim_types NetCDF data dimension types
306 * @param fab_vars Fab data we are to fill
307 * @param one_time Whether to read in a single time step from file
308 * @param fill_time What time step to read from file, if only reading one step
309 */
310template<class FAB,typename DType>
311void
312BuildFABsFromNetCDFFile (const amrex::Box& domain,
313 const std::string &fname,
314 amrex::Vector<std::string> nc_var_names,
315 amrex::Vector<enum NC_Data_Dims_Type> NC_dim_types,
316 amrex::Vector<FAB*> fab_vars,
317 bool one_time=false, int fill_time=0)
318{
319 int ioproc = amrex::ParallelDescriptor::IOProcessorNumber(); // I/O rank
320
321 amrex::Vector<NDArray<amrex::Real>> nc_arrays(nc_var_names.size());
322
323
324 ReadNetCDFFile(fname, nc_var_names, nc_arrays, one_time, fill_time); // this is filled only on proc 0
325
326 for (int iv = 0; iv < nc_var_names.size(); iv++)
327 {
328 FAB tmp;
329 if (amrex::ParallelDescriptor::IOProcessor()) {
330 fill_fab_from_arrays<FAB,DType>(iv, nc_arrays, nc_var_names[iv], NC_dim_types[iv], tmp);
331 }
332
333 int ncomp = tmp.nComp();
334 amrex::Box box = tmp.box();
335
336 amrex::ParallelDescriptor::Bcast(&box, 1, ioproc);
337 amrex::ParallelDescriptor::Bcast(&ncomp, 1, ioproc);
338
339 if (!amrex::ParallelDescriptor::IOProcessor()) {
340#ifdef AMREX_USE_GPU
341 tmp.resize(box,ncomp,amrex::The_Pinned_Arena());
342#else
343 tmp.resize(box,ncomp);
344#endif
345 }
346
347 amrex::ParallelDescriptor::Bcast(tmp.dataPtr(), tmp.size(), ioproc);
348
349 // Shift box by the domain lower corner
350 amrex::Box fab_bx = tmp.box();
351 amrex::Dim3 dom_lb = lbound(domain);
352 fab_bx += amrex::IntVect(dom_lb.x,dom_lb.y,dom_lb.z);
353 // fab_vars points to data on device
354 fab_vars[iv]->resize(fab_bx,1);
355#ifdef AMREX_USE_GPU
356 amrex::Gpu::copy(amrex::Gpu::hostToDevice,
357 tmp.dataPtr(), tmp.dataPtr() + tmp.size(),
358 fab_vars[iv]->dataPtr());
359#else
360 // Provided by BaseFab inheritance through FArrayBox
361 fab_vars[iv]->copy(tmp,tmp.box(),0,fab_bx,0,1);
362#endif
363 }
364}
365
366#endif
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)
Function to read NetCDF variables and fill the corresponding Array4's.
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)
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
size_t ndim()
return the total number of data
NDArray()
default constructor
std::string get_vname()
get the variable name
NDArray(const NDArray &array)
copy constructor
std::vector< MPI_Offset > shape
NDArray(const std::string vname, const std::vector< MPI_Offset > &vshape)
constructor
std::atomic< size_t > ref_counted
typename std::remove_const< DataType >::type DType
DType * data
decltype(auto) get_data()
get the data pointer
NDArray & operator=(const NDArray &array)
copy assignment
void set_vshape(std::vector< MPI_Offset > vshape)
set the data shape information
std::vector< MPI_Offset > get_vshape()
get the variable data shape
~NDArray()
destructor