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;
49  input_sounding_time[0] = 0.0;
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)
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 *= 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  if (z == zbot) {
130  AMREX_ALWAYS_ASSERT(theta == theta_inp_sound_tmp[0]);
131  AMREX_ALWAYS_ASSERT(qv*0.001 == qv_inp_sound_tmp[0]); // convert from g/kg to kg/kg
132  U_inp_sound_tmp[0] = U;
133  V_inp_sound_tmp[0] = V;
134  } else {
135  AMREX_ALWAYS_ASSERT(z > z_inp_sound_tmp[z_inp_sound_tmp.size()-1]); // sounding is increasing in height
136  z_inp_sound_tmp.push_back(z);
137  theta_inp_sound_tmp.push_back(theta);
138  qv_inp_sound_tmp.push_back(qv*0.001); // convert from g/kg to kg/kg
139  U_inp_sound_tmp.push_back(U);
140  V_inp_sound_tmp.push_back(V);
141  if (z >= ztop) break;
142  }
143  }
144 
145  // At this point, we have an input_sounding from zbot up to
146  // z_inp_sound_tmp[N-1] >= ztop. Now, interpolate to grid level 0 heights
147  const int Ninp = z_inp_sound_tmp.size();
148  z_inp_sound[itime][0] = zbot;
149  theta_inp_sound[itime][0] = theta_inp_sound_tmp[0];
150  qv_inp_sound[itime][0] = qv_inp_sound_tmp[0];
151  U_inp_sound[itime][0] = U_inp_sound_tmp[0];
152  V_inp_sound[itime][0] = V_inp_sound_tmp[0];
153  for (int k=0; k < Nz; ++k) {
154  z_inp_sound[itime][k+1] = 0.5 * (zlevels_stag[k] + zlevels_stag[k+1]);
155  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);
156  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);
157  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);
158  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);
159  }
160  z_inp_sound[itime][Nz+1] = ztop;
161  theta_inp_sound[itime][Nz+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), theta_inp_sound_tmp.dataPtr(), ztop, Ninp);
162  qv_inp_sound[itime][Nz+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), qv_inp_sound_tmp.dataPtr(), ztop, Ninp);
163  U_inp_sound[itime][Nz+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), U_inp_sound_tmp.dataPtr(), ztop, Ninp);
164  V_inp_sound[itime][Nz+1] = interpolate_1d(z_inp_sound_tmp.dataPtr(), V_inp_sound_tmp.dataPtr(), ztop, Ninp);
165  }
166 
167  amrex::Print() << "Successfully read the " << itime << "th input_sounding file..." << std::endl;
168  input_sounding_reader.close();
169 
170  host_to_device(itime);
171  }
172 
173  void calc_rho_p (int itime)
174  {
175  /* Calculate density and pressure, roughly following the procedure in
176  * WRF dyn_em/module_initialize_ideal.F. We integrate hydrostatically
177  * from the surface up through the air column to get the dry density
178  * and moist pressure.
179  */
180  const amrex::Real tol = 1.0e-12;
181  const int Ninp = size(itime);
182  pm_integ.resize(Ninp);
183  rhod_integ.resize(Ninp);
184 
185  // evaluate surface quantities (k=0): total pressure and dry air
189  R_d/Cp_d,
191 
192  amrex::Print() << "ideal sounding init: surface dry air density = "
193  << std::setprecision(15)
194  << rhod_integ[0] << " kg/m^3" << std::endl;
195 
196  // Note:
197  // p_dry = rho_d R_d T
198  // p_tot = rho_m R_d T_v
199  // = rho_d(1 + q_v) R_d T_v
200 
201 #if 0 // Printing
202  // In this absence of moisture, this moist profile will match the
203  // following dry profile
204  amrex::Print() << "z p_m rho_d theta qv U V" << std::endl;
205  amrex::Print() << z_inp_sound[itime][0]
206  << " " << pm_integ[0]
207  << " " << rhod_integ[0]
208  << " " << theta_inp_sound[itime][0]
209  << " " << qv_inp_sound[itime][0]
210  << " " << U_inp_sound[itime][0]
211  << " " << V_inp_sound[itime][0]
212  << std::endl;
213 #endif
214 
215  // integrate from surface to domain top
216  amrex::Real dz, F, C;
217  amrex::Real rho_tot_hi, rho_tot_lo;
218  for (int k=1; k < size(itime); ++k)
219  {
220  // Vertical grid spacing
221  dz = z_inp_sound[itime][k] - z_inp_sound[itime][k-1];
222 
223  // Establish known constant
224  rho_tot_lo = rhod_integ[k-1] * (1. + qv_inp_sound[itime][k-1]);
225  C = -pm_integ[k-1] + 0.5*rho_tot_lo*CONST_GRAV*dz;
226 
227  // Initial guess and residual
228  pm_integ[k] = pm_integ[k-1];
230  pm_integ[k],
231  R_d/Cp_d,
232  qv_inp_sound[itime][k]);
233  rho_tot_hi = rhod_integ[k] * (1. + qv_inp_sound[itime][k]);
234  F = pm_integ[k] + 0.5*rho_tot_hi*CONST_GRAV*dz + C;
235 
236  // Do iterations
237  if (std::abs(F)>tol) HSEutils::Newton_Raphson_hse(tol, R_d/Cp_d, dz,
238  CONST_GRAV, C, theta_inp_sound[itime][k],
239  qv_inp_sound[itime][k], qv_inp_sound[itime][k],
240  pm_integ[k], rhod_integ[k], F);
241 #if 0 // Printing
242  amrex::Print() << z_inp_sound[itime][k]
243  << " " << pm_integ[k]
244  << " " << rhod_integ[k]
245  << " " << theta_inp_sound[itime][k]
246  << " " << qv_inp_sound[itime][k]
247  << " " << U_inp_sound[itime][k]
248  << " " << V_inp_sound[itime][k]
249  << std::endl;
250 #endif
251  }
252  // Note: at this point, the surface pressure, density of the dry air
253  // column is stored in pm_integ[0], rhod_integ[0]
254 
255  // update
256  host_to_device(itime);
257  }
258 
259  void calc_rho_p_isentropic (int itime)
260  {
261  /* Calculate density and pressure assuming isentropic (constant theta)
262  background conditions. This does not use Newton-Raphson iterations
263  to calculate rho and p.
264  */
265  const int Ninp = size(itime);
266  pm_integ.resize(Ninp);
267  rhod_integ.resize(Ninp);
268 
269  // evaluate surface quantities (k=0): total pressure and dry air
273  R_d/Cp_d,
275 
276  const amrex::Real th0 = theta_ref_inp_sound;
277  const amrex::Real p0_pow = std::pow(p_0, (Gamma-1)/Gamma);
278 
279  amrex::Print() << "isentropic sounding init: surface dry air density = "
280  << std::setprecision(15)
281  << rhod_integ[0] << " kg/m^3" << std::endl;
282 
283 #if 0 // Printing
284  // In this absence of moisture, this moist profile will match the
285  // following dry profile
286  amrex::Print() << "z p_m rho_d theta qv U V" << std::endl;
287  amrex::Print() << z_inp_sound[itime][0]
288  << " " << pm_integ[0]
289  << " " << rhod_integ[0]
290  << " " << theta_inp_sound[itime][0]
291  << " " << qv_inp_sound[itime][0]
292  << " " << U_inp_sound[itime][0]
293  << " " << V_inp_sound[itime][0]
294  << std::endl;
295 #endif
296 
297  // integrate from surface to domain top
298  amrex::Real dz;
299  for (int k=1; k < size(itime); ++k)
300  {
301  // Vertical grid spacing
302  dz = z_inp_sound[itime][k] - z_inp_sound[itime][k-1];
303 
304  // Isentropic pressure at next level
305  amrex::Real qvmean = (assume_dry) ? 0.0 : 0.5*(qv_inp_sound[itime][k-1] + qv_inp_sound[itime][k]);
306  amrex::Real thm0 = th0 * (1 + R_v/R_d * qvmean);
307  amrex::Real plo_pow = std::pow(pm_integ[k-1], (Gamma-1)/Gamma);
308  pm_integ[k] = (Gamma-1)/Gamma *
309  (-CONST_GRAV * p0_pow / (R_d * thm0) * (1 + qvmean) * dz
310  + Gamma/(Gamma-1) * plo_pow);
311  pm_integ[k] = std::pow(pm_integ[k], Gamma / (Gamma-1));
312 
313  // Note: The pressure calculated here is copied to `p_inp_sound_d`
314  // and currently not used.
315 
316  // Get corresponding dry air density
318  pm_integ[k],
319  R_d/Cp_d,
320  qv_inp_sound[itime][k]);
321 
322 #if 0 // Printing
323  amrex::Print() << z_inp_sound[itime][k]
324  << " " << pm_integ[k]
325  << " " << rhod_integ[k]
326  << " " << theta_inp_sound[itime][k]
327  << " " << qv_inp_sound[itime][k]
328  << " " << U_inp_sound[itime][k]
329  << " " << V_inp_sound[itime][k]
330  << std::endl;
331 #endif
332  }
333  // Note: at this point, the surface pressure, density of the dry air
334  // column is stored in pm_integ[0], rhod_integ[0]
335 
336  // update
337  host_to_device(itime);
338  }
339 
340  void host_to_device (int itime)
341  {
342  const int Ninp = size(itime);
343  z_inp_sound_d[itime].resize(Ninp);
344  theta_inp_sound_d[itime].resize(Ninp);
345  qv_inp_sound_d[itime].resize(Ninp);
346  U_inp_sound_d[itime].resize(Ninp);
347  V_inp_sound_d[itime].resize(Ninp);
348 
349  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
350  z_inp_sound[itime].begin(), z_inp_sound[itime].end(),
351  z_inp_sound_d[itime].begin());
352  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
353  theta_inp_sound[itime].begin(), theta_inp_sound[itime].end(),
354  theta_inp_sound_d[itime].begin());
355  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
356  qv_inp_sound[itime].begin(), qv_inp_sound[itime].end(),
357  qv_inp_sound_d[itime].begin());
358  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
359  U_inp_sound[itime].begin(), U_inp_sound[itime].end(),
360  U_inp_sound_d[itime].begin());
361  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
362  V_inp_sound[itime].begin(), V_inp_sound[itime].end(),
363  V_inp_sound_d[itime].begin());
364 
365  if (rhod_integ.size() > 0)
366  {
367  //amrex::Print() << "Copying rho_d, p_d to device" << std::endl;
368  rho_inp_sound_d.resize(size(itime)+2);
369  p_inp_sound_d.resize(size(itime)+2);
370  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
371  rhod_integ.begin(), rhod_integ.end(),
372  rho_inp_sound_d.begin());
373  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
374  pm_integ.begin(), pm_integ.end(),
375  p_inp_sound_d.begin());
376  }
377  }
378 
379  int size (int itime) const
380  {
381  AMREX_ALWAYS_ASSERT(z_inp_sound[itime].size() == theta_inp_sound[itime].size());
382  AMREX_ALWAYS_ASSERT(z_inp_sound[itime].size() == qv_inp_sound[itime].size());
383  AMREX_ALWAYS_ASSERT(z_inp_sound[itime].size() == U_inp_sound[itime].size());
384  AMREX_ALWAYS_ASSERT(z_inp_sound[itime].size() == V_inp_sound[itime].size());
385  return z_inp_sound[itime].size();
386  }
387 
388  // Members
389  int ntimes;
390 
391  amrex::Real tau_nudging = 5.0; // time scale used for nudging
392 
393  amrex::Vector<std::string> input_sounding_file = {};
394  amrex::Vector<amrex::Real> input_sounding_time = {};
397 
398  bool assume_dry{false};
399 
400  // - read from file
402 
403  // This is a vector (over time) of Vectors
404  amrex::Vector<amrex::Vector<amrex::Real>> z_inp_sound, theta_inp_sound, qv_inp_sound, U_inp_sound, V_inp_sound;
405 
406  // This is a vector (over time) of DeviceVectors
407  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;
408 
409  // - moist profiles
410  amrex::Vector<amrex::Real> pm_integ; // from integrating up air column
411  // - dry profiles
412  amrex::Vector<amrex::Real> rhod_integ; // from integrating down air column
413  // - to set solution fields
414  amrex::Gpu::DeviceVector<amrex::Real> p_inp_sound_d, rho_inp_sound_d;
415 };
416 #endif
constexpr amrex::Real R_v
Definition: ERF_Constants.H:11
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:12
constexpr amrex::Real p_0
Definition: ERF_Constants.H:18
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:21
constexpr amrex::Real R_d
Definition: ERF_Constants.H:10
constexpr amrex::Real Gamma
Definition: ERF_Constants.H:19
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=0.0)
Definition: ERF_EOS.H:96
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:12
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real pp(amrex::Real y)
Definition: ERF_MicrophysicsUtils.H:233
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 &T, const Real &qt, const Real &qv, Real &P, Real &rd, Real &F)
Definition: ERF_HSEUtils.H:35
@ theta
Definition: ERF_MM5.H:20
@ qv
Definition: ERF_Kessler.H:28
@ U
Definition: ERF_IndexDefines.H:108
@ V
Definition: ERF_IndexDefines.H:109
Definition: ERF_InputSoundingData.H:22
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > theta_inp_sound_d
Definition: ERF_InputSoundingData.H:407
amrex::Real press_ref_inp_sound
Definition: ERF_InputSoundingData.H:401
amrex::Gpu::DeviceVector< amrex::Real > p_inp_sound_d
Definition: ERF_InputSoundingData.H:414
void host_to_device(int itime)
Definition: ERF_InputSoundingData.H:340
amrex::Real theta_ref_inp_sound
Definition: ERF_InputSoundingData.H:401
amrex::Vector< amrex::Real > pm_integ
Definition: ERF_InputSoundingData.H:410
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > V_inp_sound_d
Definition: ERF_InputSoundingData.H:407
amrex::Vector< amrex::Real > rhod_integ
Definition: ERF_InputSoundingData.H:412
void resize_arrays()
Definition: ERF_InputSoundingData.H:60
int n_sounding_times
Definition: ERF_InputSoundingData.H:396
amrex::Vector< amrex::Real > input_sounding_time
Definition: ERF_InputSoundingData.H:394
int n_sounding_files
Definition: ERF_InputSoundingData.H:395
amrex::Real tau_nudging
Definition: ERF_InputSoundingData.H:391
void read_from_file(const amrex::Geometry &geom, const amrex::Vector< amrex::Real > &zlevels_stag, int itime)
Definition: ERF_InputSoundingData.H:77
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > qv_inp_sound_d
Definition: ERF_InputSoundingData.H:407
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > z_inp_sound_d
Definition: ERF_InputSoundingData.H:407
amrex::Vector< std::string > input_sounding_file
Definition: ERF_InputSoundingData.H:393
amrex::Gpu::DeviceVector< amrex::Real > rho_inp_sound_d
Definition: ERF_InputSoundingData.H:414
amrex::Vector< amrex::Vector< amrex::Real > > theta_inp_sound
Definition: ERF_InputSoundingData.H:404
void calc_rho_p(int itime)
Definition: ERF_InputSoundingData.H:173
void calc_rho_p_isentropic(int itime)
Definition: ERF_InputSoundingData.H:259
amrex::Real qv_ref_inp_sound
Definition: ERF_InputSoundingData.H:401
amrex::Vector< amrex::Vector< amrex::Real > > z_inp_sound
Definition: ERF_InputSoundingData.H:404
amrex::Vector< amrex::Vector< amrex::Real > > qv_inp_sound
Definition: ERF_InputSoundingData.H:404
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > U_inp_sound_d
Definition: ERF_InputSoundingData.H:407
amrex::Vector< amrex::Vector< amrex::Real > > U_inp_sound
Definition: ERF_InputSoundingData.H:404
amrex::Vector< amrex::Vector< amrex::Real > > V_inp_sound
Definition: ERF_InputSoundingData.H:404
InputSoundingData()
Definition: ERF_InputSoundingData.H:24
bool assume_dry
Definition: ERF_InputSoundingData.H:398
int size(int itime) const
Definition: ERF_InputSoundingData.H:379
int ntimes
Definition: ERF_InputSoundingData.H:389