ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
rrtmgp Namespace Reference

Functions

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real inverse_exner (const amrex::Real pressure, const amrex::Real rdOcp)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void resolve_surface_temperature (const bool is_land, const bool has_lsm_t_sfc, const bool valid_lsm_t_sfc, const amrex::Real lsm_t_sfc, const bool has_surface_layer, const amrex::Real surface_layer_theta, const amrex::Real surface_pressure, const amrex::Real rdOcp, const amrex::Real default_t_sfc, amrex::Real &t_sfc, amrex::Real *lsm_t_sfc_out)
 
optical_props2_t get_cloud_optics_sw (const int ncol, const int nlay, cloud_optics_t &cloud_optics, gas_optics_t &kdist, real2d_k &lwp, real2d_k &iwp, real2d_k &rel, real2d_k &rei)
 
optical_props1_t get_cloud_optics_lw (const int ncol, const int nlay, cloud_optics_t &cloud_optics, gas_optics_t &kdist, real2d_k &lwp, real2d_k &iwp, real2d_k &rel, real2d_k &rei)
 
optical_props2_t get_subsampled_clouds (const int ncol, const int nlay, const int nbnd, const int ngpt, optical_props2_t &cloud_optics, gas_optics_t &kdist, real2d_k &cld, real2d_k &p_lay)
 
optical_props1_t get_subsampled_clouds (const int ncol, const int nlay, const int nbnd, const int ngpt, optical_props1_t &cloud_optics, gas_optics_t &kdist, real2d_k &cld, real2d_k &p_lay)
 
void rrtmgp_initialize (gas_concs_t &gas_concs_k, const std::string &coefficients_file_sw, const std::string &coefficients_file_lw, const std::string &cloud_optics_file_sw, const std::string &cloud_optics_file_lw, const int &nvar)
 
void rrtmgp_finalize ()
 
void compute_band_by_band_surface_albedos (const int ncol, const int nswbands, real1d_k &sfc_alb_dir_vis, real1d_k &sfc_alb_dir_nir, real1d_k &sfc_alb_dif_vis, real1d_k &sfc_alb_dif_nir, real2d_k &sfc_alb_dir, real2d_k &sfc_alb_dif)
 
void compute_broadband_surface_fluxes (const int ncol, const int kbot, const int nswbands, real3d_k &sw_bnd_flux_dir, real3d_k &sw_bnd_flux_dif, real1d_k &sfc_flux_dir_vis, real1d_k &sfc_flux_dir_nir, real1d_k &sfc_flux_dif_vis, real1d_k &sfc_flux_dif_nir)
 
void rrtmgp_main (const int ncol, const int nlay, real2d_k &p_lay, real2d_k &t_lay, real2d_k &p_lev, real2d_k &t_lev, gas_concs_t &gas_concs, real2d_k &sfc_alb_dir, real2d_k &sfc_alb_dif, real1d_k &mu0, real1d_k &t_sfc, real1d_k &sfc_emis, real1d_k &lw_src, real2d_k &lwp, real2d_k &iwp, real2d_k &rel, real2d_k &rei, real2d_k &cldfrac, real2d_k &sw_flux_up, real2d_k &sw_flux_dn, real2d_k &sw_flux_dn_dir, real2d_k &lw_flux_up, real2d_k &lw_flux_dn, real2d_k &sw_clnclrsky_flux_up, real2d_k &sw_clnclrsky_flux_dn, real2d_k &sw_clnclrsky_flux_dn_dir, real2d_k &sw_clrsky_flux_up, real2d_k &sw_clrsky_flux_dn, real2d_k &sw_clrsky_flux_dn_dir, real2d_k &sw_clnsky_flux_up, real2d_k &sw_clnsky_flux_dn, real2d_k &sw_clnsky_flux_dn_dir, real2d_k &lw_clnclrsky_flux_up, real2d_k &lw_clnclrsky_flux_dn, real2d_k &lw_clrsky_flux_up, real2d_k &lw_clrsky_flux_dn, real2d_k &lw_clnsky_flux_up, real2d_k &lw_clnsky_flux_dn, real3d_k &sw_bnd_flux_up, real3d_k &sw_bnd_flux_dn, real3d_k &sw_bnd_flux_dn_dir, real3d_k &lw_bnd_flux_up, real3d_k &lw_bnd_flux_dn, const RealT tsi_scaling, const bool extra_clnclrsky_diag, const bool extra_clnsky_diag)
 
int3d_k get_subcolumn_mask (const int ncol, const int nlay, const int ngpt, real2d_k &cldf, const int overlap_option, int1d_k &seeds)
 
void rrtmgp_sw (const int ncol, const int nlay, gas_optics_t &k_dist, real2d_k &p_lay, real2d_k &t_lay, real2d_k &p_lev, real2d_k &t_lev, gas_concs_t &gas_concs, real2d_k &sfc_alb_dir, real2d_k &sfc_alb_dif, real1d_k &mu0, optical_props2_t &aerosol, optical_props2_t &clouds, fluxes_t &fluxes, fluxes_broadband_t &clnclrsky_fluxes, fluxes_broadband_t &clrsky_fluxes, fluxes_broadband_t &clnsky_fluxes, const RealT tsi_scaling, const bool extra_clnclrsky_diag, const bool extra_clnsky_diag)
 
void rrtmgp_lw (const int ncol, const int nlay, gas_optics_t &k_dist, real2d_k &p_lay, real2d_k &t_lay, real2d_k &p_lev, real2d_k &t_lev, real1d_k &t_sfc, real1d_k &sfc_emis, real1d_k &lw_src, gas_concs_t &gas_concs, optical_props1_t &aerosol, optical_props1_t &clouds, fluxes_t &fluxes, fluxes_broadband_t &clnclrsky_fluxes, fluxes_broadband_t &clrsky_fluxes, fluxes_broadband_t &clnsky_fluxes, const bool extra_clnclrsky_diag, const bool extra_clnsky_diag)
 
void compute_cloud_area (int ncol, int nlay, int ngpt, const RealT pmin, const RealT pmax, const real2d_k &pmid, const real3d_k &cld_tau_gpt, real1d_k &cld_area)
 
int get_wavelength_index_sw (RealT wavelength)
 
int get_wavelength_index_lw (RealT wavelength)
 
int get_wavelength_index (optical_props_t &kdist, RealT wavelength)
 
void compute_aerocom_cloudtop (int ncol, int nlay, const real2d_k &tmid, const real2d_k &pmid, const real2d_k &p_del, const real2d_k &z_del, const real2d_k &qc, const real2d_k &qi, const real2d_k &rel, const real2d_k &rei, const real2d_k &cldfrac_tot, const real2d_k &nc, real1d_k &T_mid_at_cldtop, real1d_k &p_mid_at_cldtop, real1d_k &cldfrac_ice_at_cldtop, real1d_k &cldfrac_liq_at_cldtop, real1d_k &cldfrac_tot_at_cldtop, real1d_k &cdnc_at_cldtop, real1d_k &eff_radius_qc_at_cldtop, real1d_k &eff_radius_qi_at_cldtop)
 
template<class View1 , class View2 , class View3 , class View4 , class View5 >
void mixing_ratio_to_cloud_mass (View1 const &mixing_ratio, View2 const &cloud_fraction, View3 const &rho, View4 const &dz, View5 const &cloud_mass)
 
template<typename InT , typename OutT , typename T >
void limit_to_bounds_1d (InT const &arr_in, T const lower, T const upper, OutT &arr_out)
 
template<typename InT , typename OutT , typename T >
void limit_to_bounds_2d (InT const &arr_in, T const lower, T const upper, OutT &arr_out)
 
template<class View1 , class View2 , class View3 , class View4 , class View5 >
void compute_heating_rate (View1 const &flux_up, View2 const &flux_dn, View3 const &rho, View4 const &dz, View5 &heating_rate)
 
bool radiation_do (const int irad, const int nstep)
 

Variables

std::unique_ptr< gas_optics_tk_dist_sw_k
 
std::unique_ptr< gas_optics_tk_dist_lw_k
 
std::unique_ptr< cloud_optics_tcloud_optics_sw_k
 
std::unique_ptr< cloud_optics_tcloud_optics_lw_k
 
pool_t kokkos_mem_pool
 
bool initialized = false
 
size_t pool_max_ncol = 0
 
size_t pool_max_nlay = 0
 
int pool_nvar = 0
 

Function Documentation

◆ compute_aerocom_cloudtop()

void rrtmgp::compute_aerocom_cloudtop ( int  ncol,
int  nlay,
const real2d_k tmid,
const real2d_k pmid,
const real2d_k p_del,
const real2d_k z_del,
const real2d_k qc,
const real2d_k qi,
const real2d_k rel,
const real2d_k rei,
const real2d_k cldfrac_tot,
const real2d_k nc,
real1d_k T_mid_at_cldtop,
real1d_k p_mid_at_cldtop,
real1d_k cldfrac_ice_at_cldtop,
real1d_k cldfrac_liq_at_cldtop,
real1d_k cldfrac_tot_at_cldtop,
real1d_k cdnc_at_cldtop,
real1d_k eff_radius_qc_at_cldtop,
real1d_k eff_radius_qi_at_cldtop 
)
1159 {
1160  /* The goal of this routine is to calculate properties at cloud top
1161  * based on the AeroCom recommendation. See reference for routine
1162  * get_subcolumn_mask above, where equation 14 is used for the
1163  * maximum-random overlap assumption for subcolumn generation. We use
1164  * equation 13, the column counterpart.
1165  */
1166  // Set outputs to zero
1167  Kokkos::deep_copy(T_mid_at_cldtop, zero);
1168  Kokkos::deep_copy(p_mid_at_cldtop, zero);
1169  Kokkos::deep_copy(cldfrac_ice_at_cldtop, zero);
1170  Kokkos::deep_copy(cldfrac_liq_at_cldtop, zero);
1171  Kokkos::deep_copy(cldfrac_tot_at_cldtop, zero);
1172  Kokkos::deep_copy(cdnc_at_cldtop, zero);
1173  Kokkos::deep_copy(eff_radius_qc_at_cldtop, zero);
1174  Kokkos::deep_copy(eff_radius_qi_at_cldtop, zero);
1175 
1176  // Initialize the 1D "clear fraction" as 1 (totally clear)
1177  real1d_k aerocom_clr("aerocom_clr", ncol);
1178  Kokkos::deep_copy(aerocom_clr, one);
1179 
1180  // TODO: move tunable constant to namelist
1181  constexpr RealT q_threshold = zero; // BAD_CONSTANT!
1182 
1183  // TODO: move tunable constant to namelist
1184  constexpr RealT cldfrac_tot_threshold = amrex::Real(0.001); // BAD_CONSTANT!
1185 
1186  // Loop over all columns in parallel
1187  Kokkos::parallel_for(ncol, KOKKOS_LAMBDA(int icol)
1188  {
1189  // Loop over all layers in serial (due to accumulative
1190  // product), starting at 2 (second highest) layer because the
1191  // highest is assumed to have no clouds
1192  for(int ilay = 1; ilay < nlay; ++ilay) {
1193  // Only do the calculation if certain conditions are met
1194  if((qc(icol, ilay) + qi(icol, ilay)) > q_threshold &&
1195  (cldfrac_tot(icol, ilay) > cldfrac_tot_threshold)) {
1196  /* PART I: Probabilistically determining cloud top */
1197  // Populate aerocom_tmp as the clear-sky fraction
1198  // probability of this level, where aerocom_clr is that of
1199  // the previous level
1200  auto aerocom_tmp = aerocom_clr(icol) *
1201  (one - std::max(cldfrac_tot(icol, ilay - 1),
1202  cldfrac_tot(icol, ilay))) /
1203  (one - std::min(cldfrac_tot(icol, ilay - 1),
1204  one - cldfrac_tot_threshold));
1205  // Temporary variable for probability "weights"
1206  auto aerocom_wts = aerocom_clr(icol) - aerocom_tmp;
1207  // Temporary variable for liquid "phase"
1208  auto aerocom_phi = qc(icol, ilay) / (qc(icol, ilay) + qi(icol, ilay));
1209  /* PART II: The inferred properties */
1210  /* In general, converting a 3D property X to a 2D cloud-top
1211  * counterpart x follows: x(i) += X(i,k) * weights * Phase
1212  * but X and Phase are not always needed */
1213  // T_mid_at_cldtop
1214  T_mid_at_cldtop(icol) += tmid(icol, ilay) * aerocom_wts;
1215  // p_mid_at_cldtop
1216  p_mid_at_cldtop(icol) += pmid(icol, ilay) * aerocom_wts;
1217  // cldfrac_ice_at_cldtop
1218  cldfrac_ice_at_cldtop(icol) += (one - aerocom_phi) * aerocom_wts;
1219  // cldfrac_liq_at_cldtop
1220  cldfrac_liq_at_cldtop(icol) += aerocom_phi * aerocom_wts;
1221  // cdnc_at_cldtop
1222  /* We need to convert nc from 1/mass to 1/volume first, and
1223  * from grid-mean to in-cloud, but after that, the
1224  * calculation follows the general logic */
1225  // AML NOTE: p_del/z_del/g should be replaced with RHO for our dycore
1226  auto cdnc = nc(icol, ilay) * p_del(icol, ilay) /
1227  z_del(icol, ilay) / CONST_GRAV /
1228  cldfrac_tot(icol, ilay);
1229  cdnc_at_cldtop(icol) += cdnc * aerocom_phi * aerocom_wts;
1230  // eff_radius_qc_at_cldtop
1231  eff_radius_qc_at_cldtop(icol) += rel(icol, ilay) * aerocom_phi * aerocom_wts;
1232  // eff_radius_qi_at_cldtop
1233  eff_radius_qi_at_cldtop(icol) += rei(icol, ilay) * (one - aerocom_phi) * aerocom_wts;
1234  // Reset aerocom_clr to aerocom_tmp to accumulate
1235  aerocom_clr(icol) = aerocom_tmp;
1236  }
1237  }
1238  // After the serial loop over levels, the cloudy fraction is
1239  // defined as (1 - aerocom_clr). This is true because
1240  // aerocom_clr is the result of accumulative probabilities
1241  // (their products)
1242  cldfrac_tot_at_cldtop(icol) = one - aerocom_clr(icol);
1243  });
1244 }
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:56
Kokkos::View< RealT *, KokkosDefaultDevice > real1d_k
Definition: ERF_Kokkos.H:18
amrex::Real RealT
Definition: ERF_Kokkos.H:13
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:30
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:29
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ nc
Definition: ERF_Morrison.H:46
@ qc
Definition: ERF_SatAdj.H:42
@ qi
Definition: ERF_WDM6.H:28

◆ compute_band_by_band_surface_albedos()

void rrtmgp::compute_band_by_band_surface_albedos ( const int  ncol,
const int  nswbands,
real1d_k sfc_alb_dir_vis,
real1d_k sfc_alb_dir_nir,
real1d_k sfc_alb_dif_vis,
real1d_k sfc_alb_dif_nir,
real2d_k sfc_alb_dir,
real2d_k sfc_alb_dif 
)
299 {
300  auto wavenumber_limits = k_dist_sw_k->get_band_lims_wavenumber();
301 
302  // Loop over bands, and determine for each band whether it is broadly in the
303  // visible or infrared part of the spectrum (visible or "not visible")
304  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, nswbands}),
305  KOKKOS_LAMBDA (int icol, int ibnd)
306  {
307  // Threshold between visible and infrared is amrex::Real(0.7) micron, or 14286 cm^-one
308  const RealT visible_wavenumber_threshold = amrex::Real(14286.0);
309 
310  // Wavenumber is in the visible if it is above the visible wavenumber
311  // threshold, and in the infrared if it is below the threshold
312  const bool is_visible_wave1 = (wavenumber_limits(0, ibnd) > visible_wavenumber_threshold ? true : false);
313  const bool is_visible_wave2 = (wavenumber_limits(1, ibnd) > visible_wavenumber_threshold ? true : false);
314 
315  if (is_visible_wave1 && is_visible_wave2) {
316  // Entire band is in the visible
317  sfc_alb_dir(icol,ibnd) = sfc_alb_dir_vis(icol);
318  sfc_alb_dif(icol,ibnd) = sfc_alb_dif_vis(icol);
319  } else if (!is_visible_wave1 && !is_visible_wave2) {
320  // Entire band is in the longwave (near-infrared)
321  sfc_alb_dir(icol,ibnd) = sfc_alb_dir_nir(icol);
322  sfc_alb_dif(icol,ibnd) = sfc_alb_dif_nir(icol);
323  } else {
324  // Band straddles the visible to near-infrared transition, so we take
325  // the albedo to be the average of the visible and near-infrared
326  // broadband albedos
327  sfc_alb_dir(icol,ibnd) = myhalf*(sfc_alb_dir_vis(icol) + sfc_alb_dir_nir(icol));
328  sfc_alb_dif(icol,ibnd) = myhalf*(sfc_alb_dif_vis(icol) + sfc_alb_dif_nir(icol));
329  }
330  });
331 }
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:34
std::unique_ptr< gas_optics_t > k_dist_sw_k
Definition: ERF_RRTMGP_Interface.cpp:11

Referenced by Radiation::run_impl().

Here is the caller graph for this function:

◆ compute_broadband_surface_fluxes()

void rrtmgp::compute_broadband_surface_fluxes ( const int  ncol,
const int  kbot,
const int  nswbands,
real3d_k sw_bnd_flux_dir,
real3d_k sw_bnd_flux_dif,
real1d_k sfc_flux_dir_vis,
real1d_k sfc_flux_dir_nir,
real1d_k sfc_flux_dif_vis,
real1d_k sfc_flux_dif_nir 
)
344 {
345  // Band 10 straddles the near-IR and visible, so divide contributions from band 10 between both broadband sums
346  // TODO: Hard-coding these band indices is really bad practice. If the bands ever were to change (like when
347  // the RRTMG bands were re-ordered for RRTMGP), we would be using the wrong bands for the IR and UV/VIS. This
348  // should be refactored to grab the correct bands by specifying appropriate wavenumber rather than index.
349  //sfc_flux_dir_nir(i) = sum(sw_bnd_flux_dir(i+1,kbot,1:9)) + myhalf * sw_bnd_flux_dir(i+1,kbot,10);
350  //sfc_flux_dir_vis(i) = sum(sw_bnd_flux_dir(i+1,kbot,11:14)) + myhalf * sw_bnd_flux_dir(i+1,kbot,10);
351  //sfc_flux_dif_nir(i) = sum(sw_bnd_flux_dif(i+1,kbot,1:9)) + myhalf * sw_bnd_flux_dif(i+1,kbot,10);
352  //sfc_flux_dif_vis(i) = sum(sw_bnd_flux_dif(i+1,kbot,11:14)) + myhalf * sw_bnd_flux_dif(i+1,kbot,10);
353 
354  // Initialize sums over bands
355  Kokkos::deep_copy(sfc_flux_dir_nir, 0);
356  Kokkos::deep_copy(sfc_flux_dir_vis, 0);
357  Kokkos::deep_copy(sfc_flux_dif_nir, 0);
358  Kokkos::deep_copy(sfc_flux_dif_vis, 0);
359 
360  // Threshold between visible and infrared is amrex::Real(0.7) micron, or 14286 cm^-one
361  const RealT visible_wavenumber_threshold = amrex::Real(14286.0);
362  auto wavenumber_limits = k_dist_sw_k->get_band_lims_wavenumber();
363  Kokkos::parallel_for(ncol, KOKKOS_LAMBDA(const int icol)
364  {
365  for (int ibnd = 0; ibnd < nswbands; ++ibnd) {
366  // Wavenumber is in the visible if it is above the visible wavenumber
367  // threshold, and in the infrared if it is below the threshold
368  const bool is_visible_wave1 = (wavenumber_limits(0, ibnd) > visible_wavenumber_threshold ? true : false);
369  const bool is_visible_wave2 = (wavenumber_limits(1, ibnd) > visible_wavenumber_threshold ? true : false);
370 
371  if (is_visible_wave1 && is_visible_wave2) {
372  // Entire band is in the visible
373  sfc_flux_dir_vis(icol) += sw_bnd_flux_dir(icol,kbot,ibnd);
374  sfc_flux_dif_vis(icol) += sw_bnd_flux_dif(icol,kbot,ibnd);
375  } else if (!is_visible_wave1 && !is_visible_wave2) {
376  // Entire band is in the longwave (near-infrared)
377  sfc_flux_dir_nir(icol) += sw_bnd_flux_dir(icol,kbot,ibnd);
378  sfc_flux_dif_nir(icol) += sw_bnd_flux_dif(icol,kbot,ibnd);
379  } else {
380  // Band straddles the visible to near-infrared transition, so put myhalf
381  // the flux in visible and myhalf in near-infrared fluxes
382  sfc_flux_dir_vis(icol) += myhalf * sw_bnd_flux_dir(icol,kbot,ibnd);
383  sfc_flux_dif_vis(icol) += myhalf * sw_bnd_flux_dif(icol,kbot,ibnd);
384  sfc_flux_dir_nir(icol) += myhalf * sw_bnd_flux_dir(icol,kbot,ibnd);
385  sfc_flux_dif_nir(icol) += myhalf * sw_bnd_flux_dif(icol,kbot,ibnd);
386  }
387  }
388  });
389 }

Referenced by Radiation::run_impl().

Here is the caller graph for this function:

◆ compute_cloud_area()

void rrtmgp::compute_cloud_area ( int  ncol,
int  nlay,
int  ngpt,
const RealT  pmin,
const RealT  pmax,
const real2d_k pmid,
const real3d_k cld_tau_gpt,
real1d_k cld_area 
)
1084 {
1085  // Subcolumn binary cld mask; if any layers with pressure between pmin and pmax are cloudy
1086  // then 2d subcol mask is 1, otherwise it is 0
1087  real2d_k subcol_mask("subcol_mask", ncol, ngpt);
1088  Kokkos::deep_copy(subcol_mask, zero);
1089  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {ncol, nlay, ngpt}),
1090  KOKKOS_LAMBDA (int icol, int ilay, int igpt)
1091  {
1092  // NOTE: using plev would need to assume level ordering (top to bottom or bottom to top), but
1093  // using play/pmid does not
1094  if (cld_tau_gpt(icol,ilay,igpt) > 0 && pmid(icol,ilay) >= pmin && pmid(icol,ilay) < pmax) {
1095  subcol_mask(icol,igpt) = one;
1096  }
1097  });
1098  // Compute average over subcols to get cloud area
1099  auto ngpt_inv = one / ngpt;
1100  Kokkos::deep_copy(cld_area, 0);
1101  Kokkos::parallel_for(ncol, KOKKOS_LAMBDA(int icol)
1102  {
1103  // This loop needs to be serial because of the atomic reduction
1104  for (int igpt = 0; igpt < ngpt; ++igpt) {
1105  cld_area(icol) += subcol_mask(icol,igpt) * ngpt_inv;
1106  }
1107  });
1108 }
Kokkos::View< RealT **, layout_t, KokkosDefaultDevice > real2d_k
Definition: ERF_Kokkos.H:19

◆ compute_heating_rate()

template<class View1 , class View2 , class View3 , class View4 , class View5 >
void rrtmgp::compute_heating_rate ( View1 const &  flux_up,
View2 const &  flux_dn,
View3 const &  rho,
View4 const &  dz,
View5 &  heating_rate 
)
86 {
87  const int ncol = (int)flux_up.extent(0);
88  const int nlay = (int)flux_up.extent(1)-1;
89  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, nlay}),
90  KOKKOS_LAMBDA (int icol, int ilay)
91  {
92  // NOTE: This calculation defines Fnet = Up - Dn
93  // H = dT/dt = g/Cp dF/dP
94  // Using dF/dz * dz/dP = dF/dz * (1/(-rho*g)) = dF/dP
95  // We have: dT/dt = dF/dz * (1/(-rho*Cp))
96  heating_rate(icol,ilay) = ( flux_up(icol,ilay+1) - flux_up(icol,ilay)
97  - flux_dn(icol,ilay+1) + flux_dn(icol,ilay) )
98  / ( -rho(icol,ilay) * dz(icol,ilay) * Cp_d );
99  });
100 }
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:36
@ rho
Definition: ERF_Kessler.H:25
@ dz
Definition: ERF_AdvanceWDM6.cpp:272

Referenced by Radiation::finalize_impl(), and Radiation::run_impl().

Here is the caller graph for this function:

◆ get_cloud_optics_lw()

optical_props1_t rrtmgp::get_cloud_optics_lw ( const int  ncol,
const int  nlay,
cloud_optics_t cloud_optics,
gas_optics_t kdist,
real2d_k lwp,
real2d_k iwp,
real2d_k rel,
real2d_k rei 
)
76 {
77  // Initialize optics
78  optical_props1_t clouds;
79  clouds.init(kdist.get_band_lims_wavenumber());
80  clouds.alloc_1scl(ncol, nlay);
81 
82  // Needed for consistency with all-sky example problem?
83  cloud_optics.set_ice_roughness(2);
84 
85  // Limit effective radii to be within bounds of lookup table
86  real2d_k rel_limited("rel_limited", ncol, nlay);
87  real2d_k rei_limited("rei_limited", ncol, nlay);
88  limit_to_bounds_2d(rel, cloud_optics.radliq_lwr,
89  cloud_optics.radliq_upr, rel_limited);
90  limit_to_bounds_2d(rei, cloud_optics.radice_lwr,
91  cloud_optics.radice_upr, rei_limited);
92 
93  // Calculate cloud optics
94  cloud_optics.cloud_optics(ncol, nlay, lwp, iwp, rel_limited, rei_limited, clouds);
95 
96  // Return optics
97  return clouds;
98 }
OpticalProps1sclK< RealT, layout_t, KokkosDefaultDevice > optical_props1_t
Definition: ERF_RRTMGP_Interface.H:35
void limit_to_bounds_2d(InT const &arr_in, T const lower, T const upper, OutT &arr_out)
Definition: ERF_RRTMGP_Utils.H:55

Referenced by rrtmgp_main().

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

◆ get_cloud_optics_sw()

optical_props2_t rrtmgp::get_cloud_optics_sw ( const int  ncol,
const int  nlay,
cloud_optics_t cloud_optics,
gas_optics_t kdist,
real2d_k lwp,
real2d_k iwp,
real2d_k rel,
real2d_k rei 
)
42 {
43  // Initialize optics
44  optical_props2_t clouds;
45  clouds.init(kdist.get_band_lims_wavenumber());
46  clouds.alloc_2str(ncol, nlay);
47 
48  // Needed for consistency with all-sky example problem?
49  cloud_optics.set_ice_roughness(2);
50 
51  // Limit effective radii to be within bounds of lookup table
52  real2d_k rel_limited("rel_limited", ncol, nlay);
53  real2d_k rei_limited("rei_limited", ncol, nlay);
54  limit_to_bounds_2d(rel, cloud_optics.radliq_lwr,
55  cloud_optics.radliq_upr, rel_limited);
56  limit_to_bounds_2d(rei, cloud_optics.radice_lwr,
57  cloud_optics.radice_upr, rei_limited);
58 
59  // Calculate cloud optics
60  cloud_optics.cloud_optics(ncol, nlay, lwp, iwp, rel_limited, rei_limited, clouds);
61 
62  // Return optics
63  return clouds;
64 }
OpticalProps2strK< RealT, layout_t, KokkosDefaultDevice > optical_props2_t
Definition: ERF_RRTMGP_Interface.H:36

Referenced by rrtmgp_main().

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

◆ get_subcolumn_mask()

int3d_k rrtmgp::get_subcolumn_mask ( const int  ncol,
const int  nlay,
const int  ngpt,
real2d_k cldf,
const int  overlap_option,
int1d_k seeds 
)
529 {
530  // Routine will return subcolumn mask with values of 0 indicating no cloud, 1 indicating cloud
531  int3d_k subcolumn_mask("subcolumn_mask", ncol, nlay, ngpt);
532 
533  // Subcolumn generators are a means for producing a variable x(i,j,k), where
534  //
535  // c(i,j,k) = 1 for x(i,j,k) > 1 - cldf(i,j)
536  // c(i,j,k) = 0 for x(i,j,k) <= 1 - cldf(i,j)
537  //
538  // I am going to call this "cldx" to be just slightly less ambiguous
539  real3d_k cldx("cldx", ncol, nlay, ngpt);
540 
541  // Apply overlap assumption to set cldx
542  if (overlap_option == 0) { // Dummy mask, always cloudy
543  Kokkos::deep_copy(cldx, 1);
544  } else { // Default case, maximum-random overlap
545  // Maximum-random overlap:
546  // Uses essentially the algorithm described in eq (14) in Raisanen et al. 2004,
547  // https://rmets.onlinelibrary.wiley.com/doi/epdf/amrex::Real(10.1256)/qj.03.99. Also the same
548  // algorithm used in RRTMG implementation of maximum-random overlap (see
549  // https://github.com/AER-RC/RRTMG_SW/blob/master/src/mcica_subcol_gen_sw.f90)
550  //
551  // First, fill cldx with random numbers.
552  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {ncol, nlay, ngpt}),
553  KOKKOS_LAMBDA (int icol, int ilay, int igpt)
554  {
555  conv::Random rand(seeds(icol) + ilay*ngpt + igpt);
556  cldx(icol,ilay,igpt) = rand.genFP<RealT>();
557  });
558 
559  // Step down columns and apply algorithm from eq (14)
560  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, ngpt}),
561  KOKKOS_LAMBDA (int icol, int igpt)
562  {
563  for (int ilay = 1; ilay < nlay; ilay++) {
564  // Check cldx in level above and see if it satisfies conditions to create a cloudy subcolumn
565  if (cldx(icol,ilay-1,igpt) > one - cldf(icol,ilay-1)) {
566  // Cloudy subcolumn above, use same random number here so that clouds in these two adjacent
567  // layers are maximimally overlapped
568  cldx(icol,ilay,igpt) = cldx(icol,ilay-1,igpt);
569  } else {
570  // Cloud-less above, use new random number so that clouds are distributed
571  // randomly in this layer. Need to scale new random number to range
572  // [0, one - cldf(ilay-1)] because we have artificially changed the distribution
573  // of random numbers in this layer with the above branch of the conditional,
574  // which would otherwise inflate cloud fraction in this layer.
575  cldx(icol,ilay,igpt) = cldx(icol,ilay ,igpt) * (one - cldf(icol,ilay-1));
576  }
577  }
578  });
579  }
580 
581  // Use cldx array to create subcolumn mask
582  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {ncol, nlay, ngpt}),
583  KOKKOS_LAMBDA (int icol, int ilay, int igpt)
584  {
585  if (cldx(icol,ilay,igpt) > one - cldf(icol,ilay)) {
586  subcolumn_mask(icol,ilay,igpt) = 1;
587  } else {
588  subcolumn_mask(icol,ilay,igpt) = 0;
589  }
590  });
591  return subcolumn_mask;
592 }
Kokkos::View< RealT ***, layout_t, KokkosDefaultDevice > real3d_k
Definition: ERF_Kokkos.H:20
Kokkos::View< int ***, layout_t, KokkosDefaultDevice > int3d_k
Definition: ERF_Kokkos.H:23

Referenced by get_subsampled_clouds().

Here is the caller graph for this function:

◆ get_subsampled_clouds() [1/2]

optical_props1_t rrtmgp::get_subsampled_clouds ( const int  ncol,
const int  nlay,
const int  nbnd,
const int  ngpt,
optical_props1_t cloud_optics,
gas_optics_t kdist,
real2d_k cld,
real2d_k p_lay 
)
178 {
179  // Initialized subsampled optics
180  optical_props1_t subsampled_optics;
181  subsampled_optics.init(kdist.get_band_lims_wavenumber(), kdist.get_band_lims_gpoint(), "subsampled_optics");
182  subsampled_optics.alloc_1scl(ncol, nlay);
183 
184  // Check that we do not have clouds with no optical properties; this would get corrected
185  // when we assign optical props, but we want to use a "radiative cloud fraction"
186  // for the subcolumn sampling too because otherwise we can get vertically-contiguous cloud
187  // mask profiles with no actual cloud properties in between, which would just further overestimate
188  // the vertical correlation of cloudy layers. I.e., cloudy layers might look maximally overlapped
189  // even when separated by layers with no cloud properties, when in fact those layers should be
190  // randomly overlapped.
191  real2d_k cldfrac_rad("cldfrac_rad", ncol, nlay);
192  Kokkos::deep_copy(cldfrac_rad, zero);
193  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {ncol, nlay, nbnd}),
194  KOKKOS_LAMBDA (int icol, int ilay, int ibnd)
195  {
196  if (cloud_optics.tau(icol,ilay,ibnd) > 0) {
197  cldfrac_rad(icol,ilay) = cld(icol,ilay);
198  }
199  });
200 
201  // Get subcolumn cloud mask
202  int overlap = 1;
203  // Get unique seeds for each column that are reproducible across different MPI rank layouts;
204  // use decimal part of pressure for this, consistent with the implementation in EAM; use different
205  // seed values for longwave and shortwave
206  int1d_k seeds("seeds", ncol);
207  Kokkos::parallel_for(ncol, KOKKOS_LAMBDA(int icol)
208  {
209  seeds(icol) = 1e9 * (p_lay(icol,nlay-1) - int(p_lay(icol,nlay-1)));
210  });
211  auto cldmask = get_subcolumn_mask(ncol, nlay, ngpt, cldfrac_rad, overlap, seeds);
212 
213  // Assign optical properties to subcolumns (note this implements MCICA)
214  auto gpoint_bands = kdist.get_gpoint_bands();
215  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {ncol, nlay, ngpt}),
216  KOKKOS_LAMBDA (int icol, int ilay, int igpt)
217  {
218  auto ibnd = gpoint_bands(igpt);
219  if (cldmask(icol,ilay,igpt) == 1) {
220  subsampled_optics.tau(icol,ilay,igpt) = cloud_optics.tau(icol,ilay,ibnd);
221  } else {
222  subsampled_optics.tau(icol,ilay,igpt) = 0;
223  }
224  });
225  return subsampled_optics;
226 }
Kokkos::View< int *, KokkosDefaultDevice > int1d_k
Definition: ERF_Kokkos.H:21
int3d_k get_subcolumn_mask(const int ncol, const int nlay, const int ngpt, real2d_k &cldf, const int overlap_option, int1d_k &seeds)
Definition: ERF_RRTMGP_Interface.cpp:523
Here is the call graph for this function:

◆ get_subsampled_clouds() [2/2]

optical_props2_t rrtmgp::get_subsampled_clouds ( const int  ncol,
const int  nlay,
const int  nbnd,
const int  ngpt,
optical_props2_t cloud_optics,
gas_optics_t kdist,
real2d_k cld,
real2d_k p_lay 
)
110 {
111  // Initialized subsampled optics
112  optical_props2_t subsampled_optics;
113  subsampled_optics.init(kdist.get_band_lims_wavenumber(), kdist.get_band_lims_gpoint(), "subsampled_optics");
114  subsampled_optics.alloc_2str(ncol, nlay);
115 
116  // Check that we do not have clouds with no optical properties; this would get corrected
117  // when we assign optical props, but we want to use a "radiative cloud fraction"
118  // for the subcolumn sampling too because otherwise we can get vertically-contiguous cloud
119  // mask profiles with no actual cloud properties in between, which would just further overestimate
120  // the vertical correlation of cloudy layers. I.e., cloudy layers might look maximally overlapped
121  // even when separated by layers with no cloud properties, when in fact those layers should be
122  // randomly overlapped.
123  real2d_k cldfrac_rad("cldfrac_rad", ncol, nlay);
124  Kokkos::deep_copy(cldfrac_rad, zero);
125  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {ncol, nlay, nbnd}),
126  KOKKOS_LAMBDA (int icol, int ilay, int ibnd)
127  {
128  if (cloud_optics.tau(icol,ilay,ibnd) > 0) {
129  cldfrac_rad(icol,ilay) = cld(icol,ilay);
130  }
131  });
132 
133  // Get subcolumn cloud mask; note that get_subcolumn_mask exposes overlap assumption as an option,
134  // but the only currently supported options are 0 (trivial all-or-nothing cloud) or 1 (max-rand),
135  // so overlap has not been exposed as an option beyond this subcolumn. In the future, we should
136  // support generalized overlap as well, with parameters derived from DPSCREAM simulations with very
137  // high resolution.
138  int overlap = 1;
139 
140  // Get unique seeds for each column that are reproducible across different MPI rank layouts;
141  // use decimal part of pressure for this, consistent with the implementation in EAM
142  int1d_k seeds("seeds", ncol);
143  Kokkos::parallel_for(ncol, KOKKOS_LAMBDA(int icol)
144  {
145  seeds(icol) = amrex::Real(1.0e9) * (p_lay(icol,nlay-1) - int(p_lay(icol,nlay-1)));
146  });
147  auto cldmask = get_subcolumn_mask(ncol, nlay, ngpt, cldfrac_rad, overlap, seeds);
148 
149  // Assign optical properties to subcolumns (note this implements MCICA)
150  auto gpoint_bands = kdist.get_gpoint_bands();
151  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {ncol, nlay, ngpt}),
152  KOKKOS_LAMBDA (int icol, int ilay, int igpt)
153  {
154  auto ibnd = gpoint_bands(igpt);
155  if (cldmask(icol,ilay,igpt) == 1) {
156  subsampled_optics.tau(icol,ilay,igpt) = cloud_optics.tau(icol,ilay,ibnd);
157  subsampled_optics.ssa(icol,ilay,igpt) = cloud_optics.ssa(icol,ilay,ibnd);
158  subsampled_optics.g (icol,ilay,igpt) = cloud_optics.g (icol,ilay,ibnd);
159  } else {
160  subsampled_optics.tau(icol,ilay,igpt) = 0;
161  subsampled_optics.ssa(icol,ilay,igpt) = 0;
162  subsampled_optics.g (icol,ilay,igpt) = 0;
163  }
164  });
165  return subsampled_optics;
166 }

Referenced by rrtmgp_main().

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

◆ get_wavelength_index()

int rrtmgp::get_wavelength_index ( optical_props_t kdist,
RealT  wavelength 
)
1122 {
1123  // Get wavelength bounds for all wavelength bands
1124  auto band_lims_wvn = kdist.get_band_lims_wavenumber();
1125  real2d_k wavelength_bounds("wavelength_bounds",band_lims_wvn.extent(0), band_lims_wvn.extent(1));
1126  wavelength_bounds = kdist.get_band_lims_wavelength();
1127 
1128  // Find the band index for the specified wavelength
1129  // Note that bands are stored in wavenumber space, units of cm-1, so if we are passed wavelength
1130  // in units of meters, we need a conversion factor of 10^2
1131  int nbnds = kdist.get_nband();
1132  int band_index = -1;
1133  Kokkos::parallel_reduce(nbnds, KOKKOS_LAMBDA(int ibnd, int& band_index_inner)
1134  {
1135  if (wavelength_bounds(0,ibnd) < wavelength_bounds(1,ibnd)) {
1136  if (wavelength_bounds(0,ibnd) <= wavelength * 1e2 && wavelength * 1e2 <= wavelength_bounds(1,ibnd)) {
1137  band_index_inner = ibnd;
1138  }
1139  } else {
1140  if (wavelength_bounds(0,ibnd) >= wavelength * 1e2 && wavelength * 1e2 >= wavelength_bounds(1,ibnd)) {
1141  band_index_inner = ibnd;
1142  }
1143  }
1144  }, Kokkos::Max<int>(band_index));
1145  return band_index;
1146 }
Real wavelength
Definition: ERF_InitCustomPert_MovingTerrain.H:5

Referenced by get_wavelength_index_lw(), and get_wavelength_index_sw().

Here is the caller graph for this function:

◆ get_wavelength_index_lw()

int rrtmgp::get_wavelength_index_lw ( RealT  wavelength)
int get_wavelength_index(optical_props_t &kdist, RealT wavelength)
Definition: ERF_RRTMGP_Interface.cpp:1120
std::unique_ptr< gas_optics_t > k_dist_lw_k
Definition: ERF_RRTMGP_Interface.cpp:12
Here is the call graph for this function:

◆ get_wavelength_index_sw()

int rrtmgp::get_wavelength_index_sw ( RealT  wavelength)
Here is the call graph for this function:

◆ inverse_exner()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real rrtmgp::inverse_exner ( const amrex::Real  pressure,
const amrex::Real  rdOcp 
)
13 {
14  return one / getExnergivenP(pressure, rdOcp);
15 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getExnergivenP(const amrex::Real P, const amrex::Real rdOcp)
Definition: ERF_EOS.H:141
const Real rdOcp
Definition: ERF_InitCustomPert_ABL.H:72

Referenced by Radiation::kokkos_buffers_to_mf().

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

◆ limit_to_bounds_1d()

template<typename InT , typename OutT , typename T >
void rrtmgp::limit_to_bounds_1d ( InT const &  arr_in,
T const  lower,
T const  upper,
OutT &  arr_out 
)
45 {
46  Kokkos::parallel_for(arr_out.size(), KOKKOS_LAMBDA(int i)
47  {
48  arr_out(i) = std::min(std::max(arr_in(i), lower), upper);
49  });
50 }

◆ limit_to_bounds_2d()

template<typename InT , typename OutT , typename T >
void rrtmgp::limit_to_bounds_2d ( InT const &  arr_in,
T const  lower,
T const  upper,
OutT &  arr_out 
)
59 {
60  const int ex0 = (int) arr_out.extent(0);
61  const int ex1 = (int) arr_out.extent(1);
62  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ex0, ex1}),
63  KOKKOS_LAMBDA (int i, int j)
64  {
65  arr_out(i, j) = std::min(std::max(arr_in(i, j), lower), upper);
66  });
67 }

Referenced by get_cloud_optics_lw(), get_cloud_optics_sw(), rrtmgp_lw(), and rrtmgp_sw().

Here is the caller graph for this function:

◆ mixing_ratio_to_cloud_mass()

template<class View1 , class View2 , class View3 , class View4 , class View5 >
void rrtmgp::mixing_ratio_to_cloud_mass ( View1 const &  mixing_ratio,
View2 const &  cloud_fraction,
View3 const &  rho,
View4 const &  dz,
View5 const &  cloud_mass 
)
17 {
18  const int ncol = mixing_ratio.extent(0);
19  const int nlay = mixing_ratio.extent(1);
20  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, nlay}),
21  KOKKOS_LAMBDA (int icol, int ilay)
22  {
23  // Compute in-cloud mixing ratio (mixing ratio of the cloudy part of the layer)
24  // NOTE: these thresholds (from E3SM) seem arbitrary, but included here for consistency
25  // This limits in-cloud mixing ratio to amrex::Real(0.005) kg/kg. According to note in cloud_diagnostics
26  // in EAM, this is consistent with limits in MG2. Is this true for P3?
27  if (cloud_fraction(icol,ilay) > 0) {
28  // Compute layer-integrated cloud mass (per unit area)
29  auto incloud_mixing_ratio = std::min(mixing_ratio(icol,ilay) /
30  std::max(amrex::Real(0.0001), cloud_fraction(icol,ilay)), amrex::Real(0.005));
31  cloud_mass(icol,ilay) = incloud_mixing_ratio * rho(icol,ilay) * dz(icol,ilay);
32  } else {
33  cloud_mass(icol,ilay) = 0;
34  }
35  });
36 }

Referenced by Radiation::run_impl().

Here is the caller graph for this function:

◆ radiation_do()

bool rrtmgp::radiation_do ( const int  irad,
const int  nstep 
)
inline
107 {
108  // If irad == 0, then never do radiation;
109  // Otherwise, we always call radiation at the first step,
110  // and afterwards we do radiation if the timestep is divisible
111  // by irad
112  if (irad == 0) {
113  return false;
114  } else {
115  return ( (nstep == 0) || (nstep % irad == 0) );
116  }
117 }

◆ resolve_surface_temperature()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void rrtmgp::resolve_surface_temperature ( const bool  is_land,
const bool  has_lsm_t_sfc,
const bool  valid_lsm_t_sfc,
const amrex::Real  lsm_t_sfc,
const bool  has_surface_layer,
const amrex::Real  surface_layer_theta,
const amrex::Real  surface_pressure,
const amrex::Real  rdOcp,
const amrex::Real  default_t_sfc,
amrex::Real t_sfc,
amrex::Real lsm_t_sfc_out 
)

Resolve the RRTMGP surface-temperature input from ERF's surface sources.

RRTMGP and LSM surface-temperature fields contain absolute temperature, while the SurfaceLayer field contains potential temperature. The latter must therefore be converted with the physical surface pressure diagnosed from the lowest atmospheric cell before it is handed to RRTMGP.

Parameters
[in]is_landtrue when the column is over land
[in]has_lsm_t_sfctrue when an LSM t_sfc field exists
[in]valid_lsm_t_sfctrue when the LSM value is not a sentinel
[in]lsm_t_sfcLSM t_sfc value, when present
[in]has_surface_layertrue when a SurfaceLayer field exists
[in]surface_layer_thetaSurfaceLayer potential temperature
[in]surface_pressurephysical surface pressure diagnosed from the lowest atmospheric cell
[in]rdOcpconfigured Rd/cp exponent
[in]default_t_sfcconfigured absolute-temperature fallback
[out]t_sfcresolved absolute temperature for RRTMGP
[in,out]lsm_t_sfc_outoptional LSM fallback/writeback field
51 {
52  // A valid land-model value is already absolute temperature. A valid LSM
53  // value over water follows the existing fallback chain instead.
54  const bool use_lsm_t_sfc = is_land && has_lsm_t_sfc && valid_lsm_t_sfc;
55 
56  if (use_lsm_t_sfc) {
57  t_sfc = lsm_t_sfc;
58  } else if (has_surface_layer &&
60  surface_layer_theta, surface_pressure, rdOcp, t_sfc)) {
61  // SurfaceLayer::get_t_surf() returns theta. RRTMGP requires T.
62  // The helper also rejects an invalid pressure or nonphysical theta.
63  } else {
64  t_sfc = default_t_sfc;
65  }
66 
67  // Preserve the existing fallback/writeback behavior, but write the value
68  // in the LSM contract: absolute temperature.
69  if (lsm_t_sfc_out != nullptr && !use_lsm_t_sfc) {
70  *lsm_t_sfc_out = t_sfc;
71  }
72 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool theta_to_temperature(const amrex::Real theta, const amrex::Real pressure, const amrex::Real rdOcp, amrex::Real &temperature)
Definition: ERF_SurfaceTemperature.H:54

Referenced by Radiation::mf_to_kokkos_buffers().

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

◆ rrtmgp_finalize()

void rrtmgp::rrtmgp_finalize ( )
276 {
277  initialized = false;
278  k_dist_sw_k->finalize();
279  k_dist_lw_k->finalize();
280  cloud_optics_sw_k->finalize();
281  cloud_optics_lw_k->finalize();
282  k_dist_sw_k = nullptr;
283  k_dist_lw_k = nullptr;
284  cloud_optics_sw_k = nullptr;
285  cloud_optics_lw_k = nullptr;
286  pool_t::finalize();
287 }
std::unique_ptr< cloud_optics_t > cloud_optics_sw_k
Definition: ERF_RRTMGP_Interface.cpp:19
std::unique_ptr< cloud_optics_t > cloud_optics_lw_k
Definition: ERF_RRTMGP_Interface.cpp:20
bool initialized
Definition: ERF_RRTMGP_Interface.cpp:25

Referenced by Radiation::~Radiation().

Here is the caller graph for this function:

◆ rrtmgp_initialize()

void rrtmgp::rrtmgp_initialize ( gas_concs_t gas_concs_k,
const std::string &  coefficients_file_sw,
const std::string &  coefficients_file_lw,
const std::string &  cloud_optics_file_sw,
const std::string &  cloud_optics_file_lw,
const int &  nvar 
)
241 {
242  // Initialize Kokkos
243  if (!Kokkos::is_initialized()) { Kokkos::initialize(); }
244 
245  // Create objects for static ptrs
246  k_dist_sw_k = std::make_unique<gas_optics_t>();
247  k_dist_lw_k = std::make_unique<gas_optics_t>();
248  cloud_optics_sw_k = std::make_unique<cloud_optics_t>();
249  cloud_optics_lw_k = std::make_unique<cloud_optics_t>();
250 
251  // Load and initialize absorption coefficient data
252  load_and_init(*k_dist_sw_k, coefficients_file_sw, gas_concs_k);
253  load_and_init(*k_dist_lw_k, coefficients_file_lw, gas_concs_k);
254 
255  // Load and initialize cloud optical property look-up table information
256  load_cld_lutcoeff(*cloud_optics_sw_k, cloud_optics_file_sw);
257  load_cld_lutcoeff(*cloud_optics_lw_k, cloud_optics_file_lw);
258 
259  // Initialize kokkos rrtmgp pool allocator
260  const size_t ngpt = std::max(k_dist_sw_k->get_ngpt(),k_dist_lw_k->get_ngpt());
261  const size_t ncol = gas_concs_k.ncol;
262  const size_t nlay = gas_concs_k.nlay;
263  auto my_size_ref = static_cast<unsigned long>(nvar * ncol * nlay * ngpt);
264  pool_t::init(my_size_ref);
265  pool_max_ncol = ncol;
266  pool_max_nlay = nlay;
267  pool_nvar = nvar;
268 
269  // We are now initialized!
270  initialized = true;
271 }
int pool_nvar
Definition: ERF_RRTMGP_Interface.cpp:30
size_t pool_max_ncol
Definition: ERF_RRTMGP_Interface.cpp:28
size_t pool_max_nlay
Definition: ERF_RRTMGP_Interface.cpp:29

Referenced by Radiation::initialize_impl().

Here is the caller graph for this function:

◆ rrtmgp_lw()

void rrtmgp::rrtmgp_lw ( const int  ncol,
const int  nlay,
gas_optics_t k_dist,
real2d_k p_lay,
real2d_k t_lay,
real2d_k p_lev,
real2d_k t_lev,
real1d_k t_sfc,
real1d_k sfc_emis,
real1d_k lw_src,
gas_concs_t gas_concs,
optical_props1_t aerosol,
optical_props1_t clouds,
fluxes_t fluxes,
fluxes_broadband_t clnclrsky_fluxes,
fluxes_broadband_t clrsky_fluxes,
fluxes_broadband_t clnsky_fluxes,
const bool  extra_clnclrsky_diag,
const bool  extra_clnsky_diag 
)
917 {
918  // Problem size
919  int nbnd = k_dist.get_nband();
920 
921  // Associate local pointers for fluxes
922  auto& flux_up = fluxes.flux_up;
923  auto& flux_dn = fluxes.flux_dn;
924  auto& bnd_flux_up = fluxes.bnd_flux_up;
925  auto& bnd_flux_dn = fluxes.bnd_flux_dn;
926  auto& clnclrsky_flux_up = clnclrsky_fluxes.flux_up;
927  auto& clnclrsky_flux_dn = clnclrsky_fluxes.flux_dn;
928  auto& clrsky_flux_up = clrsky_fluxes.flux_up;
929  auto& clrsky_flux_dn = clrsky_fluxes.flux_dn;
930  auto& clnsky_flux_up = clnsky_fluxes.flux_up;
931  auto& clnsky_flux_dn = clnsky_fluxes.flux_dn;
932 
933  // Reset fluxes to zero
934  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, nlay+1}),
935  KOKKOS_LAMBDA (int icol, int ilev)
936  {
937  flux_up(icol, ilev) = 0;
938  flux_dn(icol, ilev) = 0;
939  clrsky_flux_up(icol, ilev) = 0;
940  clrsky_flux_dn(icol, ilev) = 0;
941  });
942  if (extra_clnclrsky_diag) {
943  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, nlay+1}),
944  KOKKOS_LAMBDA (int icol, int ilev)
945  {
946  clnclrsky_flux_up(icol, ilev) = 0;
947  clnclrsky_flux_dn(icol, ilev) = 0;
948  });
949  }
950  if (extra_clnsky_diag) {
951  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, nlay+1}),
952  KOKKOS_LAMBDA (int icol, int ilev)
953  {
954  clnsky_flux_up(icol, ilev) = 0;
955  clnsky_flux_dn(icol, ilev) = 0;
956  });
957  }
958  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {ncol, nlay+1, nbnd}),
959  KOKKOS_LAMBDA (int icol, int ilev, int ibnd)
960  {
961  bnd_flux_up(icol, ilev, ibnd) = 0;
962  bnd_flux_dn(icol, ilev, ibnd) = 0;
963  });
964 
965  // Allocate space for optical properties
966  optical_props1_t optics;
967  optics.alloc_1scl(ncol, nlay, k_dist);
968 
969  optical_props1_t optics_no_aerosols;
970  if (extra_clnsky_diag) {
971  // Allocate space for optical properties (no aerosols)
972  optics_no_aerosols.alloc_1scl(ncol, nlay, k_dist);
973  }
974 
975  bool top_at_1 = false;
976  Kokkos::parallel_reduce(1, KOKKOS_LAMBDA(int, bool& val)
977  {
978  val |= p_lay(0, 0) < p_lay(0, nlay-1);
979  }, Kokkos::LOr<bool>(top_at_1));
980 
981  // Boundary conditions
982  //=====================================================================
983  source_func_t lw_sources;
984  lw_sources.alloc(ncol, nlay, k_dist);
985 
986  /*
987  // Surface LW source
988  // AML NOTE: This is removed in EAMXX, LSM doesn't transfer its lw_src?
989  auto d_lw_src = lw_sources.sfc_source;
990  Kokkos::parallel_for(ncol, KOKKOS_LAMBDA (int icol)
991  {
992  d_lw_src(icol, 0) = lw_src(icol);
993  });
994  */
995 
996  // Surface temperature
997  // AML NOTE: We already populate this when initializing data
998 
999 
1000  // Surface emissivity (transposed in RRTMGP)
1001  // AML NOTE: This transfer was removed in EAMXX, LSM doesn't transfer its emis_sfc?
1002  real2d_k emis_sfc_T("emis_sfc",nbnd,ncol);
1003  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, nbnd}),
1004  KOKKOS_LAMBDA (int icol, int ibnd)
1005  {
1006  emis_sfc_T(ibnd,icol) = sfc_emis(icol);
1007  });
1008  //Kokkos::deep_copy(emis_sfc_T, amrex::Real(0.98));
1009 
1010  // Get Gaussian quadrature weights
1011  // Weights and angle secants for first order (k=1) Gaussian quadrature.
1012  // Values from Table 2, Clough et al, 1992, doi:Real(10.1029)/92JD01419
1013  // after Abramowitz & Stegun 1972, page 921
1014  int constexpr max_gauss_pts = 4;
1015  RealT gauss_Ds_host_raw[max_gauss_pts][max_gauss_pts] = { {amrex::Real(1.66), amrex::Real(1.18350343), amrex::Real(1.09719858), amrex::Real(1.06056257)},
1016  {zero , amrex::Real(2.81649655), amrex::Real(1.69338507), amrex::Real(1.38282560)},
1017  {zero , zero , amrex::Real(4.70941630), amrex::Real(2.40148179)},
1018  {zero , zero , zero , amrex::Real(7.15513024)} };
1019  realHost2d_k gauss_Ds_host(&gauss_Ds_host_raw[0][0], max_gauss_pts, max_gauss_pts);
1020 
1021  RealT gauss_wts_host_raw[max_gauss_pts][max_gauss_pts] = { {myhalf, amrex::Real(0.3180413817), amrex::Real(0.2009319137), amrex::Real(0.1355069134)},
1022  {zero , amrex::Real(0.1819586183), amrex::Real(0.2292411064), amrex::Real(0.2034645680)},
1023  {zero , zero , amrex::Real(0.0698269799), amrex::Real(0.1298475476)},
1024  {zero , zero , zero , amrex::Real(0.0311809710)} };
1025  realHost2d_k gauss_wts_host(&gauss_wts_host_raw[0][0],max_gauss_pts,max_gauss_pts);
1026 
1027  real2d_k gauss_Ds ("gauss_Ds" ,max_gauss_pts,max_gauss_pts);
1028  real2d_k gauss_wts("gauss_wts",max_gauss_pts,max_gauss_pts);
1029  Kokkos::deep_copy(gauss_Ds, gauss_Ds_host);
1030  Kokkos::deep_copy(gauss_wts, gauss_wts_host);
1031 
1032  // Limit temperatures for gas optics look-up tables
1033  real2d_k t_lay_limited("t_lay_limited", ncol, nlay );
1034  real2d_k t_lev_limited("t_lev_limited", ncol, nlay+1);
1035  limit_to_bounds_2d(t_lay, k_dist.get_temp_min(),
1036  k_dist.get_temp_max(), t_lay_limited);
1037  limit_to_bounds_2d(t_lev, k_dist.get_temp_min(),
1038  k_dist.get_temp_max(), t_lev_limited);
1039 
1040  // Do gas optics
1041  real3d_k col_gas("col_gas", ncol, nlay, k_dist.get_ngas()+1);
1042  k_dist.gas_optics(ncol, nlay, top_at_1, p_lay, p_lev, t_lay_limited,
1043  t_sfc, gas_concs, col_gas, optics, lw_sources, view_t<RealT**>(), t_lev_limited);
1044  if (extra_clnsky_diag) {
1045  k_dist.gas_optics(ncol, nlay, top_at_1, p_lay, p_lev, t_lay_limited,
1046  t_sfc, gas_concs, col_gas, optics_no_aerosols, lw_sources, view_t<RealT**>(), t_lev_limited);
1047  }
1048 
1049  if (extra_clnclrsky_diag) {
1050  // Compute clean-clear-sky fluxes before we add in aerosols and clouds
1051  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics, top_at_1, lw_sources, emis_sfc_T, clnclrsky_fluxes);
1052  }
1053 
1054  // Combine gas and aerosol optics
1055  aerosol.increment(optics);
1056 
1057  // Compute clear-sky fluxes before we add in clouds
1058  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics, top_at_1, lw_sources, emis_sfc_T, clrsky_fluxes);
1059 
1060  // Combine gas and cloud optics
1061  clouds.increment(optics);
1062 
1063  // Compute allsky fluxes
1064  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics, top_at_1, lw_sources, emis_sfc_T, fluxes);
1065 
1066  if (extra_clnsky_diag) {
1067  // First increment clouds in optics_no_aerosols
1068  clouds.increment(optics_no_aerosols);
1069  // Compute clean-sky fluxes
1070  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics_no_aerosols, top_at_1, lw_sources, emis_sfc_T, clnsky_fluxes);
1071  }
1072 }
Kokkos::View< RealT **, layout_t, KokkosHostDevice > realHost2d_k
Definition: ERF_Kokkos.H:17
Kokkos::View< T, layout_t, KokkosDefaultDevice > view_t
Definition: ERF_RRTMGP_Interface.H:25
SourceFuncLWK< RealT, layout_t, KokkosDefaultDevice > source_func_t
Definition: ERF_RRTMGP_Interface.H:37

Referenced by rrtmgp_main().

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

◆ rrtmgp_main()

void rrtmgp::rrtmgp_main ( const int  ncol,
const int  nlay,
real2d_k p_lay,
real2d_k t_lay,
real2d_k p_lev,
real2d_k t_lev,
gas_concs_t gas_concs,
real2d_k sfc_alb_dir,
real2d_k sfc_alb_dif,
real1d_k mu0,
real1d_k t_sfc,
real1d_k sfc_emis,
real1d_k lw_src,
real2d_k lwp,
real2d_k iwp,
real2d_k rel,
real2d_k rei,
real2d_k cldfrac,
real2d_k sw_flux_up,
real2d_k sw_flux_dn,
real2d_k sw_flux_dn_dir,
real2d_k lw_flux_up,
real2d_k lw_flux_dn,
real2d_k sw_clnclrsky_flux_up,
real2d_k sw_clnclrsky_flux_dn,
real2d_k sw_clnclrsky_flux_dn_dir,
real2d_k sw_clrsky_flux_up,
real2d_k sw_clrsky_flux_dn,
real2d_k sw_clrsky_flux_dn_dir,
real2d_k sw_clnsky_flux_up,
real2d_k sw_clnsky_flux_dn,
real2d_k sw_clnsky_flux_dn_dir,
real2d_k lw_clnclrsky_flux_up,
real2d_k lw_clnclrsky_flux_dn,
real2d_k lw_clrsky_flux_up,
real2d_k lw_clrsky_flux_dn,
real2d_k lw_clnsky_flux_up,
real2d_k lw_clnsky_flux_dn,
real3d_k sw_bnd_flux_up,
real3d_k sw_bnd_flux_dn,
real3d_k sw_bnd_flux_dn_dir,
real3d_k lw_bnd_flux_up,
real3d_k lw_bnd_flux_dn,
const RealT  tsi_scaling,
const bool  extra_clnclrsky_diag,
const bool  extra_clnsky_diag 
)
413 {
414  // Check if we need to reinitialize the memory pool for larger ncol or nlay
415  // This happens with vertical refinement (more layers) or regridding (more columns per chunk)
416  if (static_cast<size_t>(ncol) > pool_max_ncol || static_cast<size_t>(nlay) > pool_max_nlay) {
417 
418  // Finalize old pool (pass false to avoid printing from every MPI rank)
419  pool_t::finalize(false);
420 
421  // Reinitialize with new larger size using maximum ncol and nlay seen so far
422  // This prevents the pool from shrinking and causing heap overwrites
423  const size_t ngpt = std::max(k_dist_sw_k->get_ngpt(), k_dist_lw_k->get_ngpt());
424  const size_t new_max_ncol = std::max(pool_max_ncol, static_cast<size_t>(ncol));
425  const size_t new_max_nlay = std::max(pool_max_nlay, static_cast<size_t>(nlay));
426  auto my_size_ref = static_cast<unsigned long>(pool_nvar * new_max_ncol * new_max_nlay * ngpt);
427  pool_t::init(my_size_ref);
428  pool_max_ncol = new_max_ncol;
429  pool_max_nlay = new_max_nlay;
430  }
431 
432  // Setup pointers to RRTMGP SW fluxes
433  fluxes_t fluxes_sw;
434  fluxes_sw.flux_up = sw_flux_up;
435  fluxes_sw.flux_dn = sw_flux_dn;
436  fluxes_sw.flux_dn_dir = sw_flux_dn_dir;
437  fluxes_sw.bnd_flux_up = sw_bnd_flux_up;
438  fluxes_sw.bnd_flux_dn = sw_bnd_flux_dn;
439  fluxes_sw.bnd_flux_dn_dir = sw_bnd_flux_dn_dir;
440  // Clean-clear-sky
441  fluxes_broadband_t clnclrsky_fluxes_sw;
442  clnclrsky_fluxes_sw.flux_up = sw_clnclrsky_flux_up;
443  clnclrsky_fluxes_sw.flux_dn = sw_clnclrsky_flux_dn;
444  clnclrsky_fluxes_sw.flux_dn_dir = sw_clnclrsky_flux_dn_dir;
445  // Clear-sky
446  fluxes_broadband_t clrsky_fluxes_sw;
447  clrsky_fluxes_sw.flux_up = sw_clrsky_flux_up;
448  clrsky_fluxes_sw.flux_dn = sw_clrsky_flux_dn;
449  clrsky_fluxes_sw.flux_dn_dir = sw_clrsky_flux_dn_dir;
450  // Clean-sky
451  fluxes_broadband_t clnsky_fluxes_sw;
452  clnsky_fluxes_sw.flux_up = sw_clnsky_flux_up;
453  clnsky_fluxes_sw.flux_dn = sw_clnsky_flux_dn;
454  clnsky_fluxes_sw.flux_dn_dir = sw_clnsky_flux_dn_dir;
455 
456  // Setup pointers to RRTMGP LW fluxes
457  fluxes_t fluxes_lw;
458  fluxes_lw.flux_up = lw_flux_up;
459  fluxes_lw.flux_dn = lw_flux_dn;
460  fluxes_lw.bnd_flux_up = lw_bnd_flux_up;
461  fluxes_lw.bnd_flux_dn = lw_bnd_flux_dn;
462  // Clean-clear-sky
463  fluxes_broadband_t clnclrsky_fluxes_lw;
464  clnclrsky_fluxes_lw.flux_up = lw_clnclrsky_flux_up;
465  clnclrsky_fluxes_lw.flux_dn = lw_clnclrsky_flux_dn;
466  // Clear-sky
467  fluxes_broadband_t clrsky_fluxes_lw;
468  clrsky_fluxes_lw.flux_up = lw_clrsky_flux_up;
469  clrsky_fluxes_lw.flux_dn = lw_clrsky_flux_dn;
470  // Clean-sky
471  fluxes_broadband_t clnsky_fluxes_lw;
472  clnsky_fluxes_lw.flux_up = lw_clnsky_flux_up;
473  clnsky_fluxes_lw.flux_dn = lw_clnsky_flux_dn;
474 
475  auto nswbands = k_dist_sw_k->get_nband();
476  auto nlwbands = k_dist_lw_k->get_nband();
477 
478  // Setup aerosol optical properties
479  optical_props2_t aerosol_sw;
480  optical_props1_t aerosol_lw;
481  aerosol_sw.init(k_dist_sw_k->get_band_lims_wavenumber());
482  aerosol_sw.alloc_2str(ncol, nlay);
483  aerosol_lw.init(k_dist_lw_k->get_band_lims_wavenumber());
484  aerosol_lw.alloc_1scl(ncol, nlay);
485 
486  // Convert cloud physical properties to optical properties for input to RRTMGP
487  optical_props2_t clouds_sw = get_cloud_optics_sw(ncol, nlay,
489  lwp, iwp, rel, rei);
490  optical_props1_t clouds_lw = get_cloud_optics_lw(ncol, nlay,
492  lwp, iwp, rel, rei);
493  // Do subcolumn sampling to map bands -> gpoints based on cloud fraction and overlap assumption;
494  // This implements the Monte Carlo Independent Column Approximation by mapping only a single
495  // subcolumn (cloud state) to each gpoint.
496  auto nswgpts = k_dist_sw_k->get_ngpt();
497  auto clouds_sw_gpt = get_subsampled_clouds(ncol, nlay, nswbands, nswgpts,
498  clouds_sw, *k_dist_sw_k, cldfrac, p_lay);
499  // Longwave
500  auto nlwgpts = k_dist_lw_k->get_ngpt();
501  auto clouds_lw_gpt = get_subsampled_clouds(ncol, nlay, nlwbands, nlwgpts,
502  clouds_lw, *k_dist_lw_k, cldfrac, p_lay);
503 
504  // Do shortwave
505  rrtmgp_sw(ncol, nlay,
506  *k_dist_sw_k, p_lay, t_lay, p_lev, t_lev, gas_concs,
507  sfc_alb_dir, sfc_alb_dif, mu0, aerosol_sw, clouds_sw_gpt,
508  fluxes_sw, clnclrsky_fluxes_sw, clrsky_fluxes_sw, clnsky_fluxes_sw,
509  tsi_scaling, extra_clnclrsky_diag, extra_clnsky_diag);
510 
511  // Do longwave
512  rrtmgp_lw(ncol, nlay,
513  *k_dist_lw_k, p_lay, t_lay, p_lev, t_lev,
514  t_sfc, sfc_emis, lw_src,
515  gas_concs, aerosol_lw, clouds_lw_gpt,
516  fluxes_lw, clnclrsky_fluxes_lw, clrsky_fluxes_lw, clnsky_fluxes_lw,
517  extra_clnclrsky_diag, extra_clnsky_diag);
518 
519 }
FluxesBroadbandK< RealT, layout_t, KokkosDefaultDevice > fluxes_broadband_t
Definition: ERF_RRTMGP_Interface.H:33
FluxesBybandK< RealT, layout_t, KokkosDefaultDevice > fluxes_t
Definition: ERF_RRTMGP_Interface.H:32
optical_props1_t get_cloud_optics_lw(const int ncol, const int nlay, cloud_optics_t &cloud_optics, gas_optics_t &kdist, real2d_k &lwp, real2d_k &iwp, real2d_k &rel, real2d_k &rei)
Definition: ERF_RRTMGP_Interface.cpp:68
optical_props1_t get_subsampled_clouds(const int ncol, const int nlay, const int nbnd, const int ngpt, optical_props1_t &cloud_optics, gas_optics_t &kdist, real2d_k &cld, real2d_k &p_lay)
Definition: ERF_RRTMGP_Interface.cpp:170
optical_props2_t get_cloud_optics_sw(const int ncol, const int nlay, cloud_optics_t &cloud_optics, gas_optics_t &kdist, real2d_k &lwp, real2d_k &iwp, real2d_k &rel, real2d_k &rei)
Definition: ERF_RRTMGP_Interface.cpp:34
void rrtmgp_sw(const int ncol, const int nlay, gas_optics_t &k_dist, real2d_k &p_lay, real2d_k &t_lay, real2d_k &p_lev, real2d_k &t_lev, gas_concs_t &gas_concs, real2d_k &sfc_alb_dir, real2d_k &sfc_alb_dif, real1d_k &mu0, optical_props2_t &aerosol, optical_props2_t &clouds, fluxes_t &fluxes, fluxes_broadband_t &clnclrsky_fluxes, fluxes_broadband_t &clrsky_fluxes, fluxes_broadband_t &clnsky_fluxes, const RealT tsi_scaling, const bool extra_clnclrsky_diag, const bool extra_clnsky_diag)
Definition: ERF_RRTMGP_Interface.cpp:596
void rrtmgp_lw(const int ncol, const int nlay, gas_optics_t &k_dist, real2d_k &p_lay, real2d_k &t_lay, real2d_k &p_lev, real2d_k &t_lev, real1d_k &t_sfc, real1d_k &sfc_emis, real1d_k &lw_src, gas_concs_t &gas_concs, optical_props1_t &aerosol, optical_props1_t &clouds, fluxes_t &fluxes, fluxes_broadband_t &clnclrsky_fluxes, fluxes_broadband_t &clrsky_fluxes, fluxes_broadband_t &clnsky_fluxes, const bool extra_clnclrsky_diag, const bool extra_clnsky_diag)
Definition: ERF_RRTMGP_Interface.cpp:902

Referenced by Radiation::run_impl().

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

◆ rrtmgp_sw()

void rrtmgp::rrtmgp_sw ( const int  ncol,
const int  nlay,
gas_optics_t k_dist,
real2d_k p_lay,
real2d_k t_lay,
real2d_k p_lev,
real2d_k t_lev,
gas_concs_t gas_concs,
real2d_k sfc_alb_dir,
real2d_k sfc_alb_dif,
real1d_k mu0,
optical_props2_t aerosol,
optical_props2_t clouds,
fluxes_t fluxes,
fluxes_broadband_t clnclrsky_fluxes,
fluxes_broadband_t clrsky_fluxes,
fluxes_broadband_t clnsky_fluxes,
const RealT  tsi_scaling,
const bool  extra_clnclrsky_diag,
const bool  extra_clnsky_diag 
)
613 {
614  // Get problem sizes
615  int nbnd = k_dist.get_nband();
616  int ngpt = k_dist.get_ngpt();
617  int ngas = gas_concs.get_num_gases();
618 
619  // Associate local pointers for fluxes
620  auto& flux_up = fluxes.flux_up;
621  auto& flux_dn = fluxes.flux_dn;
622  auto& flux_dn_dir = fluxes.flux_dn_dir;
623  auto& bnd_flux_up = fluxes.bnd_flux_up;
624  auto& bnd_flux_dn = fluxes.bnd_flux_dn;
625  auto& bnd_flux_dn_dir = fluxes.bnd_flux_dn_dir;
626  auto& clnclrsky_flux_up = clnclrsky_fluxes.flux_up;
627  auto& clnclrsky_flux_dn = clnclrsky_fluxes.flux_dn;
628  auto& clnclrsky_flux_dn_dir = clnclrsky_fluxes.flux_dn_dir;
629  auto& clrsky_flux_up = clrsky_fluxes.flux_up;
630  auto& clrsky_flux_dn = clrsky_fluxes.flux_dn;
631  auto& clrsky_flux_dn_dir = clrsky_fluxes.flux_dn_dir;
632  auto& clnsky_flux_up = clnsky_fluxes.flux_up;
633  auto& clnsky_flux_dn = clnsky_fluxes.flux_dn;
634  auto& clnsky_flux_dn_dir = clnsky_fluxes.flux_dn_dir;
635 
636  // Reset fluxes to zero
637  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, nlay+1}),
638  KOKKOS_LAMBDA (int icol, int ilev)
639  {
640  flux_up (icol,ilev) = 0;
641  flux_dn (icol,ilev) = 0;
642  flux_dn_dir(icol,ilev) = 0;
643  clrsky_flux_up (icol,ilev) = 0;
644  clrsky_flux_dn (icol,ilev) = 0;
645  clrsky_flux_dn_dir(icol,ilev) = 0;
646  });
647  if (extra_clnclrsky_diag) {
648  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, nlay+1}),
649  KOKKOS_LAMBDA (int icol, int ilev)
650  {
651  clnclrsky_flux_up (icol,ilev) = 0;
652  clnclrsky_flux_dn (icol,ilev) = 0;
653  clnclrsky_flux_dn_dir(icol,ilev) = 0;
654  });
655  }
656  if (extra_clnsky_diag) {
657  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {ncol, nlay+1}),
658  KOKKOS_LAMBDA (int icol, int ilev)
659  {
660  clnsky_flux_up (icol,ilev) = 0;
661  clnsky_flux_dn (icol,ilev) = 0;
662  clnsky_flux_dn_dir(icol,ilev) = 0;
663  });
664  }
665  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {ncol, nlay+1, nbnd}),
666  KOKKOS_LAMBDA (int icol, int ilev, int ibnd)
667  {
668  bnd_flux_up (icol,ilev,ibnd) = 0;
669  bnd_flux_dn (icol,ilev,ibnd) = 0;
670  bnd_flux_dn_dir(icol,ilev,ibnd) = 0;
671  });
672 
673  // Get daytime indices
674  int1d_k dayIndices("dayIndices", ncol);
675  Kokkos::deep_copy(dayIndices, -1);
676 
677  // Serialized for now.
678  int nday = 0;
679  Kokkos::parallel_reduce(1, KOKKOS_LAMBDA(int, int& nday_inner)
680  {
681  for (int icol = 0; icol < ncol; ++icol) {
682  if (mu0(icol) > 0) {
683  dayIndices(nday_inner++) = icol;
684  }
685  }
686  }, Kokkos::Sum<int>(nday));
687 
688  // Copy data back to the device
689  if (nday == 0) {
690  // No daytime columns in this chunk, skip the rest of this routine
691  return;
692  }
693 
694  // Subset mu0
695  real1d_k mu0_day("mu0_day", nday);
696  Kokkos::parallel_for(nday, KOKKOS_LAMBDA(int iday)
697  {
698  mu0_day(iday) = mu0(dayIndices(iday));
699  });
700 
701  // subset state variables
702  real2d_k p_lay_day("p_lay_day", nday, nlay);
703  real2d_k t_lay_day("t_lay_day", nday, nlay);
704  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {nday, nlay}),
705  KOKKOS_LAMBDA (int iday, int ilay)
706  {
707  p_lay_day(iday,ilay) = p_lay(dayIndices(iday),ilay);
708  t_lay_day(iday,ilay) = t_lay(dayIndices(iday),ilay);
709  });
710  real2d_k p_lev_day("p_lev_day", nday, nlay+1);
711  real2d_k t_lev_day("t_lev_day", nday, nlay+1);
712  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {nday, nlay+1}),
713  KOKKOS_LAMBDA (int iday, int ilay)
714  {
715  p_lev_day(iday,ilay) = p_lev(dayIndices(iday),ilay);
716  t_lev_day(iday,ilay) = t_lev(dayIndices(iday),ilay);
717  });
718 
719  // Subset gases
720  auto gas_names = gas_concs.get_gas_names();
721  gas_concs_t gas_concs_day;
722  gas_concs_day.init(gas_names, nday, nlay);
723  for (int igas = 0; igas < ngas; igas++) {
724  real2d_k vmr_day("vmr_day", nday, nlay);
725  real2d_k vmr("vmr" , ncol, nlay);
726  gas_concs.get_vmr(gas_names[igas], vmr);
727  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {nday, nlay}),
728  KOKKOS_LAMBDA (int iday, int ilay)
729  {
730  vmr_day(iday,ilay) = vmr(dayIndices(iday),ilay);
731  });
732  gas_concs_day.set_vmr(gas_names[igas], vmr_day);
733  }
734 
735  // Subset aerosol optics
736  optical_props2_t aerosol_day;
737  aerosol_day.init(k_dist.get_band_lims_wavenumber());
738  aerosol_day.alloc_2str(nday, nlay);
739  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {nday, nlay, nbnd}),
740  KOKKOS_LAMBDA (int iday, int ilay, int ibnd)
741  {
742  aerosol_day.tau(iday,ilay,ibnd) = aerosol.tau(dayIndices(iday),ilay,ibnd);
743  aerosol_day.ssa(iday,ilay,ibnd) = aerosol.ssa(dayIndices(iday),ilay,ibnd);
744  aerosol_day.g (iday,ilay,ibnd) = aerosol.g (dayIndices(iday),ilay,ibnd);
745  });
746 
747  // Subset cloud optics
748  // TODO: nbnd -> ngpt once we pass sub-sampled cloud state
749  optical_props2_t clouds_day;
750  clouds_day.init(k_dist.get_band_lims_wavenumber(), k_dist.get_band_lims_gpoint());
751  clouds_day.alloc_2str(nday, nlay);
752  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {nday, nlay, ngpt}),
753  KOKKOS_LAMBDA (int iday, int ilay, int igpt)
754  {
755  clouds_day.tau(iday,ilay,igpt) = clouds.tau(dayIndices(iday),ilay,igpt);
756  clouds_day.ssa(iday,ilay,igpt) = clouds.ssa(dayIndices(iday),ilay,igpt);
757  clouds_day.g (iday,ilay,igpt) = clouds.g (dayIndices(iday),ilay,igpt);
758  });
759 
760  // RRTMGP assumes surface albedos have a screwy dimension ordering
761  // for some strange reason, so we need to transpose these; also do
762  // daytime subsetting in the same kernel
763  real2d_k sfc_alb_dir_T("sfc_alb_dir", nbnd, nday);
764  real2d_k sfc_alb_dif_T("sfc_alb_dif", nbnd, nday);
765  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {nbnd, nday}),
766  KOKKOS_LAMBDA (int ibnd, int icol)
767  {
768  sfc_alb_dir_T(ibnd,icol) = sfc_alb_dir(dayIndices(icol),ibnd);
769  sfc_alb_dif_T(ibnd,icol) = sfc_alb_dif(dayIndices(icol),ibnd);
770  });
771 
772  // Temporaries we need for daytime-only fluxes
773  real2d_k flux_up_day("flux_up_day", nday, nlay+1);
774  real2d_k flux_dn_day("flux_dn_day", nday, nlay+1);
775  real2d_k flux_dn_dir_day("flux_dn_dir_day", nday, nlay+1);
776  real3d_k bnd_flux_up_day("bnd_flux_up_day", nday, nlay+1, nbnd);
777  real3d_k bnd_flux_dn_day("bnd_flux_dn_day", nday, nlay+1, nbnd);
778  real3d_k bnd_flux_dn_dir_day("bnd_flux_dn_dir_day", nday, nlay+1, nbnd);
779  fluxes_t fluxes_day;
780  fluxes_day.flux_up = flux_up_day;
781  fluxes_day.flux_dn = flux_dn_day;
782  fluxes_day.flux_dn_dir = flux_dn_dir_day;
783  fluxes_day.bnd_flux_up = bnd_flux_up_day;
784  fluxes_day.bnd_flux_dn = bnd_flux_dn_day;
785  fluxes_day.bnd_flux_dn_dir = bnd_flux_dn_dir_day;
786 
787  // Allocate space for optical properties
788  optical_props2_t optics;
789  optics.alloc_2str(nday, nlay, k_dist);
790 
791  optical_props2_t optics_no_aerosols;
792  if (extra_clnsky_diag) {
793  // Allocate space for optical properties (no aerosols)
794  optics_no_aerosols.alloc_2str(nday, nlay, k_dist);
795  }
796 
797  // Limit temperatures for gas optics look-up tables
798  real2d_k t_lay_limited("t_lay_limited", nday, nlay);
799  limit_to_bounds_2d(t_lay_day, k_dist_sw_k->get_temp_min(),
800  k_dist_sw_k->get_temp_max(), t_lay_limited);
801 
802  // Do gas optics
803  real2d_k toa_flux("toa_flux", nday, ngpt);
804  real3d_k col_gas("col_gas", ncol, nlay, k_dist.get_ngas()+1);
805  bool top_at_1 = false;
806  Kokkos::parallel_reduce(1, KOKKOS_LAMBDA(int, bool& val)
807  {
808  val |= p_lay(0, 0) < p_lay(0, nlay-1);
809  }, Kokkos::LOr<bool>(top_at_1));
810 
811  k_dist.gas_optics(nday, nlay, top_at_1, p_lay_day, p_lev_day,
812  t_lay_limited, gas_concs_day, col_gas, optics, toa_flux);
813  if (extra_clnsky_diag) {
814  k_dist.gas_optics(nday, nlay, top_at_1, p_lay_day, p_lev_day,
815  t_lay_limited, gas_concs_day, col_gas, optics_no_aerosols, toa_flux);
816  }
817 
818  // Apply tsi_scaling
819  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {nday, ngpt}),
820  KOKKOS_LAMBDA (int iday, int igpt)
821  {
822  toa_flux(iday,igpt) = tsi_scaling * toa_flux(iday,igpt);
823  });
824 
825  if (extra_clnclrsky_diag) {
826  // Compute clear-clean-sky (just gas) fluxes on daytime columns
827  rte_sw(optics, top_at_1, mu0_day, toa_flux, sfc_alb_dir_T, sfc_alb_dif_T, fluxes_day);
828  // Expand daytime fluxes to all columns
829  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {nday, nlay+1}),
830  KOKKOS_LAMBDA (int iday, int ilev)
831  {
832  int icol = dayIndices(iday);
833  clnclrsky_flux_up (icol,ilev) = flux_up_day (iday,ilev);
834  clnclrsky_flux_dn (icol,ilev) = flux_dn_day (iday,ilev);
835  clnclrsky_flux_dn_dir(icol,ilev) = flux_dn_dir_day(iday,ilev);
836  });
837  }
838 
839  // Combine gas and aerosol optics
840  aerosol_day.delta_scale();
841  aerosol_day.increment(optics);
842 
843  // Compute clearsky (gas + aerosol) fluxes on daytime columns
844  rte_sw(optics, top_at_1, mu0_day, toa_flux, sfc_alb_dir_T, sfc_alb_dif_T, fluxes_day);
845 
846  // Expand daytime fluxes to all columns
847  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {nday, nlay+1}),
848  KOKKOS_LAMBDA (int iday, int ilev)
849  {
850  int icol = dayIndices(iday);
851  clrsky_flux_up (icol,ilev) = flux_up_day (iday,ilev);
852  clrsky_flux_dn (icol,ilev) = flux_dn_day (iday,ilev);
853  clrsky_flux_dn_dir(icol,ilev) = flux_dn_dir_day(iday,ilev);
854  });
855 
856  // Now merge in cloud optics and do allsky calculations
857 
858  // Combine gas and cloud optics
859  clouds_day.delta_scale();
860  clouds_day.increment(optics);
861 
862  // Compute fluxes on daytime columns
863  rte_sw(optics, top_at_1, mu0_day, toa_flux, sfc_alb_dir_T, sfc_alb_dif_T, fluxes_day);
864 
865  // Expand daytime fluxes to all columns
866  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {nday, nlay+1}),
867  KOKKOS_LAMBDA (int iday, int ilev)
868  {
869  int icol = dayIndices(iday);
870  flux_up (icol,ilev) = flux_up_day (iday,ilev);
871  flux_dn (icol,ilev) = flux_dn_day (iday,ilev);
872  flux_dn_dir(icol,ilev) = flux_dn_dir_day(iday,ilev);
873  });
874  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<3>>({0, 0, 0}, {nday, nlay+1, nbnd}),
875  KOKKOS_LAMBDA (int iday, int ilev, int ibnd)
876  {
877  int icol = dayIndices(iday);
878  bnd_flux_up (icol,ilev,ibnd) = bnd_flux_up_day (iday,ilev,ibnd);
879  bnd_flux_dn (icol,ilev,ibnd) = bnd_flux_dn_day (iday,ilev,ibnd);
880  bnd_flux_dn_dir(icol,ilev,ibnd) = bnd_flux_dn_dir_day(iday,ilev,ibnd);
881  });
882 
883  if (extra_clnsky_diag) {
884  // First increment clouds in optics_no_aerosols
885  clouds_day.increment(optics_no_aerosols);
886  // Compute cleansky (gas + clouds) fluxes on daytime columns
887  rte_sw(optics_no_aerosols, top_at_1, mu0_day, toa_flux, sfc_alb_dir_T, sfc_alb_dif_T, fluxes_day);
888  // Expand daytime fluxes to all columns
889  Kokkos::parallel_for(Kokkos::MDRangePolicy<Kokkos::Rank<2>>({0, 0}, {nday, nlay+1}),
890  KOKKOS_LAMBDA (int iday, int ilev)
891  {
892  int icol = dayIndices(iday);
893  clnsky_flux_up (icol,ilev) = flux_up_day (iday,ilev);
894  clnsky_flux_dn (icol,ilev) = flux_dn_day (iday,ilev);
895  clnsky_flux_dn_dir(icol,ilev) = flux_dn_dir_day(iday,ilev);
896  });
897  }
898 }
GasConcsK< RealT, layout_t, KokkosDefaultDevice > gas_concs_t
Definition: ERF_RRTMGP_Interface.H:31

Referenced by rrtmgp_main().

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

Variable Documentation

◆ cloud_optics_lw_k

std::unique_ptr< cloud_optics_t > rrtmgp::cloud_optics_lw_k

◆ cloud_optics_sw_k

std::unique_ptr< cloud_optics_t > rrtmgp::cloud_optics_sw_k

◆ initialized

◆ k_dist_lw_k

std::unique_ptr< gas_optics_t > rrtmgp::k_dist_lw_k

◆ k_dist_sw_k

◆ kokkos_mem_pool

pool_t rrtmgp::kokkos_mem_pool

◆ pool_max_ncol

size_t rrtmgp::pool_max_ncol = 0

Referenced by rrtmgp_initialize(), and rrtmgp_main().

◆ pool_max_nlay

size_t rrtmgp::pool_max_nlay = 0

Referenced by rrtmgp_initialize(), and rrtmgp_main().

◆ pool_nvar

int rrtmgp::pool_nvar = 0

Referenced by rrtmgp_initialize(), and rrtmgp_main().