ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
HSEutils Namespace Reference

Functions

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)
 
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)
 
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)
 
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)
 
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_saturation_pressure (const Real T_b, const bool use_empirical)
 
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)
 
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)
 
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)
 
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)
 
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_dewpoint_temperature (const Real T_b, const Real RH)
 
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)
 
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)
 
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)
 
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)
 
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))
 

Variables

const int MAX_ITER = 10
 
const amrex::Real TOL = Real(1.e-8)
 

Detailed Description

Utility functions for calculating a hydrostatic equilibrium (HSE) base state

Function Documentation

◆ compute_dewpoint_temperature()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real HSEutils::compute_dewpoint_temperature ( const Real  T_b,
const Real  RH 
)
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 }
amrex::Real gamma
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:9
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ T
Definition: ERF_IndexDefines.H:128

Referenced by compute_rho().

Here is the caller graph for this function:

◆ compute_F()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real HSEutils::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 
)
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 }
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:56
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
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:31
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:34
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
@ dz
Definition: ERF_AdvanceWDM6.cpp:272

Referenced by compute_p_k_in_zone().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ compute_F_for_temp_in_zone()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real HSEutils::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 
)
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 }
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:36
constexpr amrex::Real p_0
Definition: ERF_Constants.H:53
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
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 compute_relative_humidity(amrex::Real qv, amrex::Real T, amrex::Real P)
Compute relative humidity from water vapor mixing ratio.
Definition: ERF_PrognosticCloudFraction.H:36
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_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_saturation_pressure(const Real T_b, const bool use_empirical)
Definition: ERF_HSEUtils.H:436

Referenced by compute_temperature().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ compute_p_k_in_zone()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real HSEutils::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 
)
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 }
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

Referenced by init_isentropic_hse_no_terrain().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ compute_relative_humidity()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real HSEutils::compute_relative_humidity ( const Real  p_b,
const Real  T_b,
const bool  use_empirical,
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 }
constexpr amrex::Real RdoRv
Definition: ERF_Constants.H:44
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:30
constexpr amrex::Real fourth
Definition: ERF_NumericalConstants.H:35

Referenced by compute_F_for_temp_in_zone(), compute_rho(), and ParallelFor().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ compute_rho()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void HSEutils::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 
)
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 }
constexpr amrex::Real RdoCp
Definition: ERF_Constants.H:41
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 getTgivenPandTh(const amrex::Real P, const amrex::Real th, const amrex::Real rdOcp)
Definition: ERF_EOS.H:32
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real compute_dewpoint_temperature(const Real T_b, const Real RH)
Definition: ERF_HSEUtils.H:521
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_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
@ rho
Definition: ERF_Kessler.H:25

Referenced by compute_F().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ compute_saturation_pressure()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real HSEutils::compute_saturation_pressure ( const Real  T_b,
const bool  use_empirical 
)
437 {
438  Real p_s = erf_esatw(T_b,use_empirical);
439  return p_s * Real(100.0);
440 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esatw(amrex::Real t, bool use_empirical=false)
Definition: ERF_MicrophysicsUtils.H:159

Referenced by compute_F_for_temp_in_zone(), compute_relative_humidity(), and vapor_mixing_ratio().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ compute_temperature()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real HSEutils::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 
)
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 }
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
const amrex::Real TOL
Definition: ERF_HSEUtils.H:23

Referenced by compute_rho(), and ShocPDF::diagnose_pdf().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ compute_theta()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real HSEutils::compute_theta ( const Real  scaled_height,
const Real  theta_0,
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 }

Referenced by compute_rho().

Here is the caller graph for this function:

◆ init_isentropic_hse_constant_dz()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void HSEutils::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 
)

Function to calculate the hydrostatic density and pressure with constant dz

Parameters
[in]r_sfcsurface density
[in]thetasurface potential temperature
[out]rhydrostatically balanced density profile
[out]phydrostatically balanced pressure profile
[in]dzvertical grid spacing (constant)
[in]kloz-index corresponding to the small end of the domain
[in]khiz-index corresponding to the big end of the domain
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  }
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 getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:81
const int klo
Definition: ERF_InitCustomPert_ABL.H:75
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
const int MAX_ITER
Definition: ERF_HSEUtils.H:19
constexpr int A
Definition: ERF_TwoStreamColumn.H:603
@ p
Definition: ERF_WSM6.H:280

Referenced by erf_init_dens_hse_dry().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ init_isentropic_hse_no_terrain()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE void HSEutils::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) 
)

Initialize an isentropic hydrostatic equilibrium base state without terrain.

Parameters
[out]thetaPotential temperature profile.
[out]rDensity profile.
[out]pPressure profile.
[out]q_vWater vapor mixing ratio profile.
[in]dzVertical grid spacing.
[in]khiTop index of the domain.
[in]q_tTotal water mixing ratio.
[in]eq_pot_tempEquilibrium potential temperature.
[in]use_empiricalUse empirical saturation formulas.
[in]T_from_thetaWhether temperature is derived from potential temperature.
[in]z_tr_1First transition height.
[in]z_tr_2Second transition height.
[in]theta_0Surface potential temperature.
[in]theta_trPotential temperature at transition height.
[in]T_trTemperature at transition height.
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 }
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

Referenced by erf_init_dens_hse_moist(), and if().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ init_isentropic_hse_stretched_dz()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void HSEutils::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 
)

Function to calculate the hydrostatic density and pressure with stretched dz

Parameters
[in]r_sfcsurface density
[in]thetasurface potential temperature
[out]rhydrostatically balanced density profile
[out]phydrostatically balanced pressure profile
[in]stretched_dzvertical grid spacing (stretched)
[in]kloz-index corresponding to the small end of the domain
[in]khiz-index corresponding to the big end of the domain
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  }

Referenced by erf_init_dens_hse_dry().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ init_isentropic_hse_terrain()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void HSEutils::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 
)

Function to calculate the hydrostatic density and pressure over terrain

Parameters
[in]ix-index
[in]jy-index
[in]r_sfcsurface density
[in]thetasurface potential temperature
[out]rhydrostatically balanced density profile
[out]phydrostatically balanced pressure profile
[in]z_cccell-center heights
[in]khiz-index corresponding to the big end of the domain
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  }

Referenced by erf_init_dens_hse_dry().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Newton_Raphson_hse()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void HSEutils::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 
)

Function to calculate the hydrostatic density and pressure from Newton-Raphson iterations at constant Th & Qv

Parameters
[in]m_toliteration tolerance
[in]RdOCpRd/Cp
[out]dzchange in vertical height
[out]gmagnitude of gravity
[in]Csum of known terms in HSE balance
[in]Ththeta at the current cell center
[in]qttotal moisture (non-precip and precip)
[in]qvwater vapor
[in]Ppressure at cell center
[in]rddry density at cell center
[in]Fstarting residual of non-linear eq
[in]maintain_Thmaintain theta or temperature?
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  }
constexpr amrex::Real iGamma
Definition: ERF_Constants.H:61
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_ALWAYS_ASSERT(bx.length()[2]==khi+1)
@ P
Definition: ERF_IndexDefines.H:204
@ qt
Definition: ERF_Kessler.H:30
@ qv
Definition: ERF_Kessler.H:31
real(c_double), parameter g
Definition: ERF_module_model_constants.F90:19

Referenced by InputSoundingData::calc_rho_p(), and rebalance_columns().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ vapor_mixing_ratio()

AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real HSEutils::vapor_mixing_ratio ( const Real  p_b,
const Real  T_b,
const Real  RH,
const bool  use_empirical,
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 }

Referenced by compute_F_for_temp_in_zone(), sea_level_pressure_diagnostics::compute_from_erf_lowest_level_state(), compute_rho(), sea_level_pressure_diagnostics::fill_sea_level_pressure(), if(), ParallelFor(), and sea_level_pressure_diagnostics::specific_humidity_from_dry_mixing_ratio().

Here is the call graph for this function:
Here is the caller graph for this function:

Variable Documentation

◆ MAX_ITER

◆ TOL