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
230 {
231  auto brunt = col.brunt.array();
232  auto shoc_mix = col.shoc_mix.array();
233  const auto zt = col.zt.const_array();
234  const auto zi = col.zi.const_array();
235  const auto dz = col.dz.const_array();
236  const auto thetal = col.thetal.const_array();
237  const auto qc = col.qc.const_array();
238  const auto qi = col.qi.const_array();
239  const auto qv = col.qv.const_array();
240  const auto exner = col.exner.const_array();
241  const auto tke = col.tke.const_array();
242  const Real max_horiz_len = std::sqrt(dx * dy);
243  const auto layout = col.layout;
244  const Box col_box(IntVect(0,0,0), IntVect(layout.ncell - 1, 0, 0));
245 
246  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
247  {
248  const Real z_sfc = zi(ic,0,0);
249  Real numerator = 0.0_rt;
250  Real denom = 0.0_rt;
251  for (int k = 0; k < layout.nlev; ++k) {
252  const Real tke_sqrt = std::sqrt(amrex::max(tke(ic,k,0), shoc_min_tke()));
253  const Real zt_agl = shoc::height_agl(zt(ic,k,0), z_sfc);
254  numerator += tke_sqrt * zt_agl * dz(ic,k,0);
255  denom += tke_sqrt * dz(ic,k,0);
256  }
257  const Real l_inf = (denom > 0.0_rt) ? 0.1_rt * numerator / denom : shoc_min_len();
258 
259  for (int k = 0; k < layout.nlev; ++k) {
260  const Real theta_v_k = virtual_theta_from_shoc_state(thetal(ic,k,0), qc(ic,k,0), qi(ic,k,0),
261  qv(ic,k,0), exner(ic,k,0));
262  Real theta_v_lo = theta_v_k;
263  Real theta_v_hi = theta_v_k;
264  if (layout.nlev > 1) {
265  if (k == 0) {
266  const Real theta_v_kp1 = virtual_theta_from_shoc_state(thetal(ic,1,0), qc(ic,1,0), qi(ic,1,0),
267  qv(ic,1,0), exner(ic,1,0));
268  theta_v_lo = weighted_linear_interp(zt(ic,0,0), zt(ic,1,0),
269  theta_v_k, theta_v_kp1, zi(ic,0,0));
270  } else {
271  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),
272  qv(ic,k-1,0), exner(ic,k-1,0));
273  theta_v_lo = weighted_linear_interp(zt(ic,k-1,0), zt(ic,k,0),
274  theta_v_km1, theta_v_k, zi(ic,k,0));
275  }
276 
277  if (k == layout.nlev - 1) {
278  const Real theta_v_km1 = virtual_theta_from_shoc_state(thetal(ic,layout.nlev-2,0),
279  qc(ic,layout.nlev-2,0), qi(ic,layout.nlev-2,0),
280  qv(ic,layout.nlev-2,0), exner(ic,layout.nlev-2,0));
281  theta_v_hi = weighted_linear_interp(zt(ic,layout.nlev-2,0), zt(ic,layout.nlev-1,0),
282  theta_v_km1, theta_v_k, zi(ic,layout.nlev,0));
283  } else {
284  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),
285  qv(ic,k+1,0), exner(ic,k+1,0));
286  theta_v_hi = weighted_linear_interp(zt(ic,k,0), zt(ic,k+1,0),
287  theta_v_k, theta_v_kp1, zi(ic,k+1,0));
288  }
289  }
290 
291  // ERF columns use bottom-up indexing, so the upper interface is
292  // k+1 and the lower interface is k. Stable stratification must
293  // therefore yield positive Brunt-Vaisala frequency.
294  brunt(ic,k,0) = (CONST_GRAV / amrex::max(theta_v_k, 1.0e-12_rt)) *
295  (theta_v_hi - theta_v_lo) / amrex::max(dz(ic,k,0), 1.0e-12_rt);
296  const Real tkes = std::sqrt(amrex::max(tke(ic,k,0), shoc_min_tke()));
297  const Real brunt_pos = amrex::max(brunt(ic,k,0), 0.0_rt);
298  const Real zt_agl = shoc::height_agl(zt(ic,k,0), z_sfc);
299  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)) +
300  (1.0_rt / amrex::max(400.0_rt * tkes * amrex::max(l_inf, shoc_min_len()), 1.0e-12_rt)) +
301  0.01_rt * brunt_pos / amrex::max(tke(ic,k,0), shoc_min_tke());
302  Real mix = amrex::min(shoc_max_len(),
303  2.8284_rt * std::sqrt(1.0_rt / amrex::max(inv_term, 1.0e-12_rt)) /
304  amrex::max(opts.length_fac, 1.0e-12_rt));
305  mix = amrex::min(max_horiz_len, amrex::max(shoc_min_len(), mix));
306  shoc_mix(ic,k,0) = mix;
307  }
308  });
309 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:55
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:56
const Real dy
Definition: ERF_InitCustomPert_ABL.H:45
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
ParallelFor(fab_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:31
@ qc
Definition: ERF_SatAdj.H:42
@ qi
Definition: ERF_WDM6.H:28
@ dz
Definition: ERF_AdvanceWDM6.cpp:272
@ zi
Definition: ERF_AdvanceWDM6.cpp:276
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real height_agl(amrex::Real z, amrex::Real z_sfc) noexcept
Definition: ERF_ShocGpuUtils.H:50
amrex::FArrayBox qi
Definition: ERF_ShocTypes.H:88
amrex::FArrayBox dz
Definition: ERF_ShocTypes.H:78
amrex::FArrayBox shoc_mix
Definition: ERF_ShocTypes.H:101
amrex::FArrayBox qc
Definition: ERF_ShocTypes.H:87
amrex::FArrayBox tke
Definition: ERF_ShocTypes.H:91
amrex::FArrayBox exner
Definition: ERF_ShocTypes.H:83
ShocColumnLayout layout
Definition: ERF_ShocTypes.H:73
amrex::FArrayBox zi
Definition: ERF_ShocTypes.H:77
amrex::FArrayBox zt
Definition: ERF_ShocTypes.H:76
amrex::FArrayBox qv
Definition: ERF_ShocTypes.H:86
amrex::FArrayBox brunt
Definition: ERF_ShocTypes.H:102
amrex::FArrayBox thetal
Definition: ERF_ShocTypes.H:85
amrex::Real length_fac
Definition: ERF_ShocTypes.H:28

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

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
77 {
78  auto ustar = col.ustar.array();
79  auto obklen = col.obklen.array();
80  auto wthv_sec = col.wthv_sec.array();
81  auto pblh = col.pblh.array();
82  const auto zi = col.zi.const_array();
83  const auto thetal = col.thetal.const_array();
84  const auto qc = col.qc.const_array();
85  const auto qi = col.qi.const_array();
86  const auto qv = col.qv.const_array();
87  const auto exner = col.exner.const_array();
88  const auto sflux = col.surf_sens_flux.const_array();
89  const auto lflux = col.surf_lat_flux.const_array();
90  const auto tauu = col.surf_tau_u.const_array();
91  const auto tauv = col.surf_tau_v.const_array();
92  const auto zt = col.zt.const_array();
93  const auto layout = col.layout;
94  const Box col_box(IntVect(0,0,0), IntVect(layout.ncell - 1, 0, 0));
95 
96  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
97  {
98  const Real cldliq_sfc = qc(ic,0,0) + qi(ic,0,0);
99  const Real th_sfc = theta_from_shoc_state(thetal(ic,0,0), qc(ic,0,0), qi(ic,0,0), exner(ic,0,0));
100  const Real thv_sfc = th_sfc * (1.0_rt + shoc_zvir() * qv(ic,0,0) - cldliq_sfc);
101  const Real stress_mag = std::sqrt(tauu(ic,0,0) * tauu(ic,0,0) +
102  tauv(ic,0,0) * tauv(ic,0,0));
103  const Real ustar_val = std::sqrt(stress_mag);
104  const Real kbfs = sflux(ic,0,0) + shoc_zvir() * th_sfc * lflux(ic,0,0);
105  const Real sign_val = (kbfs >= 0.0_rt) ? shoc_kbfs_eps() : -shoc_kbfs_eps();
106 
107  ustar(ic,0,0) = amrex::max(shoc_u_star_min(), ustar_val);
108  const Real ustar_cu = ustar(ic,0,0) * ustar(ic,0,0) * ustar(ic,0,0);
109  obklen(ic,0,0) = -thv_sfc * ustar_cu /
110  (CONST_GRAV * KAPPA * (kbfs + sign_val));
111  pblh(ic,0,0) = shoc::height_agl(zt(ic,0,0), zi(ic,0,0));
112 
113  // E3SM SHOC advances TKE using the carried buoyancy-flux profile from
114  // the previous SHOC call. ERF overwrites only the surface entry here
115  // with the current lower-boundary forcing and preserves the interior
116  // profile that was diagnosed later in the previous SHOC call.
117  wthv_sec(ic,0,0) = kbfs;
118  });
119 }
amrex::FArrayBox surf_lat_flux
Definition: ERF_ShocTypes.H:145
amrex::FArrayBox surf_tau_v
Definition: ERF_ShocTypes.H:147
amrex::FArrayBox surf_sens_flux
Definition: ERF_ShocTypes.H:144
amrex::FArrayBox surf_tau_u
Definition: ERF_ShocTypes.H:146

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: