ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_InputSoundingData.H
Go to the documentation of this file.
1 #ifndef ERF_INPUT_SOUNDING_DATA_H_
2 #define ERF_INPUT_SOUNDING_DATA_H_
3 
4 #include <string>
5 #include <iostream>
6 
7 #include <AMReX_ParmParse.H>
8 #include <AMReX_Print.H>
9 #include <AMReX_Gpu.H>
10 #include <AMReX_Geometry.H>
11 
12 #include <ERF_EOS.H>
13 #include <ERF_Constants.H>
14 #include <ERF_Interpolation_1D.H>
15 #include <ERF_HSEUtils.H>
16 
17 /**
18  * Data structure storing input sounding data. Also
19  * handles reading the input file for sounding data and
20  * hydrostatic column integration.
21  */
23 public:
25  {
26  amrex::ParmParse pp("erf");
27  pp.query("tau_nudging", tau_nudging);
28 
29  // Read in input_sounding filename
30  n_sounding_files = pp.countval("input_sounding_file");
31  if (n_sounding_files > 0) {
33  pp.queryarr("input_sounding_file", input_sounding_file, 0, n_sounding_files);
34  } else {
35  n_sounding_files = 1;
37  input_sounding_file[0] = "input_sounding";
38  }
39 
40  // Read in input_sounding times
41  n_sounding_times = pp.countval("input_sounding_time");
42 
43  if (n_sounding_times > 0) {
45  pp.queryarr("input_sounding_time", input_sounding_time, 0, n_sounding_times);
46  } else {
47  n_sounding_times = 1;
50  }
51 
52  // If we have more files than times or times than files we just use the minimum
53  int n = std::min(n_sounding_times, n_sounding_files);
54  n_sounding_files = n;
55  n_sounding_times = n;
56  input_sounding_file.resize(n);
57  input_sounding_time.resize(n);
58  }
59 
60  void resize_arrays ()
61  {
63 
64  z_inp_sound.resize(ntimes);
65  theta_inp_sound.resize(ntimes);
66  qv_inp_sound.resize(ntimes);
67  U_inp_sound.resize(ntimes);
68  V_inp_sound.resize(ntimes);
69 
70  z_inp_sound_d.resize(ntimes);
71  theta_inp_sound_d.resize(ntimes);
72  qv_inp_sound_d.resize(ntimes);
73  U_inp_sound_d.resize(ntimes);
74  V_inp_sound_d.resize(ntimes);
75  }
76 
77  void read_from_file (const amrex::Geometry &geom,
78  const amrex::Vector<amrex::Real>& zlevels_stag,
79  int itime, bool is_moist)
80  {
81  const int klo = 0;
82  const int khi = geom.Domain().bigEnd()[AMREX_SPACEDIM-1];
83  const int Nz = geom.Domain().size()[AMREX_SPACEDIM-1];
84 
85  const amrex::Real zbot = zlevels_stag[klo];
86  const amrex::Real ztop = zlevels_stag[khi+1];
87 
88  z_inp_sound[itime].resize(Nz+2);
89  theta_inp_sound[itime].resize(Nz+2);
90  qv_inp_sound[itime].resize(Nz+2);
91  U_inp_sound[itime].resize(Nz+2);
92  V_inp_sound[itime].resize(Nz+2);
93 
94  // Read the input_sounding file
95  amrex::Print() << "input_sounding file location : " << input_sounding_file[itime] << std::endl;
96  std::ifstream input_sounding_reader(input_sounding_file[itime]);
97  if(!input_sounding_reader.is_open()) {
98  amrex::Error("Error opening the input_sounding file\n");
99  }
100  else {
101  // Read the contents of the input_sounding file
102  amrex::Print() << "Successfully opened the input_sounding file. Now reading... " << std::endl;
103  std::string line;
104 
105  // First, read the input data into temp vectors; then, interpolate vectors to the
106  // domain lo/hi and cell centers (from level 0)
107  amrex::Vector<amrex::Real> z_inp_sound_tmp, theta_inp_sound_tmp, qv_inp_sound_tmp,
108  U_inp_sound_tmp, V_inp_sound_tmp;
109 
110  // Read surface quantities from the first line
111  std::getline(input_sounding_reader, line);
112  std::istringstream iss(line);
114  press_ref_inp_sound *= 100; // convert from hPa to Pa
115  qv_ref_inp_sound *= amrex::Real(0.001); // convert from g/kg to kg/kg
116 
117  // Add surface
118  z_inp_sound_tmp.push_back(zbot); // height above sea level [m]
119  theta_inp_sound_tmp.push_back(theta_ref_inp_sound);
120  qv_inp_sound_tmp.push_back(qv_ref_inp_sound);
121  U_inp_sound_tmp.push_back(0);
122  V_inp_sound_tmp.push_back(0);
123 
124  // Read the vertical profile at each given height
125  amrex::Real z, theta, qv, U, V;
126  while(std::getline(input_sounding_reader, line)) {
127  std::istringstream iss_z(line);
128  iss_z >> z >> theta >> qv >> U >> V;
129 
130  // Dont read in non-zero qv if not using a moisture model
131  AMREX_ALWAYS_ASSERT(qv == zero || is_moist);
132 
133  if (z == zbot) {
134  AMREX_ALWAYS_ASSERT(theta == theta_inp_sound_tmp[0]);
135  AMREX_ALWAYS_ASSERT(qv*amrex::Real(0.001) == qv_inp_sound_tmp[0]); // convert from g/kg to kg/kg
136  U_inp_sound_tmp[0] = U;
137  V_inp_sound_tmp[0] = V;
138  } else {
139  AMREX_ALWAYS_ASSERT(z > z_inp_sound_tmp[z_inp_sound_tmp.size()-1]); // sounding is increasing in height
140  z_inp_sound_tmp.push_back(z);
141 
142  theta_inp_sound_tmp.push_back(theta);
143  qv_inp_sound_tmp.push_back(qv*amrex::Real(0.001)); // convert from g/kg to kg/kg
144  U_inp_sound_tmp.push_back(U);
145  V_inp_sound_tmp.push_back(V);
146  if (z >= ztop) break;
147  }
148  }
149 
150  // At this point, we have an input_sounding from zbot up to
151  // z_inp_sound_tmp[N-1] >= ztop. Now, interpolate to grid level 0 heights
152  const int Ninp = z_inp_sound_tmp.size();
153  z_inp_sound[itime][0] = zbot;
154  theta_inp_sound[itime][0] = theta_inp_sound_tmp[0];
155  qv_inp_sound[itime][0] = qv_inp_sound_tmp[0];
156  U_inp_sound[itime][0] = U_inp_sound_tmp[0];
157  V_inp_sound[itime][0] = V_inp_sound_tmp[0];
158  for (int k=0; k < Nz; ++k) {
159  z_inp_sound[itime][k+1] = myhalf * (zlevels_stag[k] + zlevels_stag[k+1]);
160  theta_inp_sound[itime][k+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), theta_inp_sound_tmp.dataPtr(), z_inp_sound[itime][k+1], Ninp);
161  qv_inp_sound[itime][k+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), qv_inp_sound_tmp.dataPtr(), z_inp_sound[itime][k+1], Ninp);
162  U_inp_sound[itime][k+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), U_inp_sound_tmp.dataPtr(), z_inp_sound[itime][k+1], Ninp);
163  V_inp_sound[itime][k+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), V_inp_sound_tmp.dataPtr(), z_inp_sound[itime][k+1], Ninp);
164  }
165  z_inp_sound[itime][Nz+1] = ztop;
166  theta_inp_sound[itime][Nz+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), theta_inp_sound_tmp.dataPtr(), ztop, Ninp);
167  qv_inp_sound[itime][Nz+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), qv_inp_sound_tmp.dataPtr(), ztop, Ninp);
168  U_inp_sound[itime][Nz+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), U_inp_sound_tmp.dataPtr(), ztop, Ninp);
169  V_inp_sound[itime][Nz+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), V_inp_sound_tmp.dataPtr(), ztop, Ninp);
170  }
171 
172  amrex::Print() << "Successfully read the " << itime << "th input_sounding file..." << std::endl;
173  input_sounding_reader.close();
174 
175  host_to_device(itime);
176  }
177 
178  void calc_rho_p (int itime)
179  {
180  /* Calculate density and pressure, roughly following the procedure in
181  * WRF dyn_em/module_initialize_ideal.F. We integrate hydrostatically
182  * from the surface up through the air column to get the dry density
183  * and moist pressure.
184  */
185  const amrex::Real tol = amrex::Real(1.0e-12);
186  const int Ninp = size(itime);
187  pm_integ.resize(Ninp);
188  rhod_integ.resize(Ninp);
189 
190  // evaluate surface quantities (k=0): total pressure and dry air
194  RdoCp,
196 
197  amrex::Print() << "ideal sounding init: surface dry air density = "
198  << std::setprecision(15)
199  << rhod_integ[0] << " kg/m^3" << std::endl;
200 
201  // Note:
202  // p_dry = rho_d R_d T
203  // p_tot = rho_m R_d T_v
204  // = rho_d(1 + q_v) R_d T_v
205 
206 #if 0 // Printing
207  // In this absence of moisture, this moist profile will match the
208  // following dry profile
209  amrex::Print() << "z p_m rho_d theta qv U V" << std::endl;
210  amrex::Print() << z_inp_sound[itime][0]
211  << " " << pm_integ[0]
212  << " " << rhod_integ[0]
213  << " " << theta_inp_sound[itime][0]
214  << " " << qv_inp_sound[itime][0]
215  << " " << U_inp_sound[itime][0]
216  << " " << V_inp_sound[itime][0]
217  << std::endl;
218 #endif
219 
220  // integrate from surface to domain top
221  amrex::Real dz, F, C;
222  amrex::Real T_hi;
223  amrex::Real rho_tot_hi, rho_tot_lo;
224  for (int k=1; k < size(itime); ++k)
225  {
226  // Vertical grid spacing
227  dz = z_inp_sound[itime][k] - z_inp_sound[itime][k-1];
228 
229  // Establish known constant
230  rho_tot_lo = rhod_integ[k-1] * (one + qv_inp_sound[itime][k-1]);
231  C = -pm_integ[k-1] + myhalf*rho_tot_lo*CONST_GRAV*dz;
232 
233  // Initial guess and residual
234  pm_integ[k] = pm_integ[k-1];
235  T_hi = getTgivenPandTh(pm_integ[k], theta_inp_sound[itime][k], RdoCp);
237  pm_integ[k],
238  RdoCp,
239  qv_inp_sound[itime][k]);
240  rho_tot_hi = rhod_integ[k] * (one + qv_inp_sound[itime][k]);
241  F = pm_integ[k] + myhalf*rho_tot_hi*CONST_GRAV*dz + C;
242 
243  // Do iterations
244  if (std::abs(F)>tol) {
245  bool maintain_Th = true;
247  CONST_GRAV, C, theta_inp_sound[itime][k], T_hi,
248  qv_inp_sound[itime][k], qv_inp_sound[itime][k],
249  pm_integ[k], rhod_integ[k], F, maintain_Th);
250  }
251 
252 #if 0 // Printing
253  amrex::Print() << z_inp_sound[itime][k]
254  << " " << pm_integ[k]
255  << " " << rhod_integ[k]
256  << " " << theta_inp_sound[itime][k]
257  << " " << qv_inp_sound[itime][k]
258  << " " << U_inp_sound[itime][k]
259  << " " << V_inp_sound[itime][k]
260  << std::endl;
261 #endif
262  }
263  // Note: at this point, the surface pressure, density of the dry air
264  // column is stored in pm_integ[0], rhod_integ[0]
265 
266  // update
267  host_to_device(itime);
268  }
269 
270  void calc_rho_p_isentropic (int itime)
271  {
272  /* Calculate density and pressure assuming isentropic (constant theta)
273  background conditions. This does not use Newton-Raphson iterations
274  to calculate rho and p.
275  */
276  const int Ninp = size(itime);
277  pm_integ.resize(Ninp);
278  rhod_integ.resize(Ninp);
279 
280  // evaluate surface quantities (k=0): total pressure and dry air
284  RdoCp,
286 
287  const amrex::Real th0 = theta_ref_inp_sound;
288  const amrex::Real p0_pow = std::pow(p_0, (Gamma-1)/Gamma);
289 
290  amrex::Print() << "isentropic sounding init: surface dry air density = "
291  << std::setprecision(15)
292  << rhod_integ[0] << " kg/m^3" << std::endl;
293 
294 #if 0 // Printing
295  // In this absence of moisture, this moist profile will match the
296  // following dry profile
297  amrex::Print() << "z p_m rho_d theta qv U V" << std::endl;
298  amrex::Print() << z_inp_sound[itime][0]
299  << " " << pm_integ[0]
300  << " " << rhod_integ[0]
301  << " " << theta_inp_sound[itime][0]
302  << " " << qv_inp_sound[itime][0]
303  << " " << U_inp_sound[itime][0]
304  << " " << V_inp_sound[itime][0]
305  << std::endl;
306 #endif
307 
308  // integrate from surface to domain top
309  amrex::Real dz;
310  for (int k=1; k < size(itime); ++k)
311  {
312  // Vertical grid spacing
313  dz = z_inp_sound[itime][k] - z_inp_sound[itime][k-1];
314 
315  // Isentropic pressure at next level
316  amrex::Real qvmean = (assume_dry) ? zero : myhalf*(qv_inp_sound[itime][k-1] + qv_inp_sound[itime][k]);
317  amrex::Real thm0 = th0 * (one + RvoRd * qvmean);
318  amrex::Real plo_pow = std::pow(pm_integ[k-1], (Gamma-1)/Gamma);
319  pm_integ[k] = (Gamma-1)/Gamma *
320  (-CONST_GRAV * p0_pow / (R_d * thm0) * (1 + qvmean) * dz
321  + Gamma/(Gamma-1) * plo_pow);
322  pm_integ[k] = std::pow(pm_integ[k], Gamma / (Gamma-1));
323 
324  // Note: The pressure calculated here is copied to `p_inp_sound_d`
325  // and currently not used.
326 
327  // Get corresponding dry air density
329  pm_integ[k],
330  RdoCp,
331  qv_inp_sound[itime][k]);
332 
333 #if 0 // Printing
334  amrex::Print() << z_inp_sound[itime][k]
335  << " " << pm_integ[k]
336  << " " << rhod_integ[k]
337  << " " << theta_inp_sound[itime][k]
338  << " " << qv_inp_sound[itime][k]
339  << " " << U_inp_sound[itime][k]
340  << " " << V_inp_sound[itime][k]
341  << std::endl;
342 #endif
343  }
344  // Note: at this point, the surface pressure, density of the dry air
345  // column is stored in pm_integ[0], rhod_integ[0]
346 
347  // update
348  host_to_device(itime);
349  }
350 
351  void host_to_device (int itime)
352  {
353  const int Ninp = size(itime);
354  z_inp_sound_d[itime].resize(Ninp);
355  theta_inp_sound_d[itime].resize(Ninp);
356  qv_inp_sound_d[itime].resize(Ninp);
357  U_inp_sound_d[itime].resize(Ninp);
358  V_inp_sound_d[itime].resize(Ninp);
359 
360  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
361  z_inp_sound[itime].begin(), z_inp_sound[itime].end(),
362  z_inp_sound_d[itime].begin());
363  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
364  theta_inp_sound[itime].begin(), theta_inp_sound[itime].end(),
365  theta_inp_sound_d[itime].begin());
366  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
367  qv_inp_sound[itime].begin(), qv_inp_sound[itime].end(),
368  qv_inp_sound_d[itime].begin());
369  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
370  U_inp_sound[itime].begin(), U_inp_sound[itime].end(),
371  U_inp_sound_d[itime].begin());
372  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
373  V_inp_sound[itime].begin(), V_inp_sound[itime].end(),
374  V_inp_sound_d[itime].begin());
375 
376  if (rhod_integ.size() > 0)
377  {
378  //amrex::Print() << "Copying rho_d, p_d to device" << std::endl;
379  rho_inp_sound_d.resize(size(itime)+2);
380  p_inp_sound_d.resize(size(itime)+2);
381  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
382  rhod_integ.begin(), rhod_integ.end(),
383  rho_inp_sound_d.begin());
384  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
385  pm_integ.begin(), pm_integ.end(),
386  p_inp_sound_d.begin());
387  }
388  }
389 
390  int size (int itime) const
391  {
393  AMREX_ALWAYS_ASSERT(z_inp_sound[itime].size() == qv_inp_sound[itime].size());
394  AMREX_ALWAYS_ASSERT(z_inp_sound[itime].size() == U_inp_sound[itime].size());
395  AMREX_ALWAYS_ASSERT(z_inp_sound[itime].size() == V_inp_sound[itime].size());
396  return z_inp_sound[itime].size();
397  }
398 
399  // Members
400  int ntimes;
401 
402  amrex::Real tau_nudging = amrex::Real(5.0); // time scale used for nudging
403 
404  amrex::Vector<std::string> input_sounding_file = {};
405  amrex::Vector<amrex::Real> input_sounding_time = {};
408 
409  bool assume_dry{false};
410 
411  // - read from file
413 
414  // This is a vector (over time) of Vectors
415  amrex::Vector<amrex::Vector<amrex::Real>> z_inp_sound, theta_inp_sound, qv_inp_sound, U_inp_sound, V_inp_sound;
416 
417  // This is a vector (over time) of DeviceVectors
418  amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real>> z_inp_sound_d, theta_inp_sound_d, qv_inp_sound_d, U_inp_sound_d, V_inp_sound_d;
419 
420  // - moist profiles
421  amrex::Vector<amrex::Real> pm_integ; // from integrating up air column
422  // - dry profiles
423  amrex::Vector<amrex::Real> rhod_integ; // from integrating down air column
424  // - to set solution fields
425  amrex::Gpu::DeviceVector<amrex::Real> p_inp_sound_d, rho_inp_sound_d;
426 };
427 #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
constexpr amrex::Real p_0
Definition: ERF_Constants.H:61
constexpr amrex::Real RvoRd
Definition: ERF_Constants.H:56
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:64
constexpr amrex::Real R_d
Definition: ERF_Constants.H:47
constexpr amrex::Real RdoCp
Definition: ERF_Constants.H:54
constexpr amrex::Real Gamma
Definition: ERF_Constants.H:62
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getRhogivenThetaPress(const amrex::Real th, const amrex::Real p, const amrex::Real rdOcp, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:96
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenPandTh(const amrex::Real P, const amrex::Real th, const amrex::Real rdOcp)
Definition: ERF_EOS.H:32
const Real ztop
Definition: ERF_InitCustomPertVels_ParticleTests.H:4
ParmParse pp("prob")
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real interpolate_1d(const amrex::Real *alpha, const amrex::Real *beta, const amrex::Real alpha_interp, const int alpha_size)
Definition: ERF_Interpolation_1D.H:14
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void Newton_Raphson_hse(const Real &m_tol, const Real &RdoCp, const Real &dz, const Real &g, const Real &C, const Real &Th, const Real &T, const Real &qt, const Real &qv, Real &P, Real &rd, Real &F, const bool &maintain_Th)
Definition: ERF_HSEUtils.H:44
@ theta
Definition: ERF_MM5.H:20
@ qv
Definition: ERF_Kessler.H:30
@ U
Definition: ERF_IndexDefines.H:123
@ V
Definition: ERF_IndexDefines.H:124
@ dz
Definition: ERF_AdvanceWSM6.cpp:104
Definition: ERF_InputSoundingData.H:22
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > theta_inp_sound_d
Definition: ERF_InputSoundingData.H:418
amrex::Real press_ref_inp_sound
Definition: ERF_InputSoundingData.H:412
amrex::Gpu::DeviceVector< amrex::Real > p_inp_sound_d
Definition: ERF_InputSoundingData.H:425
void host_to_device(int itime)
Definition: ERF_InputSoundingData.H:351
amrex::Real theta_ref_inp_sound
Definition: ERF_InputSoundingData.H:412
amrex::Vector< amrex::Real > pm_integ
Definition: ERF_InputSoundingData.H:421
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > V_inp_sound_d
Definition: ERF_InputSoundingData.H:418
amrex::Vector< amrex::Real > rhod_integ
Definition: ERF_InputSoundingData.H:423
void resize_arrays()
Definition: ERF_InputSoundingData.H:60
int n_sounding_times
Definition: ERF_InputSoundingData.H:407
amrex::Vector< amrex::Real > input_sounding_time
Definition: ERF_InputSoundingData.H:405
int n_sounding_files
Definition: ERF_InputSoundingData.H:406
amrex::Real tau_nudging
Definition: ERF_InputSoundingData.H:402
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > qv_inp_sound_d
Definition: ERF_InputSoundingData.H:418
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > z_inp_sound_d
Definition: ERF_InputSoundingData.H:418
amrex::Vector< std::string > input_sounding_file
Definition: ERF_InputSoundingData.H:404
amrex::Gpu::DeviceVector< amrex::Real > rho_inp_sound_d
Definition: ERF_InputSoundingData.H:425
amrex::Vector< amrex::Vector< amrex::Real > > theta_inp_sound
Definition: ERF_InputSoundingData.H:415
void calc_rho_p(int itime)
Definition: ERF_InputSoundingData.H:178
void calc_rho_p_isentropic(int itime)
Definition: ERF_InputSoundingData.H:270
amrex::Real qv_ref_inp_sound
Definition: ERF_InputSoundingData.H:412
amrex::Vector< amrex::Vector< amrex::Real > > z_inp_sound
Definition: ERF_InputSoundingData.H:415
void read_from_file(const amrex::Geometry &geom, const amrex::Vector< amrex::Real > &zlevels_stag, int itime, bool is_moist)
Definition: ERF_InputSoundingData.H:77
amrex::Vector< amrex::Vector< amrex::Real > > qv_inp_sound
Definition: ERF_InputSoundingData.H:415
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > U_inp_sound_d
Definition: ERF_InputSoundingData.H:418
amrex::Vector< amrex::Vector< amrex::Real > > U_inp_sound
Definition: ERF_InputSoundingData.H:415
amrex::Vector< amrex::Vector< amrex::Real > > V_inp_sound
Definition: ERF_InputSoundingData.H:415
InputSoundingData()
Definition: ERF_InputSoundingData.H:24
bool assume_dry
Definition: ERF_InputSoundingData.H:409
int size(int itime) const
Definition: ERF_InputSoundingData.H:390
int ntimes
Definition: ERF_InputSoundingData.H:400