One radiation call at one level.
566 BL_PROFILE(
"TwoStreamRadiation::advance()");
569 if (!
active()) {
return; }
579 const BoxArray& lev_ba = cons_old.boxArray();
580 const Box& dom = geom.Domain();
581 for (
int ibox = 0; ibox < lev_ba.size(); ++ibox) {
582 const Box& b = lev_ba[ibox];
583 if (b.smallEnd(2) != dom.smallEnd(2) || b.bigEnd(2) != dom.bigEnd(2)) {
return; }
590 "TwoStreamRadiation::advance: call_site must be pre_dycore or post_dycore");
592 "TwoStreamRadiation::advance called on a level define_level has not built");
596 m_alb_sw[lev]->boxArray().size() == cons_old.boxArray().size() &&
597 m_alb_sw[lev]->DistributionMap() == cons_old.DistributionMap(),
598 "TwoStreamRadiation: the 2D surface fields and the 3D state are laid out differently");
599 if (qheating !=
nullptr) {
601 qheating->boxArray() == cons_old.boxArray() &&
602 qheating->DistributionMap() == cons_old.DistributionMap(),
603 "TwoStreamRadiation: qheating must be the 2-component (SW, LW) field on the state's grids");
609 "TwoStreamRadiation: seb_enable is set but the SEB fields were not allocated");
617 call_site ==
"post_dycore",
618 "TwoStreamRadiation: supplying a land-surface model's forcing needs the rad_fluxes array");
621 "TwoStreamRadiation: the force-restore update needs a positive, finite dt_step");
642 const bool seb_active = rad_choice.
seb_enable;
649 const bool has_external_surface_temperature =
650 (radiation_inputs.size() > 0 && radiation_inputs[0] !=
nullptr) ||
651 (noahmp_active && lsm_has_field(lsm, lev,
"t_sfc"));
663 if (call_site ==
"pre_dycore") {
664 ReduceOps<ReduceOpMin, ReduceOpMin> state_ops;
665 ReduceData<amrex::Real, amrex::Real> state_data(state_ops);
666 using StateTuple =
typename decltype(state_data)::Type;
667 for (MFIter mfi(cons_old, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
668 const auto& arr = cons_old.const_array(mfi);
669 state_ops.eval(mfi.tilebox(), state_data,
670 [=] AMREX_GPU_DEVICE (
int i,
int j,
int k) -> StateTuple {
671 constexpr
amrex::Real neg_inf = -std::numeric_limits<amrex::Real>::infinity();
674 return {amrex::Math::isfinite(
rho) ?
rho : neg_inf,
675 amrex::Math::isfinite(rth) ? rth : neg_inf};
678 auto state_tuple = state_data.value(state_ops);
679 amrex::Real mins[2] = {amrex::get<0>(state_tuple), amrex::get<1>(state_tuple)};
680 ParallelDescriptor::ReduceRealMin(mins, 2);
683 if (!std::isfinite(rho_min) || !std::isfinite(rth_min)) {
684 amrex::Abort(
"TwoStreamRadiation: the state handed to the column sweep at level " +
685 std::to_string(lev) +
", step " + std::to_string(nstep) +
686 " has a non-finite density or rho*theta");
688 if (!(rho_min > 0.0) || !(rth_min > 0.0)) {
689 amrex::Abort(
"TwoStreamRadiation: the state handed to the column sweep at level " +
690 std::to_string(lev) +
", step " + std::to_string(nstep) +
691 " has a non-positive density (min " + std::to_string(rho_min) +
692 ") or rho*theta (min " + std::to_string(rth_min) +
")");
700 const bool do_sweep = (call_site !=
"post_dycore");
709 m_diag[lev] = std::make_unique<RadiationDiagnostics>(
731 amrex::Real seb_residual_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
732 amrex::Real seb_residual_max = std::numeric_limits<amrex::Real>::quiet_NaN();
735 amrex::Real t_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
736 amrex::Real t_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
737 amrex::Real q_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
738 amrex::Real q_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
741 const MultiFab& state_cons = cons_old;
744 if (state_cons.nComp() > 0 ) {
752 if (do_sweep) { set_solar_state(ts_params, rad_choice,
m_orbit, epoch_time, have_datetime, lev, nstep); }
761 amrex::Long n_columns_total = 0;
765 amrex::Long n_seb_columns = 0;
775 MultiFab* qheating_mf = qheating;
782 const bool sw_flux_from_rad = seb_active &&
784 !(noahmp_active && lsm_has_field(lsm, lev,
"sav"));
785 const bool lw_flux_from_rad = seb_active &&
787 !(noahmp_active && lsm_has_field(lsm, lev,
"fira"));
805 if (!sw_flux_from_rad) {
809 if (!lw_flux_from_rad) {
823 const int surface_k = geom.Domain().smallEnd(2);
825 lsm_field(lsm, lev,
"hfx"), sfc_sens_flux, surface_k,
829 lsm_field(lsm, lev,
"lh"), sfc_laten_flux, surface_k,
836 fill_or_copy_seb_field(
m_q_sfc[lev].
get(), lsm, lev,
"noahmp_water_vapor_mixing_ratio_2m_vegetated", rad_choice.
seb_q_sfc_default);
851 const Box& rad_domain = geom.Domain();
853 for (MFIter mfi(state_cons,
false); mfi.isValid(); ++mfi)
855 const Box& bx = mfi.validbox();
863 bx.smallEnd(2) == rad_domain.smallEnd(2) &&
864 bx.bigEnd(2) == rad_domain.bigEnd(2),
865 "TwoStream radiation requires grids that span the domain in z; "
866 "set amr.max_grid_size_z to at least amr.n_cell in z");
867 const auto& state_arr = state_cons.const_array(mfi);
870 const amrex::Real dz_uniform_lev = geom.CellSize(2);
874 Array4<const amrex::Real> z_phys_nd_arr;
875 if (z_phys_nd !=
nullptr) {
876 z_phys_nd_arr = z_phys_nd->const_array(mfi);
882 Array4<amrex::Real> scratch_arr = scratch_fab.array();
898 bool has_hetero_alb_sw =
false;
899 Array4<const amrex::Real> hetero_alb_sw_arr;
901 const MultiFab* lsm_albedo = noahmp_active
902 ? lsm_field(lsm, lev, lsm_broadband_albedo_name)
904 if (lsm_albedo !=
nullptr) {
905 hetero_alb_sw_arr = lsm_albedo->const_array(mfi);
906 has_hetero_alb_sw =
true;
907 }
else if (radiation_inputs.size() > 2 && radiation_inputs[2]) {
908 hetero_alb_sw_arr = radiation_inputs[2]->const_array(mfi);
909 has_hetero_alb_sw =
true;
911 hetero_alb_sw_arr =
m_alb_sw[lev]->const_array(mfi);
912 has_hetero_alb_sw =
true;
917 bool has_hetero_emiss_lw =
false;
918 Array4<const amrex::Real> hetero_emiss_lw_arr;
920 std::string varname_emiss =
"sfc_emis";
921 int lsm_idx = noahmp_active ? lsm.
Get_DataIdx(lev, varname_emiss) : -1;
922 if (radiation_inputs.size() > 1 && radiation_inputs[1]) {
923 hetero_emiss_lw_arr = radiation_inputs[1]->const_array(mfi);
924 has_hetero_emiss_lw =
true;
925 }
else if (lsm_idx >= 0) {
928 hetero_emiss_lw_arr = lsm_ptr->const_array(mfi);
929 has_hetero_emiss_lw =
true;
932 hetero_emiss_lw_arr =
m_emiss_lw[lev]->const_array(mfi);
933 has_hetero_emiss_lw =
true;
944 bool has_lsm_t_sfc =
false;
945 Array4<const amrex::Real> lsm_t_sfc_arr;
946 bool has_seb_t_sfc =
false;
947 Array4<const amrex::Real> seb_t_sfc_arr;
948 bool has_surface_layer =
false;
949 Array4<const amrex::Real> surface_layer_theta_arr;
951 std::string varname_t_sfc =
"t_sfc";
952 int lsm_idx = noahmp_active ? lsm.
Get_DataIdx(lev, varname_t_sfc) : -1;
953 if (radiation_inputs.size() > 0 && radiation_inputs[0]) {
954 lsm_t_sfc_arr = radiation_inputs[0]->const_array(mfi);
955 has_lsm_t_sfc =
true;
956 }
else if (lsm_idx >= 0) {
959 lsm_t_sfc_arr = lsm_ptr->const_array(mfi);
960 has_lsm_t_sfc =
true;
969 seb_t_sfc_arr =
m_t_sfc[lev]->const_array(mfi);
970 has_seb_t_sfc =
true;
972 if (t_surf !=
nullptr) {
973 surface_layer_theta_arr = t_surf->const_array(mfi);
974 has_surface_layer =
true;
981 const bool has_latlon = (lat_m !=
nullptr) && (lon_m !=
nullptr);
982 Array4<const amrex::Real> lat_arr, lon_arr;
984 lat_arr = lat_m->const_array(mfi);
985 lon_arr = lon_m->const_array(mfi);
994 const bool write_fluxes = (rad_fluxes !=
nullptr);
995 Array4<amrex::Real> rad_flux_clear_arr;
996 FArrayBox rad_flux_cloudy_fab;
997 Array4<amrex::Real> rad_flux_cloudy_arr;
999 rad_flux_clear_arr = rad_fluxes->array(mfi);
1000 if (cloud_fraction > 0.0) {
1002 rad_flux_cloudy_arr = rad_flux_cloudy_fab.array();
1008 Array4<amrex::Real> sw_sfc_out;
1009 Array4<amrex::Real> lw_sfc_out;
1010 if (sw_flux_from_rad) sw_sfc_out =
m_sw_flux_sfc[lev]->array(mfi);
1011 if (lw_flux_from_rad) lw_sfc_out =
m_lw_flux_sfc[lev]->array(mfi);
1016 Array4<amrex::Real> sw_dn_out, lw_dn_out, coszen_out;
1028 const int m_canopy_top = supply_canopy ?
m_canopy_m[lev] : 0;
1030 Array4<amrex::Real> canopy_out, canopy_cosz_out;
1031 if (supply_canopy) {
1033 "TwoStreamRadiation: the canopy forcing of the building faces needs the rad_fluxes array");
1035 "TwoStreamRadiation: the canopy reaches above the top of the column");
1036 canopy_out =
m_canopy[lev]->array(mfi);
1042 const auto&
lo = bx.loVect();
1043 const auto&
hi = bx.hiVect();
1044 Box xy_box(IntVect(
lo[0],
lo[1], 0), IntVect(
hi[0],
hi[1], 0));
1047 amrex::Long n_cols =
static_cast<amrex::Long
>(bx.length(0)) *
1048 static_cast<amrex::Long
>(bx.length(1));
1049 n_columns_total += n_cols;
1055 FArrayBox qheating_fallback_fab;
1056 Array4<amrex::Real> qheating_clear_arr;
1057 if (qheating_mf !=
nullptr) {
1058 qheating_clear_arr = qheating_mf->array(mfi);
1060 qheating_fallback_fab.resize(bx, 2);
1061 qheating_clear_arr = qheating_fallback_fab.array();
1066 FArrayBox qheating_cloudy_fab;
1067 Array4<amrex::Real> qheating_cloudy_arr;
1068 if (cloud_fraction > 0.0) {
1069 qheating_cloudy_fab.resize(bx, 2);
1070 qheating_cloudy_arr = qheating_cloudy_fab.array();
1083 ReduceOps<ReduceOpMax, ReduceOpSum, ReduceOpSum, ReduceOpSum, ReduceOpSum, ReduceOpSum> reduce_ops;
1084 ReduceData<amrex::Real, amrex::Real, amrex::Real, amrex::Real, amrex::Real, amrex::Real> reduce_data(reduce_ops);
1086 using ReduceTuple =
typename decltype(reduce_data)::Type;
1089 reduce_ops.eval(xy_box, reduce_data,
1090 [=] AMREX_GPU_DEVICE (
int i,
int j,
int ) -> ReduceTuple
1102 i, j, bx, dz_uniform_lev, state_arr, ts_params,
false,
1104 max_heating_clear, sw_flux_clear, sw_up_clear, lw_net_clear, lw_up_clear,
1106 z_phys_nd_arr, scratch_arr,
1107 has_hetero_alb_sw, &hetero_alb_sw_arr,
1108 has_hetero_emiss_lw, &hetero_emiss_lw_arr,
1109 has_lsm_t_sfc, &lsm_t_sfc_arr,
1110 has_seb_t_sfc, &seb_t_sfc_arr,
1111 has_surface_layer, &surface_layer_theta_arr,
1112 has_latlon, &lat_arr, &lon_arr,
1113 write_fluxes ? &rad_flux_clear_arr :
nullptr,
1117 if (supply_canopy) {
1120 cloud_fraction,
false, canopy_out);
1132 if (cloud_fraction > 0.0) {
1140 i, j, bx, dz_uniform_lev, state_arr, ts_params,
true,
1141 qheating_cloudy_arr,
1142 max_heating_cloudy, sw_flux_cloudy, sw_up_cloudy, lw_net_cloudy, lw_up_cloudy,
1144 z_phys_nd_arr, scratch_arr,
1145 has_hetero_alb_sw, &hetero_alb_sw_arr,
1146 has_hetero_emiss_lw, &hetero_emiss_lw_arr,
1147 has_lsm_t_sfc, &lsm_t_sfc_arr,
1148 has_seb_t_sfc, &seb_t_sfc_arr,
1149 has_surface_layer, &surface_layer_theta_arr,
1150 has_latlon, &lat_arr, &lon_arr,
1151 write_fluxes ? &rad_flux_cloudy_arr :
nullptr);
1154 sw_flux_col = (1.0 - cloud_fraction) * sw_flux_clear +
1155 cloud_fraction * sw_flux_cloudy;
1156 sw_up_col = (1.0 - cloud_fraction) * sw_up_clear +
1157 cloud_fraction * sw_up_cloudy;
1158 lw_net_col = (1.0 - cloud_fraction) * lw_net_clear +
1159 cloud_fraction * lw_net_cloudy;
1160 lw_up_col = (1.0 - cloud_fraction) * lw_up_clear +
1161 cloud_fraction * lw_up_cloudy;
1162 max_heating_col = std::max(max_heating_clear, max_heating_cloudy);
1163 if (supply_canopy) {
1166 cloud_fraction,
true, canopy_out);
1172 int kmin = bx.smallEnd(2);
1173 int kmax = bx.bigEnd(2);
1174 for (
int k = kmin; k <= kmax; ++k) {
1175 for (
int comp = 0; comp < 2; ++comp) {
1176 amrex::Real q_clear_val = qheating_clear_arr(i, j, k, comp);
1177 amrex::Real q_cloudy_val = qheating_cloudy_arr(i, j, k, comp);
1178 qheating_clear_arr(i, j, k, comp) =
1179 (1.0 - cloud_fraction) * q_clear_val +
1180 cloud_fraction * q_cloudy_val;
1185 for (
int k = kmin; k <= kmax + 1; ++k) {
1186 for (
int comp = 0; comp < 4; ++comp) {
1187 rad_flux_clear_arr(i, j, k, comp) =
1188 (1.0 - cloud_fraction) * rad_flux_clear_arr(i, j, k, comp) +
1189 cloud_fraction * rad_flux_cloudy_arr(i, j, k, comp);
1197 if (sw_flux_from_rad) sw_sfc_out(i, j, 0) = sw_flux_col;
1198 if (lw_flux_from_rad) lw_sfc_out(i, j, 0) = -lw_net_col;
1203 cos_zenith_col, sw_dn_out(i, j, 0),
1204 lw_dn_out(i, j, 0), coszen_out(i, j, 0));
1208 if (supply_canopy) {
1214 return {max_heating_col, sw_flux_col, sw_up_col, lw_net_col, lw_up_col, sw_toa_clear};
1219 amrex::Gpu::synchronize();
1220 auto reduce_tuple = reduce_data.value(reduce_ops);
1221 max_heating_box = amrex::get<0>(reduce_tuple);
1222 sw_sum_box = amrex::get<1>(reduce_tuple);
1223 sw_up_sum_box = amrex::get<2>(reduce_tuple);
1224 lw_sum_box = amrex::get<3>(reduce_tuple);
1225 lw_up_sum_box = amrex::get<4>(reduce_tuple);
1226 sw_toa_sum_box = amrex::get<5>(reduce_tuple);
1229 max_heating_global = std::max(max_heating_global, max_heating_box);
1230 sw_surface_sum += sw_sum_box;
1231 sw_up_toa_sum += sw_up_sum_box;
1232 lw_net_sum += lw_sum_box;
1233 lw_up_toa_sum += lw_up_sum_box;
1234 sw_toa_sum += sw_toa_sum_box;
1242 amrex::Real sums[5] = {sw_surface_sum, sw_up_toa_sum, lw_net_sum, lw_up_toa_sum, sw_toa_sum};
1243 ParallelDescriptor::ReduceRealSum(sums, 5);
1244 sw_surface_sum = sums[0]; sw_up_toa_sum = sums[1];
1245 lw_net_sum = sums[2]; lw_up_toa_sum = sums[3];
1246 sw_toa_sum = sums[4];
1247 ParallelDescriptor::ReduceLongSum(n_columns_total);
1248 ParallelDescriptor::ReduceRealMax(max_heating_global);
1256 if (do_sweep && qheating_mf !=
nullptr &&
1257 (qheating_mf->contains_nan(0, 2, 0) || qheating_mf->contains_inf(0, 2, 0))) {
1258 amrex::Abort(
"TwoStreamRadiation: non-finite heating rate after the column sweep at level " +
1259 std::to_string(lev) +
", step " + std::to_string(nstep));
1264 static bool warned_seb_misconfig =
false;
1265 if (!warned_seb_misconfig && ParallelDescriptor::IOProcessor()) {
1266 Print() <<
"WARNING: erf.radiation.seb_diagnostic_enable=true but "
1267 "seb_enable=false; SEB residual diagnostics will report NaN. "
1268 "Set erf.radiation.seb_enable=true to enable SEB field "
1270 warned_seb_misconfig =
true;
1275 seb_residual_max = 0.0;
1279 for (MFIter mfi(state_cons,
false); mfi.isValid(); ++mfi) {
1280 const Box& bx = mfi.validbox();
1281 const auto&
lo = bx.loVect();
1282 const auto&
hi = bx.hiVect();
1283 Box xy_box(IntVect(
lo[0],
lo[1], 0), IntVect(
hi[0],
hi[1], 0));
1286 Array4<const amrex::Real> sw_flux_arr =
m_sw_flux_sfc[lev]->const_array(mfi);
1287 Array4<const amrex::Real> lw_flux_arr =
m_lw_flux_sfc[lev]->const_array(mfi);
1288 Array4<const amrex::Real> hfx_arr =
m_hfx_sfc[lev]->const_array(mfi);
1289 Array4<const amrex::Real> lh_arr =
m_lh_sfc[lev]->const_array(mfi);
1290 Array4<const amrex::Real> grdflx_arr =
m_grdflx_sfc[lev]->const_array(mfi);
1293 amrex::Long n_cols_box =
static_cast<amrex::Long
>(bx.length(0)) *
1294 static_cast<amrex::Long
>(bx.length(1));
1299 ReduceOps<ReduceOpSum, ReduceOpMax> seb_reduce_ops;
1300 ReduceData<amrex::Real, amrex::Real> seb_reduce_data(seb_reduce_ops);
1302 using SEBReduceTuple =
typename decltype(seb_reduce_data)::Type;
1304 seb_reduce_ops.eval(xy_box, seb_reduce_data,
1305 [=] AMREX_GPU_DEVICE (
int i,
int j,
int ) -> SEBReduceTuple {
1316 return {residual, std::abs(residual)};
1321 amrex::Gpu::synchronize();
1322 auto seb_reduce_tuple = seb_reduce_data.value(seb_reduce_ops);
1323 residual_sum_box = amrex::get<0>(seb_reduce_tuple);
1324 residual_max_box = amrex::get<1>(seb_reduce_tuple);
1327 seb_residual_sum += residual_sum_box;
1328 seb_residual_max = std::max(seb_residual_max, residual_max_box);
1329 n_seb_columns += n_cols_box;
1331 ParallelDescriptor::ReduceRealSum(seb_residual_sum);
1332 ParallelDescriptor::ReduceRealMax(seb_residual_max);
1333 ParallelDescriptor::ReduceLongSum(n_seb_columns);
1346 const bool seb_radiation_ready =
m_flux_diag[lev].valid ||
1347 !(sw_flux_from_rad || lw_flux_from_rad);
1349 call_site ==
"post_dycore" && seb_radiation_ready) {
1350 if (!has_external_surface_temperature) {
1355 amrex::Real t_s_max_val = -std::numeric_limits<amrex::Real>::max();
1357 amrex::Real q_s_max_val = -std::numeric_limits<amrex::Real>::max();
1358 amrex::Long n_prog_columns = 0;
1364 for (MFIter mfi(state_cons,
false); mfi.isValid(); ++mfi) {
1365 const Box& bx = mfi.validbox();
1366 const auto&
lo = bx.loVect();
1367 const auto&
hi = bx.hiVect();
1368 Box xy_box(IntVect(
lo[0],
lo[1], 0), IntVect(
hi[0],
hi[1], 0));
1371 Array4<const amrex::Real> sw_flux_arr =
m_sw_flux_sfc[lev]->const_array(mfi);
1372 Array4<const amrex::Real> lw_flux_arr =
m_lw_flux_sfc[lev]->const_array(mfi);
1373 Array4<const amrex::Real> hfx_arr =
m_hfx_sfc[lev]->const_array(mfi);
1374 Array4<const amrex::Real> lh_arr =
m_lh_sfc[lev]->const_array(mfi);
1375 Array4<const amrex::Real> grdflx_arr =
m_grdflx_sfc[lev]->const_array(mfi);
1376 Array4<const amrex::Real> t_deep_arr =
m_t_deep[lev]->const_array(mfi);
1377 Array4<const amrex::Real> q_deep_arr =
m_q_deep[lev]->const_array(mfi);
1380 Array4<amrex::Real> t_s_arr =
m_t_sfc[lev]->array(mfi);
1381 Array4<amrex::Real> q_s_arr =
m_q_sfc[lev]->array(mfi);
1383 amrex::Long n_cols_box =
static_cast<amrex::Long
>(bx.length(0)) *
1384 static_cast<amrex::Long
>(bx.length(1));
1386 amrex::Real t_s_max_box = -std::numeric_limits<amrex::Real>::max();
1388 amrex::Real q_s_max_box = -std::numeric_limits<amrex::Real>::max();
1391 ReduceOps<ReduceOpSum, ReduceOpMax, ReduceOpSum, ReduceOpMax> prog_reduce_ops;
1392 ReduceData<amrex::Real, amrex::Real, amrex::Real, amrex::Real> prog_reduce_data(prog_reduce_ops);
1394 using ProgReduceTuple =
typename decltype(prog_reduce_data)::Type;
1396 prog_reduce_ops.eval(xy_box, prog_reduce_data,
1405 AMREX_GPU_DEVICE (
int i,
int j,
int ) -> ProgReduceTuple {
1428 amrex::Real t_s_new = t_s_old + dt_step * dT_s_dt;
1429 amrex::Real q_s_new = q_s_old + dt_step * dq_s_dt;
1432 t_s_new = amrex::max(t_min, amrex::min(t_max, t_s_new));
1433 q_s_new = amrex::max(q_min, amrex::min(q_max, q_s_new));
1436 t_s_arr(i, j, 0) = t_s_new;
1437 q_s_arr(i, j, 0) = q_s_new;
1440 return {t_s_new, std::abs(t_s_new), q_s_new, std::abs(q_s_new)};
1444 amrex::Gpu::synchronize();
1445 auto prog_reduce_tuple = prog_reduce_data.value(prog_reduce_ops);
1446 t_s_sum_box = amrex::get<0>(prog_reduce_tuple);
1447 t_s_max_box = amrex::get<1>(prog_reduce_tuple);
1448 q_s_sum_box = amrex::get<2>(prog_reduce_tuple);
1449 q_s_max_box = amrex::get<3>(prog_reduce_tuple);
1452 t_s_sum += t_s_sum_box;
1453 t_s_max_val = std::max(t_s_max_val, t_s_max_box);
1454 q_s_sum += q_s_sum_box;
1455 q_s_max_val = std::max(q_s_max_val, q_s_max_box);
1456 n_prog_columns += n_cols_box;
1462 if (
m_t_sfc[lev]->contains_nan(0, 1, 0) ||
m_q_sfc[lev]->contains_nan(0, 1, 0)) {
1463 amrex::Abort(
"TwoStreamRadiation: non-finite surface temperature or moisture "
1464 "after the force-restore update at level " + std::to_string(lev) +
1465 ", step " + std::to_string(nstep));
1470 ParallelDescriptor::ReduceRealSum(sums, 2);
1471 t_s_sum = sums[0]; q_s_sum = sums[1];
1472 ParallelDescriptor::ReduceRealMax(t_s_max_val);
1473 ParallelDescriptor::ReduceRealMax(q_s_max_val);
1474 ParallelDescriptor::ReduceLongSum(n_prog_columns);
1478 if (n_prog_columns > 0) {
1479 t_s_mean = t_s_sum /
static_cast<amrex::Real>(n_prog_columns);
1480 t_s_max = t_s_max_val;
1481 q_s_mean = q_s_sum /
static_cast<amrex::Real>(n_prog_columns);
1482 q_s_max = q_s_max_val;
1484 t_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
1485 t_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
1486 q_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
1487 q_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
1492 t_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
1493 t_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
1494 q_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
1495 q_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
1506 if (n_columns_total > 0) {
1508 SW_surface = sw_surface_sum * inv_n;
1509 SW_TOA = sw_toa_sum * inv_n;
1510 SW_up_TOA = sw_up_toa_sum * inv_n;
1511 LW_net_surface = lw_net_sum * inv_n;
1512 LW_up_TOA = lw_up_toa_sum * inv_n;
1514 heating_rate_max = max_heating_global;
1515 m_flux_diag[lev] = FluxDiag{
true, SW_surface, SW_TOA, SW_up_TOA,
1516 LW_net_surface, LW_up_TOA,
1520 if (!cached.valid) {
1531 SW_surface = cached.SW_surface;
1532 SW_TOA = cached.SW_TOA;
1533 SW_up_TOA = cached.SW_up_TOA;
1534 LW_net_surface = cached.LW_net_surface;
1535 LW_up_TOA = cached.LW_up_TOA;
1536 heating_rate_max = cached.heating_rate_max;
1541 seb_residual_mean = seb_residual_sum /
static_cast<amrex::Real>(n_seb_columns);
1544 seb_residual_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
1545 seb_residual_max = std::numeric_limits<amrex::Real>::quiet_NaN();
1551 if (rad_choice.
verbosity >= 1 && ParallelDescriptor::IOProcessor()) {
1552 Print() <<
"Radiation diagnostics at step " << nstep <<
":\n"
1553 <<
" SW TOA = " << SW_TOA <<
" W/m^2\n"
1554 <<
" SW surface = " << SW_surface <<
" W/m^2\n"
1555 <<
" SW up (TOA) = " << SW_up_TOA <<
" W/m^2\n"
1556 <<
" LW net (surface) = " << LW_net_surface <<
" W/m^2\n"
1557 <<
" LW up (TOA) = " << LW_up_TOA <<
" W/m^2\n"
1558 <<
" Max heating rate = " << heating_rate_max <<
" K/s\n";
1560 Print() <<
" SEB residual (mean) = " << seb_residual_mean <<
" W/m^2\n"
1561 <<
" SEB residual (max) = " << seb_residual_max <<
" W/m^2\n";
1564 Print() <<
" Surface temperature (mean) = " << t_s_mean <<
" K\n"
1565 <<
" Surface temperature (max) = " << t_s_max <<
" K\n"
1566 <<
" Surface moisture (mean) = " << q_s_mean <<
" m^3/m^3\n"
1567 <<
" Surface moisture (max) = " << q_s_max <<
" m^3/m^3\n";
1571 rad_diag.
append(nstep, time, call_site, SW_surface, SW_TOA,
1572 SW_up_TOA, LW_net_surface, LW_up_TOA, heating_rate_max,
1573 seb_residual_mean, seb_residual_max,
1574 t_s_mean, t_s_max, q_s_mean, q_s_max);
#define Rho_comp
Definition: ERF_IndexDefines.H:53
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:54
pp_for_pert_vels get("prob_type", prob_type)
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(axis >=0 &&axis< AMREX_SPACEDIM, "prob.axis must be 0, 1 or 2")
SEBFluxOrigin fill_seb_turbulent_flux(MultiFab &seb_flux, SEBTurbulentFlux kind, const MultiFab *land_surface_field, const MultiFab *surface_layer_flux, int surface_k, SEBTurbulentFluxSource source, Real fallback)
Definition: ERF_SEBTurbulentFlux.cpp:63
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real diagnose_seb_residual(amrex::Real sw_net, amrex::Real lw_net, amrex::Real hfx, amrex::Real lh, amrex::Real grdflx)
Diagnose the surface energy balance residual.
Definition: ERF_SimplifiedSEB.H:68
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real prognostic_dqs_dt(amrex::Real le, amrex::Real q_s, amrex::Real q_deep, amrex::Real d_s, amrex::Real tau_q)
Compute prognostic surface moisture tendency using force-restore formulation.
Definition: ERF_SimplifiedSEB.H:169
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real prognostic_dTs_dt(amrex::Real seb_residual, amrex::Real t_s, amrex::Real t_deep, amrex::Real c_s, amrex::Real tau)
Compute prognostic surface temperature tendency using force-restore formulation.
Definition: ERF_SimplifiedSEB.H:114
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void two_stream_canopy_beam(int i, int j, int kmin, int m_top, const amrex::Array4< const amrex::Real > &scratch, int f_dir_comp, bool sw_on, amrex::Real cos_zenith, amrex::Real cloud_fraction, bool blend, const amrex::Array4< amrex::Real > &beam)
Write the direct beam on a horizontal surface at the interfaces 0 .. m_top of column (i,...
Definition: ERF_TwoStreamCanopyForcing.H:77
TwoStreamParams make_two_stream_params(const RadChoice &rc, const amrex::Real rdOcp)
Copy the column-kernel parameters out of a RadChoice.
Definition: ERF_TwoStreamColumn.H:127
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void two_stream_land_forcing(int i, int j, int kmin, const amrex::Array4< const amrex::Real > &rad_flux, amrex::Real cos_zenith, amrex::Real &sw_dn, amrex::Real &lw_dn, amrex::Real &coszen)
The radiative forcing a land-surface model integrates on, for column (i,j).
Definition: ERF_TwoStreamColumn.H:858
amrex::Box two_stream_scratch_box(const amrex::Box &bx)
Box of the scratch FArrayBox a sweep over box bx needs.
Definition: ERF_TwoStreamColumn.H:612
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void vertical_two_stream_sweep(int i, int j, const amrex::Box &bx, amrex::Real dz_uniform, const amrex::Array4< const amrex::Real > &state_arr, const TwoStreamParams &rad_choice, bool cloudy, const amrex::Array4< amrex::Real > &qheating_arr, amrex::Real &max_heating_rate, amrex::Real &sw_surface_flux, amrex::Real &sw_up_toa, amrex::Real &lw_net_surface, amrex::Real &lw_up_toa, amrex::Real &sw_down_toa, const amrex::Array4< const amrex::Real > &z_phys_nd, const amrex::Array4< amrex::Real > &scratch, bool has_hetero_alb_sw=false, const amrex::Array4< const amrex::Real > *hetero_alb_sw=nullptr, bool has_hetero_emiss_lw=false, const amrex::Array4< const amrex::Real > *hetero_emiss_lw=nullptr, bool has_lsm_t_sfc=false, const amrex::Array4< const amrex::Real > *lsm_t_sfc=nullptr, bool has_seb_t_sfc=false, const amrex::Array4< const amrex::Real > *seb_t_sfc=nullptr, bool has_surface_layer=false, const amrex::Array4< const amrex::Real > *surface_layer_theta=nullptr, bool has_latlon=false, const amrex::Array4< const amrex::Real > *lat_arr=nullptr, const amrex::Array4< const amrex::Real > *lon_arr=nullptr, const amrex::Array4< amrex::Real > *rad_flux_out=nullptr, amrex::Real *cos_zenith_out=nullptr)
GPU-safe per-column vertical integration kernel for two-stream radiation, computing either the clear-...
Definition: ERF_TwoStreamColumn.H:945
int Get_DataIdx(const int &lev, const std::string &varname)
Definition: ERF_LandSurface.H:186
amrex::MultiFab * Get_Data_Ptr(const int &lev, const int &varIdx)
Definition: ERF_LandSurface.H:142
CSV logger and debug-print manager for radiation module.
Definition: ERF_RadiationDiagnostics.H:75
void append(int step, amrex::Real time, std::string const &call_site, amrex::Real sw_surface, amrex::Real sw_toa, amrex::Real sw_up_toa, amrex::Real lw_net_surface, amrex::Real lw_up_toa, amrex::Real heating_rate_max, amrex::Real seb_residual_mean=std::numeric_limits< amrex::Real >::quiet_NaN(), amrex::Real seb_residual_max=std::numeric_limits< amrex::Real >::quiet_NaN(), amrex::Real t_s_mean=std::numeric_limits< amrex::Real >::quiet_NaN(), amrex::Real t_s_max=std::numeric_limits< amrex::Real >::quiet_NaN(), amrex::Real q_s_mean=std::numeric_limits< amrex::Real >::quiet_NaN(), amrex::Real q_s_max=std::numeric_limits< amrex::Real >::quiet_NaN())
Append a diagnostic row to the CSV file.
Definition: ERF_RadiationDiagnostics.cpp:79
bool supplies_canopy_forcing(int lev) const noexcept
True when supply_canopy_forcing() was called for lev since its last define_level().
Definition: ERF_TwoStreamRadiation.H:263
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_canopy_cosz
Definition: ERF_TwoStreamRadiation.H:360
bool supplies_land_forcing(int lev) const noexcept
True when define_level allocated the land-model forcing fields on lev.
Definition: ERF_TwoStreamRadiation.H:213
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_q_sfc
Definition: ERF_TwoStreamRadiation.H:343
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_canopy
Definition: ERF_TwoStreamRadiation.H:359
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_grdflx_sfc
Definition: ERF_TwoStreamRadiation.H:342
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_lw_flux_sfc
Definition: ERF_TwoStreamRadiation.H:339
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_q_deep
Definition: ERF_TwoStreamRadiation.H:345
amrex::Real m_rdOcp
Definition: ERF_TwoStreamRadiation.H:322
amrex::Vector< FluxDiag > m_flux_diag
Definition: ERF_TwoStreamRadiation.H:364
bool active() const
True once a level has been defined with the model selected.
Definition: ERF_TwoStreamRadiation.H:57
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_emiss_lw
Definition: ERF_TwoStreamRadiation.H:334
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_t_sfc
Definition: ERF_TwoStreamRadiation.H:335
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_lw_dn_sfc
Definition: ERF_TwoStreamRadiation.H:353
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_lh_sfc
Definition: ERF_TwoStreamRadiation.H:341
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_alb_sw
Definition: ERF_TwoStreamRadiation.H:333
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_sw_flux_sfc
Definition: ERF_TwoStreamRadiation.H:338
OrbitalCache m_orbit
Definition: ERF_TwoStreamRadiation.H:323
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_t_deep
Definition: ERF_TwoStreamRadiation.H:344
amrex::Vector< std::unique_ptr< RadiationDiagnostics > > m_diag
Definition: ERF_TwoStreamRadiation.H:330
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_sw_dn_sfc
Definition: ERF_TwoStreamRadiation.H:352
amrex::Vector< int > m_canopy_step
Definition: ERF_TwoStreamRadiation.H:362
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_hfx_sfc
Definition: ERF_TwoStreamRadiation.H:340
amrex::Vector< int > m_canopy_m
Definition: ERF_TwoStreamRadiation.H:361
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_cos_zenith
Definition: ERF_TwoStreamRadiation.H:354
@ rho
Definition: ERF_Kessler.H:25
constexpr int F_DIR
Definition: ERF_TwoStreamColumn.H:602
constexpr int NCOMP
Definition: ERF_TwoStreamColumn.H:608
Container holding radiation-related choices and parameters.
Definition: ERF_RadStruct.H:132
bool sw_enabled
Enable shortwave (solar) radiation computation. Only used by the two-stream model.
Definition: ERF_RadStruct.H:144
std::string diag_file
Output file path for radiation diagnostics CSV. Paths are relative to the simulation working director...
Definition: ERF_RadStruct.H:268
amrex::Real surface_albedo_sw
Shortwave surface albedo fallback [0, 1] for per-column heterogeneous-surface-property resolution....
Definition: ERF_RadStruct.H:402
amrex::Real surface_emissivity_lw
Longwave surface emissivity fallback [0, 1] for per-column heterogeneous-surface-property resolution....
Definition: ERF_RadStruct.H:417
amrex::Real seb_grdflx_default
Fallback SEB ground heat flux [W/m^2]. The force-restore restoring term already represents the flux i...
Definition: ERF_RadStruct.H:558
amrex::Real diag_dedup_tol
Tolerance for time equality in duplicate guard [s]. Times within this tolerance are considered equal....
Definition: ERF_RadStruct.H:319
amrex::Real seb_restore_timescale_s
Force-restore timescale for surface temperature [s]. Relaxation time constant toward T_deep in progno...
Definition: ERF_RadStruct.H:615
std::string diag_callsite_mode
Call-site filtering mode: "both" (pre+post rows), "pre_only", or "post_only".
Definition: ERF_RadStruct.H:312
bool diag_regtest_line_enable
Enable RADIATION_DIAG: line emission. Controls regtest-parsing diagnostics line. Default true.
Definition: ERF_RadStruct.H:299
amrex::Real seb_lh_default
Fallback SEB latent heat flux [W/m^2].
Definition: ERF_RadStruct.H:551
amrex::Real seb_moisture_restore_timescale_s
Force-restore timescale for surface moisture [s]. Relaxation time constant toward q_deep in prognosti...
Definition: ERF_RadStruct.H:631
amrex::Real seb_lw_flux_default
Fallback SEB surface net longwave flux [W/m^2].
Definition: ERF_RadStruct.H:443
amrex::Real seb_q_sfc_default
Fallback SEB surface moisture, and the initial value of the prognostic one. With seb_prognostic_enabl...
Definition: ERF_RadStruct.H:566
bool diag_csv_enable
Enable CSV file append behavior. When false, no file writes; other enabled streams may still print....
Definition: ERF_RadStruct.H:306
amrex::Real seb_t_deep_default
Fallback SEB deep soil temperature [K].
Definition: ERF_RadStruct.H:571
amrex::Real seb_prognostic_t_max_k
Maximum clamping bound for prognostic surface temperature [K]. After each Euler update,...
Definition: ERF_RadStruct.H:648
amrex::Real rad_t_sfc
erf.rad_t_sfc: surface temperature [K] used as the longwave boundary condition wherever no land-surfa...
Definition: ERF_RadStruct.H:197
bool seb_prognostic_enable
Enable prognostic SEB surface temperature and moisture evolution. When true, integrates SEB residual ...
Definition: ERF_RadStruct.H:596
bool seb_use_radiation_fluxes
Take the SEB's net surface shortwave and longwave fluxes from the two-stream sweep itself (per column...
Definition: ERF_RadStruct.H:433
amrex::Real seb_prognostic_t_min_k
Minimum clamping bound for prognostic surface temperature [K]. After each Euler update,...
Definition: ERF_RadStruct.H:640
amrex::Real cloud_fraction
Cloud fraction in [0, 1] used to blend clear-sky and cloudy-column fluxes: F = (1 - cloud_fraction) *...
Definition: ERF_RadStruct.H:354
amrex::Real seb_prognostic_q_max
Maximum clamping bound for prognostic surface moisture [kg/kg]. After each Euler update,...
Definition: ERF_RadStruct.H:664
amrex::Real seb_moisture_layer_depth_m
Effective surface moisture layer depth [m]. Used in force-restore prognostic q_s equation: dq_s/dt = ...
Definition: ERF_RadStruct.H:623
SEBTurbulentFluxSource seb_turbulent_flux_source
Source of the SEB's sensible and latent heat fluxes when no land-surface model supplies them: "surfac...
Definition: ERF_RadStruct.H:450
amrex::Real seb_hfx_default
Fallback SEB sensible heat flux [W/m^2].
Definition: ERF_RadStruct.H:546
bool diag_tagged_enable
Enable tagged [RAD][...] debug lines. Controls emission of bracketed-tag debug output....
Definition: ERF_RadStruct.H:292
amrex::Real seb_q_deep_default
Deep soil moisture the prognostic q_s restores to, in q_s's units (see seb_q_sfc_default).
Definition: ERF_RadStruct.H:577
amrex::Real seb_sw_flux_default
Fallback SEB surface net shortwave flux [W/m^2].
Definition: ERF_RadStruct.H:438
bool diag_enable
Master switch for radiation diagnostics emission. When false, no diagnostics output (stdout/tagged/re...
Definition: ERF_RadStruct.H:278
amrex::Real seb_prognostic_q_min
Minimum clamping bound for prognostic surface moisture [kg/kg]. After each Euler update,...
Definition: ERF_RadStruct.H:656
bool seb_enable
Master switch for simplified SEB infrastructure. When false (default), no SEB MultiFabs are allocated...
Definition: ERF_RadStruct.H:423
bool diag_stdout_enable
Enable human-readable stdout diagnostics block. Controls tagged [RAD][...] debug line emission (when ...
Definition: ERF_RadStruct.H:285
int verbosity
Debug verbosity level (0=off, 1+=debug output). Gated debug prints only emit when this is >= 1.
Definition: ERF_RadStruct.H:262
amrex::Real seb_surface_heat_capacity
Effective surface heat capacity [J/(m^2*K)]. Used in force-restore prognostic T_s equation: dT_s/dt =...
Definition: ERF_RadStruct.H:607
bool seb_diagnostic_enable
Enable diagnostic SEB residual computation and reporting. When true, computes and reports SEB residua...
Definition: ERF_RadStruct.H:587
Trivially copyable subset of RadChoice used by the column kernels.
Definition: ERF_TwoStreamColumn.H:57