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] klo z-index corresponding to the small end of the domain
106  * @param[in] khi z-index corresponding to the big end of the domain
107  */
108  AMREX_GPU_HOST_DEVICE
109  AMREX_FORCE_INLINE
110  void
112  const amrex::Real& theta,
113  amrex::Real* r,
114  amrex::Real* p,
115  const amrex::Real& dz,
116  const int klo, const int khi)
117  {
118  int kstart;
119 
120  // r_sfc / p_0 are the density / pressure at klo
121 
122  // Initial guess
123  Real myhalf_dz = myhalf*dz;
124  r[klo] = r_sfc;
125  p[klo] = p_0 - myhalf_dz * r[klo] * CONST_GRAV;
126 
127  if (klo == 0)
128  {
129  kstart = 1;
130 
131  // We do a Newton iteration to satisfy the EOS & HSE (with constant theta)
132  bool converged_hse = false;
133  Real p_hse;
134  Real p_eos;
135 
136  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
137  {
138  p_hse = p_0 - myhalf_dz * r[klo] * CONST_GRAV;
139  p_eos = getPgivenRTh(r[klo]*theta);
140 
141  Real A = p_hse - p_eos;
142 
144 
145  Real drho = A / (dpdr + myhalf_dz * CONST_GRAV);
146 
147  r[klo] = r[klo] + drho;
148  p[klo] = getPgivenRTh(r[klo]*theta);
149 
150  if (std::abs(drho) < TOL)
151  {
152  converged_hse = true;
153  break;
154  }
155  }
156 
157  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << klo << std::endl;
158  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
159  } else {
160  kstart = klo; // because we need to solve for r[klo] here; r[klo-1] is what was passed in r_sfc
161  r[klo-1] = r_sfc; // r_sfc is really the interpolated density in the ghost cell in this case
162  p[klo-1] = getPgivenRTh(r[klo-1]*theta);
163  }
164 
165  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
166  for (int k = kstart; k <= khi; k++)
167  {
168  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
169  // to discretely satisfy HSE -- here we assume spatial_order = 2 -- we can generalize this later if needed
170  bool converged_hse = false;
171 
172  r[k] = r[k-1];
173 
174  Real p_eos = getPgivenRTh(r[k]*theta);
175  Real p_hse;
176 
177  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
178  {
179  Real r_avg = myhalf * (r[k-1]+r[k]);
180  p_hse = p[k-1] - dz * r_avg * CONST_GRAV;
181  p_eos = getPgivenRTh(r[k]*theta);
182 
183  Real A = p_hse - p_eos;
184 
185  Real dpdr = getdPdRgivenConstantTheta(r[k],theta);
186  // Gamma * p_0 * std::pow( (R_d * theta / p_0), Gamma) * std::pow(r[k], Gamma-one) ;
187 
188  Real drho = A / (dpdr + dz * CONST_GRAV);
189 
190  r[k] = r[k] + drho;
191  p[k] = getPgivenRTh(r[k]*theta);
192 
193  if (std::abs(drho) < TOL * r[k-1])
194  {
195  converged_hse = true;
196  // amrex::Print() << " converged " << std::endl;
197  break;
198  }
199  }
200 
201  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k << std::endl;
202  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
203  }
204  r[khi+1] = r[khi];
205  }
206 
207  /**
208  * Function to calculate the hydrostatic density and pressure with stretched dz
209  *
210  * @param[in] r_sfc surface density
211  * @param[in] theta surface potential temperature
212  * @param[out] r hydrostatically balanced density profile
213  * @param[out] p hydrostatically balanced pressure profile
214  * @param[in] stretched_dz vertical grid spacing (stretched)
215  * @param[in] klo z-index corresponding to the small end of the domain
216  * @param[in] khi z-index corresponding to the big end of the domain
217  */
218  AMREX_GPU_HOST_DEVICE
219  AMREX_FORCE_INLINE
220  void
222  const amrex::Real& theta,
223  amrex::Real* r,
224  amrex::Real* p,
225  const amrex::Real* stretched_dz,
226  const int klo, const int khi)
227  {
228  int kstart;
229 
230  // r_sfc / p_0 are the density / pressure at klo
231 
232  // Initial guess
233  r[klo] = r_sfc;
234  p[klo] = p_0 - myhalf*stretched_dz[klo] * r[klo] * CONST_GRAV;
235 
236  if (klo == 0)
237  {
238  kstart = 1;
239 
240  // We do a Newton iteration to satisfy the EOS & HSE (with constant theta)
241  bool converged_hse = false;
242  Real p_hse;
243  Real p_eos;
244 
245  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
246  {
247  p_hse = p_0 - myhalf*stretched_dz[klo] * r[klo] * CONST_GRAV;
248  p_eos = getPgivenRTh(r[klo]*theta);
249 
250  Real A = p_hse - p_eos;
251 
253 
254  Real drho = A / (dpdr + myhalf*stretched_dz[klo] * CONST_GRAV);
255 
256  r[klo] = r[klo] + drho;
257  p[klo] = getPgivenRTh(r[klo]*theta);
258 
259  if (std::abs(drho) < TOL)
260  {
261  converged_hse = true;
262  break;
263  }
264  }
265 
266  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << klo << std::endl;
267  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
268  } else {
269  kstart = klo; // because we need to solve for r[klo] here; r[klo-1] is what was passed in r_sfc
270  r[klo-1] = r_sfc; // r_sfc is really the interpolated density in the ghost cell in this case
271  p[klo-1] = getPgivenRTh(r[klo-1]*theta);
272  }
273 
274  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
275  for (int k = kstart; k <= khi; k++)
276  {
277  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
278  // to discretely satisfy HSE -- here we assume spatial_order = 2 -- we can generalize this later if needed
279  bool converged_hse = false;
280 
281  r[k] = r[k-1];
282 
283  Real p_eos = getPgivenRTh(r[k]*theta);
284  Real p_hse;
285 
286  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
287  {
288  Real r_avg = myhalf * (r[k-1]+r[k]);
289  Real dz_avg = myhalf * (stretched_dz[k-1] + stretched_dz[k]);
290  p_hse = p[k-1] - dz_avg * r_avg * CONST_GRAV;
291  p_eos = getPgivenRTh(r[k]*theta);
292 
293  Real A = p_hse - p_eos;
294 
295  Real dpdr = getdPdRgivenConstantTheta(r[k],theta);
296  // Gamma * p_0 * std::pow( (R_d * theta / p_0), Gamma) * std::pow(r[k], Gamma-one) ;
297 
298  Real drho = A / (dpdr + dz_avg * CONST_GRAV);
299 
300  r[k] = r[k] + drho;
301  p[k] = getPgivenRTh(r[k]*theta);
302 
303  if (std::abs(drho) < TOL * r[k-1])
304  {
305  converged_hse = true;
306  // amrex::Print() << " converged " << std::endl;
307  break;
308  }
309  }
310 
311  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k << std::endl;
312  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
313  }
314  r[khi+1] = r[khi];
315  }
316 
317 
318  /**
319  * Function to calculate the hydrostatic density and pressure over terrain
320  *
321  * @param[in] i x-index
322  * @param[in] j y-index
323  * @param[in] r_sfc surface density
324  * @param[in] theta surface potential temperature
325  * @param[out] r hydrostatically balanced density profile
326  * @param[out] p hydrostatically balanced pressure profile
327  * @param[in] z_cc cell-center heights
328  * @param[in] khi z-index corresponding to the big end of the domain
329  */
330  AMREX_GPU_HOST_DEVICE
331  AMREX_FORCE_INLINE
332  void
334  int j,
335  const amrex::Real& r_sfc,
336  const amrex::Real& theta,
337  amrex::Real* r,
338  amrex::Real* p,
339  const amrex::Array4<amrex::Real const> z_cc,
340  const int& klo, const int& khi)
341  {
342  int kstart;
343 
344  if (klo == 0) {
345  //
346  // r_sfc / p_0 are the density / pressure at the surface
347  //
348  // Initial guess
349  int k0 = 0;
350 
351  // Where we start the lower iteration
352  kstart = 1;
353 
354  Real myhalf_dz = z_cc(i,j,k0);
355  r[k0] = r_sfc;
356  p[k0] = p_0 - myhalf_dz * r[k0] * CONST_GRAV;
357  {
358  // We do a Newton iteration to satisfy the EOS & HSE (with constant theta)
359  bool converged_hse = false;
360  Real p_hse;
361  Real p_eos;
362 
363  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
364  {
365  p_hse = p_0 - myhalf_dz * r[k0] * CONST_GRAV;
366  p_eos = getPgivenRTh(r[k0]*theta);
367 
368  Real A = p_hse - p_eos;
369 
370  Real dpdr = getdPdRgivenConstantTheta(r[k0],theta);
371 
372  Real drho = A / (dpdr + myhalf_dz * CONST_GRAV);
373 
374  r[k0] = r[k0] + drho;
375  p[k0] = getPgivenRTh(r[k0]*theta);
376 
377  if (std::abs(drho) < TOL)
378  {
379  converged_hse = true;
380  break;
381  }
382  }
383 
384  //if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k0 << std::endl;
385  //if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
386  }
387  } else {
388  kstart = klo; // because we need to solve for r[klo] here; r[klo-1] is what was passed in r_sfc
389  r[klo-1] = r_sfc; // r_sfc is really the interpolated density in the ghost cell in this case
390  p[klo-1] = getPgivenRTh(r[klo-1]*theta);
391  }
392 
393  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
394  for (int k = kstart; k <= khi; k++)
395  {
396  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
397  // to discretely satisfy HSE -- here we assume spatial_order = 2 -- we can generalize this later if needed
398  bool converged_hse = false;
399 
400  Real dz_loc = (z_cc(i,j,k) - z_cc(i,j,k-1));
401 
402  r[k] = r[k-1];
403 
404  Real p_eos = getPgivenRTh(r[k]*theta);
405  Real p_hse;
406 
407  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
408  {
409  p_hse = p[k-1] - dz_loc * myhalf * (r[k-1]+r[k]) * CONST_GRAV;
410  p_eos = getPgivenRTh(r[k]*theta);
411 
412  Real A = p_hse - p_eos;
413 
414  Real dpdr = getdPdRgivenConstantTheta(r[k],theta);
415 
416  Real drho = A / (dpdr + dz_loc * CONST_GRAV);
417 
418  r[k] = r[k] + drho;
419  p[k] = getPgivenRTh(r[k]*theta);
420 
421  if (std::abs(drho) < TOL * r[k-1])
422  {
423  converged_hse = true;
424  //amrex::Print() << " converged " << std::endl;
425  break;
426  }
427  }
428 
429  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k << std::endl;
430  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
431  }
432  }
433 
434 AMREX_FORCE_INLINE
435 AMREX_GPU_HOST_DEVICE
437 {
438  Real p_s = erf_esatw(T_b,use_empirical);
439  return p_s * Real(100.0);
440 }
441 
442 AMREX_FORCE_INLINE
443 AMREX_GPU_HOST_DEVICE
444 Real compute_relative_humidity (const Real p_b, const Real T_b, const bool use_empirical,
445  const int which_zone, const Real scaled_height)
446 {
447  if (which_zone > 0) {
448  if (which_zone == 1) { // z <= height
450  Real q_s = RdoRv*p_s/(p_b - p_s);
451  return Real(0.014)/q_s;
452  } else if (which_zone == 2) { // z > height and z <= z_tr; scaled_height = z/z_tr
453  return one - Real(0.75)*std::pow(scaled_height,Real(1.25));
454  } else { // z > z_tr
455  return fourth;
456  }
457  } else {
458  return one;
459  }
460 }
461 
462 AMREX_FORCE_INLINE
463 AMREX_GPU_HOST_DEVICE
464 Real vapor_mixing_ratio (const Real p_b, const Real T_b, const Real RH, const bool use_empirical,
465  int which_zone)
466 {
468  Real p_v = compute_vapor_pressure(p_s, RH);
469  Real q_v = RdoRv*p_v/(p_b - p_v);
470 
471  if (which_zone == 1) { // z <= height
472  return Real(0.014);
473  } else {
474  return q_v;
475  }
476 }
477 
478 AMREX_FORCE_INLINE
479 AMREX_GPU_HOST_DEVICE
480 Real compute_F_for_temp_in_zone(const Real T_b, const Real p_b, const Real q_t, const Real eq_pot_temp,
481  const bool use_empirical, const int which_zone, const Real scaled_height)
482 {
483  Real fac = Cp_d + Cp_l*q_t;
484  Real RH = compute_relative_humidity(p_b, T_b, use_empirical, which_zone, scaled_height);
485  Real q_v = vapor_mixing_ratio(p_b, T_b, RH, use_empirical, which_zone);
487  Real p_v = compute_vapor_pressure(p_s, RH);
488  return eq_pot_temp - T_b*std::pow((p_b - p_v)/p_0, -R_d/fac)*std::exp(L_v*q_v/(fac*T_b));
489 }
490 
491 AMREX_FORCE_INLINE
492 AMREX_GPU_HOST_DEVICE
493 Real compute_temperature (const Real p_b, const Real q_t, const Real eq_pot_temp, const bool use_empirical,
494  const int which_zone, const Real scaled_height)
495 {
496  Real T_b = Real(200.0), delta_T; // Initial guess
497 
498 #ifdef AMREX_USE_FLOAT
499  Real eps = Real(1.e-4) * T_b;
500 #else
501  Real eps = Real(1.e-10) * T_b;
502 #endif
503  for (int iter=0; iter<20; iter++)
504  {
505  Real F = compute_F_for_temp_in_zone(T_b , p_b, q_t, eq_pot_temp, use_empirical, which_zone, scaled_height);
506  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);
507  Real F_prime = (F_plus_dF - F)/eps;
508  delta_T = -F/F_prime;
509  T_b = T_b + delta_T;
510  }
511 
512  if (std::fabs(delta_T) > TOL * T_b) {
513  amrex::Abort("Newton Raphson for temperature could not converge");
514  }
515 
516  return T_b;
517 }
518 
519 AMREX_FORCE_INLINE
520 AMREX_GPU_HOST_DEVICE
522 {
523  Real T_dp, gamma, T;
524  T = T_b - Real(273.15);
525 
526  Real b = Real(18.678), c = Real(257.14), d = Real(234.5);
527  gamma = std::log(RH*std::exp((b - T/d)*T/(c + T)));
528 
529  T_dp = c*gamma/(b - gamma);
530 
531  return T_dp;
532 }
533 
534 AMREX_FORCE_INLINE
535 AMREX_GPU_HOST_DEVICE
536 Real compute_theta (const Real scaled_height, const Real theta_0,
537  const Real theta_tr, const Real z_tr, const Real T_tr)
538 {
539  if(scaled_height <= one) {
540  return theta_0 + (theta_tr - theta_0)*std::pow(scaled_height,Real(1.25));
541  } else {
542  return theta_tr*std::exp(CONST_GRAV/(Cp_d*T_tr)*z_tr*(scaled_height - one));
543  }
544 }
545 
546 AMREX_FORCE_INLINE
547 AMREX_GPU_HOST_DEVICE
548 void compute_rho (const Real& pressure, Real& theta, Real& rho, Real& q_v, Real& T_dp, Real& T_b,
549  const Real q_t, const Real eq_pot_temp, const bool use_empirical, const int which_zone,
550  const Real scaled_height, const bool T_from_theta,
551  const Real theta_0, const Real theta_tr, const Real z_tr, const Real T_tr)
552 {
553 
554  if (T_from_theta) {
555  theta = compute_theta(scaled_height, theta_0, theta_tr, z_tr, T_tr);
556  T_b = getTgivenPandTh(pressure, theta, RdoCp);
557  } else {
558  T_b = compute_temperature(pressure, q_t, eq_pot_temp, use_empirical, which_zone, scaled_height);
559  theta = getThgivenTandP(T_b, pressure, RdoCp);
560  }
561 
562  Real RH = compute_relative_humidity(pressure, T_b, use_empirical, which_zone, scaled_height);
563 
564  q_v = vapor_mixing_ratio(pressure, T_b, RH, use_empirical, which_zone);
565 
566  rho = getRhogivenTandPress(T_b, pressure, q_v);
567 
568  if (T_from_theta) {
569  rho *= (one + q_v);
570  } else {
571  rho *= (one + q_t);
572  }
573 
574  T_dp = compute_dewpoint_temperature(T_b, RH);
575 }
576 
577 AMREX_FORCE_INLINE
578 AMREX_GPU_HOST_DEVICE
579 Real compute_F (const Real& p_k, const Real& p_k_minus_1, Real &theta_k, Real& rho_k, Real& q_v_k,
580  Real& T_dp, Real& T_b, const Real& dz, const Real& rho_k_minus_1, const Real q_t,
581  const Real eq_pot_temp, const bool use_empirical,
582  const int which_zone, const Real scaled_height, const bool T_from_theta,
583  const Real theta_0, const Real theta_tr, const Real z_tr, const Real T_tr)
584 {
585  Real F;
586 
587  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,
588  T_from_theta, theta_0, theta_tr, z_tr, T_tr);
589 
590  if(rho_k_minus_1 == amrex::Real(0)) // This loop is for the first point above the ground
591  {
592  F = p_k - p_k_minus_1 + rho_k*CONST_GRAV*dz/two;
593  }
594  else
595  {
596  F = p_k - p_k_minus_1 + myhalf * (rho_k + rho_k_minus_1)*CONST_GRAV*dz;
597  }
598 
599  return F;
600 }
601 
602 AMREX_FORCE_INLINE
603 AMREX_GPU_HOST_DEVICE
604 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,
605  Real& T_dp, Real& T_b, const Real dz, const Real rho_k_minus_1, const Real q_t,
606  const Real eq_pot_temp, const bool use_empirical,
607  const int which_zone, const Real scaled_height, const bool T_from_theta,
608  const Real theta_0, const Real theta_tr, const Real z_tr, const Real T_tr)
609 {
610  Real delta_p_k;
611 
612  // Perturbation used to form the difference quotient for dF/dp. It has to
613  // be scaled by p_k rather than fixed in Pa: the pressures here are O(1e5),
614  // and in single precision one ulp at that magnitude is already ~1e-2 Pa,
615  // so a fixed 1e-4 Pa perturbation rounds away entirely -- p_k + eps == p_k,
616  // the difference quotient is zero, and the iteration returns NaN. The
617  // double-precision perturbation is left exactly as it was so that results
618  // in double are unchanged.
619 #ifdef AMREX_USE_FLOAT
620  Real eps = Real(1.e-5) * std::abs(p_k);
621 #else
622  Real eps = Real(1e-10);
623 #endif
624 
625  for(int iter=0; iter<20; iter++)
626  {
627  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,
628  q_t, eq_pot_temp, use_empirical, which_zone, scaled_height, T_from_theta,
630  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,
631  q_t, eq_pot_temp, use_empirical, which_zone, scaled_height, T_from_theta,
633  Real F_prime = (F_plus_dF - F)/eps;
634  delta_p_k = -F/F_prime;
635  p_k = p_k + delta_p_k;
636  }
637 
638  // Convergence is measured on the last Newton step relative to p_k, for the
639  // same reason: an absolute tolerance in Pa that is smaller than an ulp of
640  // p_k can never be met in single precision. The double-precision value is
641  // set so that p_rel_tol * p_k reproduces the previous absolute tolerance of
642  // 1e-8 at the O(1e5) Pa pressures this is used at.
643 #ifdef AMREX_USE_FLOAT
644  const Real p_rel_tol = Real(1.e-5);
645 #else
646  const Real p_rel_tol = Real(1.e-13);
647 #endif
648  if (std::fabs(delta_p_k) > p_rel_tol * std::abs(p_k)) {
649  amrex::Abort("Newton Raphson for pressure could not converge");
650  }
651 
652  return p_k;
653 }
654 
655 /**
656  * Initialize an isentropic hydrostatic equilibrium base state without terrain.
657  *
658  * @param[out] theta Potential temperature profile.
659  * @param[out] r Density profile.
660  * @param[out] p Pressure profile.
661  * @param[out] q_v Water vapor mixing ratio profile.
662  * @param[in] dz Vertical grid spacing.
663  * @param[in] khi Top index of the domain.
664  * @param[in] q_t Total water mixing ratio.
665  * @param[in] eq_pot_temp Equilibrium potential temperature.
666  * @param[in] use_empirical Use empirical saturation formulas.
667  * @param[in] T_from_theta Whether temperature is derived from potential temperature.
668  * @param[in] z_tr_1 First transition height.
669  * @param[in] z_tr_2 Second transition height.
670  * @param[in] theta_0 Surface potential temperature.
671  * @param[in] theta_tr Potential temperature at transition height.
672  * @param[in] T_tr Temperature at transition height.
673  */
674 AMREX_FORCE_INLINE
675 AMREX_GPU_HOST_DEVICE
676 void
678  const Real& dz, const int& khi, const Real q_t,
679  const Real eq_pot_temp, const bool use_empirical,
680  const bool T_from_theta = false,
681  const Real z_tr_1 = -one, const Real z_tr_2 = -one,
682  const Real theta_0 = amrex::Real(300), const Real theta_tr = amrex::Real(300), const Real T_tr = amrex::Real(300))
683 {
684  // theta_0, theta_tr and T_tr are used only when T_from_theta is set. Their
685  // defaults are still physical temperatures: compute_theta is inlined into
686  // the Newton loops and optimised builds evaluate its arm speculatively, so
687  // zeros would raise an FPE (g/(Cp_d*0), then 0*exp(inf)) on a discarded value.
688  Real T_b, T_dp;
689 
690  int which_zone = -1;
691  Real scaled_height = amrex::Real(0);
692 
693  // Compute the quantities at z = myhalf*dz (first cell center)
694  p[0] = p_0;
695  if (z_tr_1 > amrex::Real(0)) {
696  Real z = myhalf * dz;
697  if (z <= z_tr_1) {
698  which_zone = 1;
699  } else if (z <= z_tr_2) {
700  which_zone = 2;
701  } else {
702  which_zone = 3;
703  }
704  scaled_height = z/z_tr_2;
705  }
706 
707  compute_p_k_in_zone(p[0], p_0, theta[0], r[0], q_v[0], T_dp, T_b, dz, amrex::Real(0), q_t, eq_pot_temp,
708  use_empirical, which_zone, scaled_height, T_from_theta,
709  theta_0, theta_tr, z_tr_2, T_tr);
710 
711  for (int k=1; k<=khi; k++)
712  {
713  p[k] = p[k-1];
714 
715  if (z_tr_1 > amrex::Real(0)) {
716  Real z = (k+myhalf) * dz;
717  if (z <= z_tr_1) {
718  which_zone = 1;
719  } else if (z <= z_tr_2) {
720  which_zone = 2;
721  } else {
722  which_zone = 3;
723  }
724  scaled_height = z/z_tr_2;
725  }
726 
727  compute_p_k_in_zone(p[k], p[k-1], theta[k], r[k], q_v[k], T_dp, T_b, dz, r[k-1], q_t, eq_pot_temp,
728  use_empirical, which_zone, scaled_height, T_from_theta,
729  theta_0, theta_tr, z_tr_2, T_tr);
730  }
731 
732  r[khi+1] = r[khi];
733 }
734 
735 } // namespace
736 
737 #endif
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:36
constexpr amrex::Real p_0
Definition: ERF_Constants.H:53
constexpr amrex::Real iGamma
Definition: ERF_Constants.H:61
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:56
constexpr amrex::Real Cp_l
Definition: ERF_Constants.H:38
constexpr amrex::Real R_d
Definition: ERF_Constants.H:34
constexpr amrex::Real L_v
Definition: ERF_Constants.H:51
constexpr amrex::Real RdoCp
Definition: ERF_Constants.H:41
constexpr amrex::Real RdoRv
Definition: ERF_Constants.H:44
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 klo
Definition: ERF_InitCustomPert_ABL.H:75
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
amrex::Real gamma
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:9
Real eq_pot_temp
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:23
bool use_empirical
Definition: ERF_InitCustomPert_MultiSpeciesBubble.H:25
Real T_tr
Definition: ERF_InitCustomPert_SquallLine.H:43
Real q_t
Definition: ERF_InitCustomPert_SquallLine.H:24
Real theta_tr
Definition: ERF_InitCustomPert_SquallLine.H:47
Real theta_0
Definition: ERF_InitCustomPert_SquallLine.H:46
Real z_tr
Definition: ERF_InitCustomPert_SquallLine.H:36
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:31
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:30
constexpr amrex::Real fourth
Definition: ERF_NumericalConstants.H:35
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:34
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:548
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:521
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 int klo, const int khi)
Definition: ERF_HSEUtils.H:221
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:464
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 int klo, const int khi)
Definition: ERF_HSEUtils.H:111
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_saturation_pressure(const Real T_b, const bool use_empirical)
Definition: ERF_HSEUtils.H:436
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:604
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 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:677
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:480
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:493
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:444
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:579
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:333
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:536
@ theta
Definition: ERF_SLM.H:19
@ P
Definition: ERF_IndexDefines.H:204
@ rho
Definition: ERF_Kessler.H:25
@ qt
Definition: ERF_Kessler.H:30
@ qv
Definition: ERF_Kessler.H:31
@ T
Definition: ERF_IndexDefines.H:128
constexpr int A
Definition: ERF_TwoStreamColumn.H:603
@ dz
Definition: ERF_AdvanceWDM6.cpp:272
@ p
Definition: ERF_WSM6.H:280
Definition: ERF_ConsoleIO.cpp:15
real(c_double), parameter g
Definition: ERF_module_model_constants.F90:19