ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ShocStructure Class Reference

#include <ERF_ShocStructure.H>

Static Public Member Functions

static void diagnose_surface_layer (ShocColumnData &col)
 
static void diagnose_pblh (ShocColumnData &col)
 
static void diagnose_length_and_brunt (ShocColumnData &col, const ShocRuntimeOptions &opts, amrex::Real dx, amrex::Real dy)
 

Member Function Documentation

◆ diagnose_length_and_brunt()

void ShocStructure::diagnose_length_and_brunt ( ShocColumnData col,
const ShocRuntimeOptions opts,
amrex::Real  dx,
amrex::Real  dy 
)
static
220 {
221  auto brunt = col.brunt.array();
222  auto shoc_mix = col.shoc_mix.array();
223  const auto zt = col.zt.const_array();
224  const auto zi = col.zi.const_array();
225  const auto dz = col.dz.const_array();
226  const auto thetal = col.thetal.const_array();
227  const auto qc = col.qc.const_array();
228  const auto qi = col.qi.const_array();
229  const auto qv = col.qv.const_array();
230  const auto exner = col.exner.const_array();
231  const auto tke = col.tke.const_array();
232  const Real max_horiz_len = std::sqrt(dx * dy);
233  const auto layout = col.layout;
234  const Box col_box(IntVect(0,0,0), IntVect(layout.ncell - 1, 0, 0));
235 
236  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
237  {
238  const Real z_sfc = zi(ic,0,0);
239  Real numerator = 0.0_rt;
240  Real denom = 0.0_rt;
241  for (int k = 0; k < layout.nlev; ++k) {
242  const Real tke_sqrt = std::sqrt(amrex::max(tke(ic,k,0), shoc_min_tke()));
243  const Real zt_agl = shoc::height_agl(zt(ic,k,0), z_sfc);
244  numerator += tke_sqrt * zt_agl * dz(ic,k,0);
245  denom += tke_sqrt * dz(ic,k,0);
246  }
247  const Real l_inf = (denom > 0.0_rt) ? 0.1_rt * numerator / denom : shoc_min_len();
248 
249  for (int k = 0; k < layout.nlev; ++k) {
250  const Real theta_v_k = virtual_theta_from_shoc_state(thetal(ic,k,0), qc(ic,k,0), qi(ic,k,0),
251  qv(ic,k,0), exner(ic,k,0));
252  Real theta_v_lo = theta_v_k;
253  Real theta_v_hi = theta_v_k;
254  if (layout.nlev > 1) {
255  if (k == 0) {
256  const Real theta_v_kp1 = virtual_theta_from_shoc_state(thetal(ic,1,0), qc(ic,1,0), qi(ic,1,0),
257  qv(ic,1,0), exner(ic,1,0));
258  theta_v_lo = weighted_linear_interp(zt(ic,0,0), zt(ic,1,0),
259  theta_v_k, theta_v_kp1, zi(ic,0,0));
260  } else {
261  const Real theta_v_km1 = virtual_theta_from_shoc_state(thetal(ic,k-1,0), qc(ic,k-1,0), qi(ic,k-1,0),
262  qv(ic,k-1,0), exner(ic,k-1,0));
263  theta_v_lo = weighted_linear_interp(zt(ic,k-1,0), zt(ic,k,0),
264  theta_v_km1, theta_v_k, zi(ic,k,0));
265  }
266 
267  if (k == layout.nlev - 1) {
268  const Real theta_v_km1 = virtual_theta_from_shoc_state(thetal(ic,layout.nlev-2,0),
269  qc(ic,layout.nlev-2,0), qi(ic,layout.nlev-2,0),
270  qv(ic,layout.nlev-2,0), exner(ic,layout.nlev-2,0));
271  theta_v_hi = weighted_linear_interp(zt(ic,layout.nlev-2,0), zt(ic,layout.nlev-1,0),
272  theta_v_km1, theta_v_k, zi(ic,layout.nlev,0));
273  } else {
274  const Real theta_v_kp1 = virtual_theta_from_shoc_state(thetal(ic,k+1,0), qc(ic,k+1,0), qi(ic,k+1,0),
275  qv(ic,k+1,0), exner(ic,k+1,0));
276  theta_v_hi = weighted_linear_interp(zt(ic,k,0), zt(ic,k+1,0),
277  theta_v_k, theta_v_kp1, zi(ic,k+1,0));
278  }
279  }
280 
281  // ERF columns use bottom-up indexing, so the upper interface is
282  // k+1 and the lower interface is k. Stable stratification must
283  // therefore yield positive Brunt-Vaisala frequency.
284  brunt(ic,k,0) = (CONST_GRAV / amrex::max(theta_v_k, 1.0e-12_rt)) *
285  (theta_v_hi - theta_v_lo) / amrex::max(dz(ic,k,0), 1.0e-12_rt);
286  const Real tkes = std::sqrt(amrex::max(tke(ic,k,0), shoc_min_tke()));
287  const Real brunt_pos = amrex::max(brunt(ic,k,0), 0.0_rt);
288  const Real zt_agl = shoc::height_agl(zt(ic,k,0), z_sfc);
289  const Real inv_term = (1.0_rt / amrex::max(400.0_rt * tkes * KAPPA * amrex::max(zt_agl, 1.0_rt), 1.0e-12_rt)) +
290  (1.0_rt / amrex::max(400.0_rt * tkes * amrex::max(l_inf, shoc_min_len()), 1.0e-12_rt)) +
291  0.01_rt * brunt_pos / amrex::max(tke(ic,k,0), shoc_min_tke());
292  Real mix = amrex::min(shoc_max_len(),
293  2.8284_rt * std::sqrt(1.0_rt / amrex::max(inv_term, 1.0e-12_rt)) /
294  amrex::max(opts.length_fac, 1.0e-12_rt));
295  mix = amrex::min(max_horiz_len, amrex::max(shoc_min_len(), mix));
296  shoc_mix(ic,k,0) = mix;
297  }
298  });
299 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:52
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:53
const Real dy
Definition: ERF_InitCustomPert_ABL.H:24
const Real dx
Definition: ERF_InitCustomPert_ABL.H:23
ParallelFor(grown_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ qv
Definition: ERF_Kessler.H:29
@ qc
Definition: ERF_SatAdj.H:40
@ qi
Definition: ERF_WSM6.H:26
@ dz
Definition: ERF_AdvanceWSM6.cpp:104
@ zi
Definition: ERF_AdvanceWSM6.cpp:133
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real height_agl(amrex::Real z, amrex::Real z_sfc) noexcept
Definition: ERF_ShocGpuUtils.H:46
amrex::FArrayBox qi
Definition: ERF_ShocTypes.H:220
amrex::FArrayBox dz
Definition: ERF_ShocTypes.H:210
amrex::FArrayBox shoc_mix
Definition: ERF_ShocTypes.H:233
amrex::FArrayBox qc
Definition: ERF_ShocTypes.H:219
amrex::FArrayBox tke
Definition: ERF_ShocTypes.H:223
amrex::FArrayBox exner
Definition: ERF_ShocTypes.H:215
ShocColumnLayout layout
Definition: ERF_ShocTypes.H:205
amrex::FArrayBox zi
Definition: ERF_ShocTypes.H:209
amrex::FArrayBox zt
Definition: ERF_ShocTypes.H:208
amrex::FArrayBox qv
Definition: ERF_ShocTypes.H:218
amrex::FArrayBox brunt
Definition: ERF_ShocTypes.H:234
amrex::FArrayBox thetal
Definition: ERF_ShocTypes.H:217
amrex::Real length_fac
Definition: ERF_ShocTypes.H:92

Referenced by ShocDiagnostics::diagnose_pre_implicit().

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

◆ diagnose_pblh()

void ShocStructure::diagnose_pblh ( ShocColumnData col)
static
113 {
114  auto pblh = col.pblh.array();
115  const auto zt = col.zt.const_array();
116  const auto zi = col.zi.const_array();
117  const auto u = col.u.const_array();
118  const auto v = col.v.const_array();
119  const auto ustar = col.ustar.const_array();
120  const auto obklen = col.obklen.const_array();
121  const auto wthv_sec = col.wthv_sec.const_array();
122  const auto thetal = col.thetal.const_array();
123  const auto qc = col.qc.const_array();
124  const auto qi = col.qi.const_array();
125  const auto qv = col.qv.const_array();
126  const auto exner = col.exner.const_array();
127  const auto p_mid = col.p_mid.const_array();
128  const auto layout = col.layout;
129  const Box col_box(IntVect(0,0,0), IntVect(layout.ncell - 1, 0, 0));
130 
131  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
132  {
133  const int npbl = diagnose_npbl(p_mid, layout, ic);
134  const Real ustar_loc = ustar(ic,0,0);
135  const Real z_sfc = zi(ic,0,0);
136  const Real zt0_agl = shoc::height_agl(zt(ic,0,0), z_sfc);
137  const Real thv0 = virtual_theta_from_shoc_state(thetal(ic,0,0), qc(ic,0,0), qi(ic,0,0),
138  qv(ic,0,0), exner(ic,0,0));
139  Real pblh_loc = shoc::height_agl(zt(ic,npbl-1,0), z_sfc);
140  Real prev_rino = 0.0_rt;
141  bool found_pblh = false;
142 
143  for (int k = 1; k < npbl; ++k) {
144  const Real ztk_agl = shoc::height_agl(zt(ic,k,0), z_sfc);
145  const Real thvk = virtual_theta_from_shoc_state(thetal(ic,k,0), qc(ic,k,0), qi(ic,k,0),
146  qv(ic,k,0), exner(ic,k,0));
147  const Real du = u(ic,k,0) - u(ic,0,0);
148  const Real dv = v(ic,k,0) - v(ic,0,0);
149  const Real vvk = amrex::max(1.0e-36_rt,
150  du * du +
151  dv * dv +
152  shoc_pbl_fac() * ustar_loc * ustar_loc);
153  const Real rino = CONST_GRAV * (thvk - thv0) * (ztk_agl - zt0_agl) /
154  (amrex::max(thv0, 1.0e-12_rt) * vvk);
155  if (rino >= shoc_pbl_ricr()) {
156  if (k == 1 || amrex::Math::abs(rino - prev_rino) <= 1.0e-12_rt) {
157  pblh_loc = ztk_agl;
158  } else {
159  const Real ztkm1_agl = shoc::height_agl(zt(ic,k-1,0), z_sfc);
160  pblh_loc = ztkm1_agl + (shoc_pbl_ricr() - prev_rino) *
161  (ztk_agl - ztkm1_agl) / (rino - prev_rino);
162  }
163  found_pblh = true;
164  break;
165  }
166  prev_rino = rino;
167  }
168 
169  if (!found_pblh) {
170  pblh_loc = shoc::height_agl(zt(ic,npbl-1,0), z_sfc);
171  }
172 
173  if (wthv_sec(ic,0,0) > 0.0_rt) {
174  const Real obk_abs = amrex::max(amrex::Math::abs(obklen(ic,0,0)), 1.0e-6_rt);
175  const Real obk = std::copysign(obk_abs, obklen(ic,0,0));
176  const Real binm = shoc_pbl_betam() * shoc_pbl_sffrac();
177  const Real phiminv = std::cbrt(amrex::max(1.0e-12_rt, 1.0_rt - binm * pblh_loc / obk));
178  const Real tlv = thv0 + wthv_sec(ic,0,0) * shoc_pbl_fak() /
179  (amrex::max(ustar_loc, shoc_u_star_min()) * phiminv);
180  prev_rino = 0.0_rt;
181  for (int k = 1; k < npbl; ++k) {
182  const Real ztk_agl = shoc::height_agl(zt(ic,k,0), z_sfc);
183  const Real thvk = virtual_theta_from_shoc_state(thetal(ic,k,0), qc(ic,k,0), qi(ic,k,0),
184  qv(ic,k,0), exner(ic,k,0));
185  const Real du = u(ic,k,0) - u(ic,0,0);
186  const Real dv = v(ic,k,0) - v(ic,0,0);
187  const Real vvk = amrex::max(1.0e-36_rt,
188  du * du +
189  dv * dv +
190  shoc_pbl_fac() * ustar_loc * ustar_loc);
191  const Real rino = CONST_GRAV * (thvk - tlv) * (ztk_agl - zt0_agl) /
192  (amrex::max(thv0, 1.0e-12_rt) * vvk);
193  if (rino >= shoc_pbl_ricr()) {
194  if (k == 1 || amrex::Math::abs(rino - prev_rino) <= 1.0e-12_rt) {
195  pblh_loc = ztk_agl;
196  } else {
197  const Real ztkm1_agl = shoc::height_agl(zt(ic,k-1,0), z_sfc);
198  pblh_loc = ztkm1_agl + (shoc_pbl_ricr() - prev_rino) *
199  (ztk_agl - ztkm1_agl) / (rino - prev_rino);
200  }
201  break;
202  }
203  prev_rino = rino;
204  }
205  }
206 
207  pblh_loc = amrex::max(pblh_loc, 700.0_rt * ustar_loc);
208  if (qc(ic,0,0) + qi(ic,0,0) > 0.0_rt && layout.nlev > 1) {
209  pblh_loc = amrex::max(pblh_loc, shoc::height_agl(zi(ic,1,0), z_sfc) + 50.0_rt);
210  }
211  pblh(ic,0,0) = pblh_loc;
212  });
213 }
amrex::FArrayBox ustar
Definition: ERF_ShocTypes.H:232
amrex::FArrayBox wthv_sec
Definition: ERF_ShocTypes.H:241
amrex::FArrayBox pblh
Definition: ERF_ShocTypes.H:230
amrex::FArrayBox v
Definition: ERF_ShocTypes.H:225
amrex::FArrayBox p_mid
Definition: ERF_ShocTypes.H:211
amrex::FArrayBox u
Definition: ERF_ShocTypes.H:224
amrex::FArrayBox obklen
Definition: ERF_ShocTypes.H:231

Referenced by ShocDiagnostics::diagnose_pre_implicit().

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

◆ diagnose_surface_layer()

void ShocStructure::diagnose_surface_layer ( ShocColumnData col)
static
67 {
68  auto ustar = col.ustar.array();
69  auto obklen = col.obklen.array();
70  auto wthv_sec = col.wthv_sec.array();
71  auto pblh = col.pblh.array();
72  const auto zi = col.zi.const_array();
73  const auto thetal = col.thetal.const_array();
74  const auto qc = col.qc.const_array();
75  const auto qi = col.qi.const_array();
76  const auto qv = col.qv.const_array();
77  const auto exner = col.exner.const_array();
78  const auto sflux = col.surf_sens_flux.const_array();
79  const auto lflux = col.surf_lat_flux.const_array();
80  const auto tauu = col.surf_tau_u.const_array();
81  const auto tauv = col.surf_tau_v.const_array();
82  const auto zt = col.zt.const_array();
83  const auto layout = col.layout;
84  const Box col_box(IntVect(0,0,0), IntVect(layout.ncell - 1, 0, 0));
85 
86  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
87  {
88  const Real cldliq_sfc = qc(ic,0,0) + qi(ic,0,0);
89  const Real th_sfc = theta_from_shoc_state(thetal(ic,0,0), qc(ic,0,0), qi(ic,0,0), exner(ic,0,0));
90  const Real thv_sfc = th_sfc * (1.0_rt + shoc_zvir() * qv(ic,0,0) - cldliq_sfc);
91  const Real stress_mag = std::sqrt(tauu(ic,0,0) * tauu(ic,0,0) +
92  tauv(ic,0,0) * tauv(ic,0,0));
93  const Real ustar_val = std::sqrt(stress_mag);
94  const Real kbfs = sflux(ic,0,0) + shoc_zvir() * th_sfc * lflux(ic,0,0);
95  const Real sign_val = (kbfs >= 0.0_rt) ? shoc_kbfs_eps() : -shoc_kbfs_eps();
96 
97  ustar(ic,0,0) = amrex::max(shoc_u_star_min(), ustar_val);
98  const Real ustar_cu = ustar(ic,0,0) * ustar(ic,0,0) * ustar(ic,0,0);
99  obklen(ic,0,0) = -thv_sfc * ustar_cu /
100  (CONST_GRAV * KAPPA * (kbfs + sign_val));
101  pblh(ic,0,0) = shoc::height_agl(zt(ic,0,0), zi(ic,0,0));
102 
103  // E3SM SHOC advances TKE using the carried buoyancy-flux profile from
104  // the previous SHOC call. ERF overwrites only the surface entry here
105  // with the current lower-boundary forcing and preserves the interior
106  // profile that was diagnosed later in the previous SHOC call.
107  wthv_sec(ic,0,0) = kbfs;
108  });
109 }
amrex::FArrayBox surf_lat_flux
Definition: ERF_ShocTypes.H:277
amrex::FArrayBox surf_tau_v
Definition: ERF_ShocTypes.H:279
amrex::FArrayBox surf_sens_flux
Definition: ERF_ShocTypes.H:276
amrex::FArrayBox surf_tau_u
Definition: ERF_ShocTypes.H:278

Referenced by ShocDiagnostics::diagnose_pre_implicit().

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

The documentation for this class was generated from the following files: