ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_NCWpsFile.H
Go to the documentation of this file.
1 #ifndef ERF_NCWPSFILE_H_
2 #define ERF_NCWPSFILE_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 #include "ERF_NCInterface.H"
12 
13 using PlaneVector = amrex::Vector<amrex::FArrayBox>;
14 
15 /*
16  // Read from metgrid
17  NetCDF variables of dimensions Time_BT_SN_WE: "UU", "VV", "TT", "RH", "PRES", "GHT"
18  NetCDF variables of dimensions Time_SN_WE : "HGT", "MAPFAC_U", "MAPFAC_V", "MAPFAC_M", "PSFC"
19  NetCDF global attributes of type int : "FLAG_PSFC", "FLAG_MAPFAC_U", "FLAG_MAPFAC_V", "FLAG_MAPFAC_M",
20  "FLAG_HGT_M", "WEST-EAST_GRID_DIMENSION", "SOUTH-NORTH_GRID_DIMENSION"
21  NetCDF global attributes of type string : "SIMULATION_START_DATE"
22  NetCDF global attributes of type real : "DX", "DY"
23 
24  // Read from wrfbdy
25  NetCDF variables of dimensions Time_BdyWidth_BT_SN : "U_BXS", "U_BXE", "V_BXS", "V_BXE" etc.
26  NetCDF variables of dimensions Time_BdyWidth_BT_WE : "U_BYS", "U_BYE", "V_BYS", "V_BYE" etc.
27  NetCDF variables of dimensions Time_BdyWidth_SN : "MU_BXS", "MU_BXE", "PC_BXS", "PC_BXE", etc.
28  NetCDF variables of dimensions Time_BdyWidth_WE : "MU_BYS", "MU_BYE", "PC_BYS", "PC_BYE", etc.
29 */
30 enum class NC_Data_Dims_Type {
32  Time_SN_WE,
33  Time_BT,
34  Time,
39 };
40 
41 //
42 // NDArray is the datatype designed to hold any data, including scalars, multidimensional
43 // arrays, that read from the NetCDF file.
44 //
45 // The data read from NetCDF file are stored in a continuous memory, and the data layout is described
46 // by using a vector (shape). AMRex Box can be constructed using the data shape information, and MultiFab
47 // data array can be setup using the data that stored in the NDArray.
48 //
49 template <typename DataType>
50 struct NDArray
51 {
52  using DType = typename std::remove_const<DataType>::type;
53 
54  // constructor
55  explicit NDArray (const std::string vname, const std::vector<size_t>& vshape)
56  : name{vname}, shape{vshape}, ref_counted{1}, owned{true} {
57  data = new DType [this->ndim()];
58  }
59 
60  // default constructor
61  NDArray () : name{"null"}, ref_counted{1}, owned{false}, data{nullptr} {}
62 
63  // copy constructor
64  NDArray (const NDArray& array) {
65  name = array.name;
66  shape = array.shape;
67  data = array.data;
68  owned = false;
69  ref_counted.fetch_add(1, std::memory_order_relaxed);
70  }
71 
72  // copy assignment
73  NDArray& operator=(const NDArray& array) {
74  name = array.name;
75  shape = array.shape;
76  data = array.data;
77  owned = false;
78  ref_counted.fetch_add(1, std::memory_order_relaxed);
79  return *this;
80  }
81 
82  // destructor
83  ~NDArray () {
84  ref_counted.fetch_sub(1, std::memory_order_acq_rel);
85  if (ref_counted == 1 && owned) delete[] data;
86  }
87 
88  // get the data pointer
89  decltype(auto) get_data () {
90  ref_counted.fetch_add(1, std::memory_order_relaxed);
91  return data;
92  }
93 
94  // get the variable name
95  std::string get_vname () {
96  return name;
97  }
98 
99  // get the variable data shape
100  std::vector<size_t> get_vshape () {
101  return shape;
102  }
103 
104  // return the total number of data
105  size_t ndim () {
106  size_t num = 1;
107  int isize = static_cast<int>(shape.size());
108  for (auto i=0; i<isize; ++i) num *= shape[i];
109  return num;
110  }
111 
112  // set the data shape information
113  void set_vshape (std::vector<size_t> vshape) {
114  shape = vshape;
115  }
116 
117  private:
118  std::string name;
119  std::vector<size_t> shape;
120  mutable std::atomic<size_t> ref_counted;
121  bool owned;
123 };
124 
125 int BuildFABsFromWRFBdyFile (const std::string &fname,
126  amrex::Vector<amrex::Vector<amrex::FArrayBox>>& bdy_data_xlo,
127  amrex::Vector<amrex::Vector<amrex::FArrayBox>>& bdy_data_xhi,
128  amrex::Vector<amrex::Vector<amrex::FArrayBox>>& bdy_data_ylo,
129  amrex::Vector<amrex::Vector<amrex::FArrayBox>>& bdy_data_yhi);
130 
131 AMREX_FORCE_INLINE
132 std::time_t
133 getEpochTime (const std::string& dateTime, const std::string& dateTimeFormat)
134 {
135  // Create a stream which we will use to parse the string,
136  // which we provide to constructor of stream to fill the buffer.
137  std::istringstream ss{ dateTime };
138 
139  // Create a tm object to store the parsed date and time.
140  std::tm tmTime;
141  memset(&tmTime, 0, sizeof(tmTime));
142 
143  // Now we read from buffer using get_time manipulator
144  // and formatting the input appropriately.
145  strptime(dateTime.c_str(), dateTimeFormat.c_str(), &tmTime);
146 
147  // Convert the tm structure to time_t value and return.
148  // Here we use timegm since the output should be relative to UTC.
149  auto epoch = timegm(&tmTime);
150  // Print() << "Time Stamp: "<< std::put_time(&tmTime, "%c")
151  // << " , Epoch: " << epoch << std::endl;
152 
153  return epoch;
154 }
155 
156 template<typename DType>
157 void ReadNetCDFFile (const std::string& fname, amrex::Vector<std::string> names,
158  amrex::Vector<NDArray<DType> >& arrays)
159 {
160  AMREX_ASSERT(arrays.size() == names.size());
161 
162  if (amrex::ParallelDescriptor::IOProcessor())
163  {
164  auto ncf = ncutils::NCFile::open(fname, NC_CLOBBER | NC_NETCDF4);
165 
166  /*
167  // get the dimension information
168  int Time = static_cast<int>(ncf.dim("Time").len());
169  int DateStrLen = static_cast<int>(ncf.dim("DateStrLen").len());
170  int west_east = static_cast<int>(ncf.dim("west_east").len());
171  int south_north = static_cast<int>(ncf.dim("south_north").len());
172  int bottom_top = static_cast<int>(ncf.dim("bottom_top").len());
173  int bottom_top_stag = static_cast<int>(ncf.dim("bottom_top_stag").len());
174  int west_east_stag = static_cast<int>(ncf.dim("west_east_stag").len());
175  int south_north_stag = static_cast<int>(ncf.dim("south_north_stag").len());
176  int bdy_width = static_cast<int>(ncf.dim("bdy_width").len());
177  */
178 
179  // amrex::Print() << "Reading the dimensions from the netcdf file " << "\n";
180  for (auto n=0; n<arrays.size(); ++n) {
181  // read the data from NetCDF file
182  std::string vname_to_write = names[n];
183  std::string vname_to_read = names[n];
184  if (vname_to_read.substr(0,2) == "R_") {
185  vname_to_read = names[n+4]; // This allows us to read "T" instead -- we will over-write this later
186  }
187 
188  /*
189  amrex::AllPrint() << "About to read " << vname_to_read
190  << " while filling the array for " << vname_to_write << std::endl;
191  */
192 
193  std::vector<size_t> shape = ncf.var(vname_to_read).shape();
194  arrays[n] = NDArray<DType>(vname_to_read,shape);
195  DType* dataPtr = arrays[n].get_data();
196 
197  std::vector<size_t> start(shape.size(), 0);
198 
199  // auto numPts = arrays[n].ndim();
200  // amrex::Print() << "NetCDF Variable name = " << vname_to_read << std::endl;
201  // amrex::Print() << "numPts read from NetCDF file/var = " << numPts << std::endl;
202  // amrex::Print() << "Dims of the variable = (";
203  // for (auto &dim:shape)
204  // amrex::Print() << dim << ", " ;
205  // amrex::Print() << ")" << std::endl;
206 
207  ncf.var(vname_to_read).get(dataPtr, start, shape);
208  }
209  ncf.close();
210  }
211 }
212 
213 /**
214  * Helper function for reading data from NetCDF file into a
215  * provided FAB.
216  *
217  * @param iv Index for which variable we are going to fill
218  * @param nc_arrays Arrays of data from NetCDF file
219  * @param var_name Variable name
220  * @param NC_dim_type Dimension type for the variable as stored in the NetCDF file
221  * @param temp FAB where we store the variable data from the NetCDF Arrays
222  */
223 template<class FAB,typename DType>
224 void
226  amrex::Real& Latitude,
227  amrex::Real& Longitude,
228  std::string& Lat_var_name,
229  std::string& Lon_var_name,
230  amrex::Vector<NDArray<float>>& nc_arrays,
231  const std::string& var_name,
232  NC_Data_Dims_Type& NC_dim_type,
233  FAB& temp)
234 {
235  int ns1, ns2, ns3;
236  if (NC_dim_type == NC_Data_Dims_Type::Time_BT) {
237  ns1 = nc_arrays[iv].get_vshape()[1];
238  ns2 = 1;
239  ns3 = 1;
240  // amrex::Print() << "TYPE BT " << ns1 << std::endl;
241  } else if (NC_dim_type == NC_Data_Dims_Type::Time_SN_WE) {
242  ns1 = 1;
243  ns2 = nc_arrays[iv].get_vshape()[1];
244  ns3 = nc_arrays[iv].get_vshape()[2];
245  // amrex::Print() << "TYPE SN WE " << ns2 << " " << ns3 << std::endl;
246  } else if (NC_dim_type == NC_Data_Dims_Type::Time_BT_SN_WE) {
247  ns1 = nc_arrays[iv].get_vshape()[1];
248  ns2 = nc_arrays[iv].get_vshape()[2];
249  ns3 = nc_arrays[iv].get_vshape()[3];
250  // amrex::Print() << "TYPE BT SN WE " << ns1 << " " << ns2 << " " << ns3 << std::endl;
251  } else {
252  amrex::Abort("Dont know this NC_Data_Dims_Type");
253  }
254 
255  // TODO: The box will only start at (0,0,0) at level 0 -- we need to generalize this
256  amrex::Box my_box(amrex::IntVect(0,0,0), amrex::IntVect(ns3-1,ns2-1,ns1-1));
257  // amrex::Print() <<" MY BOX " << my_box << std::endl;
258 
259  if (var_name == "U" || var_name == "UU" ||
260  var_name == "MAPFAC_U" || var_name == "MAPFAC_UY") my_box.setType(amrex::IndexType(amrex::IntVect(1,0,0)));
261  if (var_name == "V" || var_name == "VV" ||
262  var_name == "MAPFAC_V" || var_name == "MAPFAC_VY") my_box.setType(amrex::IndexType(amrex::IntVect(0,1,0)));
263  if (var_name == "W" || var_name == "WW") my_box.setType(amrex::IndexType(amrex::IntVect(0,0,1)));
264 
265  amrex::Arena* Arena_Used = amrex::The_Arena();
266 #ifdef AMREX_USE_GPU
267  // Make sure temp lives on CPU since nc_arrays lives on CPU only
268  Arena_Used = amrex::The_Pinned_Arena();
269 #endif
270  temp.resize(my_box,1, Arena_Used);
271  amrex::Array4<DType> fab_arr = temp.array();
272 
273  int ioff = temp.box().smallEnd()[0];
274  int joff = temp.box().smallEnd()[1];
275 
276  auto num_pts = my_box.numPts();
277 
278  // amrex::Print() <<" ns1 * ns2 * ns3 " << ns1 * ns2 * ns3 << std::endl;
279  // amrex::Print() <<" NUMPTS " << num_pts << std::endl;
280 
281  for (int n(0); n < num_pts; ++n) {
282  int k = n / (ns2*ns3);
283  int j = (n - k*(ns2*ns3)) / ns3 + joff;
284  int i = n - k*(ns2*ns3) - (j-joff) * ns3 + ioff;
285  fab_arr(i,j,k,0) = static_cast<DType>(*(nc_arrays[iv].get_data()+n));
286  }
287 
288  if (var_name == Lat_var_name) Latitude = fab_arr(0,0,0);
289  if (var_name == Lon_var_name) Longitude = fab_arr(0,0,0);
290 }
291 
292 /**
293  * Function to read NetCDF variables and fill the corresponding Array4's
294  *
295  * @param fname Name of the NetCDF file to be read
296  * @param nc_var_names Variable names in the NetCDF file
297  * @param NC_dim_types NetCDF data dimension types
298  * @param fab_vars Fab data we are to fill
299  */
300 template<class FAB,typename DType>
301 void
302 BuildFABsFromNetCDFFile (const amrex::Box& domain,
303  amrex::Real& Latitude,
304  amrex::Real& Longitude,
305  std::string& Lat_var_name,
306  std::string& Lon_var_name,
307  const std::string &fname,
308  amrex::Vector<std::string> nc_var_names,
309  amrex::Vector<enum NC_Data_Dims_Type> NC_dim_types,
310  amrex::Vector<FAB*> fab_vars)
311 {
312  int ioproc = amrex::ParallelDescriptor::IOProcessorNumber(); // I/O rank
313 
314  amrex::Vector<NDArray<float>> nc_arrays(nc_var_names.size());
315 
316  if (amrex::ParallelDescriptor::IOProcessor())
317  {
318  ReadNetCDFFile(fname, nc_var_names, nc_arrays);
319  }
320 
321  for (int iv = 0; iv < nc_var_names.size(); iv++)
322  {
323  FAB tmp;
324  if (amrex::ParallelDescriptor::IOProcessor()) {
325  fill_fab_from_arrays<FAB,DType>(iv, Latitude, Longitude,
326  Lat_var_name, Lon_var_name,
327  nc_arrays, nc_var_names[iv],
328  NC_dim_types[iv], tmp);
329  }
330 
331  int ncomp = tmp.nComp();
332  amrex::Box box = tmp.box();
333 
334  amrex::ParallelDescriptor::Bcast(&box, 1, ioproc);
335  amrex::ParallelDescriptor::Bcast(&ncomp, 1, ioproc);
336 
337  if (!amrex::ParallelDescriptor::IOProcessor()) {
338 #ifdef AMREX_USE_GPU
339  tmp.resize(box,ncomp,amrex::The_Pinned_Arena());
340 #else
341  tmp.resize(box,ncomp);
342 #endif
343  }
344 
345  amrex::ParallelDescriptor::Bcast(tmp.dataPtr(), tmp.size(), ioproc);
346 
347  // Shift box by the domain lower corner
348  amrex::Box fab_bx = tmp.box();
349  amrex::Dim3 dom_lb = lbound(domain);
350  fab_bx += amrex::IntVect(dom_lb.x,dom_lb.y,dom_lb.z);
351  // fab_vars points to data on device
352  fab_vars[iv]->resize(fab_bx,1);
353 #ifdef AMREX_USE_GPU
354  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
355  tmp.dataPtr(), tmp.dataPtr() + tmp.size(),
356  fab_vars[iv]->dataPtr());
357 #else
358  // Provided by BaseFab inheritance through FArrayBox
359  fab_vars[iv]->copy(tmp,tmp.box(),0,fab_bx,0,1);
360 #endif
361  }
362 }
363 
364 #endif
NC_Data_Dims_Type
Definition: ERF_NCWpsFile.H:30
AMREX_FORCE_INLINE std::time_t getEpochTime(const std::string &dateTime, const std::string &dateTimeFormat)
Definition: ERF_NCWpsFile.H:133
amrex::Vector< amrex::FArrayBox > PlaneVector
Definition: ERF_NCWpsFile.H:13
void fill_fab_from_arrays(int iv, amrex::Real &Latitude, amrex::Real &Longitude, std::string &Lat_var_name, std::string &Lon_var_name, amrex::Vector< NDArray< float >> &nc_arrays, const std::string &var_name, NC_Data_Dims_Type &NC_dim_type, FAB &temp)
Definition: ERF_NCWpsFile.H:225
void ReadNetCDFFile(const std::string &fname, amrex::Vector< std::string > names, amrex::Vector< NDArray< DType > > &arrays)
Definition: ERF_NCWpsFile.H:157
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 BuildFABsFromNetCDFFile(const amrex::Box &domain, amrex::Real &Latitude, amrex::Real &Longitude, std::string &Lat_var_name, std::string &Lon_var_name, 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)
Definition: ERF_NCWpsFile.H:302
static NCFile open(const std::string &name, const int cmode=NC_NOWRITE)
Definition: ERF_NCInterface.cpp:674
Definition: ERF_NCWpsFile.H:51
bool owned
Definition: ERF_NCWpsFile.H:121
std::string name
Definition: ERF_NCWpsFile.H:118
size_t ndim()
Definition: ERF_NCWpsFile.H:105
NDArray()
Definition: ERF_NCWpsFile.H:61
std::string get_vname()
Definition: ERF_NCWpsFile.H:95
NDArray(const NDArray &array)
Definition: ERF_NCWpsFile.H:64
NDArray & operator=(const NDArray &array)
Definition: ERF_NCWpsFile.H:73
void set_vshape(std::vector< size_t > vshape)
Definition: ERF_NCWpsFile.H:113
NDArray(const std::string vname, const std::vector< size_t > &vshape)
Definition: ERF_NCWpsFile.H:55
std::atomic< size_t > ref_counted
Definition: ERF_NCWpsFile.H:120
typename std::remove_const< DataType >::type DType
Definition: ERF_NCWpsFile.H:52
std::vector< size_t > shape
Definition: ERF_NCWpsFile.H:119
DType * data
Definition: ERF_NCWpsFile.H:122
decltype(auto) get_data()
Definition: ERF_NCWpsFile.H:89
std::vector< size_t > get_vshape()
Definition: ERF_NCWpsFile.H:100
~NDArray()
Definition: ERF_NCWpsFile.H:83