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_Constants.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 
232  z_int_lsf.resize(num_times);
233  t_int_lsf.resize(num_times);
234  q_int_lsf.resize(num_times);
235  u_int_lsf.resize(num_times);
236  v_int_lsf.resize(num_times);
237  w_int_lsf.resize(num_times);
238 
239  z_int_lsf_d.resize(num_times);
240  t_int_lsf_d.resize(num_times);
241  q_int_lsf_d.resize(num_times);
242  u_int_lsf_d.resize(num_times);
243  v_int_lsf_d.resize(num_times);
244  w_int_lsf_d.resize(num_times);
245  for (int itime = 0; itime < num_times; itime++)
246  {
247  z_int_lsf[itime].resize(Nz+1);
248  t_int_lsf[itime].resize(Nz+1);
249  q_int_lsf[itime].resize(Nz+1);
250  u_int_lsf[itime].resize(Nz+1);
251  v_int_lsf[itime].resize(Nz+1);
252  w_int_lsf[itime].resize(Nz+1);
253 
254  for (int k = 0; k < Nz; k++)
255  {
256  z_int_lsf[itime][k] = (use_terrain) ? myhalf * (zlevels_stag[k] + zlevels_stag[k+1])
257  : zbot + (k + myhalf) * dz;
258 
259  bool set = false;
260  for (int lk = 2; lk < nz_lsf + 1; lk++)
261  {
262  // Determine if grid cell k falls in bracket [lk-1, lk]:
263  // z-grid mode: cell z <= forcing z (z increases with lk)
264  // p-grid mode: cell p >= forcing p (p decreases with lk)
265  bool in_bracket = use_z_grid
266  ? (z_int_lsf[itime][k] <= z_lsf[itime][lk])
267  : (sounding.pm_integ[k] >= p_lsf[itime][lk]);
268 
269  if (in_bracket)
270  {
271  // Interpolation coordinate: z or p depending on mode
272  const auto& coord_lsf = use_z_grid ? z_lsf[itime] : p_lsf[itime];
273  amrex::Real coord_k = use_z_grid ? z_int_lsf[itime][k] : sounding.pm_integ[k];
274  amrex::Real coef = (coord_k - coord_lsf[lk - 1]) / (coord_lsf[lk] - coord_lsf[lk - 1]);
275 
276  t_int_lsf[itime][k] = t_lsf[itime][lk - 1] + (t_lsf[itime][lk] - t_lsf[itime][lk-1])*coef;
277  q_int_lsf[itime][k] = q_lsf[itime][lk - 1] + (q_lsf[itime][lk] - q_lsf[itime][lk-1])*coef;
278  u_int_lsf[itime][k] = u_lsf[itime][lk - 1] + (u_lsf[itime][lk] - u_lsf[itime][lk-1])*coef;
279  v_int_lsf[itime][k] = v_lsf[itime][lk - 1] + (v_lsf[itime][lk] - v_lsf[itime][lk-1])*coef;
280  w_int_lsf[itime][k] = w_lsf[itime][lk - 1] + (w_lsf[itime][lk] - w_lsf[itime][lk-1])*coef;
281  set = true;
282  break;
283  }
284  }
285 
286  if (set) {
287  continue;
288  }
289 
290  t_int_lsf[itime][k] = zero;
291  q_int_lsf[itime][k] = zero;
292  u_int_lsf[itime][k] = (k > 0) ? u_int_lsf[itime][k-1] : zero;
293  v_int_lsf[itime][k] = (k > 0) ? v_int_lsf[itime][k-1] : zero;
294  w_int_lsf[itime][k] = zero;
295  }
296 
297  z_int_lsf[itime][Nz] = ztop;
298  t_int_lsf[itime][Nz] = zero;
299  q_int_lsf[itime][Nz] = zero;
300  u_int_lsf[itime][Nz] = u_int_lsf[itime][Nz-1];
301  v_int_lsf[itime][Nz] = v_int_lsf[itime][Nz-1];
302  w_int_lsf[itime][Nz] = zero;
303 
304  host_to_device(itime);
305  }
306 
307  // debugging print
308  if (verbose_print && !times_lsf.empty()) {
309  const int i = 0; // print only the first time block
310  amrex::Print() << " INTERPOLATED LSF at time = " << times_lsf[i] << std::endl;
311  for (int k = 0; k <= Nz; k++)
312  {
313  amrex::Print() << " level " << k << ": z = " << z_int_lsf[i][k] << " tls = " << t_int_lsf[i][k]
314  << " qls = " << q_int_lsf[i][k] << " uls = " << u_int_lsf[i][k] << " vls = "
315  << v_int_lsf[i][k] << " wls = " << w_int_lsf[i][k] << std::endl;
316  }
317  }
318  }
319 
320  /**
321  * @brief Determine interpolation coefficients to apply tendencies for the given time
322  * @param time Elapsed simulation time in seconds.
323  * @param curr Index of the lower bracketing forcing time.
324  * @param next Index of the upper bracketing forcing time.
325  * @param coeff_curr Interpolation weight for the lower time slice.
326  * @param coeff_next Interpolation weight for the upper time slice.
327  */
328  void
330  int& curr,
331  int& next,
332  amrex::Real& coeff_curr,
333  amrex::Real& coeff_next)
334  {
335  // helper function to get the current time indices of forcing data to use and
336  // coefficients based on the current simulation time.
337  curr = 0;
338  next = 0;
339  coeff_curr = one;
340  coeff_next = zero;
341 
342  // find the time index of forcing data to use
343  for (int nt = 1; nt < num_times; nt++)
344  {
345  if (time > times_lsf[nt])
346  {
347  curr = nt;
348  }
349  }
350 
351  if (curr == num_times - 1)
352  {
353  // use last set if time > last forcing time
354  next = num_times - 1;
355  coeff_curr = one;
356  coeff_next = zero;
357  } else {
358  next = curr + 1;
359  coeff_next = (time - times_lsf[curr]) / (times_lsf[next] - times_lsf[curr]);
360  coeff_curr = (one - coeff_next);
361  }
362  }
363 
364  /**
365  * @brief Copy one interpolated forcing slice from host memory to device memory.
366  * @param itime Time-slice index to copy.
367  */
368  void host_to_device (int itime)
369  {
370  const int nz = z_int_lsf[itime].size();
371  z_int_lsf_d[itime].resize(nz);
372  t_int_lsf_d[itime].resize(nz);
373  q_int_lsf_d[itime].resize(nz);
374  u_int_lsf_d[itime].resize(nz);
375  v_int_lsf_d[itime].resize(nz);
376  w_int_lsf_d[itime].resize(nz);
377 
378  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
379  z_int_lsf[itime].begin(), z_int_lsf[itime].end(),
380  z_int_lsf_d[itime].begin());
381  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
382  t_int_lsf[itime].begin(), t_int_lsf[itime].end(),
383  t_int_lsf_d[itime].begin());
384  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
385  q_int_lsf[itime].begin(), q_int_lsf[itime].end(),
386  q_int_lsf_d[itime].begin());
387  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
388  u_int_lsf[itime].begin(), u_int_lsf[itime].end(),
389  u_int_lsf_d[itime].begin());
390  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
391  v_int_lsf[itime].begin(), v_int_lsf[itime].end(),
392  v_int_lsf_d[itime].begin());
393  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
394  w_int_lsf[itime].begin(), w_int_lsf[itime].end(),
395  w_int_lsf_d[itime].begin());
396  }
397 
398  /**
399  * Relaxation time scale used by the large-scale forcing and nudging terms.
400  */
402  /**
403  * Path to the large-scale forcing input file.
404  */
405  std::string lsf_file = "";
406 
407  /**
408  * True when the forcing file is tabulated on height levels, false when it is
409  * tabulated on pressure levels.
410  */
411  bool use_z_grid = true;
412 
413  /**
414  * Whether to display the lsf at initialization (before and after interpolation)
415  */
416  bool verbose_print = false;
417 
418  /**
419  * Number of vertical levels and times found in the forcing file.
420  */
421  int nz_lsf = -1;
422  int num_times = -1;
423  /**
424  * Forcing times read from the file, converted to elapsed seconds.
425  */
426  amrex::Vector<amrex::Real> times_lsf;
427  /**
428  * Raw forcing profiles read from the input file, indexed by time then level.
429  */
430  amrex::Vector<amrex::Vector<amrex::Real>> z_lsf;
431  amrex::Vector<amrex::Vector<amrex::Real>> p_lsf;
432  amrex::Vector<amrex::Vector<amrex::Real>> t_lsf;
433  amrex::Vector<amrex::Vector<amrex::Real>> q_lsf;
434  amrex::Vector<amrex::Vector<amrex::Real>> u_lsf;
435  amrex::Vector<amrex::Vector<amrex::Real>> v_lsf;
436  amrex::Vector<amrex::Vector<amrex::Real>> w_lsf;
437 
438  /**
439  * Forcing profiles interpolated onto the model grid, indexed by time then level.
440  */
441  amrex::Vector<amrex::Vector<amrex::Real>> z_int_lsf;
442  amrex::Vector<amrex::Vector<amrex::Real>> t_int_lsf;
443  amrex::Vector<amrex::Vector<amrex::Real>> q_int_lsf;
444  amrex::Vector<amrex::Vector<amrex::Real>> u_int_lsf;
445  amrex::Vector<amrex::Vector<amrex::Real>> v_int_lsf;
446  amrex::Vector<amrex::Vector<amrex::Real>> w_int_lsf;
447 
448  /**
449  * Device copies of the interpolated forcing profiles.
450  */
451  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;
452 
453 };
454 #endif
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
const Real ztop
Definition: ERF_InitCustomPertVels_ParticleTests.H:4
const bool use_terrain
Definition: ERF_InitCustomPertVels_Terrain3DHemisphere.H:26
ParmParse pp("prob")
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ dz
Definition: ERF_AdvanceWDM6.cpp:270
@ p
Definition: ERF_WSM6.H:191
@ tmp
Definition: ERF_AdvanceWSM6.cpp:114
Definition: ERF_InputSoundingData.H:23
amrex::Vector< amrex::Real > pm_integ
Hydrostatically integrated moist pressure profile.
Definition: ERF_InputSoundingData.H:512
Definition: ERF_LargeScaleForcingData.H:31
bool use_z_grid
Definition: ERF_LargeScaleForcingData.H:411
amrex::Vector< amrex::Real > times_lsf
Definition: ERF_LargeScaleForcingData.H:426
amrex::Vector< amrex::Vector< amrex::Real > > u_lsf
Definition: ERF_LargeScaleForcingData.H:434
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:451
bool verbose_print
Definition: ERF_LargeScaleForcingData.H:416
amrex::Vector< amrex::Vector< amrex::Real > > z_lsf
Definition: ERF_LargeScaleForcingData.H:430
amrex::Vector< amrex::Vector< amrex::Real > > q_lsf
Definition: ERF_LargeScaleForcingData.H:433
std::string lsf_file
Definition: ERF_LargeScaleForcingData.H:405
int nz_lsf
Definition: ERF_LargeScaleForcingData.H:421
amrex::Vector< amrex::Vector< amrex::Real > > q_int_lsf
Definition: ERF_LargeScaleForcingData.H:443
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > v_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:451
amrex::Vector< amrex::Vector< amrex::Real > > w_int_lsf
Definition: ERF_LargeScaleForcingData.H:446
amrex::Vector< amrex::Vector< amrex::Real > > v_lsf
Definition: ERF_LargeScaleForcingData.H:435
amrex::Vector< amrex::Vector< amrex::Real > > u_int_lsf
Definition: ERF_LargeScaleForcingData.H:444
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > t_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:451
amrex::Vector< amrex::Vector< amrex::Real > > t_int_lsf
Definition: ERF_LargeScaleForcingData.H:442
amrex::Vector< amrex::Vector< amrex::Real > > p_lsf
Definition: ERF_LargeScaleForcingData.H:431
amrex::Vector< amrex::Vector< amrex::Real > > w_lsf
Definition: ERF_LargeScaleForcingData.H:436
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > z_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:451
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:445
int get_columns_in_str(std::string &str)
Count the number of whitespace-separated columns in a line.
Definition: ERF_LargeScaleForcingData.H:48
void get_forcing_time_coeffs(const amrex::Real &time, int &curr, int &next, amrex::Real &coeff_curr, amrex::Real &coeff_next)
Determine interpolation coefficients to apply tendencies for the given time.
Definition: ERF_LargeScaleForcingData.H:329
amrex::Real tau_lsf
Definition: ERF_LargeScaleForcingData.H:401
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > u_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:451
amrex::Vector< amrex::Vector< amrex::Real > > t_lsf
Definition: ERF_LargeScaleForcingData.H:432
amrex::Vector< amrex::Vector< amrex::Real > > z_int_lsf
Definition: ERF_LargeScaleForcingData.H:441
void host_to_device(int itime)
Copy one interpolated forcing slice from host memory to device memory.
Definition: ERF_LargeScaleForcingData.H:368
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > w_int_lsf_d
Definition: ERF_LargeScaleForcingData.H:451
int num_times
Definition: ERF_LargeScaleForcingData.H:422
LargeScaleForcingData()
Construct large-scale forcing metadata from the ERF input namespace.
Definition: ERF_LargeScaleForcingData.H:36