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 /**
5  * Utility functions for calculating a hydrostatic equilibrium (HSE) base state
6 */
7 
8 namespace HSEutils
9 {
10  using namespace amrex;
11 
12  // to control Newton iterations in init_isentropic_hse*
13  const int MAX_ITER = 10;
14  const amrex::Real TOL = 1.e-8;
15 
16  /**
17  * Function to calculate the hydrostatic density and pressure
18  * from Newton-Raphson iterations
19  *
20  * @param[in] m_tol iteration tolerance
21  * @param[in] RdOCp Rd/Cp
22  * @param[out] dz change in vertical height
23  * @param[out] g magnitude of gravity
24  * @param[in] C sum of known terms in HSE balance
25  * @param[in] T theta at the current cell center
26  * @param[in] qt total moisture (non-precip and precip)
27  * @param[in] qv water vapor
28  * @param[in] P pressure at cell center
29  * @param[in] rd dry density at cell center
30  * @param[in] F starting residual of non-linear eq
31  */
32  AMREX_GPU_HOST_DEVICE
33  AMREX_FORCE_INLINE
34  void
35  Newton_Raphson_hse (const Real& m_tol,
36  const Real& RdOCp,
37  const Real& dz,
38  const Real& g,
39  const Real& C,
40  const Real& T,
41  const Real& qt,
42  const Real& qv,
43  Real& P,
44  Real& rd,
45  Real& F)
46  {
47  int iter=0;
48  int max_iter=30;
49  Real eps = 1.0e-6;
50  Real ieps = 1.0e6;
51  do {
52  // Compute change in pressure
53  Real hi = getRhogivenThetaPress(T, P + eps, RdOCp, qv);
54  Real lo = getRhogivenThetaPress(T, P - eps, RdOCp, qv);
55  Real dFdp = 1.0 + 0.25*ieps*(hi - lo)*g*dz;
56  P -= F/dFdp;
57  if (P < 1.0e3) amrex::Warning("P < 1000 [Pa]; Domain height may be too large...");
58  AMREX_ALWAYS_ASSERT(P > 0.0);
59 
60  // Diagnose density and residual
61  rd = getRhogivenThetaPress(T, P, RdOCp, qv);
62  AMREX_ALWAYS_ASSERT(rd > 0.0);
63  Real r_tot = rd * (1. + qt);
64  F = P + 0.5*r_tot*g*dz + C;
65  ++iter;
66  }
67  while (std::abs(F)>m_tol && iter<max_iter);
68  if (iter>=max_iter) {
69  printf("WARNING: HSE Newton iterations did not converge to tolerance!\n");
70  printf("HSE Newton tol: %e %e\n",F,m_tol);
71  }
72  }
73 
74 
75  /**
76  * Function to calculate the hydrostatic density and pressure
77  *
78  * @param[in] r_sfc surface density
79  * @param[in] theta surface potential temperature
80  * @param[out] r hydrostatically balanced density profile
81  * @param[out] p hydrostatically balanced pressure profile
82  * @param[in] dz vertical grid spacing (constant)
83  * @param[in] prob_lo_z surface height
84  * @param[in] khi z-index corresponding to the big end of the domain
85  */
86  AMREX_GPU_HOST_DEVICE
87  AMREX_FORCE_INLINE
88  void
89  init_isentropic_hse (const amrex::Real& r_sfc,
90  const amrex::Real& theta,
91  amrex::Real* r,
92  amrex::Real* p,
93  const amrex::Real& dz,
94  const int klo, const int khi)
95  {
96  int kstart;
97 
98  // r_sfc / p_0 are the density / pressure at klo
99 
100  // Initial guess
101  Real half_dz = 0.5*dz;
102  r[klo] = r_sfc;
103  p[klo] = p_0 - half_dz * r[klo] * CONST_GRAV;
104 
105  if (klo == 0)
106  {
107  kstart = 1;
108 
109  // We do a Newton iteration to satisfy the EOS & HSE (with constant theta)
110  bool converged_hse = false;
111  Real p_hse;
112  Real p_eos;
113 
114  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
115  {
116  p_hse = p_0 - half_dz * r[klo] * CONST_GRAV;
117  p_eos = getPgivenRTh(r[klo]*theta);
118 
119  Real A = p_hse - p_eos;
120 
121  Real dpdr = getdPdRgivenConstantTheta(r[klo],theta);
122 
123  Real drho = A / (dpdr + half_dz * CONST_GRAV);
124 
125  r[klo] = r[klo] + drho;
126  p[klo] = getPgivenRTh(r[klo]*theta);
127 
128  if (std::abs(drho) < TOL)
129  {
130  converged_hse = true;
131  break;
132  }
133  }
134 
135  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << klo << std::endl;
136  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
137  } else {
138  kstart = klo; // because we need to solve for r[klo] here; r[klo-1] is what was passed in r_sfc
139  r[klo-1] = r_sfc; // r_sfc is really the interpolated density in the ghost cell in this case
140  p[klo-1] = getPgivenRTh(r[klo-1]*theta);
141  }
142 
143  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
144  for (int k = kstart; k <= khi; k++)
145  {
146  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
147  // to discretely satisfy HSE -- here we assume spatial_order = 2 -- we can generalize this later if needed
148  bool converged_hse = false;
149 
150  r[k] = r[k-1];
151 
152  Real p_eos = getPgivenRTh(r[k]*theta);
153  Real p_hse;
154 
155  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
156  {
157  Real r_avg = 0.5 * (r[k-1]+r[k]);
158  p_hse = p[k-1] - dz * r_avg * CONST_GRAV;
159  p_eos = getPgivenRTh(r[k]*theta);
160 
161  Real A = p_hse - p_eos;
162 
163  Real dpdr = getdPdRgivenConstantTheta(r[k],theta);
164  // Gamma * p_0 * std::pow( (R_d * theta / p_0), Gamma) * std::pow(r[k], Gamma-1.0) ;
165 
166  Real drho = A / (dpdr + dz * CONST_GRAV);
167 
168  r[k] = r[k] + drho;
169  p[k] = getPgivenRTh(r[k]*theta);
170 
171  if (std::abs(drho) < TOL * r[k-1])
172  {
173  converged_hse = true;
174  // amrex::Print() << " converged " << std::endl;
175  break;
176  }
177  }
178 
179  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k << std::endl;
180  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
181  }
182  r[khi+1] = r[khi];
183  }
184 
185 
186  /**
187  * Function to calculate the hydrostatic density and pressure over terrain
188  *
189  * @param[in] i x-index
190  * @param[in] j y-index
191  * @param[in] r_sfc surface density
192  * @param[in] theta surface potential temperature
193  * @param[out] r hydrostatically balanced density profile
194  * @param[out] p hydrostatically balanced pressure profile
195  * @param[in] z_cc cell-center heights
196  * @param[in] khi z-index corresponding to the big end of the domain
197  */
198  AMREX_GPU_HOST_DEVICE
199  AMREX_FORCE_INLINE
200  void
202  int j,
203  const amrex::Real& r_sfc,
204  const amrex::Real& theta,
205  amrex::Real* r,
206  amrex::Real* p,
207  const amrex::Array4<amrex::Real const> z_cc,
208  const int& klo, const int& khi)
209 {
210  int kstart;
211 
212  if (klo == 0) {
213  //
214  // r_sfc / p_0 are the density / pressure at the surface
215  //
216  // Initial guess
217  int k0 = 0;
218 
219  // Where we start the lower iteration
220  kstart = 1;
221 
222  Real half_dz = z_cc(i,j,k0);
223  r[k0] = r_sfc;
224  p[k0] = p_0 - half_dz * r[k0] * CONST_GRAV;
225  {
226  // We do a Newton iteration to satisfy the EOS & HSE (with constant theta)
227  bool converged_hse = false;
228  Real p_hse;
229  Real p_eos;
230 
231  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
232  {
233  p_hse = p_0 - half_dz * r[k0] * CONST_GRAV;
234  p_eos = getPgivenRTh(r[k0]*theta);
235 
236  Real A = p_hse - p_eos;
237 
238  Real dpdr = getdPdRgivenConstantTheta(r[k0],theta);
239 
240  Real drho = A / (dpdr + half_dz * CONST_GRAV);
241 
242  r[k0] = r[k0] + drho;
243  p[k0] = getPgivenRTh(r[k0]*theta);
244 
245  if (std::abs(drho) < TOL)
246  {
247  converged_hse = true;
248  break;
249  }
250  }
251 
252  //if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k0 << std::endl;
253  //if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
254  }
255  } else {
256  kstart = klo; // because we need to solve for r[klo] here; r[klo-1] is what was passed in r_sfc
257  r[klo-1] = r_sfc; // r_sfc is really the interpolated density in the ghost cell in this case
258  p[klo-1] = getPgivenRTh(r[klo-1]*theta);
259  }
260 
261  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
262  for (int k = kstart; k <= khi; k++)
263  {
264  // To get values at k > 0 we do a Newton iteration to satisfy the EOS (with constant theta) and
265  // to discretely satisfy HSE -- here we assume spatial_order = 2 -- we can generalize this later if needed
266  bool converged_hse = false;
267 
268  Real dz_loc = (z_cc(i,j,k) - z_cc(i,j,k-1));
269 
270  r[k] = r[k-1];
271 
272  Real p_eos = getPgivenRTh(r[k]*theta);
273  Real p_hse;
274 
275  for (int iter = 0; iter < MAX_ITER && !converged_hse; iter++)
276  {
277  p_hse = p[k-1] - dz_loc * 0.5 * (r[k-1]+r[k]) * CONST_GRAV;
278  p_eos = getPgivenRTh(r[k]*theta);
279 
280  Real A = p_hse - p_eos;
281 
282  Real dpdr = getdPdRgivenConstantTheta(r[k],theta);
283 
284  Real drho = A / (dpdr + dz_loc * CONST_GRAV);
285 
286  r[k] = r[k] + drho;
287  p[k] = getPgivenRTh(r[k]*theta);
288 
289  if (std::abs(drho) < TOL * r[k-1])
290  {
291  converged_hse = true;
292  //amrex::Print() << " converged " << std::endl;
293  break;
294  }
295  }
296 
297  // if (!converged_hse) amrex::Print() << "DOING ITERATIONS AT K = " << k << std::endl;
298  // if (!converged_hse) amrex::Error("Didn't converge the iterations in init");
299  }
300 }
301 
302 } // namespace
303 
304 #endif
constexpr amrex::Real p_0
Definition: ERF_Constants.H:18
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:21
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=0.)
Definition: ERF_EOS.H:84
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getRhogivenThetaPress(const amrex::Real th, const amrex::Real p, const amrex::Real rdOcp, const amrex::Real qv=0.0)
Definition: ERF_EOS.H:99
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getdPdRgivenConstantTheta(const amrex::Real rho, const amrex::Real theta, const amrex::Real qv=0.0)
Definition: ERF_EOS.H:130
Definition: ERF_HSEUtils.H:9
const int MAX_ITER
Definition: ERF_HSEUtils.H:13
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void Newton_Raphson_hse(const Real &m_tol, const Real &RdOCp, const Real &dz, const Real &g, const Real &C, const Real &T, const Real &qt, const Real &qv, Real &P, Real &rd, Real &F)
Definition: ERF_HSEUtils.H:35
const amrex::Real TOL
Definition: ERF_HSEUtils.H:14
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:201
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void init_isentropic_hse(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:89
@ theta
Definition: ERF_MM5.H:20
@ qt
Definition: ERF_Kessler.H:35
@ qv
Definition: ERF_Kessler.H:36
@ T
Definition: ERF_IndexDefines.H:99
Definition: ERF_ConsoleIO.cpp:12