ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_NCWpsFile.H
Go to the documentation of this file.
1 /**
2  * \file ERF_NCWpsFile.H
3  */
4 #ifndef ERF_NCWPSFILE_H_
5 #define ERF_NCWPSFILE_H_
6 
7 #include <sstream>
8 #include <string>
9 #include <atomic>
10 #include <cfenv>
11 
12 #include "AMReX_FArrayBox.H"
13 #include "AMReX_IArrayBox.H"
14 #include "ERF_EpochTime.H"
15 #include "ERF_NCInterface.H"
16 
17 using PlaneVector = amrex::Vector<amrex::FArrayBox>;
18 
19 /*
20  // Read from metgrid
21  NetCDF variables of dimensions Time_BT_SN_WE: "UU", "VV", "TT", "RH", "PRES", "GHT"
22  NetCDF variables of dimensions Time_SN_WE : "HGT", "MAPFAC_U", "MAPFAC_V", "MAPFAC_M", "PSFC"
23  NetCDF global attributes of type int : "WEST-EAST_GRID_DIMENSION", "SOUTH-NORTH_GRID_DIMENSION"
24  NetCDF global attributes of type string : "SIMULATION_START_DATE"
25  NetCDF global attributes of type real : "DX", "DY"
26 
27  // Read from wrfbdy
28  NetCDF variables of dimensions Time_BdyWidth_BT_SN : "U_BXS", "U_BXE", "V_BXS", "V_BXE" etc.
29  NetCDF variables of dimensions Time_BdyWidth_BT_WE : "U_BYS", "U_BYE", "V_BYS", "V_BYE" etc.
30  NetCDF variables of dimensions Time_BdyWidth_SN : "MU_BXS", "MU_BXE", "PC_BXS", "PC_BXE", etc.
31  NetCDF variables of dimensions Time_BdyWidth_WE : "MU_BYS", "MU_BYE", "PC_BYS", "PC_BYE", etc.
32 */
33 enum class NC_Data_Dims_Type {
34  Time_SL_SN_WE, // Time, Soil Layers, South-North, West-East
36  Time_SN_WE,
37  Time_BT,
38  Time_SL, // Time, Soil layers
39  Time,
44 };
45 
46 //
47 // NDArray is the datatype designed to hold any data, including scalars, multidimensional
48 // arrays, that read from the NetCDF file.
49 //
50 // The data read from NetCDF file are stored in a continuous memory, and the data layout is described
51 // by using a vector (shape). AMReX Box can be constructed using the data shape information, and MultiFab
52 // data array can be setup using the data that stored in the NDArray.
53 //
54 template <typename DataType>
55 struct NDArray
56 {
57  using DType = typename std::remove_const<DataType>::type;
58 
59  // constructor
60  explicit NDArray (const std::string vname, const std::vector<size_t>& vshape)
61  : name{vname}, shape{vshape},
62  data{std::shared_ptr<DType[]>(new DType[this->ndim()],
63  std::default_delete<DType[]>())} {}
64 
65  // default constructor
66  NDArray () : name{"null"}, data{nullptr} {}
67 
68  // get the data pointer
69  decltype(auto) get_data () {
70  return data.get();
71  }
72 
73  // get the variable name
74  std::string get_vname () {
75  return name;
76  }
77 
78  // get the variable data shape
79  std::vector<size_t> get_vshape () {
80  return shape;
81  }
82 
83  // return the total number of data
84  size_t ndim () {
85  size_t num = 1;
86  int isize = static_cast<int>(shape.size());
87  for (auto i=0; i<isize; ++i) num *= shape[i];
88  return num;
89  }
90 
91  // set the data shape information
92  void set_vshape (std::vector<size_t> vshape) {
93  shape = vshape;
94  }
95 
96  private:
97  std::string name;
98  std::vector<size_t> shape;
99  std::shared_ptr<DType[]> data;
100 };
101 
102 int BuildFABsFromWRFBdyFile (const std::string &fname,
103  amrex::Vector<amrex::Vector<amrex::FArrayBox>>& bdy_data_xlo,
104  amrex::Vector<amrex::Vector<amrex::FArrayBox>>& bdy_data_xhi,
105  amrex::Vector<amrex::Vector<amrex::FArrayBox>>& bdy_data_ylo,
106  amrex::Vector<amrex::Vector<amrex::FArrayBox>>& bdy_data_yhi);
107 
108 template<typename DType>
109 void ReadTimeSliceFromNetCDFFile (const std::string& fname,
110  const int tidx,
111  amrex::Vector<std::string> names,
112  amrex::Vector<NDArray<DType> >& arrays,
113  amrex::Vector<int>& success)
114 {
115  amrex::Print() << "Reading time slice " << tidx << " from " << fname << std::endl;
116  AMREX_ASSERT(arrays.size() == names.size());
117 
118  if (amrex::ParallelDescriptor::IOProcessor())
119  {
120  auto ncf = ncutils::NCFile::open(fname, NC_CLOBBER | NC_NETCDF4);
121 
122  int ntimes = ncf.dim("Time").len();
123  AMREX_ALWAYS_ASSERT((tidx >= 0) && (tidx < ntimes));
124 
125  for (auto n=0; n<arrays.size(); ++n)
126  {
127  std::string vname_to_write = names[n];
128  std::string vname_to_read = names[n];
129  if (vname_to_read.substr(0,2) == "R_") {
130  vname_to_read = names[n+4]; // This allows us to read "T" instead -- we will over-write this later
131  }
132 
133  success[n] = ncf.has_var(vname_to_read);
134 
135  if (success[n] == 1)
136  {
137  std::vector<std::string> dimnames = ncf.var(vname_to_read).dimnames();
138  AMREX_ALWAYS_ASSERT(dimnames[0] == "Time");
139 
140  std::vector<size_t> count = ncf.var(vname_to_read).shape();
141  std::vector<size_t> start(count.size(), 0);
142  start[0] = tidx;
143  count[0] = 1;
144 
145  arrays[n] = NDArray<DType>(vname_to_read, count);
146  DType* dataPtr = arrays[n].get_data();
147 
148  ncf.var(vname_to_read).get(dataPtr, start, count);
149  } // has_var
150  }
151  ncf.close();
152  }
153 }
154 
155 template<typename DType>
156 void ReadNetCDFFile (const std::string& fname, amrex::Vector<std::string> names,
157  amrex::Vector<NDArray<DType> >& arrays, amrex::Vector<int>& success)
158 {
159  AMREX_ASSERT(arrays.size() == names.size());
160 
161  if (amrex::ParallelDescriptor::IOProcessor())
162  {
163  auto prev_fpe_except = amrex::disableFPExcept(amrex::FPExcept::all);
164 
165  auto ncf = ncutils::NCFile::open(fname, NC_CLOBBER | NC_NETCDF4);
166 
167  /*
168  // get the dimension information
169  int Time = static_cast<int>(ncf.dim("Time").len());
170  int DateStrLen = static_cast<int>(ncf.dim("DateStrLen").len());
171  int west_east = static_cast<int>(ncf.dim("west_east").len());
172  int south_north = static_cast<int>(ncf.dim("south_north").len());
173  int bottom_top = static_cast<int>(ncf.dim("bottom_top").len());
174  int bottom_top_stag = static_cast<int>(ncf.dim("bottom_top_stag").len());
175  int west_east_stag = static_cast<int>(ncf.dim("west_east_stag").len());
176  int south_north_stag = static_cast<int>(ncf.dim("south_north_stag").len());
177  int bdy_width = static_cast<int>(ncf.dim("bdy_width").len());
178  */
179 
180  // amrex::Print() << "Reading the dimensions from the netcdf file " << "\n";
181 
182  for (auto n=0; n<arrays.size(); ++n)
183  {
184  std::string vname_to_write = names[n];
185  std::string vname_to_read = names[n];
186 
187  if (vname_to_read.substr(0,2) == "R_") {
188  vname_to_read = names[n+4]; // This allows us to read "T" instead -- we will over-write this later
189  }
190 
191  success[n] = ncf.has_var(vname_to_read);
192  if (success[n] == 0) {
193  amrex::Print() << " Skipping " << vname_to_read << std::endl;
194  } else {
195  amrex::Print() << " Reading " << vname_to_read << std::endl;
196  }
197 
198  if (success[n] == 1) {
199 
200  std::vector<std::string> dimnames = ncf.var(vname_to_read).dimnames();
201  AMREX_ALWAYS_ASSERT(dimnames[0] == "Time" || dimnames[0] == "time");
202 
203  std::vector<size_t> shape = ncf.var(vname_to_read).shape();
204  arrays[n] = NDArray<DType>(vname_to_read,shape);
205  DType* dataPtr = arrays[n].get_data();
206 
207  std::vector<size_t> start(shape.size(), 0);
208 
209 #if 0
210  auto numPts = arrays[n].ndim();
211  amrex::Print() << "NetCDF Variable name = " << vname_to_read << std::endl;
212  amrex::Print() << "numPts read from NetCDF file/var = " << numPts << std::endl;
213  amrex::Print() << "Dims in var = " << ncf.var(vname_to_read).ndim() << std::endl;
214  amrex::Print() << "Dim names = (";
215  for (auto &dim:dimnames)
216  amrex::Print() << dim << ", " ;
217  amrex::Print() << ")" << std::endl;
218  amrex::Print() << "Dims of the variable = (";
219  for (auto &dim:shape)
220  amrex::Print() << dim << ", " ;
221  amrex::Print() << ")" << std::endl;
222 #endif
223 
224  ncf.var(vname_to_read).get(dataPtr, start, shape);
225 
226  } // has_var
227  }
228  ncf.close();
229 
230  std::feclearexcept(FE_ALL_EXCEPT);
231  amrex::setFPExcept(prev_fpe_except);
232  }
233 }
234 
235 /**
236  * Helper function for reading data from NetCDF file into a
237  * provided FAB.
238  *
239  * @param iv Index for which variable we are going to fill
240  * @param domain Box specifying the horizontal and vertical extent to read
241  * @param nc_arrays Arrays of data from NetCDF file
242  * @param var_name Variable name
243  * @param NC_dim_type Dimension type for the variable as stored in the NetCDF file
244  * @param temp FAB where we store the variable data from the NetCDF Arrays
245  */
246 template<class FAB,typename DType>
247 void
249  const amrex::Box& domain,
250  amrex::Vector<NDArray<float>>& nc_arrays,
251  const std::string& var_name,
252  NC_Data_Dims_Type& NC_dim_type,
253  FAB& temp)
254 {
255  // bottom_top, south_north, west_east (these can be staggered or unstaggered).
256  // Initialised because amrex::Abort is not [[noreturn]], so the compiler
257  // cannot tell that the final else never falls through.
258  int ns1 = 0, ns2 = 0, ns3 = 0;
259  if (NC_dim_type == NC_Data_Dims_Type::Time_BT) {
260  ns1 = nc_arrays[iv].get_vshape()[1];
261  ns2 = 1;
262  ns3 = 1;
263  // amrex::Print() << "TYPE BT " << ns1 << std::endl;
264  } else if (NC_dim_type == NC_Data_Dims_Type::Time_SN_WE) {
265  ns1 = 1;
266  ns2 = nc_arrays[iv].get_vshape()[1];
267  ns3 = nc_arrays[iv].get_vshape()[2];
268  // amrex::Print() << "TYPE SN WE " << ns2 << " " << ns3 << std::endl;
269  } else if (NC_dim_type == NC_Data_Dims_Type::Time_BT_SN_WE) {
270  ns1 = nc_arrays[iv].get_vshape()[1];
271  ns2 = nc_arrays[iv].get_vshape()[2];
272  ns3 = nc_arrays[iv].get_vshape()[3];
273  // amrex::Print() << "TYPE BT SN WE " << ns1 << " " << ns2 << " " << ns3 << std::endl;
274  } else if (NC_dim_type == NC_Data_Dims_Type::Time_SL_SN_WE) {
275  ns1 = nc_arrays[iv].get_vshape()[1];
276  ns2 = nc_arrays[iv].get_vshape()[2];
277  ns3 = nc_arrays[iv].get_vshape()[3];
278  // amrex::Print() << "TYPE SL SN WE " << ns1 << " " << ns2 << " " << ns3 << std::endl;
279  } else if (NC_dim_type == NC_Data_Dims_Type::Time_SL) {
280  ns1 = nc_arrays[iv].get_vshape()[1];
281  ns2 = 1;
282  ns3 = 1;
283  // amrex::Print() << "TYPE SL " << ns1 << std::endl;
284  }
285  else {
286  amrex::Abort("fill_fab_from_arrays: variable " + var_name +
287  " has NC_Data_Dims_Type " + std::to_string(static_cast<int>(NC_dim_type)) +
288  ", which this reader does not handle (only Time_BT, Time_SN_WE,"
289  " Time_BT_SN_WE, Time_SL_SN_WE and Time_SL)");
290  }
291 
292  // allow fewer z levels to be read in
293  // - ns1 may be the bottom_top or bottom_top_stag dim
294  // - domain.bigEnd(2) is the input number of cells in z
295  // - khi may be nz==bottom_top, nz < bottom_top, or 0 for 2D fields
296  int khi = std::min(domain.bigEnd(2), ns1-1);
297 
298  // TODO: The box will only start at (0,0,0) at level 0 -- we need to generalize this
299  amrex::Box in_box(amrex::IntVect(0,0,0), amrex::IntVect(ns3-1,ns2-1,ns1-1)); // ns1 may or may not be staggered
300  amrex::Box my_box(amrex::IntVect(0,0,0), amrex::IntVect(ns3-1,ns2-1,khi));
301 
302  if (var_name == "PH" || var_name == "PHB") {
303  my_box.setType(amrex::IndexType(amrex::IntVect(0,0,1)));
304  my_box.setBig(2, khi+1);
305  }
306  else if (var_name == "U" || var_name == "UU" || var_name == "MAPFAC_U") {
307  my_box.setType(amrex::IndexType(amrex::IntVect(1,0,0)));
308  }
309  else if (var_name == "V" || var_name == "VV" || var_name == "MAPFAC_V") {
310  my_box.setType(amrex::IndexType(amrex::IntVect(0,1,0)));
311  }
312  else if (var_name == "W" || var_name == "WW") {
313  my_box.setType(amrex::IndexType(amrex::IntVect(0,0,1)));
314  my_box.setBig(2, khi+1);
315  }
316  else if (var_name == "TSLB" || var_name == "SMOIS" || var_name == "SH2O" || var_name == "ZS" || var_name == "DZS") {
317  // soil variables are staggered in z
318  my_box.setType(amrex::IndexType(amrex::IntVect(0,0,1)));
319  }
320 
321  amrex::Arena* Arena_Used = amrex::The_Arena();
322 #ifdef AMREX_USE_GPU
323  // Make sure temp lives on CPU since nc_arrays lives on CPU only
324  Arena_Used = amrex::The_Pinned_Arena();
325 #endif
326  temp.resize(my_box,1, Arena_Used);
327  amrex::Array4<DType> fab_arr = temp.array();
328 
329  int ioff = temp.box().smallEnd()[0];
330  int joff = temp.box().smallEnd()[1];
331  int kmax = temp.box().bigEnd()[2]; // highest z level to be read in
332  //amrex::Print() << var_name << " domain khi, kmax, ns1 = " << khi << " " << kmax << " " << ns1 << std::endl;
333 
334  auto num_pts = in_box.numPts();
335 
336  for (int n(0); n < num_pts; ++n) {
337  int k = n / (ns2*ns3);
338  if (k > kmax) continue;
339  int j = (n - k*(ns2*ns3)) / ns3 + joff;
340  int i = n - k*(ns2*ns3) - (j-joff) * ns3 + ioff;
341  fab_arr(i,j,k,0) = static_cast<DType>(*(nc_arrays[iv].get_data()+n));
342  }
343 }
344 
345 /**
346  * Function to read NetCDF variables and fill the corresponding Array4's
347  *
348  * @param domain Box specifying the domain covered by the NetCDF fields
349  * @param fname Name of the NetCDF file to be read
350  * @param nc_var_names Variable names in the NetCDF file
351  * @param NC_dim_types NetCDF data dimension types
352  * @param fab_vars Fab data we are to fill
353  * @param success Success flags for each requested variable
354  */
355 template<class FAB,typename DType>
356 void
357 BuildFABsFromNetCDFFile (const amrex::Box& domain,
358  const std::string &fname,
359  amrex::Vector<std::string> nc_var_names,
360  amrex::Vector<enum NC_Data_Dims_Type> NC_dim_types,
361  amrex::Vector<FAB*> fab_vars,
362  amrex::Vector<int>& success)
363 {
364  int ioproc = amrex::ParallelDescriptor::IOProcessorNumber(); // I/O rank
365 
366  amrex::Vector<NDArray<float>> nc_arrays(nc_var_names.size());
367 
368  if (amrex::ParallelDescriptor::IOProcessor())
369  {
370  ReadNetCDFFile(fname, nc_var_names, nc_arrays, success);
371  }
372 
373  amrex::ParallelDescriptor::Bcast(success.dataPtr(), success.size(), ioproc);
374 
375  for (int iv = 0; iv < nc_var_names.size(); iv++)
376  {
377  if (success[iv] == 1) {
378  FAB tmp;
379  if (amrex::ParallelDescriptor::IOProcessor()) {
380  fill_fab_from_arrays<FAB,DType>(iv, domain, nc_arrays, nc_var_names[iv], NC_dim_types[iv], tmp);
381  }
382 
383  int ncomp = tmp.nComp();
384  amrex::Box box = tmp.box();
385 
386  amrex::ParallelDescriptor::Bcast(&box, 1, ioproc);
387  amrex::ParallelDescriptor::Bcast(&ncomp, 1, ioproc);
388 
389  if (!amrex::ParallelDescriptor::IOProcessor()) {
390 #ifdef AMREX_USE_GPU
391  tmp.resize(box,ncomp,amrex::The_Pinned_Arena());
392 #else
393  tmp.resize(box,ncomp);
394 #endif
395  }
396 
397  amrex::ParallelDescriptor::Bcast(tmp.dataPtr(), tmp.size(), ioproc);
398 
399  // Shift box by the domain lower corner
400  amrex::Box fab_bx = tmp.box();
401  amrex::Dim3 dom_lb = lbound(domain);
402  fab_bx += amrex::IntVect(dom_lb.x,dom_lb.y,dom_lb.z);
403  // fab_vars points to data on device
404  fab_vars[iv]->resize(fab_bx,1);
405 #ifdef AMREX_USE_GPU
406  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
407  tmp.dataPtr(), tmp.dataPtr() + tmp.size(),
408  fab_vars[iv]->dataPtr());
409 #else
410  // Provided by BaseFab inheritance through FArrayBox
411  fab_vars[iv]->copy(tmp,tmp.box(),0,fab_bx,0,1);
412 #endif
413  } // success
414  } // iv
415 }
416 #endif
@ num
Definition: ERF_DataStruct.H:37
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
Arena * Arena_Used
Definition: ERF_Morrison_Advance_F.H:23
void ReadNetCDFFile(const std::string &fname, amrex::Vector< std::string > names, amrex::Vector< NDArray< DType > > &arrays, amrex::Vector< int > &success)
Definition: ERF_NCWpsFile.H:156
NC_Data_Dims_Type
Definition: ERF_NCWpsFile.H:33
void fill_fab_from_arrays(int iv, const amrex::Box &domain, amrex::Vector< NDArray< float >> &nc_arrays, const std::string &var_name, NC_Data_Dims_Type &NC_dim_type, FAB &temp)
Definition: ERF_NCWpsFile.H:248
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, amrex::Vector< int > &success)
Definition: ERF_NCWpsFile.H:357
amrex::Vector< amrex::FArrayBox > PlaneVector
Definition: ERF_NCWpsFile.H:17
int BuildFABsFromWRFBdyFile(const std::string &fname, amrex::Vector< amrex::Vector< amrex::FArrayBox >> &bdy_data_xlo, amrex::Vector< amrex::Vector< amrex::FArrayBox >> &bdy_data_xhi, amrex::Vector< amrex::Vector< amrex::FArrayBox >> &bdy_data_ylo, amrex::Vector< amrex::Vector< amrex::FArrayBox >> &bdy_data_yhi)
void ReadTimeSliceFromNetCDFFile(const std::string &fname, const int tidx, amrex::Vector< std::string > names, amrex::Vector< NDArray< DType > > &arrays, amrex::Vector< int > &success)
Definition: ERF_NCWpsFile.H:109
@ tmp
Definition: ERF_AdvanceWSM6.cpp:116
Definition: ERF_NCWpsFile.H:56
std::string name
Definition: ERF_NCWpsFile.H:97
std::shared_ptr< DType[]> data
Definition: ERF_NCWpsFile.H:99
size_t ndim()
Definition: ERF_NCWpsFile.H:84
NDArray()
Definition: ERF_NCWpsFile.H:66
std::string get_vname()
Definition: ERF_NCWpsFile.H:74
void set_vshape(std::vector< size_t > vshape)
Definition: ERF_NCWpsFile.H:92
NDArray(const std::string vname, const std::vector< size_t > &vshape)
Definition: ERF_NCWpsFile.H:60
typename std::remove_const< DataType >::type DType
Definition: ERF_NCWpsFile.H:57
std::vector< size_t > shape
Definition: ERF_NCWpsFile.H:98
decltype(auto) get_data()
Definition: ERF_NCWpsFile.H:69
std::vector< size_t > get_vshape()
Definition: ERF_NCWpsFile.H:79