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  R_d/Cp_d,
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 rho_tot_hi, rho_tot_lo;
223  for (int k=1; k < size(itime); ++k)
224  {
225  // Vertical grid spacing
226  dz = z_inp_sound[itime][k] - z_inp_sound[itime][k-1];
227 
228  // Establish known constant
229  rho_tot_lo = rhod_integ[k-1] * (one + qv_inp_sound[itime][k-1]);
230  C = -pm_integ[k-1] + myhalf*rho_tot_lo*CONST_GRAV*dz;
231 
232  // Initial guess and residual
233  pm_integ[k] = pm_integ[k-1];
235  pm_integ[k],
236  R_d/Cp_d,
237  qv_inp_sound[itime][k]);
238  rho_tot_hi = rhod_integ[k] * (one + qv_inp_sound[itime][k]);
239  F = pm_integ[k] + myhalf*rho_tot_hi*CONST_GRAV*dz + C;
240 
241  // Do iterations
242  if (std::abs(F)>tol) HSEutils::Newton_Raphson_hse(tol, R_d/Cp_d, dz,
243  CONST_GRAV, C, theta_inp_sound[itime][k],
244  qv_inp_sound[itime][k], qv_inp_sound[itime][k],
245  pm_integ[k], rhod_integ[k], F);
246 #if 0 // Printing
247  amrex::Print() << z_inp_sound[itime][k]
248  << " " << pm_integ[k]
249  << " " << rhod_integ[k]
250  << " " << theta_inp_sound[itime][k]
251  << " " << qv_inp_sound[itime][k]
252  << " " << U_inp_sound[itime][k]
253  << " " << V_inp_sound[itime][k]
254  << std::endl;
255 #endif
256  }
257  // Note: at this point, the surface pressure, density of the dry air
258  // column is stored in pm_integ[0], rhod_integ[0]
259 
260  // update
261  host_to_device(itime);
262  }
263 
264  void calc_rho_p_isentropic (int itime)
265  {
266  /* Calculate density and pressure assuming isentropic (constant theta)
267  background conditions. This does not use Newton-Raphson iterations
268  to calculate rho and p.
269  */
270  const int Ninp = size(itime);
271  pm_integ.resize(Ninp);
272  rhod_integ.resize(Ninp);
273 
274  // evaluate surface quantities (k=0): total pressure and dry air
278  R_d/Cp_d,
280 
281  const amrex::Real th0 = theta_ref_inp_sound;
282  const amrex::Real p0_pow = std::pow(p_0, (Gamma-1)/Gamma);
283 
284  amrex::Print() << "isentropic sounding init: surface dry air density = "
285  << std::setprecision(15)
286  << rhod_integ[0] << " kg/m^3" << std::endl;
287 
288 #if 0 // Printing
289  // In this absence of moisture, this moist profile will match the
290  // following dry profile
291  amrex::Print() << "z p_m rho_d theta qv U V" << std::endl;
292  amrex::Print() << z_inp_sound[itime][0]
293  << " " << pm_integ[0]
294  << " " << rhod_integ[0]
295  << " " << theta_inp_sound[itime][0]
296  << " " << qv_inp_sound[itime][0]
297  << " " << U_inp_sound[itime][0]
298  << " " << V_inp_sound[itime][0]
299  << std::endl;
300 #endif
301 
302  // integrate from surface to domain top
303  amrex::Real dz;
304  for (int k=1; k < size(itime); ++k)
305  {
306  // Vertical grid spacing
307  dz = z_inp_sound[itime][k] - z_inp_sound[itime][k-1];
308 
309  // Isentropic pressure at next level
310  amrex::Real qvmean = (assume_dry) ? zero : myhalf*(qv_inp_sound[itime][k-1] + qv_inp_sound[itime][k]);
311  amrex::Real thm0 = th0 * (1 + R_v/R_d * qvmean);
312  amrex::Real plo_pow = std::pow(pm_integ[k-1], (Gamma-1)/Gamma);
313  pm_integ[k] = (Gamma-1)/Gamma *
314  (-CONST_GRAV * p0_pow / (R_d * thm0) * (1 + qvmean) * dz
315  + Gamma/(Gamma-1) * plo_pow);
316  pm_integ[k] = std::pow(pm_integ[k], Gamma / (Gamma-1));
317 
318  // Note: The pressure calculated here is copied to `p_inp_sound_d`
319  // and currently not used.
320 
321  // Get corresponding dry air density
323  pm_integ[k],
324  R_d/Cp_d,
325  qv_inp_sound[itime][k]);
326 
327 #if 0 // Printing
328  amrex::Print() << z_inp_sound[itime][k]
329  << " " << pm_integ[k]
330  << " " << rhod_integ[k]
331  << " " << theta_inp_sound[itime][k]
332  << " " << qv_inp_sound[itime][k]
333  << " " << U_inp_sound[itime][k]
334  << " " << V_inp_sound[itime][k]
335  << std::endl;
336 #endif
337  }
338  // Note: at this point, the surface pressure, density of the dry air
339  // column is stored in pm_integ[0], rhod_integ[0]
340 
341  // update
342  host_to_device(itime);
343  }
344 
345  void host_to_device (int itime)
346  {
347  const int Ninp = size(itime);
348  z_inp_sound_d[itime].resize(Ninp);
349  theta_inp_sound_d[itime].resize(Ninp);
350  qv_inp_sound_d[itime].resize(Ninp);
351  U_inp_sound_d[itime].resize(Ninp);
352  V_inp_sound_d[itime].resize(Ninp);
353 
354  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
355  z_inp_sound[itime].begin(), z_inp_sound[itime].end(),
356  z_inp_sound_d[itime].begin());
357  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
358  theta_inp_sound[itime].begin(), theta_inp_sound[itime].end(),
359  theta_inp_sound_d[itime].begin());
360  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
361  qv_inp_sound[itime].begin(), qv_inp_sound[itime].end(),
362  qv_inp_sound_d[itime].begin());
363  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
364  U_inp_sound[itime].begin(), U_inp_sound[itime].end(),
365  U_inp_sound_d[itime].begin());
366  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
367  V_inp_sound[itime].begin(), V_inp_sound[itime].end(),
368  V_inp_sound_d[itime].begin());
369 
370  if (rhod_integ.size() > 0)
371  {
372  //amrex::Print() << "Copying rho_d, p_d to device" << std::endl;
373  rho_inp_sound_d.resize(size(itime)+2);
374  p_inp_sound_d.resize(size(itime)+2);
375  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
376  rhod_integ.begin(), rhod_integ.end(),
377  rho_inp_sound_d.begin());
378  amrex::Gpu::copy(amrex::Gpu::hostToDevice,
379  pm_integ.begin(), pm_integ.end(),
380  p_inp_sound_d.begin());
381  }
382  }
383 
384  int size (int itime) const
385  {
387  AMREX_ALWAYS_ASSERT(z_inp_sound[itime].size() == qv_inp_sound[itime].size());
388  AMREX_ALWAYS_ASSERT(z_inp_sound[itime].size() == U_inp_sound[itime].size());
389  AMREX_ALWAYS_ASSERT(z_inp_sound[itime].size() == V_inp_sound[itime].size());
390  return z_inp_sound[itime].size();
391  }
392 
393  // Members
394  int ntimes;
395 
396  amrex::Real tau_nudging = amrex::Real(5.0); // time scale used for nudging
397 
398  amrex::Vector<std::string> input_sounding_file = {};
399  amrex::Vector<amrex::Real> input_sounding_time = {};
402 
403  bool assume_dry{false};
404 
405  // - read from file
407 
408  // This is a vector (over time) of Vectors
409  amrex::Vector<amrex::Vector<amrex::Real>> z_inp_sound, theta_inp_sound, qv_inp_sound, U_inp_sound, V_inp_sound;
410 
411  // This is a vector (over time) of DeviceVectors
412  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;
413 
414  // - moist profiles
415  amrex::Vector<amrex::Real> pm_integ; // from integrating up air column
416  // - dry profiles
417  amrex::Vector<amrex::Real> rhod_integ; // from integrating down air column
418  // - to set solution fields
419  amrex::Gpu::DeviceVector<amrex::Real> p_inp_sound_d, rho_inp_sound_d;
420 };
421 #endif
constexpr amrex::Real R_v
Definition: ERF_Constants.H:43
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:44
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:50
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:53
constexpr amrex::Real R_d
Definition: ERF_Constants.H:42
constexpr amrex::Real Gamma
Definition: ERF_Constants.H:51
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
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 &qt, const Real &qv, Real &P, Real &rd, Real &F)
Definition: ERF_HSEUtils.H:43
@ theta
Definition: ERF_MM5.H:20
@ qv
Definition: ERF_Kessler.H:29
@ 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:412
amrex::Real press_ref_inp_sound
Definition: ERF_InputSoundingData.H:406
amrex::Gpu::DeviceVector< amrex::Real > p_inp_sound_d
Definition: ERF_InputSoundingData.H:419
void host_to_device(int itime)
Definition: ERF_InputSoundingData.H:345
amrex::Real theta_ref_inp_sound
Definition: ERF_InputSoundingData.H:406
amrex::Vector< amrex::Real > pm_integ
Definition: ERF_InputSoundingData.H:415
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > V_inp_sound_d
Definition: ERF_InputSoundingData.H:412
amrex::Vector< amrex::Real > rhod_integ
Definition: ERF_InputSoundingData.H:417
void resize_arrays()
Definition: ERF_InputSoundingData.H:60
int n_sounding_times
Definition: ERF_InputSoundingData.H:401
amrex::Vector< amrex::Real > input_sounding_time
Definition: ERF_InputSoundingData.H:399
int n_sounding_files
Definition: ERF_InputSoundingData.H:400
amrex::Real tau_nudging
Definition: ERF_InputSoundingData.H:396
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > qv_inp_sound_d
Definition: ERF_InputSoundingData.H:412
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > z_inp_sound_d
Definition: ERF_InputSoundingData.H:412
amrex::Vector< std::string > input_sounding_file
Definition: ERF_InputSoundingData.H:398
amrex::Gpu::DeviceVector< amrex::Real > rho_inp_sound_d
Definition: ERF_InputSoundingData.H:419
amrex::Vector< amrex::Vector< amrex::Real > > theta_inp_sound
Definition: ERF_InputSoundingData.H:409
void calc_rho_p(int itime)
Definition: ERF_InputSoundingData.H:178
void calc_rho_p_isentropic(int itime)
Definition: ERF_InputSoundingData.H:264
amrex::Real qv_ref_inp_sound
Definition: ERF_InputSoundingData.H:406
amrex::Vector< amrex::Vector< amrex::Real > > z_inp_sound
Definition: ERF_InputSoundingData.H:409
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:409
amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real > > U_inp_sound_d
Definition: ERF_InputSoundingData.H:412
amrex::Vector< amrex::Vector< amrex::Real > > U_inp_sound
Definition: ERF_InputSoundingData.H:409
amrex::Vector< amrex::Vector< amrex::Real > > V_inp_sound
Definition: ERF_InputSoundingData.H:409
InputSoundingData()
Definition: ERF_InputSoundingData.H:24
bool assume_dry
Definition: ERF_InputSoundingData.H:403
int size(int itime) const
Definition: ERF_InputSoundingData.H:384
int ntimes
Definition: ERF_InputSoundingData.H:394