Compute vertical eddy viscosity coefficients using the Yonsei University (YSU) boundary layer scheme.
112 int klo = geom.Domain().smallEnd(2);
113 int khi = geom.Domain().bigEnd(2);
114 const int izmin = klo;
115 const int izmax =
khi;
117 const Real dz = geom.CellSize(2);
118 const Real dz_inv = geom.InvCellSize(2);
119 const auto&
dxInv = geom.InvCellSizeArray();
122 #pragma omp parallel if (Gpu::notInLaunchRegion())
124 for (MFIter mfi(eddyViscosity,
TileNoZ()); mfi.isValid(); ++mfi) {
127 const Box& gbx = mfi.growntilebox(IntVect(1,1,0));
129 gbx.bigEnd(2) ==
khi );
147 const Box xybx = PerpendicularBox<ZDir>(gbx, IntVect{0, 0, 0});
148 FArrayBox pbl_height_corrector(xybx, 1, The_Async_Arena());
149 IArrayBox pbl_index(xybx, 1, The_Async_Arena());
150 IArrayBox pbl_index_zero_ri(xybx, 1, The_Async_Arena());
151 FArrayBox hgamt_fab(xybx, 1, The_Async_Arena());
152 FArrayBox hgamq_fab(xybx, 1, The_Async_Arena());
153 FArrayBox hgamu_fab(xybx, 1, The_Async_Arena());
154 FArrayBox hgamv_fab(xybx, 1, The_Async_Arena());
155 FArrayBox wstar_fab(xybx, 1, The_Async_Arena());
156 FArrayBox vpert_fab(xybx, 1, The_Async_Arena());
157 FArrayBox entr_fab(xybx, 1, The_Async_Arena());
158 IArrayBox cloud_top_fab(xybx, 1, The_Async_Arena());
159 FArrayBox wstar3_down_fab(xybx, 1, The_Async_Arena());
160 FArrayBox sflux_fab(xybx, 1, The_Async_Arena());
161 FArrayBox wstar3_fab(xybx, 1, The_Async_Arena());
162 FArrayBox zol1_fab(xybx, 1, The_Async_Arena());
163 FArrayBox sfcflg_fab(xybx, 1, The_Async_Arena());
167 vpert_fab.setVal<RunOn::Device>(
zero);
168 sflux_fab.setVal<RunOn::Device>(
zero);
169 wstar3_fab.setVal<RunOn::Device>(
zero);
170 wstar3_down_fab.setVal<RunOn::Device>(
zero);
171 sfcflg_fab.setVal<RunOn::Device>(
zero);
172 hgamt_fab.setVal<RunOn::Device>(
zero);
173 hgamq_fab.setVal<RunOn::Device>(
zero);
174 hgamu_fab.setVal<RunOn::Device>(
zero);
175 hgamv_fab.setVal<RunOn::Device>(
zero);
176 wstar_fab.setVal<RunOn::Device>(
zero);
177 entr_fab.setVal<RunOn::Device>(
zero);
178 pbl_height_corrector.setVal<RunOn::Device>(
zero);
179 const auto& pblh_corr_arr = pbl_height_corrector.array();
180 const auto& pbli_arr = pbl_index.array();
181 const auto& pbli_zero_arr = pbl_index_zero_ri.array();
182 const auto& hgamt_arr = hgamt_fab.array();
183 const auto& hgamq_arr = hgamq_fab.array();
184 const auto& hgamu_arr = hgamu_fab.array();
185 const auto& hgamv_arr = hgamv_fab.array();
186 const auto& wstar_arr = wstar_fab.array();
187 const auto& vpert_arr = vpert_fab.array();
188 const auto& entr_arr = entr_fab.array();
189 const auto& cloud_top_arr = cloud_top_fab.array();
190 const auto& wstar3_down_arr = wstar3_down_fab.array();
191 const auto& sflux_arr = sflux_fab.array();
192 const auto& wstar3_arr = wstar3_fab.array();
193 const auto& zol1_arr = zol1_fab.array();
194 const auto& sfcflg_arr = sfcflg_fab.array();
197 const auto& cell_data = cons_in.const_array(mfi);
198 const auto& uvel =
xvel.const_array(mfi);
199 const auto& vvel =
yvel.const_array(mfi);
203 const auto& u_star_arr = SurfLayer->get_u_star(level)->const_array(mfi);
204 const auto& t_star_arr = SurfLayer->get_t_star(level)->const_array(mfi);
205 const auto& q_star_arr = SurfLayer->get_q_star(level)->const_array(mfi);
206 const auto& l_obuk_arr = SurfLayer->get_olen(level)->const_array(mfi);
207 const auto& t10av_arr = SurfLayer->get_mac_avg(level, 2)->const_array(mfi);
208 const auto& q10av_arr = SurfLayer->get_mac_avg(level, 3)->const_array(mfi);
209 const auto& ws10av_arr = SurfLayer->get_mac_avg(level, 5)->const_array(mfi);
210 const auto& z0_arr = SurfLayer->get_z0(level)->const_array(mfi);
213 const auto& lmask_arr = (SurfLayer->get_lmask(level)) ?
214 SurfLayer->get_lmask(level)->const_array(mfi) :
216 const Array4<Real const> z_nd_arr = z_phys_nd->array(mfi);
219 const Array4<Real const> qheat_arr = (qheating_rates !=
nullptr)
220 ? qheating_rates->const_array(mfi)
221 : Array4<Real const>{};
222 const bool has_qheating_rates = (qheating_rates !=
nullptr);
234 FArrayBox rib_base_fab(gbx, 1, The_Async_Arena());
235 FArrayBox rib_enhan_fab(gbx, 1, The_Async_Arena());
236 const auto& rib_base_arr = rib_base_fab.array();
237 const auto& rib_enhan_arr = rib_enhan_fab.array();
239 BL_PROFILE_VAR(
"YSUNew_Rib_Precompute", prof_rib_precomp);
240 ParallelFor(gbx, [=] AMREX_GPU_DEVICE(
int i,
int j,
int k) noexcept
246 const amrex::Real t_enh = t_layer_v + vpert_arr(i,j,0);
247 const amrex::Real z_sfc = (use_terrain_fitted_coords)
249 const amrex::Real zval = (use_terrain_fitted_coords)
254 ?
GetThetavl(i,j,k,cell_data,moisture_indices)
255 :
GetThetav(i,j,k,cell_data,moisture_indices);
257 ?
GetThetavl(i,j,klo,cell_data,moisture_indices)
258 :
GetThetav(i,j,klo,cell_data,moisture_indices);
259 const amrex::Real ws2_raw =
fourth * ((uvel(i,j,k)+uvel(i+1,j,k))*(uvel(i,j,k)+uvel(i+1,j,k))
260 + (vvel(i,j,k)+vvel(i,j+1,k))*(vvel(i,j,k)+vvel(i,j+1,k)));
262 rib_base_arr(i,j,k) =
CONST_GRAV * zrel * (theta_v - t_layer_v) / (ws2 * theta_v_klo);
263 rib_enhan_arr(i,j,k) =
CONST_GRAV * zrel * (theta_v - t_enh) / (ws2 * theta_v_klo);
265 BL_PROFILE_VAR_STOP(prof_rib_precomp);
278 BL_PROFILE_VAR(
"YSUNew_SurfFlux_Precompute", prof_sflux_precomp);
279 ParallelFor(xybx, [=, zero_d=
zero] AMREX_GPU_DEVICE(
int i,
int j,
int) noexcept
282 const Real t_layer = t10av_arr(i, j, 0);
285 const Real rhox = rho_sfc;
295 const Real ustar = u_star_arr(i, j, 0);
296 const Real tstar = t_star_arr(i, j, 0);
298 const Real hfx = -rhox * cp_air * ustar * tstar;
299 const Real qfx = -rhox * ustar * qstar;
300 const Real sflux = hfx / (rhox * cp_air) + qfx / rhox * ep1 * t_layer;
301 sflux_arr(i, j, 0) = sflux;
306 const bool sfcflg = (sflux >
zero);
307 sfcflg_arr(i, j, 0) = sfcflg ?
one :
zero;
316 const Real bfx0 = amrex::max(sflux, zero_d);
317 const Real wstar3 = govrth * bfx0 * pblh_guess;
318 wstar3_arr(i, j, 0) = wstar3;
323 const Real ust3 = ustar * ustar * ustar;
326 wscale = amrex::min(wscale, ustar *
amrex::Real(16.0));
327 wscale = amrex::max(wscale, ustar /
amrex::Real(5.0));
328 wstar_arr(i, j, 0) = wscale;
333 Real obuk_val = l_obuk_arr(i, j, 0);
336 const Real zl1 = (use_terrain_fitted_coords)
339 Real zol1 = zl1 / obuk_val;
346 zol1_arr(i, j, 0) = zol1;
348 BL_PROFILE_VAR_STOP(prof_sflux_precomp);
360 BL_PROFILE_VAR(
"YSUNew_PBLH_Passes", prof_pblh);
361 ParallelFor(xybx, [=] AMREX_GPU_DEVICE(
int i,
int j,
int) noexcept
367 bool over_land = (!lmask_arr) || (lmask_arr(i, j, 0) == 1);
371 const Real z0 = z0_arr(i, j, 0);
372 const Real ws_layer = ws10av_arr(i, j, 0);
374 Ribcr = amrex::min(
Real(0.16) * std::pow(
Real(1.0e-7) * Rossby, -
Real(0.18)),
Real(0.3));
382 Real Rib = rib_base_arr(i,j,klo);
383 bool above_critical = (Rib >= Ribcr);
386 for (
int kk = klo+1; !above_critical && kk <=
khi; ++kk) {
387 if (rib_base_arr(i,j,kk) >= Ribcr) {
389 above_critical =
true;
394 pbli_arr(i, j, 0) = kpbl;
396 BL_PROFILE_VAR_STOP(prof_pblh);
481 constexpr
Real GAMCRQ =
Real(2.e-3);
484 ParallelFor(xybx, [=] AMREX_GPU_DEVICE(
int i,
int j,
int) noexcept
490 bool over_land = (!lmask_arr) || (lmask_arr(i, j, 0) == 1);
494 const Real z0 = z0_arr(i, j, 0);
495 const Real ws_layer = ws10av_arr(i, j, 0);
497 Ribcr = amrex::min(
Real(0.16) * std::pow(
Real(1.0e-7) * Rossby, -
Real(0.18)),
Real(0.3));
507 const amrex::Real z_sfc_col = (use_terrain_fitted_coords)
515 Real Rib = rib_enhan_arr(i,j,klo);
516 zval0 = (use_terrain_fitted_coords)
520 bool above_critical = (Rib >= Ribcr);
523 for (
int kk = klo+1; !above_critical && kk <=
khi; ++kk) {
524 if (rib_enhan_arr(i,j,kk) >= Ribcr) { kpbl = kk; above_critical =
true;
break; }
525 zval0 = (use_terrain_fitted_coords)
528 Rib0 = rib_enhan_arr(i,j,kk);
533 const Real z_sfc = (use_terrain_fitted_coords)
536 const Real dz_terrain = (use_terrain_fitted_coords)
539 const Real z_max = (use_terrain_fitted_coords)
542 const Real pblh_max =
Real(0.9) * z_max;
548 pblh_min = amrex::max(z_sfc_col +
Real(0.5)*dz0,
Real(10.0));
550 pblh_min = amrex::max(z_sfc +
Real(0.5) * dz_terrain,
Real(10.0));
554 if (kpbl <
khi && rib_enhan_arr(i,j,kpbl) >= Ribcr) {
555 const Real zval = (use_terrain_fitted_coords)
558 Rib = rib_enhan_arr(i,j,kpbl);
559 Real pblh_interp = zval0 + (zval - zval0) / (Rib - Rib0) * (Ribcr - Rib0);
560 pblh_corr_arr(i, j, 0) = amrex::max(amrex::min(pblh_interp, pblh_max), pblh_min);
562 pblh_corr_arr(i, j, 0) = pblh_min;
564 pbli_arr(i, j, 0) = kpbl;
567 hgamt_arr(i, j, 0) =
zero;
568 hgamq_arr(i, j, 0) =
zero;
569 wstar_arr(i, j, 0) =
zero;
613 ParallelFor(xybx, [=] AMREX_GPU_DEVICE(
int i,
int j,
int) noexcept
615 cloud_top_arr(i, j, 0) = -1;
618 int kpbl = pbli_arr(i, j, 0);
619 for (
int kk = kpbl - 1; kk >= klo; --kk) {
621 if (moisture_indices.
qc >= 0)
622 qc_kk = cell_data(i, j, kk, moisture_indices.
qc) / cell_data(i, j, kk,
Rho_comp);
623 if (moisture_indices.
qi >= 0)
624 qi_kk = cell_data(i, j, kk, moisture_indices.
qi) / cell_data(i, j, kk,
Rho_comp);
626 if (qc_kk + qi_kk > ysu_qcloud_threshold) {
627 cloud_top_arr(i, j, 0) = kk;
634 ParallelFor(xybx, [=] AMREX_GPU_DEVICE(
int i,
int j,
int) noexcept
636 cloud_top_arr(i, j, 0) = -1;
642 const Real t_layer = t10av_arr(i, j, 0);
643 Real obuk_val = l_obuk_arr(i, j, 0);
652 const Real HOL = sf * pblh_corr_arr(i, j, 0) / obuk_val;
653 const Real HOL_bounded = amrex::max(amrex::min(HOL,
Real(100.0)),
Real(-100.0));
655 const Real phiM = (obuk_val > 0)
656 ? (1 + 5 * HOL_bounded)
658 amrex::max(1 - 16 * HOL_bounded,
Real(0.01)),
660 const Real phiM_safe = amrex::max(phiM,
Real(0.01));
666 Real wscale = u_star_arr(i, j, 0) / phiM_safe;
667 wscale = amrex::max(wscale, u_star_arr(i, j, 0) /
Real(5.0));
668 wscale = amrex::min(wscale,
Real(16.0) * u_star_arr(i, j, 0));
673 if (enable_ysu_topdown && cloud_top_arr(i, j, 0) >= klo) {
674 int k_cloud_top = cloud_top_arr(i, j, 0);
682 if (has_qheating_rates) {
685 for (
int kk = klo; kk <= k_cloud_top; ++kk) {
687 ? (z_nd_arr(i, j, kk+1) - z_nd_arr(i, j, kk))
691 LRAD += -qheat_arr(i, j, kk, 1) * ldz;
698 / cell_data(i, j, k_cloud_top,
Rho_comp);
700 wstar3_down = amrex::max(CONST_GRAV_d / t_local * LRAD
702 * pblh_corr_arr(i, j, 0), zero_d);
704 wstar3_down_arr(i, j, 0) = wstar3_down;
707 const Real bfx0_corr = amrex::max(sflux_arr(i, j, 0), zero_d);
708 const Real t_dry = t10av_arr(i, j, 0);
709 const Real wstar3_corr = (
CONST_GRAV / t_dry) * bfx0_corr * pblh_corr_arr(i, j, 0);
710 wstar3_arr(i, j, 0) = wstar3_corr;
713 const Real ust3 = u_star_arr(i,j,0) * u_star_arr(i,j,0) * u_star_arr(i,j,0);
715 wscale_corr = amrex::min(wscale_corr, u_star_arr(i,j,0) *
amrex::Real(16.0));
716 wscale_corr = amrex::max(wscale_corr, u_star_arr(i,j,0) /
amrex::Real(5.0));
717 wstar_arr(i, j, 0) = wscale_corr;
723 bool SFCFLG = (sfcflg_arr(i, j, 0) >
zero);
725 const Real hfx_col = -rho_sfc *
amrex::Real(1004.0) * u_star_arr(i,j,0) * t_star_arr(i,j,0);
726 const Real gamfac = const_b / (rho_sfc * wscale_corr);
727 const Real HGAMT = (SFCFLG && enable_ysu_countergradient)
728 ? amrex::min(gamfac * hfx_col /
amrex::Real(1004.0), GAMCRT)
733 const Real qfx_col = -rho_sfc * u_star_arr(i,j,0) * q_star_arr(i,j,0);
734 const Real HGAMQ_raw = (SFCFLG &&
use_moisture && enable_ysu_countergradient)
737 Real HGAMQ = amrex::min(amrex::max(HGAMQ_raw, zero_d), GAMCRQ);
740 if (lmask_arr && SFCFLG &&
use_moisture && enable_ysu_countergradient) {
741 bool is_land = (lmask_arr(i,j,0) == 1);
742 if (!is_land) HGAMQ =
zero;
746 if (enable_ysu_sat_limiter && moisture_indices.
qv >= 0 && SFCFLG &&
use_moisture && enable_ysu_countergradient) {
747 Real qv_klo = cell_data(i, j, klo, moisture_indices.
qv) / cell_data(i, j, klo,
Rho_comp);
754 Real rh_klo = (qsat_klo >
Real(1.0e-10)) ? (qv_klo / qsat_klo) :
zero;
755 if (rh_klo >
Real(0.95)) {
756 Real rh_scaling = amrex::max(zero_d, (
one - rh_klo) /
Real(0.05));
763 if (pbli_arr(i, j, 0) <= klo + 1) {
764 hgamt_arr(i, j, 0) =
zero;
765 hgamq_arr(i, j, 0) =
zero;
766 hgamu_arr(i, j, 0) =
zero;
767 hgamv_arr(i, j, 0) =
zero;
768 vpert_arr(i, j, 0) =
zero;
770 const Real pblh = pblh_corr_arr(i, j, 0);
771 hgamt_arr(i, j, 0) = (enable_ysu_countergradient) ? HGAMT / pblh :
zero;
772 hgamq_arr(i, j, 0) = (enable_ysu_countergradient &&
use_moisture) ? HGAMQ / pblh :
zero;
786 hgamu_arr(i, j, 0) =
zero;
787 hgamv_arr(i, j, 0) =
zero;
788 if (SFCFLG && enable_ysu_countergradient) {
789 const Real wspd_sfc = ws10av_arr(i, j, 0);
790 const Real ustar = u_star_arr(i, j, 0);
791 wscale = wstar_arr(i, j, 0);
792 const Real wstar3 = wstar3_arr(i, j, 0);
794 const Real wscale4 = amrex::max(wscale * wscale * wscale * wscale,
801 const Real u_klo =
myhalf * (uvel(i, j, klo) + uvel(i+1, j, klo));
802 const Real v_klo =
myhalf * (vvel(i, j, klo) + vvel(i, j+1, klo));
803 hgamu_arr(i, j, 0) = brint * u_klo / pblh;
804 hgamv_arr(i, j, 0) = brint * v_klo / pblh;
813 if (enable_ysu_countergradient) {
816 const Real zl1_col = (use_terrain_fitted_coords)
820 const Real height_lim = amrex::min(zl1_col / (sfcfrac_h * pblh), one_d);
823 const Real VPERT_capped = enable_ysu_unbounded_vpert
825 : amrex::min(VPERT_raw, GAMCRT);
826 vpert_arr(i, j, 0) = amrex::max(VPERT_capped, zero_d) * height_lim;
828 vpert_arr(i, j, 0) =
zero;
846 BL_PROFILE_VAR(
"YSUNew_Rib_Recompute", prof_rib_recomp);
847 ParallelFor(gbx, [=] AMREX_GPU_DEVICE(
int i,
int j,
int k) noexcept
853 const amrex::Real t_enh = t_layer_v + vpert_arr(i,j,0);
854 const amrex::Real z_sfc = (use_terrain_fitted_coords)
856 const amrex::Real zval = (use_terrain_fitted_coords)
861 ?
GetThetavl(i,j,k,cell_data,moisture_indices)
862 :
GetThetav(i,j,k,cell_data,moisture_indices);
864 ?
GetThetavl(i,j,klo,cell_data,moisture_indices)
865 :
GetThetav(i,j,klo,cell_data,moisture_indices);
866 const amrex::Real ws2_raw =
fourth * ((uvel(i,j,k)+uvel(i+1,j,k))*(uvel(i,j,k)+uvel(i+1,j,k))
867 + (vvel(i,j,k)+vvel(i,j+1,k))*(vvel(i,j,k)+vvel(i,j+1,k)));
870 rib_enhan_arr(i,j,k) =
CONST_GRAV * zrel * (theta_v - t_enh) / (ws2 * theta_v_klo);
872 BL_PROFILE_VAR_STOP(prof_rib_recomp);
881 ParallelFor(xybx, [=] AMREX_GPU_DEVICE(
int i,
int j,
int) noexcept
886 bool over_land = (!lmask_arr) || (lmask_arr(i, j, 0) == 1);
890 const Real z0 = z0_arr(i, j, 0);
891 const Real ws_layer = ws10av_arr(i, j, 0);
893 Ribcr = amrex::min(
Real(0.16) * std::pow(
Real(1.0e-7) * Rossby, -
Real(0.18)),
Real(0.3));
900 Real Rib = rib_enhan_arr(i,j,klo);
901 zval0 = (use_terrain_fitted_coords)
905 bool above_critical = (Rib >= Ribcr);
907 for (
int kk = klo+1; !above_critical && kk <=
khi; ++kk) {
908 if (rib_enhan_arr(i,j,kk) >= Ribcr) { kpbl = kk; above_critical =
true;
break; }
909 zval0 = (use_terrain_fitted_coords)
912 Rib0 = rib_enhan_arr(i,j,kk);
917 const Real z_sfc = (use_terrain_fitted_coords)
920 const Real dz_terrain = (use_terrain_fitted_coords)
923 const Real z_max = (use_terrain_fitted_coords)
926 const Real pblh_max =
Real(0.9) * z_max;
932 const amrex::Real z_sfc_col = (use_terrain_fitted_coords)
935 pblh_min = amrex::max(z_sfc_col +
Real(0.5)*dz0,
Real(10.0));
937 pblh_min = amrex::max(z_sfc +
Real(0.5) * dz_terrain,
Real(10.0));
941 if (kpbl <
khi && rib_enhan_arr(i,j,kpbl) >= Ribcr) {
942 const Real zval = (use_terrain_fitted_coords)
945 Rib = rib_enhan_arr(i,j,kpbl);
946 Real pblh_interp = zval0 + (zval - zval0) / (Rib - Rib0) * (Ribcr - Rib0);
947 pblh_corr_arr(i, j, 0) = amrex::max(amrex::min(pblh_interp, pblh_max), pblh_min);
949 pblh_corr_arr(i, j, 0) = pblh_min;
951 pbli_arr(i, j, 0) = kpbl;
962 ParallelFor(xybx, [=] AMREX_GPU_DEVICE(
int i,
int j,
int) noexcept
964 const int kpblold = pbli_arr(i,j,0);
965 bool definebrup =
false;
968 const Real thermalli =
GetThetavl(i, j, klo, cell_data, moisture_indices);
971 for (
int kk = kpblold; kk <
khi; ++kk) {
973 const Real ws2_raw =
fourth * ((uvel(i,j,kk)+uvel(i+1,j,kk))*(uvel(i,j,kk)+uvel(i+1,j,kk))
974 + (vvel(i,j,kk)+vvel(i,j+1,kk))*(vvel(i,j,kk)+vvel(i,j+1,kk)));
978 const Real z_sfc = (use_terrain_fitted_coords)
981 const Real zval_kk = (use_terrain_fitted_coords)
987 const Real thlix_kk =
GetThetavl(i, j, kk, cell_data, moisture_indices);
988 const Real thlix_klo =
GetThetavl(i, j, klo, cell_data, moisture_indices);
991 const Real bruptmp =
CONST_GRAV * zrel_kk * (thlix_kk - thermalli) / (ws2 * thlix_klo);
994 const bool stable = (bruptmp >=
zero);
997 pbli_arr(i,j,0) = kk;
1008 pbli_arr(i,j,0) = amrex::min(pbli_arr(i,j,0), izmax);
1035 ParallelFor(xybx, [=] AMREX_GPU_DEVICE(
int i,
int j,
int) noexcept
1039 int kpbl_zero = klo;
1040 Real Rib = rib_enhan_arr(i,j,klo);
1041 bool above_critical = (Rib >= Ribcr_zero);
1044 for (
int kk = klo+1; !above_critical && kk <=
khi; ++kk) {
1045 if (rib_enhan_arr(i,j,kk) >= Ribcr_zero) { kpbl_zero = kk; above_critical =
true;
break; }
1048 pbli_zero_arr(i, j, 0) = kpbl_zero;
1050 BL_PROFILE_VAR_STOP(prof_pblh);
1059 ParallelFor(xybx, [=, zero_d=
zero] AMREX_GPU_DEVICE(
int i,
int j,
int) noexcept
1061 entr_arr(i,j,0) =
zero;
1062 if (!enable_ysu_entrainment)
return;
1064 const int kpbl = pbli_arr(i,j,0);
1066 const Real pblh = pblh_corr_arr(i,j,0);
1067 const Real wscale = wstar_arr(i,j,0);
1068 const Real ustar = u_star_arr(i,j,0);
1069 const Real ustar3 = ustar * ustar * ustar;
1073 const Real wstar3_col = wstar3_arr(i,j,0);
1080 ?
GetThetavl(i, j, klo, cell_data, moisture_indices)
1081 :
GetThetav(i, j, klo, cell_data, moisture_indices);
1086 const int kpbl_p1 = amrex::min(kpbl + 1, izmax);
1088 ?
GetThetavl(i, j, kpbl, cell_data, moisture_indices)
1089 :
GetThetav(i, j, kpbl, cell_data, moisture_indices);
1091 ?
GetThetavl(i, j, kpbl_p1, cell_data, moisture_indices)
1092 :
GetThetav(i, j, kpbl_p1, cell_data, moisture_indices);
1093 const Real dthvx = amrex::max(thvx_kpbl_p1 - thvx_kpbl,
amrex::Real(1.0e-2));
1097 const Real we = amrex::max(bfxpbl / dthvx, -std::sqrt(wm2));
1100 const Real met_h = (use_terrain_fitted_coords)
1102 const Real dz_kpbl = met_h / dz_inv;
1112 Real K_entr_final = (we <
zero) ? rho_kpbl * (-we) * dz_kpbl :
zero;
1115 const int k_below = amrex::max(kpbl - 1, klo);
1117 Real qc_below = (moisture_indices.
qc >= 0)
1118 ? cell_data(i, j, k_below, moisture_indices.
qc) / rho_k :
zero;
1119 Real qi_below = (moisture_indices.
qi >= 0)
1120 ? cell_data(i, j, k_below, moisture_indices.
qi) / rho_k :
zero;
1124 if ((qc_below + qi_below) > cloud_thresh_entr && kpbl >= klo + 2) {
1132 const int k_p2 = amrex::min(kpbl + 1, izmax);
1133 const Real thlix_kbelow =
GetThetavl(i, j, k_below, cell_data, moisture_indices);
1134 const Real thlix_kp2 =
GetThetavl(i, j, k_p2, cell_data, moisture_indices);
1137 const Real dthvx_li = amrex::max(thlix_kp2 - thlix_kbelow,
amrex::Real(0.1));
1143 * xlv_over_cp * qc_below / dthvx_li,
1148 const Real we_cloud = amrex::max(bfxpbl / dthvx_li, -std::sqrt(wm2));
1152 const Real bfx0_cloud = amrex::max(sflux_arr(i, j, 0), zero_d);
1153 const Real bfxpbl_cloud = -ent_eff * bfx0_cloud;
1154 const Real we_cloud_top = amrex::max(bfxpbl_cloud / dthvx_li,
1158 we_final = we_cloud + we_cloud_top;
1161 K_entr_final = (we_final <
zero) ? rho_kpbl * (-we_final) * dz_kpbl :
zero;
1168 entr_arr(i,j,0) = amrex::min(K_entr_final, K_cap);
1172 const int kpbl_current = pbli_arr(i, j, 0);
1176 if (moisture_indices.
qc >= 0)
1177 qc_kpbl = cell_data(i, j, kpbl_current, moisture_indices.
qc) / cell_data(i, j, kpbl_current,
Rho_comp);
1178 if (moisture_indices.
qi >= 0)
1179 qi_kpbl = cell_data(i, j, kpbl_current, moisture_indices.
qi) / cell_data(i, j, kpbl_current,
Rho_comp);
1182 if ((qc_kpbl + qi_kpbl) > ysu_qcloud_threshold) {
1183 pbli_arr(i, j, 0) = amrex::min(kpbl_current + 1, izmax);
1190 FArrayBox K_down_fab(gbx, 1, The_Async_Arena());
1191 K_down_fab.setVal<RunOn::Device>(
zero);
1192 const auto& K_down_arr = K_down_fab.array();
1194 if (enable_ysu_topdown) {
1195 ParallelFor(gbx, [=, zero_d=
zero] AMREX_GPU_DEVICE(
int i,
int j,
int k) noexcept
1197 K_down_arr(i,j,k) =
zero;
1198 if (k >= pbli_arr(i,j,0))
return;
1202 const amrex::Real zval = (use_terrain_fitted_coords)
1205 const amrex::Real z_sfc = (use_terrain_fitted_coords)
1213 const amrex::Real wstar3_down_col = wstar3_down_arr(i,j,0);
1215 ? std::cbrt(wstar3_down_col)
1217 const amrex::Real zfac_up = amrex::max(zrel / pblh_rel, zero_d);
1218 K_down_arr(i,j,k) =
rho * wstar_down_eff *
KAPPA
1219 * amrex::max(pblh_rel - zrel, zero_d)
1220 * zfac_up * zfac_up;
1226 const Array4<Real>& K_turb = eddyViscosity.array(mfi);
1237 BL_PROFILE_VAR(
"YSUNew_Kprofile", prof_kprof);
1238 ParallelFor(gbx, [=, wstar3_arr_cap=wstar3_arr, zol1_arr_cap=zol1_arr, sfcflg_arr_cap=sfcflg_arr,
1239 zero_d=
zero, one_d=
one, two_d=
two] AMREX_GPU_DEVICE(
int i,
int j,
int k) noexcept
1241 Real obuk_val = l_obuk_arr(i, j, 0);
1246 const Real zval = (use_terrain_fitted_coords)
1250 const Real met_h_zeta = (use_terrain_fitted_coords)
1252 const Real dz_terrain = met_h_zeta / dz_inv;
1273 constexpr
Real qc_threshold =
Real(1.0e-5);
1278 if (moisture_indices.
qc >= 0) {
1279 qc_mix = cell_data(i, j, k, moisture_indices.
qc) /
rho;
1281 if (moisture_indices.
qi >= 0) {
1282 qi_mix = cell_data(i, j, k, moisture_indices.
qi) /
rho;
1285 const Real total_qcloud = qc_mix + qi_mix;
1286 const bool has_cloud = (total_qcloud > qc_threshold);
1295 if (k < pbli_extent) {
1316 bool SFCFLG = (sfcflg_arr_cap(i, j, 0) >
zero);
1322 const Real HOL = sf * pblh_corr_arr(i, j, 0) / obuk_val;
1323 const Real HOL_bounded = amrex::max(amrex::min(HOL,
Real(100.0)),
Real(-100.0));
1326 const Real phiM = (obuk_val > 0)
1327 ? (1 + 5 * HOL_bounded)
1329 amrex::max(1 - 16 * HOL_bounded,
Real(0.01)),
1336 const Real phit = (obuk_val > 0)
1337 ? (1 + 5 * HOL_bounded)
1339 amrex::max(1 - 16 * HOL_bounded,
Real(0.01)),
1348 Real phit_cloud = phit;
1349 Real phiM_cloud = phiM;
1350 if (has_cloud && obuk_val >
zero) {
1354 Real reduction_factor =
one -
Real(0.15) * amrex::min(total_qcloud / qc_threshold, one_d);
1356 phiM_cloud =
one +
Real(5.0) * reduction_factor * sf * pblh_corr_arr(i, j, 0) / obuk_val;
1357 phit_cloud =
one +
Real(5.0) * reduction_factor * sf * pblh_corr_arr(i, j, 0) / obuk_val;
1358 }
else if (has_cloud && obuk_val <=
zero) {
1362 Real cloud_boost =
Real(1.0) +
Real(0.05) * amrex::min(total_qcloud / qc_threshold, one_d);
1365 phiM_cloud = std::pow(
1366 amrex::max(one_d -
Real(16.0) * HOL_bounded / cloud_boost,
Real(0.01)),
1368 phit_cloud = std::pow(
1369 amrex::max(one_d -
Real(16.0) * HOL_bounded / cloud_boost,
Real(0.01)),
1374 const Real phiM_eff = phiM_cloud;
1375 const Real phit_eff = phit_cloud;
1388 const Real conpr = bfac *
KAPPA * sfcfrac;
1391 const Real zq_kp1_prandtl = zval +
myhalf * dz_terrain;
1395 const Real prfac = SFCFLG ? conpr :
zero;
1400 const Real wstar3_col = wstar3_arr_cap(i, j, 0);
1401 const Real ust3 = u_star_arr(i, j, 0) * u_star_arr(i, j, 0) * u_star_arr(i, j, 0);
1404 const Real wstar3_2 = wstar3_down_arr(i, j, 0);
1408 const Real wstar_tot3 = wstar3_col + wstar3_2;
1416 const Real pblh = pblh_corr_arr(i, j, 0);
1417 const Real sfclayer = sfcfrac * pblh;
1418 const Real zdiff = amrex::max(zq_kp1_prandtl - sfclayer, zero_d);
1419 const Real prnumfac =
amrex::Real(-3.0) * zdiff * zdiff / (pblh * pblh);
1425 Real prnum0 = (phit_eff / phiM_eff) + prfac;
1426 prnum0 = amrex::min(amrex::max(prnum0, prmin_wrf), prmax_wrf);
1430 Real prnum0_heat = prnum0 / (
one + prfac2 *
KAPPA * sfcfrac);
1431 prnum0_heat = amrex::min(amrex::max(prnum0_heat, prmin_wrf), prmax_wrf);
1432 const Real Prt =
one + (prnum0_heat -
one) * std::exp(prnumfac);
1436 const Real prnum_q =
one + (prnum0 -
one) * std::exp(prnumfac);
1453 const Real zq_kp1 = zval +
myhalf * dz_terrain;
1461 const Real zl1 = (use_terrain_fitted_coords)
1468 const Real pblh_rel = amrex::max(pblh_corr_arr(i, j, 0) - zl1,
amrex::Real(1.0e-4));
1469 const Real zfac = amrex::min(
1470 amrex::max(one_d - (zq_kp1 - zl1) / pblh_rel, zfacmin), one_d);
1474 const Real ust3_wscale = u_star_arr(i, j, 0) * u_star_arr(i, j, 0) * u_star_arr(i, j, 0);
1478 wscalek_val = std::cbrt(ust3_wscale +
amrex::Real(8.0) *
KAPPA * wstar3_col * (
one - zfac));
1482 constexpr
Real ckz_pbl =
Real(0.001);
1483 const Real K_base = ckz_pbl * dz_terrain *
rho;
1496 if (enable_ysu_topdown && k < pbli_arr(i, j, 0)) {
1507 const Real zq_kp1_stable = zval +
myhalf * dz_terrain;
1510 const Real zl1_stable = (use_terrain_fitted_coords)
1516 const Real pblh_rel_stable = amrex::max(pblh_corr_arr(i, j, 0) - zl1_stable,
amrex::Real(1.0e-4));
1517 const Real zfac_stable = amrex::min(
1518 amrex::max(one_d - (zq_kp1_stable - zl1_stable) / pblh_rel_stable, zfacmin_stable), one_d);
1522 const Real zol1_stable = zol1_arr_cap(i, j, 0);
1523 const Real zol_ratio = zq_kp1_stable / zl1_stable;
1524 const Real phim_stable_arg = zol1_stable * zol_ratio;
1526 const Real wscalek_stable = amrex::max(
1527 u_star_arr(i, j, 0) / amrex::max(phim_stable,
amrex::Real(0.01)),
1531 constexpr
Real ckz_pbl_stable =
Real(0.001);
1532 const Real K_base_stable = ckz_pbl_stable * dz_terrain *
rho;
1534 K_turb(i, j, k,
EddyDiff::Mom_v) = K_base_stable +
rho * wscalek_stable *
KAPPA * zq_kp1_stable * std::pow(zfac_stable, pfac_stable);
1539 const Real prnum_stable =
one + (prnum0 -
one) * std::exp(prnumfac);
1548 }
else if (k >= pbli_extent) {
1553 const Real lambda_min =
Real(30.0);
1554 const Real lambda_max =
Real(300.0);
1555 const Real lambdadz = amrex::min(amrex::max(
Real(0.1) * dz_terrain, lambda_min), lambda_max);
1556 const Real lscale = (lambdadz *
KAPPA * zval) / (lambdadz +
KAPPA * zval);
1557 Real dthetadz, dudz, dvdz;
1558 ComputeVerticalDerivativesPBL(i, j, k, uvel, vvel, cell_data, izmin, izmax, pbl_derivative_dz_inv(i,j,k),
1559 c_ext_dir_on_zlo, c_ext_dir_on_zhi, u_ext_dir_on_zlo,
1560 u_ext_dir_on_zhi, v_ext_dir_on_zlo, v_ext_dir_on_zhi, dthetadz,
1561 dudz, dvdz, moisture_indices);
1564 const Real dudz_safe = (k < izmax) ? dudz :
zero;
1565 const Real dvdz_safe = (k < izmax) ? dvdz :
zero;
1569 const Real wind_shear = dudz_safe * dudz_safe + dvdz_safe * dvdz_safe;
1570 const Real wind_shear_safe = std::max(wind_shear,
Real(1.0e-8));
1576 const Real theta_v =
GetThetav(i, j, k, cell_data, moisture_indices);
1577 const Real dtheta_v_dz = dthetadz;
1590 Real grad_Ri =
CONST_GRAV / theta_v * dtheta_v_dz / wind_shear_safe;
1591 grad_Ri = std::max(std::min(grad_Ri,
Real(100.0)), -
Real(100.0));
1599 Real qc_k = (moisture_indices.
qc >= 0) ? cell_data(i,j,k, moisture_indices.
qc)/rho_k :
zero;
1600 Real qc_kp1 = (moisture_indices.
qc >= 0) ? cell_data(i,j,k+1,moisture_indices.
qc)/rho_kp1 :
zero;
1601 Real qi_k = (moisture_indices.
qi >= 0) ? cell_data(i,j,k, moisture_indices.
qi)/rho_k :
zero;
1602 Real qi_kp1 = (moisture_indices.
qi >= 0) ? cell_data(i,j,k+1,moisture_indices.
qi)/rho_kp1 :
zero;
1605 if ((qc_k + qi_k) > cloud_thresh && (qc_kp1 + qi_kp1) > cloud_thresh) {
1612 const Real qv_k_mri = (moisture_indices.
qv >= 0)
1613 ? cell_data(i,j,k, moisture_indices.
qv) / rho_k :
zero;
1614 const Real qv_kp1_mri = (moisture_indices.
qv >= 0)
1615 ? cell_data(i,j,k+1,moisture_indices.
qv) / rho_kp1 :
zero;
1620 const Real qmean =
myhalf * (qv_k_mri + qv_kp1_mri);
1626 const Real chi =
xlv *
xlv * qmean / (
cp * rv * tmean * tmean);
1649 const Real grad_Ri_safe = amrex::max(grad_Ri, -
Real(100.0));
1650 const Real fm = (grad_Ri_safe > 0)
1652 : 1 - 8 * grad_Ri_safe / (1 +
Real(1.746) * std::sqrt(amrex::max(-grad_Ri_safe, zero_d)));
1653 const Real ft = (grad_Ri_safe > 0)
1655 : 1 - 8 * grad_Ri_safe / (1 +
Real(1.286) * std::sqrt(amrex::max(-grad_Ri_safe, zero_d)));
1656 const Real rl2wsp =
rho * lscale * lscale * std::sqrt(wind_shear);
1660 if (grad_Ri_safe > 0) {
1679 if (k == pbli_arr(i,j,0)) {
1705 Real rhoKmin, rhoKmax;
1711 constexpr
Real Kmax =
Real(1000.0);
1712 rhoKmin = ckz * dz_terrain *
rho;
1713 rhoKmax =
rho * Kmax;
1720 rhoKmin =
rho * Kmin;
1721 rhoKmax =
rho * Kmax;
1724 #ifdef ERF_USE_WINDFARM
1738 if (k < pbli_extent) {
1753 std::min(K_turb(i, j, k,
EddyDiff::Q_v), rhoKmax), rhoKmin);
1758 if (k < pbli_extent) {
1772 BL_PROFILE_VAR_STOP(prof_kprof);
1773 amrex::Print()<<
" Turbulent Viscosity at cell "<<K_turb(2, 2, 2,
EddyDiff::Mom_v)<<
" "<<pblh_corr_arr(2, 2, 0)<<std::endl;
1775 ParallelFor(xybx, [=] AMREX_GPU_DEVICE(
int i,
int j,
int ) noexcept
constexpr amrex::Real epsv
Definition: ERF_Constants.H:53
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:63
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real fourth
Definition: ERF_Constants.H:14
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:64
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenRandRTh(const amrex::Real rho, const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:46
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
#define Rho_comp
Definition: ERF_IndexDefines.H:39
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:40
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
Real z0
Definition: ERF_InitCustomPertVels_ScalarAdvDiff.H:8
const bool use_moisture
Definition: ERF_InitCustomPert_ABL.H:71
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsatw(amrex::Real t, amrex::Real p, amrex::Real &qsatw)
Definition: ERF_MicrophysicsUtils.H:228
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real GetThetav(const int &i, const int &j, const int &k, const amrex::Array4< amrex::Real const > &cell_data, const MoistureComponentIndices &moisture_indices)
Definition: ERF_MoistUtils.H:72
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real GetThetavl(int i, int j, int k, const amrex::Array4< amrex::Real const > &cell_data, const MoistureComponentIndices &moisture_indices)
Definition: ERF_MoistUtils.H:92
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_GPU_DEVICE AMREX_FORCE_INLINE void ComputeVerticalDerivativesPBL(int i, int j, int k, const amrex::Array4< const amrex::Real > &uvel, const amrex::Array4< const amrex::Real > &vvel, const amrex::Array4< const amrex::Real > &cell_data, const int izmin, const int izmax, const PBLDerivativeDzInv &dz_inv, const bool c_ext_dir_on_zlo, const bool c_ext_dir_on_zhi, const bool u_ext_dir_on_zlo, const bool u_ext_dir_on_zhi, const bool v_ext_dir_on_zlo, const bool v_ext_dir_on_zhi, amrex::Real &dthetadz, amrex::Real &dudz, amrex::Real &dvdz, const MoistureComponentIndices &moisture_indices)
Definition: ERF_PBLModels.H:277
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_Zrel_AtCellCenter(const int &i, const int &j, const int &k, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:740
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtCellCenter(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:179
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
@ yvel_bc
Definition: ERF_IndexDefines.H:106
@ cons_bc
Definition: ERF_IndexDefines.H:89
@ xvel_bc
Definition: ERF_IndexDefines.H:105
@ ext_dir
Definition: ERF_IndexDefines.H:292
@ Theta_v
Definition: ERF_IndexDefines.H:250
@ Turb_lengthscale
Definition: ERF_IndexDefines.H:254
@ HGAMU_v
Definition: ERF_IndexDefines.H:262
@ Q_v
Definition: ERF_IndexDefines.H:253
@ Mom_v
Definition: ERF_IndexDefines.H:249
@ HGAMQ_v
Definition: ERF_IndexDefines.H:261
@ HGAMT_v
Definition: ERF_IndexDefines.H:260
@ HGAMV_v
Definition: ERF_IndexDefines.H:263
@ rho
Definition: ERF_Kessler.H:24
@ xvel
Definition: ERF_IndexDefines.H:215
@ yvel
Definition: ERF_IndexDefines.H:216
@ dz
Definition: ERF_AdvanceWDM6.cpp:270
real(c_double), parameter cp
Definition: ERF_module_model_constants.F90:22
real(c_double), parameter xlv
Definition: ERF_module_model_constants.F90:57
real(kind=kind_phys), parameter, private alpha
Definition: ERF_module_mp_wdm6.F90:62
int qi
cloud ice
Definition: ERF_DataStruct.H:208
int qv
water vapor
Definition: ERF_DataStruct.H:206
int qc
cloud liquid water
Definition: ERF_DataStruct.H:207
Functor for inverse vertical spacings for terrain-following grids using cell-center heights.
Definition: ERF_PBLModels.H:457
bool enable_mrf_unbounded_vpert
Whether MRF leaves VPERT unlimited by GAMCRT.
Definition: ERF_TurbStruct.H:730
bool pbl_mrf_use_zero_ri_extent
Whether MRF uses the Ri=0 K-profile extent.
Definition: ERF_TurbStruct.H:731
bool enable_ysu_entrainment
Whether YSU entrainment-layer parameterization is enabled.
Definition: ERF_TurbStruct.H:706
bool enable_ysu_sat_limiter
Whether YSU applies a saturation limiter to moisture countergradient terms.
Definition: ERF_TurbStruct.H:702
bool enable_ysu_cloud_pblh
Whether YSU cloud-based PBL-height detection is enabled.
Definition: ERF_TurbStruct.H:708
amrex::Real pbl_mrf_const_b
MRF constant used to compute PBL height.
Definition: ERF_TurbStruct.H:717
amrex::Real ysu_qcloud_threshold
Cloud liquid water threshold for YSUNew [kg/kg].
Definition: ERF_TurbStruct.H:710
bool enable_ysu_countergradient
Whether YSU countergradient corrections are enabled.
Definition: ERF_TurbStruct.H:698
bool enable_ysu_topdown
Whether YSU top-down mixing is enabled.
Definition: ERF_TurbStruct.H:704
amrex::Real pbl_ysu_land_Ribcr
Critical bulk Richardson number over land for stable YSU conditions.
Definition: ERF_TurbStruct.H:692
amrex::Real pbl_mrf_sf
MRF surface flux value used to compute PBL height.
Definition: ERF_TurbStruct.H:718
bool pbl_ysunew_highres_bounds
Whether YSUNew applies high-resolution grid-dependent diffusivity bounds.
Definition: ERF_TurbStruct.H:713
amrex::Real pbl_ysu_coriolis_freq
Coriolis frequency used by YSU-family PBL schemes.
Definition: ERF_TurbStruct.H:683
bool ysu_moistvars
Whether YSU applies turbulence to moisture variables.
Definition: ERF_TurbStruct.H:712
bool enable_ysu_terrain_pblh_floor
Whether YSU applies a terrain-following PBL-height floor.
Definition: ERF_TurbStruct.H:700
bool enable_ysu_liquid_theta
Whether YSU uses liquid-water virtual potential temperature for stability.
Definition: ERF_TurbStruct.H:696