ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_HSEUtils.H
Go to the documentation of this file.
1 #ifndef ERF_HSEUTIL_H_
2 #define ERF_HSEUTIL_H_
3 
4 #include <AMReX_Math.H>
5 
7 #include "ERF_Constants.H"
8 #include "ERF_EOS.H"
9 
10 /**
11  * Utility functions for calculating a hydrostatic equilibrium (HSE) base state
12 */
13 
14 namespace HSEutils
15 {
16  using namespace amrex;
17 
18  // to control Newton iterations in init_isentropic_hse*
19  const int MAX_ITER = 10;
20 #ifdef AMREX_USE_FLOAT
21  const amrex::Real TOL = Real(1.e-4);
22 #else
23  const amrex::Real TOL = Real(1.e-8);
24 #endif
25 
26  /**
27  * Function to calculate the hydrostatic density and pressure
28  * from Newton-Raphson iterations at constant Th & Qv
29  *
30  * @param[in] m_tol iteration tolerance
31  * @param[in] RdOCp Rd/Cp
32  * @param[out] dz change in vertical height
33  * @param[out] g magnitude of gravity
34  * @param[in] C sum of known terms in HSE balance
35  * @param[in] Th theta at the current cell center
36  * @param[in] qt total moisture (non-precip and precip)
37  * @param[in] qv water vapor
38  * @param[in] P pressure at cell center
39  * @param[in] rd dry density at cell center
40  * @param[in] F starting residual of non-linear eq
41  * @param[in] maintain_Th maintain theta or temperature?
42  */
43  AMREX_GPU_HOST_DEVICE
44  AMREX_FORCE_INLINE
45  void
46  Newton_Raphson_hse (const Real& m_tol,
47  const Real& RdoCp,
48  const Real& dz,
49  const Real& g,
50  const Real& C,
51  const Real& Th,
52  const Real& T,
53  const Real& qt,
54  const Real& qv,
55  Real& P,
56  Real& rd,
57  Real& F,
58  const bool& maintain_Th)
59  {
60  // A non-finite residual (or a non-positive initial guess) would make the
61  // convergence test below evaluate to false on the very first pass, so we
62  // would silently return the initial guess and leave, e.g., an all-zero
63  // base state. Trap that here rather than propagating it downstream.
64  if (!(P > amrex::Real(0)) || !(rd > amrex::Real(0)) || !amrex::Math::isfinite(F)) {
65  AMREX_DEVICE_PRINTF("ERROR: HSE Newton started from an invalid state: P = %e, rd = %e, F = %e\n",
66  double(P), double(rd), double(F));
67  AMREX_DEVICE_PRINTF(" Check the surface pressure/temperature used to seed the integration%s\n", "");
68  }
69  AMREX_ALWAYS_ASSERT(P > amrex::Real(0) && rd > amrex::Real(0) && amrex::Math::isfinite(F));
70 
71  int iter=0;
72  int max_iter=20;
73  // Written as !(|F| <= tol) so that a NaN residual enters the loop instead
74  // of being mistaken for convergence
75  while (!(std::abs(F)<=m_tol) && iter<max_iter) {
76  // Compute change in pressure
77  Real dRdP = (maintain_Th) ? iGamma * rd / P : rd / P;
78  Real dFdp = one + myhalf*dRdP*g*dz;
79  P -= F/dFdp;
80  if (P < Real(1.e3)) amrex::Warning("P < 1000 [Pa]; Domain height may be too large...");
82 
83  // Diagnose density and residual
84  rd = (maintain_Th) ? getRhogivenThetaPress(Th, P, RdoCp, qv) :
87  Real r_tot = rd * (one + qt);
88  F = P + myhalf*r_tot*g*dz + C;
89  ++iter;
90  }
91  if (iter>=max_iter) {
92  AMREX_DEVICE_PRINTF("WARNING: HSE Newton iterations did not converge to tolerance!\n%s", "");
93  AMREX_DEVICE_PRINTF("HSE Newton tol: %e %e\n",F,m_tol);
94  }
95  }
96 
97  /**
98  * Function to calculate the hydrostatic density and pressure with constant dz
99  *
100  * @param[in] r_sfc surface density
101  * @param[in] theta surface potential temperature
102  * @param[out] r hydrostatically balanced density profile
103  * @param[out] p hydrostatically balanced pressure profile
104  * @param[in] dz vertical grid spacing (constant)
105  * @param[in] z_bot absolute height of the bottom of the domain
106  * @param[in] klo z-index corresponding to the small end of the domain
107  * @param[in] khi z-index corresponding to the big end of the domain
108  */
109  AMREX_GPU_HOST_DEVICE
110  AMREX_FORCE_INLINE
111  void
113  const amrex::Real& theta,
114  amrex::Real* r,
115  amrex::Real* p,
116  const amrex::Real& dz,
117  const amrex::Real& z_bot,
118  const int klo, const int khi)
119  {
120  int kstart;
121 
122  // r_sfc is the initial guess for the density at klo; p_0 is the pressure at z = 0
123 
124  // Absolute height of the center of cell klo
125  const Real hz = z_bot + myhalf*dz;
126 
127  // Initial guess
128  r[klo] = r_sfc;
129  p[klo] = p_0 - hz * r[klo] * CONST_GRAV;
130 
131  if (klo == 0)
132  {
133  kstart = 1;
134 
135  // We do a Newton iteration to satisfy the EOS & HSE (with constant theta)
136  bool converged_hse = false;
137  Real p_hse;
138  Real p_eos;
139 
140  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
141  {
142  p_hse = p_0 - hz * r[klo] * CONST_GRAV;
143  p_eos = getPgivenRTh(r[klo]*theta);
144 
145  Real A = p_hse - p_eos;
146 
148 
149  Real drho = A / (dpdr + hz * CONST_GRAV);
150 
151  r[klo] = r[klo] + drho;
152  p[klo] = getPgivenRTh(r[klo]*theta);
153 
154  if (std::abs(drho) < TOL)
155  {
156  converged_hse = true;
157  break;
158  }
159  }
160 
161  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << klo << std::endl;
162  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
163  } else {
164  kstart = klo; // because we need to solve for r[klo] here; r[klo-1] is what was passed in r_sfc
165  r[klo-1] = r_sfc; // r_sfc is really the interpolated density in the ghost cell in this case
166  p[klo-1] = getPgivenRTh(r[klo-1]*theta);
167  }
168 
169  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
170  for (int k = kstart; k <= khi; k++)
171  {
172  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
173  // to discretely satisfy HSE -- here we assume spatial_order = 2 -- we can generalize this later if needed
174  bool converged_hse = false;
175 
176  r[k] = r[k-1];
177 
178  Real p_eos = getPgivenRTh(r[k]*theta);
179  Real p_hse;
180 
181  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
182  {
183  Real r_avg = myhalf * (r[k-1]+r[k]);
184  p_hse = p[k-1] - dz * r_avg * CONST_GRAV;
185  p_eos = getPgivenRTh(r[k]*theta);
186 
187  Real A = p_hse - p_eos;
188 
189  Real dpdr = getdPdRgivenConstantTheta(r[k],theta);
190  // Gamma * p_0 * std::pow( (R_d * theta / p_0), Gamma) * std::pow(r[k], Gamma-one) ;
191 
192  Real drho = A / (dpdr + dz * CONST_GRAV);
193 
194  r[k] = r[k] + drho;
195  p[k] = getPgivenRTh(r[k]*theta);
196 
197  if (std::abs(drho) < TOL * r[k-1])
198  {
199  converged_hse = true;
200  // amrex::Print() << " converged " << std::endl;
201  break;
202  }
203  }
204 
205  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k << std::endl;
206  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
207  }
208  r[khi+1] = r[khi];
209  }
210 
211  /**
212  * Function to calculate the hydrostatic density and pressure with stretched dz
213  *
214  * @param[in] r_sfc surface density
215  * @param[in] theta surface potential temperature
216  * @param[out] r hydrostatically balanced density profile
217  * @param[out] p hydrostatically balanced pressure profile
218  * @param[in] stretched_dz vertical grid spacing (stretched)
219  * @param[in] z_bot absolute height of the bottom of the domain
220  * @param[in] klo z-index corresponding to the small end of the domain
221  * @param[in] khi z-index corresponding to the big end of the domain
222  */
223  AMREX_GPU_HOST_DEVICE
224  AMREX_FORCE_INLINE
225  void
227  const amrex::Real& theta,
228  amrex::Real* r,
229  amrex::Real* p,
230  const amrex::Real* stretched_dz,
231  const amrex::Real& z_bot,
232  const int klo, const int khi)
233  {
234  int kstart;
235 
236  // r_sfc is the initial guess for the density at klo; p_0 is the pressure at z = 0
237 
238  // Absolute height of the center of cell klo
239  const Real hz = z_bot + myhalf*stretched_dz[klo];
240 
241  // Initial guess
242  r[klo] = r_sfc;
243  p[klo] = p_0 - hz * r[klo] * CONST_GRAV;
244 
245  if (klo == 0)
246  {
247  kstart = 1;
248 
249  // We do a Newton iteration to satisfy the EOS & HSE (with constant theta)
250  bool converged_hse = false;
251  Real p_hse;
252  Real p_eos;
253 
254  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
255  {
256  p_hse = p_0 - hz * r[klo] * CONST_GRAV;
257  p_eos = getPgivenRTh(r[klo]*theta);
258 
259  Real A = p_hse - p_eos;
260 
262 
263  Real drho = A / (dpdr + hz * CONST_GRAV);
264 
265  r[klo] = r[klo] + drho;
266  p[klo] = getPgivenRTh(r[klo]*theta);
267 
268  if (std::abs(drho) < TOL)
269  {
270  converged_hse = true;
271  break;
272  }
273  }
274 
275  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << klo << std::endl;
276  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
277  } else {
278  kstart = klo; // because we need to solve for r[klo] here; r[klo-1] is what was passed in r_sfc
279  r[klo-1] = r_sfc; // r_sfc is really the interpolated density in the ghost cell in this case
280  p[klo-1] = getPgivenRTh(r[klo-1]*theta);
281  }
282 
283  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
284  for (int k = kstart; k <= khi; k++)
285  {
286  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
287  // to discretely satisfy HSE -- here we assume spatial_order = 2 -- we can generalize this later if needed
288  bool converged_hse = false;
289 
290  r[k] = r[k-1];
291 
292  Real p_eos = getPgivenRTh(r[k]*theta);
293  Real p_hse;
294 
295  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
296  {
297  Real r_avg = myhalf * (r[k-1]+r[k]);
298  Real dz_avg = myhalf * (stretched_dz[k-1] + stretched_dz[k]);
299  p_hse = p[k-1] - dz_avg * r_avg * CONST_GRAV;
300  p_eos = getPgivenRTh(r[k]*theta);
301 
302  Real A = p_hse - p_eos;
303 
304  Real dpdr = getdPdRgivenConstantTheta(r[k],theta);
305  // Gamma * p_0 * std::pow( (R_d * theta / p_0), Gamma) * std::pow(r[k], Gamma-one) ;
306 
307  Real drho = A / (dpdr + dz_avg * CONST_GRAV);
308 
309  r[k] = r[k] + drho;
310  p[k] = getPgivenRTh(r[k]*theta);
311 
312  if (std::abs(drho) < TOL * r[k-1])
313  {
314  converged_hse = true;
315  // amrex::Print() << " converged " << std::endl;
316  break;
317  }
318  }
319 
320  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k << std::endl;
321  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
322  }
323  r[khi+1] = r[khi];
324  }
325 
326 
327  /**
328  * Function to calculate the hydrostatic density and pressure over terrain
329  *
330  * @param[in] i x-index
331  * @param[in] j y-index
332  * @param[in] r_sfc surface density
333  * @param[in] theta surface potential temperature
334  * @param[out] r hydrostatically balanced density profile
335  * @param[out] p hydrostatically balanced pressure profile
336  * @param[in] z_cc cell-center heights
337  * @param[in] khi z-index corresponding to the big end of the domain
338  */
339  AMREX_GPU_HOST_DEVICE
340  AMREX_FORCE_INLINE
341  void
343  int j,
344  const amrex::Real& r_sfc,
345  const amrex::Real& theta,
346  amrex::Real* r,
347  amrex::Real* p,
348  const amrex::Array4<amrex::Real const> z_cc,
349  const int& klo, const int& khi)
350  {
351  int kstart;
352 
353  if (klo == 0) {
354  //
355  // r_sfc is the initial guess for the density in the bottom cell; p_0 is the
356  // pressure at z = 0, so the bottom cell's pressure comes from its absolute height
357  //
358  // Initial guess
359  int k0 = 0;
360 
361  // Where we start the lower iteration
362  kstart = 1;
363 
364  // Absolute height of the center of the bottom cell (not its height above the ground)
365  Real myhalf_dz = z_cc(i,j,k0);
366  r[k0] = r_sfc;
367  p[k0] = p_0 - myhalf_dz * r[k0] * CONST_GRAV;
368  {
369  // We do a Newton iteration to satisfy the EOS & HSE (with constant theta)
370  bool converged_hse = false;
371  Real p_hse;
372  Real p_eos;
373 
374  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
375  {
376  p_hse = p_0 - myhalf_dz * r[k0] * CONST_GRAV;
377  p_eos = getPgivenRTh(r[k0]*theta);
378 
379  Real A = p_hse - p_eos;
380 
381  Real dpdr = getdPdRgivenConstantTheta(r[k0],theta);
382 
383  Real drho = A / (dpdr + myhalf_dz * CONST_GRAV);
384 
385  r[k0] = r[k0] + drho;
386  p[k0] = getPgivenRTh(r[k0]*theta);
387 
388  if (std::abs(drho) < TOL)
389  {
390  converged_hse = true;
391  break;
392  }
393  }
394 
395  //if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k0 << std::endl;
396  //if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
397  }
398  } else {
399  kstart = klo; // because we need to solve for r[klo] here; r[klo-1] is what was passed in r_sfc
400  r[klo-1] = r_sfc; // r_sfc is really the interpolated density in the ghost cell in this case
401  p[klo-1] = getPgivenRTh(r[klo-1]*theta);
402  }
403 
404  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
405  for (int k = kstart; k <= khi; k++)
406  {
407  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
408  // to discretely satisfy HSE -- here we assume spatial_order = 2 -- we can generalize this later if needed
409  bool converged_hse = false;
410 
411  Real dz_loc = (z_cc(i,j,k) - z_cc(i,j,k-1));
412 
413  r[k] = r[k-1];
414 
415  Real p_eos = getPgivenRTh(r[k]*theta);
416  Real p_hse;
417 
418  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
419  {
420  p_hse = p[k-1] - dz_loc * myhalf * (r[k-1]+r[k]) * CONST_GRAV;
421  p_eos = getPgivenRTh(r[k]*theta);
422 
423  Real A = p_hse - p_eos;
424 
425  Real dpdr = getdPdRgivenConstantTheta(r[k],theta);
426 
427  Real drho = A / (dpdr + dz_loc * CONST_GRAV);
428 
429  r[k] = r[k] + drho;
430  p[k] = getPgivenRTh(r[k]*theta);
431 
432  if (std::abs(drho) < TOL * r[k-1])
433  {
434  converged_hse = true;
435  //amrex::Print() << " converged " << std::endl;
436  break;
437  }
438  }
439 
440  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k << std::endl;
441  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
442  }
443  }
444 
445 AMREX_FORCE_INLINE
446 AMREX_GPU_HOST_DEVICE
448 {
449  Real p_s = erf_esatw(T_b,use_empirical);
450  return p_s * Real(100.0);
451 }
452 
453 AMREX_FORCE_INLINE
454 AMREX_GPU_HOST_DEVICE
455 Real compute_relative_humidity (const Real p_b, const Real T_b, const bool use_empirical,
456  const int which_zone, const Real scaled_height)
457 {
458  if (which_zone > 0) {
459  if (which_zone == 1) { // z <= height
461  Real q_s = RdoRv*p_s/(p_b - p_s);
462  return Real(0.014)/q_s;
463  } else if (which_zone == 2) { // z > height and z <= z_tr; scaled_height = z/z_tr
464  return one - Real(0.75)*std::pow(scaled_height,Real(1.25));
465  } else { // z > z_tr
466  return fourth;
467  }
468  } else {
469  return one;
470  }
471 }
472 
473 AMREX_FORCE_INLINE
474 AMREX_GPU_HOST_DEVICE
475 Real vapor_mixing_ratio (const Real p_b, const Real T_b, const Real RH, const bool use_empirical,
476  int which_zone)
477 {
479  Real p_v = compute_vapor_pressure(p_s, RH);
480  Real q_v = RdoRv*p_v/(p_b - p_v);
481 
482  if (which_zone == 1) { // z <= height
483  return Real(0.014);
484  } else {
485  return q_v;
486  }
487 }
488 
489 AMREX_FORCE_INLINE
490 AMREX_GPU_HOST_DEVICE
491 Real compute_F_for_temp_in_zone (const Real T_b, const Real p_b, const Real q_t, const Real eq_pot_temp,
492  const bool use_empirical, const int which_zone, const Real scaled_height)
493 {
494  Real fac = Cp_d + Cp_l*q_t;
495  Real RH = compute_relative_humidity(p_b, T_b, use_empirical, which_zone, scaled_height);
496  Real q_v = vapor_mixing_ratio(p_b, T_b, RH, use_empirical, which_zone);
498  Real p_v = compute_vapor_pressure(p_s, RH);
499  // Relative-humidity factor H^(-r_v R_v / fac) of Emanuel (1994, eq. 4.5.11);
500  // exactly 1 in saturated zones (RH = 1)
501  Real H = amrex::max(Real(1.e-10), amrex::min(Real(1.0), RH));
502  return eq_pot_temp - T_b*std::pow((p_b - p_v)/p_0, -R_d/fac)
503  *std::exp(-(q_v*R_v/fac)*std::log(H))
504  *std::exp(L_v*q_v/(fac*T_b));
505 }
506 
507 AMREX_FORCE_INLINE
508 AMREX_GPU_HOST_DEVICE
509 Real compute_temperature (const Real p_b, const Real q_t, const Real eq_pot_temp, const bool use_empirical,
510  const int which_zone, const Real scaled_height)
511 {
512  Real T_b = Real(200.0), delta_T; // Initial guess
513 
514 #ifdef AMREX_USE_FLOAT
515  Real eps = Real(1.e-4) * T_b;
516 #else
517  Real eps = Real(1.e-10) * T_b;
518 #endif
519  for (int iter=0; iter<20; iter++)
520  {
521  Real F = compute_F_for_temp_in_zone(T_b , p_b, q_t, eq_pot_temp, use_empirical, which_zone, scaled_height);
522  Real F_plus_dF = compute_F_for_temp_in_zone(T_b+eps, p_b, q_t, eq_pot_temp, use_empirical, which_zone, scaled_height);
523  Real F_prime = (F_plus_dF - F)/eps;
524  delta_T = -F/F_prime;
525  T_b = T_b + delta_T;
526  }
527 
528  if (std::fabs(delta_T) > TOL * T_b) {
529  amrex::Abort("Newton Raphson for temperature could not converge");
530  }
531 
532  return T_b;
533 }
534 
535 AMREX_FORCE_INLINE
536 AMREX_GPU_HOST_DEVICE
538 {
539  Real T_dp, gamma, T;
540  T = T_b - Real(273.15);
541 
542  Real b = Real(18.678), c = Real(257.14), d = Real(234.5);
543  gamma = std::log(RH*std::exp((b - T/d)*T/(c + T)));
544 
545  T_dp = c*gamma/(b - gamma);
546 
547  return T_dp;
548 }
549 
550 AMREX_FORCE_INLINE
551 AMREX_GPU_HOST_DEVICE
552 Real compute_theta (const Real scaled_height, const Real theta_0,
553  const Real theta_tr, const Real z_tr, const Real T_tr)
554 {
555  if(scaled_height <= one) {
556  return theta_0 + (theta_tr - theta_0)*std::pow(scaled_height,Real(1.25));
557  } else {
558  return theta_tr*std::exp(CONST_GRAV/(Cp_d*T_tr)*z_tr*(scaled_height - one));
559  }
560 }
561 
562 AMREX_FORCE_INLINE
563 AMREX_GPU_HOST_DEVICE
564 void compute_rho (const Real& pressure, Real& theta, Real& rho, Real& q_v, Real& T_dp, Real& T_b,
565  const Real q_t, const Real eq_pot_temp, const bool use_empirical, const int which_zone,
566  const Real scaled_height, const bool T_from_theta,
567  const Real theta_0, const Real theta_tr, const Real z_tr, const Real T_tr)
568 {
569 
570  if (T_from_theta) {
571  theta = compute_theta(scaled_height, theta_0, theta_tr, z_tr, T_tr);
572  T_b = getTgivenPandTh(pressure, theta, RdoCp);
573  } else {
574  T_b = compute_temperature(pressure, q_t, eq_pot_temp, use_empirical, which_zone, scaled_height);
575  theta = getThgivenTandP(T_b, pressure, RdoCp);
576  }
577 
578  Real RH = compute_relative_humidity(pressure, T_b, use_empirical, which_zone, scaled_height);
579 
580  q_v = vapor_mixing_ratio(pressure, T_b, RH, use_empirical, which_zone);
581 
582  rho = getRhogivenTandPress(T_b, pressure, q_v);
583 
584  if (T_from_theta) {
585  rho *= (one + q_v);
586  } else {
587  rho *= (one + q_t);
588  }
589 
590  T_dp = compute_dewpoint_temperature(T_b, RH);
591 }
592 
593 AMREX_FORCE_INLINE
594 AMREX_GPU_HOST_DEVICE
595 Real compute_F (const Real& p_k, const Real& p_k_minus_1, Real &theta_k, Real& rho_k, Real& q_v_k,
596  Real& T_dp, Real& T_b, const Real& dz, const Real& rho_k_minus_1, const Real q_t,
597  const Real eq_pot_temp, const bool use_empirical,
598  const int which_zone, const Real scaled_height, const bool T_from_theta,
599  const Real theta_0, const Real theta_tr, const Real z_tr, const Real T_tr)
600 {
601  Real F;
602 
603  compute_rho(p_k, theta_k, rho_k, q_v_k, T_dp, T_b, q_t, eq_pot_temp, use_empirical, which_zone, scaled_height,
604  T_from_theta, theta_0, theta_tr, z_tr, T_tr);
605 
606  if(rho_k_minus_1 == amrex::Real(0)) // This loop is for the first point above the ground
607  {
608  F = p_k - p_k_minus_1 + rho_k*CONST_GRAV*dz/two;
609  }
610  else
611  {
612  F = p_k - p_k_minus_1 + myhalf * (rho_k + rho_k_minus_1)*CONST_GRAV*dz;
613  }
614 
615  return F;
616 }
617 
618 AMREX_FORCE_INLINE
619 AMREX_GPU_HOST_DEVICE
620 Real compute_p_k_in_zone (Real &p_k, const Real p_k_minus_1, Real &theta_k, Real& rho_k, Real& q_v_k,
621  Real& T_dp, Real& T_b, const Real dz, const Real rho_k_minus_1, const Real q_t,
622  const Real eq_pot_temp, const bool use_empirical,
623  const int which_zone, const Real scaled_height, const bool T_from_theta,
624  const Real theta_0, const Real theta_tr, const Real z_tr, const Real T_tr)
625 {
626  Real delta_p_k;
627 
628  // Perturbation used to form the difference quotient for dF/dp. It has to
629  // be scaled by p_k rather than fixed in Pa: the pressures here are O(1e5),
630  // and in single precision one ulp at that magnitude is already ~1e-2 Pa,
631  // so a fixed 1e-4 Pa perturbation rounds away entirely -- p_k + eps == p_k,
632  // the difference quotient is zero, and the iteration returns NaN. The
633  // double-precision perturbation is left exactly as it was so that results
634  // in double are unchanged.
635 #ifdef AMREX_USE_FLOAT
636  Real eps = Real(1.e-5) * std::abs(p_k);
637 #else
638  Real eps = Real(1e-10);
639 #endif
640 
641  for(int iter=0; iter<20; iter++)
642  {
643  Real F = compute_F(p_k , p_k_minus_1, theta_k, rho_k, q_v_k, T_dp, T_b, dz, rho_k_minus_1,
644  q_t, eq_pot_temp, use_empirical, which_zone, scaled_height, T_from_theta,
646  Real F_plus_dF = compute_F(p_k+eps, p_k_minus_1, theta_k, rho_k, q_v_k, T_dp, T_b, dz, rho_k_minus_1,
647  q_t, eq_pot_temp, use_empirical, which_zone, scaled_height, T_from_theta,
649  Real F_prime = (F_plus_dF - F)/eps;
650  delta_p_k = -F/F_prime;
651  p_k = p_k + delta_p_k;
652  }
653 
654  // Convergence is measured on the last Newton step relative to p_k, for the
655  // same reason: an absolute tolerance in Pa that is smaller than an ulp of
656  // p_k can never be met in single precision. The double-precision value is
657  // set so that p_rel_tol * p_k reproduces the previous absolute tolerance of
658  // 1e-8 at the O(1e5) Pa pressures this is used at.
659 #ifdef AMREX_USE_FLOAT
660  const Real p_rel_tol = Real(1.e-5);
661 #else
662  const Real p_rel_tol = Real(1.e-13);
663 #endif
664  if (std::fabs(delta_p_k) > p_rel_tol * std::abs(p_k)) {
665  amrex::Abort("Newton Raphson for pressure could not converge");
666  }
667 
668  return p_k;
669 }
670 
671 /**
672  * Column integration shared by the two versions of init_isentropic_hse_no_terrain.
673  *
674  * @param[in] z_cc z_cc(k) is the height of the centre of cell k above the surface.
675  * @param[in] dz_cc dz_cc(k) is the distance used for the hydrostatic step to cell k:
676  * for k = 0, twice the absolute height of the centre of cell 0 (half of
677  * it is taken), so that the column starts from p_0 at z = 0, as in the
678  * other HSE initializers; for k > 0, the distance between the centres
679  * of cells k-1 and k.
680  *
681  * The other arguments are those of init_isentropic_hse_no_terrain.
682  */
683 template <typename ZccFunc, typename DzFunc>
684 AMREX_FORCE_INLINE
685 AMREX_GPU_HOST_DEVICE
686 void
688  ZccFunc const& z_cc, DzFunc const& dz_cc,
689  const int& khi, const Real q_t,
690  const Real eq_pot_temp, const bool use_empirical,
691  const bool T_from_theta,
692  const Real z_tr_1, const Real z_tr_2,
693  const Real theta_0, const Real theta_tr, const Real T_tr)
694 {
695  // theta_0, theta_tr and T_tr are used only when T_from_theta is set. Their
696  // defaults are still physical temperatures: compute_theta is inlined into
697  // the Newton loops and optimised builds evaluate its arm speculatively, so
698  // zeros would raise an FPE (g/(Cp_d*0), then 0*exp(inf)) on a discarded value.
699  Real T_b, T_dp;
700 
701  int which_zone = -1;
702  Real scaled_height = amrex::Real(0);
703 
704  // Compute the quantities at the first cell center, integrating from p_0 at z = 0
705  p[0] = p_0;
706  if (z_tr_1 > amrex::Real(0)) {
707  Real z = z_cc(0);
708  if (z <= z_tr_1) {
709  which_zone = 1;
710  } else if (z <= z_tr_2) {
711  which_zone = 2;
712  } else {
713  which_zone = 3;
714  }
715  scaled_height = z/z_tr_2;
716  }
717 
718  compute_p_k_in_zone(p[0], p_0, theta[0], r[0], q_v[0], T_dp, T_b, dz_cc(0), amrex::Real(0), q_t, eq_pot_temp,
719  use_empirical, which_zone, scaled_height, T_from_theta,
720  theta_0, theta_tr, z_tr_2, T_tr);
721 
722  for (int k=1; k<=khi; k++)
723  {
724  p[k] = p[k-1];
725 
726  if (z_tr_1 > amrex::Real(0)) {
727  Real z = z_cc(k);
728  if (z <= z_tr_1) {
729  which_zone = 1;
730  } else if (z <= z_tr_2) {
731  which_zone = 2;
732  } else {
733  which_zone = 3;
734  }
735  scaled_height = z/z_tr_2;
736  }
737 
738  compute_p_k_in_zone(p[k], p[k-1], theta[k], r[k], q_v[k], T_dp, T_b, dz_cc(k), r[k-1], q_t, eq_pot_temp,
739  use_empirical, which_zone, scaled_height, T_from_theta,
740  theta_0, theta_tr, z_tr_2, T_tr);
741  }
742 
743  r[khi+1] = r[khi];
744 }
745 
746 /**
747  * Initialize an isentropic hydrostatic equilibrium base state without terrain.
748  *
749  * @param[out] theta Potential temperature profile.
750  * @param[out] r Density profile.
751  * @param[out] p Pressure profile.
752  * @param[out] q_v Water vapor mixing ratio profile.
753  * @param[in] dz Vertical grid spacing.
754  * @param[in] z_bot Absolute height of the bottom of the domain (geometry.prob_lo[2]).
755  * @param[in] khi Top index of the domain.
756  * @param[in] q_t Total water mixing ratio.
757  * @param[in] eq_pot_temp Equilibrium potential temperature.
758  * @param[in] use_empirical Use empirical saturation formulas.
759  * @param[in] T_from_theta Whether temperature is derived from potential temperature.
760  * @param[in] z_tr_1 First transition height.
761  * @param[in] z_tr_2 Second transition height.
762  * @param[in] theta_0 Surface potential temperature.
763  * @param[in] theta_tr Potential temperature at transition height.
764  * @param[in] T_tr Temperature at transition height.
765  */
766 AMREX_FORCE_INLINE
767 AMREX_GPU_HOST_DEVICE
768 void
770  const Real& dz, const Real& z_bot, const int& khi, const Real q_t,
771  const Real eq_pot_temp, const bool use_empirical,
772  const bool T_from_theta = false,
773  const Real z_tr_1 = -one, const Real z_tr_2 = -one,
774  const Real theta_0 = amrex::Real(300), const Real theta_tr = amrex::Real(300), const Real T_tr = amrex::Real(300))
775 {
777  [=] (int k) { return (k == 0) ? myhalf * dz : (k+myhalf) * dz; },
778  [=] (int k) { return (k == 0) ? two * (z_bot + myhalf * dz) : dz; },
779  khi, q_t, eq_pot_temp, use_empirical, T_from_theta,
780  z_tr_1, z_tr_2, theta_0, theta_tr, T_tr);
781 }
782 
783 /**
784  * As above, for a column whose staggered levels are given, e.g. a stretched mesh.
785  *
786  * @param[in] zlevels Staggered levels zlevels[0..khi+1]; heights are measured from zlevels[0],
787  * except that the column starts from p_0 at z = 0.
788  *
789  * The other arguments are those of the version with a uniform dz.
790  */
791 AMREX_FORCE_INLINE
792 AMREX_GPU_HOST_DEVICE
793 void
795  const Real* zlevels, const int& khi, const Real q_t,
796  const Real eq_pot_temp, const bool use_empirical,
797  const bool T_from_theta = false,
798  const Real z_tr_1 = -one, const Real z_tr_2 = -one,
799  const Real theta_0 = amrex::Real(300), const Real theta_tr = amrex::Real(300), const Real T_tr = amrex::Real(300))
800 {
801  auto z_cc = [=] (int k) { return myhalf * (zlevels[k] + zlevels[k+1]) - zlevels[0]; };
803  z_cc,
804  [=] (int k) { return (k == 0) ? zlevels[0] + zlevels[1] : z_cc(k) - z_cc(k-1); },
805  khi, q_t, eq_pot_temp, use_empirical, T_from_theta,
806  z_tr_1, z_tr_2, theta_0, theta_tr, T_tr);
807 }
808 
809 } // namespace
810 
811 #endif
constexpr amrex::Real R_v
Definition: ERF_Constants.H:50
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:51
constexpr amrex::Real p_0
Definition: ERF_Constants.H:69
constexpr amrex::Real iGamma
Definition: ERF_Constants.H:77
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:72
constexpr amrex::Real Cp_l
Definition: ERF_Constants.H:53
constexpr amrex::Real R_d
Definition: ERF_Constants.H:49
constexpr amrex::Real L_v
Definition: ERF_Constants.H:67
constexpr amrex::Real RdoCp
Definition: ERF_Constants.H:57
constexpr amrex::Real RdoRv
Definition: ERF_Constants.H:60
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getdPdRgivenConstantTheta(const amrex::Real rho, const amrex::Real theta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:127
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 compute_vapor_pressure(const amrex::Real p_s, const amrex::Real RH)
Definition: ERF_EOS.H:181
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getRhogivenTandPress(const amrex::Real T, const amrex::Real p, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:113
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getThgivenTandP(const amrex::Real T, const amrex::Real P, const amrex::Real rdOcp)
Definition: ERF_EOS.H:18
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:81
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 int khi
Definition: ERF_InitCustomPert_Bubble.H:25
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
amrex::Real gamma
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:9
const int klo
Definition: ERF_InitCustomPert_KE.H:9
Real H
Definition: ERF_InitCustomPert_MovingTerrain.H:8
Real eq_pot_temp
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:27
bool use_empirical
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:29
Real T_tr
Definition: ERF_InitCustomPert_SquallLine.H:47
Real q_t
Definition: ERF_InitCustomPert_SquallLine.H:28
Real theta_tr
Definition: ERF_InitCustomPert_SquallLine.H:51
Real theta_0
Definition: ERF_InitCustomPert_SquallLine.H:50
Real z_tr
Definition: ERF_InitCustomPert_SquallLine.H:40
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw(amrex::Real t, bool use_empirical=false)
Definition: ERF_MicrophysicsUtils.H:159
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:38
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
constexpr amrex::Real fourth
Definition: ERF_NumericalConstants.H:42
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
amrex::Real Real
Definition: ERF_ShocInterface.H:19
Definition: ERF_HSEUtils.H:15
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void compute_rho(const Real &pressure, Real &theta, Real &rho, Real &q_v, Real &T_dp, Real &T_b, const Real q_t, const Real eq_pot_temp, const bool use_empirical, const int which_zone, const Real scaled_height, const bool T_from_theta, const Real theta_0, const Real theta_tr, const Real z_tr, const Real T_tr)
Definition: ERF_HSEUtils.H:564
const int MAX_ITER
Definition: ERF_HSEUtils.H:19
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_dewpoint_temperature(const Real T_b, const Real RH)
Definition: ERF_HSEUtils.H:537
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real vapor_mixing_ratio(const Real p_b, const Real T_b, const Real RH, const bool use_empirical, int which_zone)
Definition: ERF_HSEUtils.H:475
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void init_isentropic_hse_no_terrain_column(Real *theta, Real *r, Real *p, Real *q_v, ZccFunc const &z_cc, DzFunc const &dz_cc, const int &khi, const Real q_t, const Real eq_pot_temp, const bool use_empirical, const bool T_from_theta, const Real z_tr_1, const Real z_tr_2, const Real theta_0, const Real theta_tr, const Real T_tr)
Definition: ERF_HSEUtils.H:687
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_saturation_pressure(const Real T_b, const bool use_empirical)
Definition: ERF_HSEUtils.H:447
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_p_k_in_zone(Real &p_k, const Real p_k_minus_1, Real &theta_k, Real &rho_k, Real &q_v_k, Real &T_dp, Real &T_b, const Real dz, const Real rho_k_minus_1, const Real q_t, const Real eq_pot_temp, const bool use_empirical, const int which_zone, const Real scaled_height, const bool T_from_theta, const Real theta_0, const Real theta_tr, const Real z_tr, const Real T_tr)
Definition: ERF_HSEUtils.H:620
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void init_isentropic_hse_constant_dz(const amrex::Real &r_sfc, const amrex::Real &theta, amrex::Real *r, amrex::Real *p, const amrex::Real &dz, const amrex::Real &z_bot, const int klo, const int khi)
Definition: ERF_HSEUtils.H:112
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void init_isentropic_hse_stretched_dz(const amrex::Real &r_sfc, const amrex::Real &theta, amrex::Real *r, amrex::Real *p, const amrex::Real *stretched_dz, const amrex::Real &z_bot, const int klo, const int khi)
Definition: ERF_HSEUtils.H:226
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_F_for_temp_in_zone(const Real T_b, const Real p_b, const Real q_t, const Real eq_pot_temp, const bool use_empirical, const int which_zone, const Real scaled_height)
Definition: ERF_HSEUtils.H:491
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_temperature(const Real p_b, const Real q_t, const Real eq_pot_temp, const bool use_empirical, const int which_zone, const Real scaled_height)
Definition: ERF_HSEUtils.H:509
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_relative_humidity(const Real p_b, const Real T_b, const bool use_empirical, const int which_zone, const Real scaled_height)
Definition: ERF_HSEUtils.H:455
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_F(const Real &p_k, const Real &p_k_minus_1, Real &theta_k, Real &rho_k, Real &q_v_k, Real &T_dp, Real &T_b, const Real &dz, const Real &rho_k_minus_1, const Real q_t, const Real eq_pot_temp, const bool use_empirical, const int which_zone, const Real scaled_height, const bool T_from_theta, const Real theta_0, const Real theta_tr, const Real z_tr, const Real T_tr)
Definition: ERF_HSEUtils.H:595
const amrex::Real TOL
Definition: ERF_HSEUtils.H:23
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void init_isentropic_hse_terrain(int i, int j, const amrex::Real &r_sfc, const amrex::Real &theta, amrex::Real *r, amrex::Real *p, const amrex::Array4< amrex::Real const > z_cc, const int &klo, const int &khi)
Definition: ERF_HSEUtils.H:342
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:46
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_theta(const Real scaled_height, const Real theta_0, const Real theta_tr, const Real z_tr, const Real T_tr)
Definition: ERF_HSEUtils.H:552
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void init_isentropic_hse_no_terrain(Real *theta, Real *r, Real *p, Real *q_v, const Real &dz, const Real &z_bot, const int &khi, const Real q_t, const Real eq_pot_temp, const bool use_empirical, const bool T_from_theta=false, const Real z_tr_1=-one, const Real z_tr_2=-one, const Real theta_0=amrex::Real(300), const Real theta_tr=amrex::Real(300), const Real T_tr=amrex::Real(300))
Definition: ERF_HSEUtils.H:769
@ P
Definition: ERF_IndexDefines.H:244
@ rho
Definition: ERF_Kessler.H:25
@ theta
Definition: ERF_Kessler.H:26
@ qt
Definition: ERF_Kessler.H:30
@ qv
Definition: ERF_Kessler.H:31
@ T
Definition: ERF_IndexDefines.H:168
constexpr int A
Definition: ERF_TwoStreamColumn.H:603
@ dz
Definition: ERF_AdvanceWDM6.cpp:273
@ p
Definition: ERF_WSM6.H:301
Definition: ERF_ConsoleIO.cpp:17
real(c_double), parameter g
Definition: ERF_module_model_constants.F90:19