One radiation call at one level.
557 BL_PROFILE(
"TwoStreamRadiation::advance()");
560 if (!
active()) {
return; }
570 const BoxArray& lev_ba = cons_old.boxArray();
571 const Box& dom = geom.Domain();
572 for (
int ibox = 0; ibox < lev_ba.size(); ++ibox) {
573 const Box& b = lev_ba[ibox];
574 if (b.smallEnd(2) != dom.smallEnd(2) || b.bigEnd(2) != dom.bigEnd(2)) {
return; }
581 "TwoStreamRadiation::advance: call_site must be pre_dycore or post_dycore");
583 "TwoStreamRadiation::advance called on a level define_level has not built");
587 m_alb_sw[lev]->boxArray().size() == cons_old.boxArray().size() &&
588 m_alb_sw[lev]->DistributionMap() == cons_old.DistributionMap(),
589 "TwoStreamRadiation: the 2D surface fields and the 3D state are laid out differently");
590 if (qheating !=
nullptr) {
592 qheating->boxArray() == cons_old.boxArray() &&
593 qheating->DistributionMap() == cons_old.DistributionMap(),
594 "TwoStreamRadiation: qheating must be the 2-component (SW, LW) field on the state's grids");
600 "TwoStreamRadiation: seb_enable is set but the SEB fields were not allocated");
608 call_site ==
"post_dycore",
609 "TwoStreamRadiation: supplying a land-surface model's forcing needs the rad_fluxes array");
612 "TwoStreamRadiation: the force-restore update needs a positive, finite dt_step");
633 const bool seb_active = rad_choice.
seb_enable;
640 const bool has_external_surface_temperature =
641 (radiation_inputs.size() > 0 && radiation_inputs[0] !=
nullptr) ||
642 (noahmp_active && lsm_has_field(lsm, lev,
"t_sfc"));
654 if (call_site ==
"pre_dycore") {
655 ReduceOps<ReduceOpMin, ReduceOpMin> state_ops;
656 ReduceData<amrex::Real, amrex::Real> state_data(state_ops);
657 using StateTuple =
typename decltype(state_data)::Type;
658 for (MFIter mfi(cons_old, TilingIfNotGPU()); mfi.isValid(); ++mfi) {
659 const auto& arr = cons_old.const_array(mfi);
660 state_ops.eval(mfi.tilebox(), state_data,
661 [=] AMREX_GPU_DEVICE (
int i,
int j,
int k) -> StateTuple {
662 constexpr
amrex::Real neg_inf = -std::numeric_limits<amrex::Real>::infinity();
665 return {amrex::Math::isfinite(
rho) ?
rho : neg_inf,
666 amrex::Math::isfinite(rth) ? rth : neg_inf};
669 auto state_tuple = state_data.value(state_ops);
670 amrex::Real mins[2] = {amrex::get<0>(state_tuple), amrex::get<1>(state_tuple)};
671 ParallelDescriptor::ReduceRealMin(mins, 2);
674 if (!std::isfinite(rho_min) || !std::isfinite(rth_min)) {
675 amrex::Abort(
"TwoStreamRadiation: the state handed to the column sweep at level " +
676 std::to_string(lev) +
", step " + std::to_string(nstep) +
677 " has a non-finite density or rho*theta");
679 if (!(rho_min > 0.0) || !(rth_min > 0.0)) {
680 amrex::Abort(
"TwoStreamRadiation: the state handed to the column sweep at level " +
681 std::to_string(lev) +
", step " + std::to_string(nstep) +
682 " has a non-positive density (min " + std::to_string(rho_min) +
683 ") or rho*theta (min " + std::to_string(rth_min) +
")");
691 const bool do_sweep = (call_site !=
"post_dycore");
700 m_diag[lev] = std::make_unique<RadiationDiagnostics>(
722 amrex::Real seb_residual_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
723 amrex::Real seb_residual_max = std::numeric_limits<amrex::Real>::quiet_NaN();
726 amrex::Real t_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
727 amrex::Real t_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
728 amrex::Real q_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
729 amrex::Real q_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
732 const MultiFab& state_cons = cons_old;
735 if (state_cons.nComp() > 0 ) {
743 if (do_sweep) { set_solar_state(ts_params, rad_choice,
m_orbit, epoch_time, have_datetime, lev, nstep); }
752 amrex::Long n_columns_total = 0;
756 amrex::Long n_seb_columns = 0;
766 MultiFab* qheating_mf = qheating;
773 const bool sw_flux_from_rad = seb_active &&
775 !(noahmp_active && lsm_has_field(lsm, lev,
"sav"));
776 const bool lw_flux_from_rad = seb_active &&
778 !(noahmp_active && lsm_has_field(lsm, lev,
"fira"));
796 if (!sw_flux_from_rad) {
800 if (!lw_flux_from_rad) {
814 const int surface_k = geom.Domain().smallEnd(2);
816 lsm_field(lsm, lev,
"hfx"), sfc_sens_flux, surface_k,
820 lsm_field(lsm, lev,
"lh"), sfc_laten_flux, surface_k,
827 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);
842 const Box& rad_domain = geom.Domain();
844 for (MFIter mfi(state_cons,
false); mfi.isValid(); ++mfi)
846 const Box& bx = mfi.validbox();
854 bx.smallEnd(2) == rad_domain.smallEnd(2) &&
855 bx.bigEnd(2) == rad_domain.bigEnd(2),
856 "TwoStream radiation requires grids that span the domain in z; "
857 "set amr.max_grid_size_z to at least amr.n_cell in z");
858 const auto& state_arr = state_cons.const_array(mfi);
861 const amrex::Real dz_uniform_lev = geom.CellSize(2);
865 Array4<const amrex::Real> z_phys_nd_arr;
866 if (z_phys_nd !=
nullptr) {
867 z_phys_nd_arr = z_phys_nd->const_array(mfi);
873 Array4<amrex::Real> scratch_arr = scratch_fab.array();
889 bool has_hetero_alb_sw =
false;
890 Array4<const amrex::Real> hetero_alb_sw_arr;
892 const MultiFab* lsm_albedo = noahmp_active
893 ? lsm_field(lsm, lev, lsm_broadband_albedo_name)
895 if (lsm_albedo !=
nullptr) {
896 hetero_alb_sw_arr = lsm_albedo->const_array(mfi);
897 has_hetero_alb_sw =
true;
898 }
else if (radiation_inputs.size() > 2 && radiation_inputs[2]) {
899 hetero_alb_sw_arr = radiation_inputs[2]->const_array(mfi);
900 has_hetero_alb_sw =
true;
902 hetero_alb_sw_arr =
m_alb_sw[lev]->const_array(mfi);
903 has_hetero_alb_sw =
true;
908 bool has_hetero_emiss_lw =
false;
909 Array4<const amrex::Real> hetero_emiss_lw_arr;
911 std::string varname_emiss =
"sfc_emis";
912 int lsm_idx = noahmp_active ? lsm.
Get_DataIdx(lev, varname_emiss) : -1;
913 if (radiation_inputs.size() > 1 && radiation_inputs[1]) {
914 hetero_emiss_lw_arr = radiation_inputs[1]->const_array(mfi);
915 has_hetero_emiss_lw =
true;
916 }
else if (lsm_idx >= 0) {
919 hetero_emiss_lw_arr = lsm_ptr->const_array(mfi);
920 has_hetero_emiss_lw =
true;
923 hetero_emiss_lw_arr =
m_emiss_lw[lev]->const_array(mfi);
924 has_hetero_emiss_lw =
true;
935 bool has_lsm_t_sfc =
false;
936 Array4<const amrex::Real> lsm_t_sfc_arr;
937 bool has_seb_t_sfc =
false;
938 Array4<const amrex::Real> seb_t_sfc_arr;
939 bool has_surface_layer =
false;
940 Array4<const amrex::Real> surface_layer_theta_arr;
942 std::string varname_t_sfc =
"t_sfc";
943 int lsm_idx = noahmp_active ? lsm.
Get_DataIdx(lev, varname_t_sfc) : -1;
944 if (radiation_inputs.size() > 0 && radiation_inputs[0]) {
945 lsm_t_sfc_arr = radiation_inputs[0]->const_array(mfi);
946 has_lsm_t_sfc =
true;
947 }
else if (lsm_idx >= 0) {
950 lsm_t_sfc_arr = lsm_ptr->const_array(mfi);
951 has_lsm_t_sfc =
true;
960 seb_t_sfc_arr =
m_t_sfc[lev]->const_array(mfi);
961 has_seb_t_sfc =
true;
963 if (t_surf !=
nullptr) {
964 surface_layer_theta_arr = t_surf->const_array(mfi);
965 has_surface_layer =
true;
972 const bool has_latlon = (lat_m !=
nullptr) && (lon_m !=
nullptr);
973 Array4<const amrex::Real> lat_arr, lon_arr;
975 lat_arr = lat_m->const_array(mfi);
976 lon_arr = lon_m->const_array(mfi);
985 const bool write_fluxes = (rad_fluxes !=
nullptr);
986 Array4<amrex::Real> rad_flux_clear_arr;
987 FArrayBox rad_flux_cloudy_fab;
988 Array4<amrex::Real> rad_flux_cloudy_arr;
990 rad_flux_clear_arr = rad_fluxes->array(mfi);
991 if (cloud_fraction > 0.0) {
993 rad_flux_cloudy_arr = rad_flux_cloudy_fab.array();
999 Array4<amrex::Real> sw_sfc_out;
1000 Array4<amrex::Real> lw_sfc_out;
1001 if (sw_flux_from_rad) sw_sfc_out =
m_sw_flux_sfc[lev]->array(mfi);
1002 if (lw_flux_from_rad) lw_sfc_out =
m_lw_flux_sfc[lev]->array(mfi);
1007 Array4<amrex::Real> sw_dn_out, lw_dn_out, coszen_out;
1019 const int m_canopy_top = supply_canopy ?
m_canopy_m[lev] : 0;
1021 Array4<amrex::Real> canopy_out, canopy_cosz_out;
1022 if (supply_canopy) {
1024 "TwoStreamRadiation: the canopy forcing of the building faces needs the rad_fluxes array");
1026 "TwoStreamRadiation: the canopy reaches above the top of the column");
1027 canopy_out =
m_canopy[lev]->array(mfi);
1033 const auto&
lo = bx.loVect();
1034 const auto&
hi = bx.hiVect();
1035 Box xy_box(IntVect(
lo[0],
lo[1], 0), IntVect(
hi[0],
hi[1], 0));
1038 amrex::Long n_cols =
static_cast<amrex::Long
>(bx.length(0)) *
1039 static_cast<amrex::Long
>(bx.length(1));
1040 n_columns_total += n_cols;
1046 FArrayBox qheating_fallback_fab;
1047 Array4<amrex::Real> qheating_clear_arr;
1048 if (qheating_mf !=
nullptr) {
1049 qheating_clear_arr = qheating_mf->array(mfi);
1051 qheating_fallback_fab.resize(bx, 2);
1052 qheating_clear_arr = qheating_fallback_fab.array();
1057 FArrayBox qheating_cloudy_fab;
1058 Array4<amrex::Real> qheating_cloudy_arr;
1059 if (cloud_fraction > 0.0) {
1060 qheating_cloudy_fab.resize(bx, 2);
1061 qheating_cloudy_arr = qheating_cloudy_fab.array();
1074 ReduceOps<ReduceOpMax, ReduceOpSum, ReduceOpSum, ReduceOpSum, ReduceOpSum, ReduceOpSum> reduce_ops;
1075 ReduceData<amrex::Real, amrex::Real, amrex::Real, amrex::Real, amrex::Real, amrex::Real> reduce_data(reduce_ops);
1077 using ReduceTuple =
typename decltype(reduce_data)::Type;
1080 reduce_ops.eval(xy_box, reduce_data,
1081 [=] AMREX_GPU_DEVICE (
int i,
int j,
int ) -> ReduceTuple
1093 i, j, bx, dz_uniform_lev, state_arr, ts_params,
false,
1095 max_heating_clear, sw_flux_clear, sw_up_clear, lw_net_clear, lw_up_clear,
1097 z_phys_nd_arr, scratch_arr,
1098 has_hetero_alb_sw, &hetero_alb_sw_arr,
1099 has_hetero_emiss_lw, &hetero_emiss_lw_arr,
1100 has_lsm_t_sfc, &lsm_t_sfc_arr,
1101 has_seb_t_sfc, &seb_t_sfc_arr,
1102 has_surface_layer, &surface_layer_theta_arr,
1103 has_latlon, &lat_arr, &lon_arr,
1104 write_fluxes ? &rad_flux_clear_arr :
nullptr,
1108 if (supply_canopy) {
1111 cloud_fraction,
false, canopy_out);
1123 if (cloud_fraction > 0.0) {
1131 i, j, bx, dz_uniform_lev, state_arr, ts_params,
true,
1132 qheating_cloudy_arr,
1133 max_heating_cloudy, sw_flux_cloudy, sw_up_cloudy, lw_net_cloudy, lw_up_cloudy,
1135 z_phys_nd_arr, scratch_arr,
1136 has_hetero_alb_sw, &hetero_alb_sw_arr,
1137 has_hetero_emiss_lw, &hetero_emiss_lw_arr,
1138 has_lsm_t_sfc, &lsm_t_sfc_arr,
1139 has_seb_t_sfc, &seb_t_sfc_arr,
1140 has_surface_layer, &surface_layer_theta_arr,
1141 has_latlon, &lat_arr, &lon_arr,
1142 write_fluxes ? &rad_flux_cloudy_arr :
nullptr);
1145 sw_flux_col = (1.0 - cloud_fraction) * sw_flux_clear +
1146 cloud_fraction * sw_flux_cloudy;
1147 sw_up_col = (1.0 - cloud_fraction) * sw_up_clear +
1148 cloud_fraction * sw_up_cloudy;
1149 lw_net_col = (1.0 - cloud_fraction) * lw_net_clear +
1150 cloud_fraction * lw_net_cloudy;
1151 lw_up_col = (1.0 - cloud_fraction) * lw_up_clear +
1152 cloud_fraction * lw_up_cloudy;
1153 max_heating_col = std::max(max_heating_clear, max_heating_cloudy);
1154 if (supply_canopy) {
1157 cloud_fraction,
true, canopy_out);
1163 int kmin = bx.smallEnd(2);
1164 int kmax = bx.bigEnd(2);
1165 for (
int k = kmin; k <= kmax; ++k) {
1166 for (
int comp = 0; comp < 2; ++comp) {
1167 amrex::Real q_clear_val = qheating_clear_arr(i, j, k, comp);
1168 amrex::Real q_cloudy_val = qheating_cloudy_arr(i, j, k, comp);
1169 qheating_clear_arr(i, j, k, comp) =
1170 (1.0 - cloud_fraction) * q_clear_val +
1171 cloud_fraction * q_cloudy_val;
1176 for (
int k = kmin; k <= kmax + 1; ++k) {
1177 for (
int comp = 0; comp < 4; ++comp) {
1178 rad_flux_clear_arr(i, j, k, comp) =
1179 (1.0 - cloud_fraction) * rad_flux_clear_arr(i, j, k, comp) +
1180 cloud_fraction * rad_flux_cloudy_arr(i, j, k, comp);
1188 if (sw_flux_from_rad) sw_sfc_out(i, j, 0) = sw_flux_col;
1189 if (lw_flux_from_rad) lw_sfc_out(i, j, 0) = -lw_net_col;
1194 cos_zenith_col, sw_dn_out(i, j, 0),
1195 lw_dn_out(i, j, 0), coszen_out(i, j, 0));
1199 if (supply_canopy) {
1205 return {max_heating_col, sw_flux_col, sw_up_col, lw_net_col, lw_up_col, sw_toa_clear};
1210 amrex::Gpu::synchronize();
1211 auto reduce_tuple = reduce_data.value(reduce_ops);
1212 max_heating_box = amrex::get<0>(reduce_tuple);
1213 sw_sum_box = amrex::get<1>(reduce_tuple);
1214 sw_up_sum_box = amrex::get<2>(reduce_tuple);
1215 lw_sum_box = amrex::get<3>(reduce_tuple);
1216 lw_up_sum_box = amrex::get<4>(reduce_tuple);
1217 sw_toa_sum_box = amrex::get<5>(reduce_tuple);
1220 max_heating_global = std::max(max_heating_global, max_heating_box);
1221 sw_surface_sum += sw_sum_box;
1222 sw_up_toa_sum += sw_up_sum_box;
1223 lw_net_sum += lw_sum_box;
1224 lw_up_toa_sum += lw_up_sum_box;
1225 sw_toa_sum += sw_toa_sum_box;
1233 amrex::Real sums[5] = {sw_surface_sum, sw_up_toa_sum, lw_net_sum, lw_up_toa_sum, sw_toa_sum};
1234 ParallelDescriptor::ReduceRealSum(sums, 5);
1235 sw_surface_sum = sums[0]; sw_up_toa_sum = sums[1];
1236 lw_net_sum = sums[2]; lw_up_toa_sum = sums[3];
1237 sw_toa_sum = sums[4];
1238 ParallelDescriptor::ReduceLongSum(n_columns_total);
1239 ParallelDescriptor::ReduceRealMax(max_heating_global);
1247 if (do_sweep && qheating_mf !=
nullptr &&
1248 (qheating_mf->contains_nan(0, 2, 0) || qheating_mf->contains_inf(0, 2, 0))) {
1249 amrex::Abort(
"TwoStreamRadiation: non-finite heating rate after the column sweep at level " +
1250 std::to_string(lev) +
", step " + std::to_string(nstep));
1255 static bool warned_seb_misconfig =
false;
1256 if (!warned_seb_misconfig && ParallelDescriptor::IOProcessor()) {
1257 Print() <<
"WARNING: erf.radiation.seb_diagnostic_enable=true but "
1258 "seb_enable=false; SEB residual diagnostics will report NaN. "
1259 "Set erf.radiation.seb_enable=true to enable SEB field "
1261 warned_seb_misconfig =
true;
1266 seb_residual_max = 0.0;
1270 for (MFIter mfi(state_cons,
false); mfi.isValid(); ++mfi) {
1271 const Box& bx = mfi.validbox();
1272 const auto&
lo = bx.loVect();
1273 const auto&
hi = bx.hiVect();
1274 Box xy_box(IntVect(
lo[0],
lo[1], 0), IntVect(
hi[0],
hi[1], 0));
1277 Array4<const amrex::Real> sw_flux_arr =
m_sw_flux_sfc[lev]->const_array(mfi);
1278 Array4<const amrex::Real> lw_flux_arr =
m_lw_flux_sfc[lev]->const_array(mfi);
1279 Array4<const amrex::Real> hfx_arr =
m_hfx_sfc[lev]->const_array(mfi);
1280 Array4<const amrex::Real> lh_arr =
m_lh_sfc[lev]->const_array(mfi);
1281 Array4<const amrex::Real> grdflx_arr =
m_grdflx_sfc[lev]->const_array(mfi);
1284 amrex::Long n_cols_box =
static_cast<amrex::Long
>(bx.length(0)) *
1285 static_cast<amrex::Long
>(bx.length(1));
1290 ReduceOps<ReduceOpSum, ReduceOpMax> seb_reduce_ops;
1291 ReduceData<amrex::Real, amrex::Real> seb_reduce_data(seb_reduce_ops);
1293 using SEBReduceTuple =
typename decltype(seb_reduce_data)::Type;
1295 seb_reduce_ops.eval(xy_box, seb_reduce_data,
1296 [=] AMREX_GPU_DEVICE (
int i,
int j,
int ) -> SEBReduceTuple {
1307 return {residual, std::abs(residual)};
1312 amrex::Gpu::synchronize();
1313 auto seb_reduce_tuple = seb_reduce_data.value(seb_reduce_ops);
1314 residual_sum_box = amrex::get<0>(seb_reduce_tuple);
1315 residual_max_box = amrex::get<1>(seb_reduce_tuple);
1318 seb_residual_sum += residual_sum_box;
1319 seb_residual_max = std::max(seb_residual_max, residual_max_box);
1320 n_seb_columns += n_cols_box;
1322 ParallelDescriptor::ReduceRealSum(seb_residual_sum);
1323 ParallelDescriptor::ReduceRealMax(seb_residual_max);
1324 ParallelDescriptor::ReduceLongSum(n_seb_columns);
1337 const bool seb_radiation_ready =
m_flux_diag[lev].valid ||
1338 !(sw_flux_from_rad || lw_flux_from_rad);
1340 call_site ==
"post_dycore" && seb_radiation_ready) {
1341 if (!has_external_surface_temperature) {
1346 amrex::Real t_s_max_val = -std::numeric_limits<amrex::Real>::max();
1348 amrex::Real q_s_max_val = -std::numeric_limits<amrex::Real>::max();
1349 amrex::Long n_prog_columns = 0;
1355 for (MFIter mfi(state_cons,
false); mfi.isValid(); ++mfi) {
1356 const Box& bx = mfi.validbox();
1357 const auto&
lo = bx.loVect();
1358 const auto&
hi = bx.hiVect();
1359 Box xy_box(IntVect(
lo[0],
lo[1], 0), IntVect(
hi[0],
hi[1], 0));
1362 Array4<const amrex::Real> sw_flux_arr =
m_sw_flux_sfc[lev]->const_array(mfi);
1363 Array4<const amrex::Real> lw_flux_arr =
m_lw_flux_sfc[lev]->const_array(mfi);
1364 Array4<const amrex::Real> hfx_arr =
m_hfx_sfc[lev]->const_array(mfi);
1365 Array4<const amrex::Real> lh_arr =
m_lh_sfc[lev]->const_array(mfi);
1366 Array4<const amrex::Real> grdflx_arr =
m_grdflx_sfc[lev]->const_array(mfi);
1367 Array4<const amrex::Real> t_deep_arr =
m_t_deep[lev]->const_array(mfi);
1368 Array4<const amrex::Real> q_deep_arr =
m_q_deep[lev]->const_array(mfi);
1371 Array4<amrex::Real> t_s_arr =
m_t_sfc[lev]->array(mfi);
1372 Array4<amrex::Real> q_s_arr =
m_q_sfc[lev]->array(mfi);
1374 amrex::Long n_cols_box =
static_cast<amrex::Long
>(bx.length(0)) *
1375 static_cast<amrex::Long
>(bx.length(1));
1377 amrex::Real t_s_max_box = -std::numeric_limits<amrex::Real>::max();
1379 amrex::Real q_s_max_box = -std::numeric_limits<amrex::Real>::max();
1382 ReduceOps<ReduceOpSum, ReduceOpMax, ReduceOpSum, ReduceOpMax> prog_reduce_ops;
1383 ReduceData<amrex::Real, amrex::Real, amrex::Real, amrex::Real> prog_reduce_data(prog_reduce_ops);
1385 using ProgReduceTuple =
typename decltype(prog_reduce_data)::Type;
1387 prog_reduce_ops.eval(xy_box, prog_reduce_data,
1396 AMREX_GPU_DEVICE (
int i,
int j,
int ) -> ProgReduceTuple {
1419 amrex::Real t_s_new = t_s_old + dt_step * dT_s_dt;
1420 amrex::Real q_s_new = q_s_old + dt_step * dq_s_dt;
1423 t_s_new = amrex::max(t_min, amrex::min(t_max, t_s_new));
1424 q_s_new = amrex::max(q_min, amrex::min(q_max, q_s_new));
1427 t_s_arr(i, j, 0) = t_s_new;
1428 q_s_arr(i, j, 0) = q_s_new;
1431 return {t_s_new, std::abs(t_s_new), q_s_new, std::abs(q_s_new)};
1435 amrex::Gpu::synchronize();
1436 auto prog_reduce_tuple = prog_reduce_data.value(prog_reduce_ops);
1437 t_s_sum_box = amrex::get<0>(prog_reduce_tuple);
1438 t_s_max_box = amrex::get<1>(prog_reduce_tuple);
1439 q_s_sum_box = amrex::get<2>(prog_reduce_tuple);
1440 q_s_max_box = amrex::get<3>(prog_reduce_tuple);
1443 t_s_sum += t_s_sum_box;
1444 t_s_max_val = std::max(t_s_max_val, t_s_max_box);
1445 q_s_sum += q_s_sum_box;
1446 q_s_max_val = std::max(q_s_max_val, q_s_max_box);
1447 n_prog_columns += n_cols_box;
1453 if (
m_t_sfc[lev]->contains_nan(0, 1, 0) ||
m_q_sfc[lev]->contains_nan(0, 1, 0)) {
1454 amrex::Abort(
"TwoStreamRadiation: non-finite surface temperature or moisture "
1455 "after the force-restore update at level " + std::to_string(lev) +
1456 ", step " + std::to_string(nstep));
1461 ParallelDescriptor::ReduceRealSum(sums, 2);
1462 t_s_sum = sums[0]; q_s_sum = sums[1];
1463 ParallelDescriptor::ReduceRealMax(t_s_max_val);
1464 ParallelDescriptor::ReduceRealMax(q_s_max_val);
1465 ParallelDescriptor::ReduceLongSum(n_prog_columns);
1469 if (n_prog_columns > 0) {
1470 t_s_mean = t_s_sum /
static_cast<amrex::Real>(n_prog_columns);
1471 t_s_max = t_s_max_val;
1472 q_s_mean = q_s_sum /
static_cast<amrex::Real>(n_prog_columns);
1473 q_s_max = q_s_max_val;
1475 t_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
1476 t_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
1477 q_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
1478 q_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
1483 t_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
1484 t_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
1485 q_s_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
1486 q_s_max = std::numeric_limits<amrex::Real>::quiet_NaN();
1497 if (n_columns_total > 0) {
1499 SW_surface = sw_surface_sum * inv_n;
1500 SW_TOA = sw_toa_sum * inv_n;
1501 SW_up_TOA = sw_up_toa_sum * inv_n;
1502 LW_net_surface = lw_net_sum * inv_n;
1503 LW_up_TOA = lw_up_toa_sum * inv_n;
1505 heating_rate_max = max_heating_global;
1506 m_flux_diag[lev] = FluxDiag{
true, SW_surface, SW_TOA, SW_up_TOA,
1507 LW_net_surface, LW_up_TOA,
1511 if (!cached.valid) {
1522 SW_surface = cached.SW_surface;
1523 SW_TOA = cached.SW_TOA;
1524 SW_up_TOA = cached.SW_up_TOA;
1525 LW_net_surface = cached.LW_net_surface;
1526 LW_up_TOA = cached.LW_up_TOA;
1527 heating_rate_max = cached.heating_rate_max;
1532 seb_residual_mean = seb_residual_sum /
static_cast<amrex::Real>(n_seb_columns);
1535 seb_residual_mean = std::numeric_limits<amrex::Real>::quiet_NaN();
1536 seb_residual_max = std::numeric_limits<amrex::Real>::quiet_NaN();
1542 if (rad_choice.
verbosity >= 1 && ParallelDescriptor::IOProcessor()) {
1543 Print() <<
"Radiation diagnostics at step " << nstep <<
":\n"
1544 <<
" SW TOA = " << SW_TOA <<
" W/m^2\n"
1545 <<
" SW surface = " << SW_surface <<
" W/m^2\n"
1546 <<
" SW up (TOA) = " << SW_up_TOA <<
" W/m^2\n"
1547 <<
" LW net (surface) = " << LW_net_surface <<
" W/m^2\n"
1548 <<
" LW up (TOA) = " << LW_up_TOA <<
" W/m^2\n"
1549 <<
" Max heating rate = " << heating_rate_max <<
" K/s\n";
1551 Print() <<
" SEB residual (mean) = " << seb_residual_mean <<
" W/m^2\n"
1552 <<
" SEB residual (max) = " << seb_residual_max <<
" W/m^2\n";
1555 Print() <<
" Surface temperature (mean) = " << t_s_mean <<
" K\n"
1556 <<
" Surface temperature (max) = " << t_s_max <<
" K\n"
1557 <<
" Surface moisture (mean) = " << q_s_mean <<
" kg/kg\n"
1558 <<
" Surface moisture (max) = " << q_s_max <<
" kg/kg\n";
1562 rad_diag.
append(nstep, time, call_site, SW_surface, SW_TOA,
1563 SW_up_TOA, LW_net_surface, LW_up_TOA, heating_rate_max,
1564 seb_residual_mean, seb_residual_max,
1565 t_s_mean, t_s_max, q_s_mean, q_s_max);
#define Rho_comp
Definition: ERF_IndexDefines.H:42
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:43
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
pp get("wavelength", wavelength)
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:359
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:342
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_canopy
Definition: ERF_TwoStreamRadiation.H:358
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_grdflx_sfc
Definition: ERF_TwoStreamRadiation.H:341
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_lw_flux_sfc
Definition: ERF_TwoStreamRadiation.H:338
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_q_deep
Definition: ERF_TwoStreamRadiation.H:344
amrex::Real m_rdOcp
Definition: ERF_TwoStreamRadiation.H:321
amrex::Vector< FluxDiag > m_flux_diag
Definition: ERF_TwoStreamRadiation.H:363
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:333
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_t_sfc
Definition: ERF_TwoStreamRadiation.H:334
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_lw_dn_sfc
Definition: ERF_TwoStreamRadiation.H:352
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_lh_sfc
Definition: ERF_TwoStreamRadiation.H:340
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_alb_sw
Definition: ERF_TwoStreamRadiation.H:332
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_sw_flux_sfc
Definition: ERF_TwoStreamRadiation.H:337
OrbitalCache m_orbit
Definition: ERF_TwoStreamRadiation.H:322
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_t_deep
Definition: ERF_TwoStreamRadiation.H:343
amrex::Vector< std::unique_ptr< RadiationDiagnostics > > m_diag
Definition: ERF_TwoStreamRadiation.H:329
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_sw_dn_sfc
Definition: ERF_TwoStreamRadiation.H:351
amrex::Vector< int > m_canopy_step
Definition: ERF_TwoStreamRadiation.H:361
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_hfx_sfc
Definition: ERF_TwoStreamRadiation.H:339
amrex::Vector< int > m_canopy_m
Definition: ERF_TwoStreamRadiation.H:360
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_cos_zenith
Definition: ERF_TwoStreamRadiation.H:353
@ 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:86
bool sw_enabled
Enable shortwave (solar) radiation computation. Only used by the two-stream model.
Definition: ERF_RadStruct.H:98
std::string diag_file
Output file path for radiation diagnostics CSV. Paths are relative to the simulation working director...
Definition: ERF_RadStruct.H:222
amrex::Real surface_albedo_sw
Shortwave surface albedo fallback [0, 1] for per-column heterogeneous-surface-property resolution....
Definition: ERF_RadStruct.H:356
amrex::Real surface_emissivity_lw
Longwave surface emissivity fallback [0, 1] for per-column heterogeneous-surface-property resolution....
Definition: ERF_RadStruct.H:371
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:512
amrex::Real diag_dedup_tol
Tolerance for time equality in duplicate guard [s]. Times within this tolerance are considered equal....
Definition: ERF_RadStruct.H:273
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:569
std::string diag_callsite_mode
Call-site filtering mode: "both" (pre+post rows), "pre_only", or "post_only".
Definition: ERF_RadStruct.H:266
bool diag_regtest_line_enable
Enable RADIATION_DIAG: line emission. Controls regtest-parsing diagnostics line. Default true.
Definition: ERF_RadStruct.H:253
amrex::Real seb_lh_default
Fallback SEB latent heat flux [W/m^2].
Definition: ERF_RadStruct.H:505
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:585
amrex::Real seb_lw_flux_default
Fallback SEB surface net longwave flux [W/m^2].
Definition: ERF_RadStruct.H:397
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:520
bool diag_csv_enable
Enable CSV file append behavior. When false, no file writes; other enabled streams may still print....
Definition: ERF_RadStruct.H:260
amrex::Real seb_t_deep_default
Fallback SEB deep soil temperature [K].
Definition: ERF_RadStruct.H:525
amrex::Real seb_prognostic_t_max_k
Maximum clamping bound for prognostic surface temperature [K]. After each Euler update,...
Definition: ERF_RadStruct.H:602
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:151
bool seb_prognostic_enable
Enable prognostic SEB surface temperature and moisture evolution. When true, integrates SEB residual ...
Definition: ERF_RadStruct.H:550
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:387
amrex::Real seb_prognostic_t_min_k
Minimum clamping bound for prognostic surface temperature [K]. After each Euler update,...
Definition: ERF_RadStruct.H:594
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:308
amrex::Real seb_prognostic_q_max
Maximum clamping bound for prognostic surface moisture [kg/kg]. After each Euler update,...
Definition: ERF_RadStruct.H:618
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:577
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:404
amrex::Real seb_hfx_default
Fallback SEB sensible heat flux [W/m^2].
Definition: ERF_RadStruct.H:500
bool diag_tagged_enable
Enable tagged [RAD][...] debug lines. Controls emission of bracketed-tag debug output....
Definition: ERF_RadStruct.H:246
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:531
amrex::Real seb_sw_flux_default
Fallback SEB surface net shortwave flux [W/m^2].
Definition: ERF_RadStruct.H:392
bool diag_enable
Master switch for radiation diagnostics emission. When false, no diagnostics output (stdout/tagged/re...
Definition: ERF_RadStruct.H:232
amrex::Real seb_prognostic_q_min
Minimum clamping bound for prognostic surface moisture [kg/kg]. After each Euler update,...
Definition: ERF_RadStruct.H:610
bool seb_enable
Master switch for simplified SEB infrastructure. When false (default), no SEB MultiFabs are allocated...
Definition: ERF_RadStruct.H:377
bool diag_stdout_enable
Enable human-readable stdout diagnostics block. Controls tagged [RAD][...] debug line emission (when ...
Definition: ERF_RadStruct.H:239
int verbosity
Debug verbosity level (0=off, 1+=debug output). Gated debug prints only emit when this is >= 1.
Definition: ERF_RadStruct.H:216
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:561
bool seb_diagnostic_enable
Enable diagnostic SEB residual computation and reporting. When true, computes and reports SEB residua...
Definition: ERF_RadStruct.H:541
Trivially copyable subset of RadChoice used by the column kernels.
Definition: ERF_TwoStreamColumn.H:57