ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_LargeScaleForcingData.H
Go to the documentation of this file.
1 #ifndef ERF_LARGE_SCALE_FORCING_DATA_H_
2 #define ERF_LARGE_SCALE_FORCING_DATA_H_
3 
4 #include <string>
5 #include <iostream>
6 #include <sstream>
7 #include <fstream>
8 
9 #include <AMReX_ParmParse.H>
10 #include <AMReX_Print.H>
11 #include <AMReX_Gpu.H>
12 #include <AMReX_Geometry.H>
13 
14 #include <ERF_EOS.H>
15 #include <ERF_NumericalConstants.H>
16 #include <ERF_Interpolation_1D.H>
17 #include <ERF_HSEUtils.H>
18 #include <ERF_InputSoundingData.H>
19 
20 /**
21  * Data structure storing large-scale forcing data.
22  *
23  * This provides time-varying vertical profiles for large scale temperature,
24  * and water vapor tendencies, as well as observed large scale zonal and meridonal wind,
25  * and vertical subsidence.
26  * Profiles are read from a text file, interpolated to the model vertical grid, and then copied
27  * to device memory for use during time integration.
28  *
29  * This mirrors the lsf functionality as implemented in SAM.
30  */
32 public:
33  /**
34  * @brief Construct large-scale forcing metadata from the ERF input namespace.
35  */
37  {
38  amrex::ParmParse pp("erf");
39  pp.queryAdd("forcing_timescale", tau_lsf);
40  pp.queryAdd("large_scale_forcing_file", lsf_file);
41  }
42 
43  /**
44  * @brief Count the number of whitespace-separated columns in a line.
45  * @param str Input line to inspect.
46  * @return Number of columns in the line.
47  */
48  int get_columns_in_str (std::string &str)
49  {
50  std::istringstream iss(str);
51  std::string tmp;
52  // Get the number of columns in the file
53  int ncols = 0;
54  while (iss >> tmp) {
55  ncols+= 1;
56  }
57  return ncols;
58  }
59 
60 
61  /**
62  * @brief Read the forcing file and cache the raw time slices on host.
63  */
65  {
66  amrex::Print() << " Opening LSF file '" << lsf_file << "'" << std::endl;
67  std::ifstream ifs(lsf_file);
68  if (!ifs.is_open())
69  {
70  amrex::Error("Error opening input forcing file " + lsf_file);
71  }
72 
73  std::string line;
74 
75  // temporary vectors for storing the values at each time
76  amrex::Vector<amrex::Real> z_in, p_in, t_in, q_in, u_in, v_in, w_in;
77 
78  // skip first header line
79  std::getline(ifs, line);
80  // header should have 7 columns: z, p, tls, qls, uls, vls, wls
82 
83  num_times = 0;
84  nz_lsf = 0;
85 
86  amrex::Real file_start_time = zero; // first time in file, in days, other inputs are relative to this time
87 
88  amrex::Real pres0;
89  while(std::getline(ifs, line)) {
90  std::istringstream iss(line);
91 
92  // The start of each day has 6 columns: day, levels, pres0, "day, levels, pres0"
93  int ncol = get_columns_in_str(line);
94 
95  if (ncol == 6)
96  {
97  // this is a new set of inputs at a given time
98 
99  // have to read these as strings first to split on commas
100  std::string s_time, s_lev, s_pres0;
101  iss >> s_time >> s_lev >> s_pres0;
102  amrex::Real time = std::stod(s_time);
103  int nlev = std::stoi(s_lev);
104  pres0 = std::stod(s_pres0);
105 
106  // convert time to seconds (relative to first time), and pres from mb to Pa
107  if (num_times == 0)
108  {
109  file_start_time = time;
110  }
111  time = (time - file_start_time) * amrex::Real(86400.0);
112 
113  if (times_lsf.size() > 0 && time != times_lsf.back())
114  {
115  // if we started a new time, store data for previous time and reset tmp arrays
116  AMREX_ALWAYS_ASSERT(z_in.size() == nz_lsf + 1);
117  z_lsf.push_back(z_in);
118  p_lsf.push_back(p_in);
119  t_lsf.push_back(t_in);
120  q_lsf.push_back(q_in);
121  u_lsf.push_back(u_in);
122  v_lsf.push_back(v_in);
123  w_lsf.push_back(w_in);
124 
125  z_in.clear();
126  p_in.clear();
127  t_in.clear();
128  q_in.clear();
129  u_in.clear();
130  v_in.clear();
131  w_in.clear();
132  }
133 
134  times_lsf.push_back(time);
135  num_times++;
136 
137  pres0 *= amrex::Real(100.0);
138  if (nz_lsf == 0)
139  {
140  nz_lsf = nlev;
141  } else {
142  // make sure this has the same number of inputs.
143  // TODO: this probably doesn't have to be true since we are interpolating onto the grid?
144  AMREX_ALWAYS_ASSERT(nlev == nz_lsf);
145  }
146 
147  z_in.push_back(zero); // assuming zbot = 0.0 for now, this will get interpolated later once we have grid info
148  p_in.push_back(pres0);
149  t_in.push_back(zero); // assuming temperature change at surf is 0
150  q_in.push_back(zero);
151  u_in.push_back(zero);
152  v_in.push_back(zero);
153  w_in.push_back(zero);
154  }
155  else if (ncol == 7)
156  {
157  // this is defining a level at the current time
158  amrex::Real z, p, tls, qls, uls, vls, wls;
159  iss >> z >> p >> tls >> qls >> uls >> vls >> wls;
160 
161  if (z < zero) {
162  // if z < 0, then LSF is defined on pressure levels
163  use_z_grid = false;
164  }
165 
166  z_in.push_back(z);
167  p_in.push_back(p * amrex::Real(100.0));
168  t_in.push_back(tls);
169  q_in.push_back(qls);
170  u_in.push_back(uls);
171  v_in.push_back(vls);
172  w_in.push_back(wls);
173  }
174  else {
175  amrex::Error("unexpected line in input file: " + line);
176  }
177  }
178 
179  ifs.close();
180 
181  // store the last set from the EOF
182  AMREX_ALWAYS_ASSERT(z_in.size() == nz_lsf + 1);
183  z_lsf.push_back(z_in);
184  p_lsf.push_back(p_in);
185  t_lsf.push_back(t_in);
186  q_lsf.push_back(q_in);
187  u_lsf.push_back(u_in);
188  v_lsf.push_back(v_in);
189  w_lsf.push_back(w_in);
190 
191  num_times = times_lsf.size();
193  "repeated timestamp in the large-scale forcing file");
194  amrex::Print() << " Read " << nz_lsf << " levels at " << num_times << " times" << std::endl;
195 
196  // debugging print
197  if (verbose_print && !times_lsf.empty()) {
198  const int i = 0; // print only the first time block
199  amrex::Print() << " LSF at time = " << times_lsf[i] << std::endl;
200  for (int lev = 0; lev < nz_lsf + 1; lev++)
201  {
202  amrex::Print() << " level " << lev << ": z = " << z_lsf[i][lev] << " p = " << p_lsf[i][lev] << " tls = "
203  << t_lsf[i][lev] << " qls = " << q_lsf[i][lev] << " uls = " << u_lsf[i][lev] << " vls = "
204  << v_lsf[i][lev] << " wls = " << w_lsf[i][lev] << std::endl;
205  }
206  }
207  }
208 
209 
210  /**
211  * @brief Interpolate the forcing profiles onto the model grid.
212  * @param geom Geometry defining the vertical grid.
213  * @param zlevels_stag Staggered vertical coordinates used for interpolation.
214  * @param sounding Input sounding data used when the forcing file is on pressure levels.
215  */
216  void
217  interp_forcing (const amrex::GeometryData &geom,
218  const amrex::Vector<amrex::Real>& zlevels_stag,
219  const InputSoundingData &sounding)
220  {
221  const int klo = 0;
222  const int khi = geom.Domain().bigEnd()[AMREX_SPACEDIM-1];
223  const int Nz = geom.Domain().size()[AMREX_SPACEDIM-1];
224  const amrex::Real dz = geom.CellSize()[AMREX_SPACEDIM-1];
225 
226  const bool use_terrain = (zlevels_stag.size() > 0);
227  const amrex::Real zbot = (use_terrain) ? zlevels_stag[klo] : geom.ProbLo(AMREX_SPACEDIM-1);
228  const amrex::Real ztop = (use_terrain) ? zlevels_stag[khi+1] : geom.ProbHi(AMREX_SPACEDIM-1);
229 
230  AMREX_ALWAYS_ASSERT(num_times == static_cast<int>(times_lsf.size()));
231  // On pressure levels, cell k is placed by the sounding's hydrostatic pressure at its
232  // centre, pm_integ[k+1] (pm_integ[0] is the surface), so the sounding must be sampled
233  // on this same grid and integrated (sounding_type Ideal or Isentropic)
234  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(use_z_grid || static_cast<int>(sounding.pm_integ.size()) >= Nz+1,
235  "a large-scale forcing file on pressure levels needs the input sounding integrated "
236  "hydrostatically on the same grid (erf.sounding_type = Ideal or Isentropic)");
237 
238  z_int_lsf.resize(num_times);
239  t_int_lsf.resize(num_times);
240  q_int_lsf.resize(num_times);
241  u_int_lsf.resize(num_times);
242  v_int_lsf.resize(num_times);
243  w_int_lsf.resize(num_times);
244 
245  z_int_lsf_d.resize(num_times);
246  t_int_lsf_d.resize(num_times);
247  q_int_lsf_d.resize(num_times);
248  u_int_lsf_d.resize(num_times);
249  v_int_lsf_d.resize(num_times);
250  w_int_lsf_d.resize(num_times);
251  for (int itime = 0; itime < num_times; itime++)
252  {
253  z_int_lsf[itime].resize(Nz+1);
254  t_int_lsf[itime].resize(Nz+1);
255  q_int_lsf[itime].resize(Nz+1);
256  u_int_lsf[itime].resize(Nz+1);
257  v_int_lsf[itime].resize(Nz+1);
258  w_int_lsf[itime].resize(Nz+1);
259 
260  for (int k = 0; k < Nz; k++)
261  {
262  z_int_lsf[itime][k] = (use_terrain) ? myhalf * (zlevels_stag[k] + zlevels_stag[k+1])
263  : zbot + (k + myhalf) * dz;
264 
265  // Level 0 is the zero-forcing surface level added when the file is read, so the
266  // search starts at the bracket [0, 1] for cells below the first given level
267  bool set = false;
268  for (int lk = 1; lk < nz_lsf + 1; lk++)
269  {
270  // Determine if grid cell k falls in bracket [lk-1, lk]:
271  // z-grid mode: cell z <= forcing z (z increases with lk)
272  // p-grid mode: cell p >= forcing p (p decreases with lk)
273  bool in_bracket = use_z_grid
274  ? (z_int_lsf[itime][k] <= z_lsf[itime][lk])
275  : (sounding.pm_integ[k+1] >= p_lsf[itime][lk]);
276 
277  if (in_bracket)
278  {
279  // Interpolation coordinate: z or p depending on mode
280  const auto& coord_lsf = use_z_grid ? z_lsf[itime] : p_lsf[itime];
281  amrex::Real coord_k = use_z_grid ? z_int_lsf[itime][k] : sounding.pm_integ[k+1];
282  amrex::Real coef = (coord_k - coord_lsf[lk - 1]) / (coord_lsf[lk] - coord_lsf[lk - 1]);
283 
284  t_int_lsf[itime][k] = t_lsf[itime][lk - 1] + (t_lsf[itime][lk] - t_lsf[itime][lk-1])*coef;
285  q_int_lsf[itime][k] = q_lsf[itime][lk - 1] + (q_lsf[itime][lk] - q_lsf[itime][lk-1])*coef;
286  u_int_lsf[itime][k] = u_lsf[itime][lk - 1] + (u_lsf[itime][lk] - u_lsf[itime][lk-1])*coef;
287  v_int_lsf[itime][k] = v_lsf[itime][lk - 1] + (v_lsf[itime][lk] - v_lsf[itime][lk-1])*coef;
288  w_int_lsf[itime][k] = w_lsf[itime][lk - 1] + (w_lsf[itime][lk] - w_lsf[itime][lk-1])*coef;
289  set = true;
290  break;
291  }
292  }
293 
294  if (set) {
295  continue;
296  }
297 
298  t_int_lsf[itime][k] = zero;
299  q_int_lsf[itime][k] = zero;
300  u_int_lsf[itime][k] = (k > 0) ? u_int_lsf[itime][k-1] : zero;
301  v_int_lsf[itime][k] = (k > 0) ? v_int_lsf[itime][k-1] : zero;
302  w_int_lsf[itime][k] = zero;
303  }
304 
305  z_int_lsf[itime][Nz] = ztop;
306  t_int_lsf[itime][Nz] = zero;
307  q_int_lsf[itime][Nz] = zero;
308  u_int_lsf[itime][Nz] = u_int_lsf[itime][Nz-1];
309  v_int_lsf[itime][Nz] = v_int_lsf[itime][Nz-1];
310  w_int_lsf[itime][Nz] = zero;
311 
312  host_to_device(itime);
313  }
314 
315  // debugging print
316  if (verbose_print && !times_lsf.empty()) {
317  const int i = 0; // print only the first time block
318  amrex::Print() << " INTERPOLATED LSF at time = " << times_lsf[i] << std::endl;
319  for (int k = 0; k <= Nz; k++)
320  {
321  amrex::Print() << " level " << k << ": z = " << z_int_lsf[i][k] << " tls = " << t_int_lsf[i][k]
322  << " qls = " << q_int_lsf[i][k] << " uls = " << u_int_lsf[i][k] << " vls = "
323  << v_int_lsf[i][k] << " wls = " << w_int_lsf[i][k] << std::endl;
324  }
325  }
326  }
327 
328  /**
329  * @brief Determine interpolation coefficients to apply tendencies for the given time
330  * @param time Elapsed simulation time in seconds.
331  * @param curr Index of the lower bracketing forcing time.
332  * @param next Index of the upper bracketing forcing time.
333  * @param coeff_curr Interpolation weight for the lower time slice.
334  * @param coeff_next Interpolation weight for the upper time slice.
335  */
336  void
338  int& curr,
339  int& next,
340  amrex::Real& coeff_curr,
341  amrex::Real& coeff_next) const
342  {
343  // helper function to get the current time indices of forcing data to use and
344  // coefficients based on the current simulation time.
345  curr = 0;
346  next = 0;
347  coeff_curr = one;
348  coeff_next = zero;
349 
350  // find the time index of forcing data to use
351  for (int nt = 1; nt < num_times; nt++)
352  {
353  if (time > times_lsf[nt])
354  {
355  curr = nt;
356  }
357  }
358 
359  if (curr == num_times - 1)
360  {
361  // use last set if time > last forcing time
362  next = num_times - 1;
363  coeff_curr = one;
364  coeff_next = zero;
365  } else {
366  next = curr + 1;
367  coeff_next = (time - times_lsf[curr]) / (times_lsf[next] - times_lsf[curr]);
368  coeff_curr = (one - coeff_next);
369  }
370  }
371 
372  /**
373  * @brief Copy one interpolated forcing slice from host memory to device memory.
374  * @param itime Time-slice index to copy.
375  */
376  void host_to_device (int itime)
377  {
378  const int nz = z_int_lsf[itime].size();
379  z_int_lsf_d[itime].resize(nz);
380  t_int_lsf_d[itime].resize(nz);
381  q_int_lsf_d[itime].resize(nz);
382  u_int_lsf_d[itime].resize(nz);
383  v_int_lsf_d[itime].resize(nz);
384  w_int_lsf_d[itime].resize(nz);
385 
386  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
387  z_int_lsf[itime].begin(), z_int_lsf[itime].end(),
388  z_int_lsf_d[itime].begin());
389  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
390  t_int_lsf[itime].begin(), t_int_lsf[itime].end(),
391  t_int_lsf_d[itime].begin());
392  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
393  q_int_lsf[itime].begin(), q_int_lsf[itime].end(),
394  q_int_lsf_d[itime].begin());
395  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
396  u_int_lsf[itime].begin(), u_int_lsf[itime].end(),
397  u_int_lsf_d[itime].begin());
398  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
399  v_int_lsf[itime].begin(), v_int_lsf[itime].end(),
400  v_int_lsf_d[itime].begin());
401  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
402  w_int_lsf[itime].begin(), w_int_lsf[itime].end(),
403  w_int_lsf_d[itime].begin());
404  }
405 
406  /**
407  * Relaxation time scale used by the large-scale forcing and nudging terms.
408  */
410  /**
411  * Path to the large-scale forcing input file.
412  */
413  std::string lsf_file = "";
414 
415  /**
416  * True when the forcing file is tabulated on height levels, false when it is
417  * tabulated on pressure levels.
418  */
419  bool use_z_grid = true;
420 
421  /**
422  * Whether to display the lsf at initialization (before and after interpolation)
423  */
424  bool verbose_print = false;
425 
426  /**
427  * Number of vertical levels and times found in the forcing file.
428  */
429  int nz_lsf = -1;
430  int num_times = -1;
431  /**
432  * Forcing times read from the file, converted to elapsed seconds.
433  */
434  amrex::Vector<amrex::Real> times_lsf;
435  /**
436  * Raw forcing profiles read from the input file, indexed by time then level.
437  */
438  amrex::Vector<amrex::Vector<amrex::Real>> z_lsf;
439  amrex::Vector<amrex::Vector<amrex::Real>> p_lsf;
440  amrex::Vector<amrex::Vector<amrex::Real>> t_lsf;
441  amrex::Vector<amrex::Vector<amrex::Real>> q_lsf;
442  amrex::Vector<amrex::Vector<amrex::Real>> u_lsf;
443  amrex::Vector<amrex::Vector<amrex::Real>> v_lsf;
444  amrex::Vector<amrex::Vector<amrex::Real>> w_lsf;
445 
446  /**
447  * Forcing profiles interpolated onto the model grid, indexed by time then level.
448  */
449  amrex::Vector<amrex::Vector<amrex::Real>> z_int_lsf;
450  amrex::Vector<amrex::Vector<amrex::Real>> t_int_lsf;
451  amrex::Vector<amrex::Vector<amrex::Real>> q_int_lsf;
452  amrex::Vector<amrex::Vector<amrex::Real>> u_int_lsf;
453  amrex::Vector<amrex::Vector<amrex::Real>> v_int_lsf;
454  amrex::Vector<amrex::Vector<amrex::Real>> w_int_lsf;
455 
456  /**
457  * Device copies of the interpolated forcing profiles.
458  */
459  amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real>> z_int_lsf_d, t_int_lsf_d, q_int_lsf_d, u_int_lsf_d, v_int_lsf_d, w_int_lsf_d;
460 
461 };
462 #endif
const Real ztop
Definition: ERF_InitCustomPertVels_ParticleTests.H:4
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(axis >=0 &&axis< AMREX_SPACEDIM, "prob.axis must be 0, 1 or 2")
ParmParse pp("prob")
const int khi
Definition: ERF_InitCustomPert_Bubble.H:25
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
const int klo
Definition: ERF_InitCustomPert_KE.H:9
const bool use_terrain
Definition: ERF_InitCustomPert_SLM.H:19
Dimensionless numeric literals and pure mathematical constants.
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ dz
Definition: ERF_AdvanceWDM6.cpp:273
@ p
Definition: ERF_WSM6.H:301
@ tmp
Definition: ERF_AdvanceWSM6.cpp:116
Definition: ERF_InputSoundingData.H:23
amrex::Vector< amrex::Real > pm_integ
Hydrostatically integrated moist pressure profile.
Definition: ERF_InputSoundingData.H:518
Definition: ERF_LargeScaleForcingData.H:31
bool use_z_grid
Definition: ERF_LargeScaleForcingData.H:419
amrex::Vector< amrex::Real > times_lsf
Definition: ERF_LargeScaleForcingData.H:434
amrex::Vector< amrex::Vector< amrex::Real > > u_lsf
Definition: ERF_LargeScaleForcingData.H:442
void read_forcing_file()
Read the forcing file and cache the raw time slices on host.
Definition: ERF_LargeScaleForcingData.H:64
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > q_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:459
bool verbose_print
Definition: ERF_LargeScaleForcingData.H:424
amrex::Vector< amrex::Vector< amrex::Real > > z_lsf
Definition: ERF_LargeScaleForcingData.H:438
amrex::Vector< amrex::Vector< amrex::Real > > q_lsf
Definition: ERF_LargeScaleForcingData.H:441
void get_forcing_time_coeffs(const amrex::Real &time, int &curr, int &next, amrex::Real &coeff_curr, amrex::Real &coeff_next) const
Determine interpolation coefficients to apply tendencies for the given time.
Definition: ERF_LargeScaleForcingData.H:337
std::string lsf_file
Definition: ERF_LargeScaleForcingData.H:413
int nz_lsf
Definition: ERF_LargeScaleForcingData.H:429
amrex::Vector< amrex::Vector< amrex::Real > > q_int_lsf
Definition: ERF_LargeScaleForcingData.H:451
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > v_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:459
amrex::Vector< amrex::Vector< amrex::Real > > w_int_lsf
Definition: ERF_LargeScaleForcingData.H:454
amrex::Vector< amrex::Vector< amrex::Real > > v_lsf
Definition: ERF_LargeScaleForcingData.H:443
amrex::Vector< amrex::Vector< amrex::Real > > u_int_lsf
Definition: ERF_LargeScaleForcingData.H:452
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > t_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:459
amrex::Vector< amrex::Vector< amrex::Real > > t_int_lsf
Definition: ERF_LargeScaleForcingData.H:450
amrex::Vector< amrex::Vector< amrex::Real > > p_lsf
Definition: ERF_LargeScaleForcingData.H:439
amrex::Vector< amrex::Vector< amrex::Real > > w_lsf
Definition: ERF_LargeScaleForcingData.H:444
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > z_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:459
void interp_forcing(const amrex::GeometryData &geom, const amrex::Vector< amrex::Real > &zlevels_stag, const InputSoundingData &sounding)
Interpolate the forcing profiles onto the model grid.
Definition: ERF_LargeScaleForcingData.H:217
amrex::Vector< amrex::Vector< amrex::Real > > v_int_lsf
Definition: ERF_LargeScaleForcingData.H:453
int get_columns_in_str(std::string &str)
Count the number of whitespace-separated columns in a line.
Definition: ERF_LargeScaleForcingData.H:48
amrex::Real tau_lsf
Definition: ERF_LargeScaleForcingData.H:409
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > u_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:459
amrex::Vector< amrex::Vector< amrex::Real > > t_lsf
Definition: ERF_LargeScaleForcingData.H:440
amrex::Vector< amrex::Vector< amrex::Real > > z_int_lsf
Definition: ERF_LargeScaleForcingData.H:449
void host_to_device(int itime)
Copy one interpolated forcing slice from host memory to device memory.
Definition: ERF_LargeScaleForcingData.H:376
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > w_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:459
int num_times
Definition: ERF_LargeScaleForcingData.H:430
LargeScaleForcingData()
Construct large-scale forcing metadata from the ERF input namespace.
Definition: ERF_LargeScaleForcingData.H:36