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

Functions

OpticalProps2str get_cloud_optics_sw (const int ncol, const int nlay, CloudOptics &cloud_optics, GasOpticsRRTMGP &kdist, real2d &lwp, real2d &iwp, real2d &rel, real2d &rei)
 
OpticalProps1scl get_cloud_optics_lw (const int ncol, const int nlay, CloudOptics &cloud_optics, GasOpticsRRTMGP &kdist, real2d &lwp, real2d &iwp, real2d &rel, real2d &rei)
 
OpticalProps2str get_subsampled_clouds (const int ncol, const int nlay, const int nbnd, const int ngpt, OpticalProps2str &cloud_optics, GasOpticsRRTMGP &kdist, real2d &cld, real2d &p_lay)
 
OpticalProps1scl get_subsampled_clouds (const int ncol, const int nlay, const int nbnd, const int ngpt, OpticalProps1scl &cloud_optics, GasOpticsRRTMGP &kdist, real2d &cld, real2d &p_lay)
 
void rrtmgp_initialize (GasConcs &gas_concs, 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)
 
void rrtmgp_finalize ()
 
void compute_band_by_band_surface_albedos (const int ncol, const int nswbands, real1d &sfc_alb_dir_vis, real1d &sfc_alb_dir_nir, real1d &sfc_alb_dif_vis, real1d &sfc_alb_dif_nir, real2d &sfc_alb_dir, real2d &sfc_alb_dif)
 
void compute_broadband_surface_fluxes (const int ncol, const int ktop, const int nswbands, real3d &sw_bnd_flux_dir, real3d &sw_bnd_flux_dif, real1d &sfc_flux_dir_vis, real1d &sfc_flux_dir_nir, real1d &sfc_flux_dif_vis, real1d &sfc_flux_dif_nir)
 
void rrtmgp_main (const int ncol, const int nlay, real2d &p_lay, real2d &t_lay, real2d &p_lev, real2d &t_lev, GasConcs &gas_concs, real2d &sfc_alb_dir, real2d &sfc_alb_dif, real1d &mu0, real2d &lwp, real2d &iwp, real2d &rel, real2d &rei, real2d &cldfrac, real3d &aer_tau_sw, real3d &aer_ssa_sw, real3d &aer_asm_sw, real3d &aer_tau_lw, real3d &cld_tau_sw_bnd, real3d &cld_tau_lw_bnd, real3d &cld_tau_sw_gpt, real3d &cld_tau_lw_gpt, real2d &sw_flux_up, real2d &sw_flux_dn, real2d &sw_flux_dn_dir, real2d &lw_flux_up, real2d &lw_flux_dn, real2d &sw_clnclrsky_flux_up, real2d &sw_clnclrsky_flux_dn, real2d &sw_clnclrsky_flux_dn_dir, real2d &sw_clrsky_flux_up, real2d &sw_clrsky_flux_dn, real2d &sw_clrsky_flux_dn_dir, real2d &sw_clnsky_flux_up, real2d &sw_clnsky_flux_dn, real2d &sw_clnsky_flux_dn_dir, real2d &lw_clnclrsky_flux_up, real2d &lw_clnclrsky_flux_dn, real2d &lw_clrsky_flux_up, real2d &lw_clrsky_flux_dn, real2d &lw_clnsky_flux_up, real2d &lw_clnsky_flux_dn, real3d &sw_bnd_flux_up, real3d &sw_bnd_flux_dn, real3d &sw_bnd_flux_dn_dir, real3d &lw_bnd_flux_up, real3d &lw_bnd_flux_dn, const real tsi_scaling, const bool extra_clnclrsky_diag, const bool extra_clnsky_diag)
 
int3d get_subcolumn_mask (const int ncol, const int nlay, const int ngpt, real2d &cldf, const int overlap_option, int1d &seeds)
 
void rrtmgp_sw (const int ncol, const int nlay, GasOpticsRRTMGP &k_dist, real2d &p_lay, real2d &t_lay, real2d &p_lev, real2d &t_lev, GasConcs &gas_concs, real2d &sfc_alb_dir, real2d &sfc_alb_dif, real1d &mu0, OpticalProps2str &aerosol, OpticalProps2str &clouds, FluxesByband &fluxes, FluxesBroadband &clnclrsky_fluxes, FluxesBroadband &clrsky_fluxes, FluxesBroadband &clnsky_fluxes, const real tsi_scaling, const bool extra_clnclrsky_diag, const bool extra_clnsky_diag)
 
void rrtmgp_lw (const int ncol, const int nlay, GasOpticsRRTMGP &k_dist, real2d &p_lay, real2d &t_lay, real2d &p_lev, real2d &t_lev, GasConcs &gas_concs, OpticalProps1scl &aerosol, OpticalProps1scl &clouds, FluxesByband &fluxes, FluxesBroadband &clnclrsky_fluxes, FluxesBroadband &clrsky_fluxes, FluxesBroadband &clnsky_fluxes, const bool extra_clnclrsky_diag, const bool extra_clnsky_diag)
 
void compute_cloud_area (int ncol, int nlay, int ngpt, const real pmin, const real pmax, const real2d &pmid, const real3d &cld_tau_gpt, real1d &cld_area)
 
int get_wavelength_index_sw (double wavelength)
 
int get_wavelength_index_lw (double wavelength)
 
int get_wavelength_index (OpticalProps &kdist, double wavelength)
 
void compute_aerocom_cloudtop (int ncol, int nlay, const real2d &tmid, const real2d &pmid, const real2d &p_del, const real2d &z_del, const real2d &qc, const real2d &qi, const real2d &rel, const real2d &rei, const real2d &cldfrac_tot, const real2d &nc, real1d &T_mid_at_cldtop, real1d &p_mid_at_cldtop, real1d &cldfrac_ice_at_cldtop, real1d &cldfrac_liq_at_cldtop, real1d &cldfrac_tot_at_cldtop, real1d &cdnc_at_cldtop, real1d &eff_radius_qc_at_cldtop, real1d &eff_radius_qi_at_cldtop)
 
template<class T , int myMem, int myStyle>
void mixing_ratio_to_cloud_mass (yakl::Array< T, 2, myMem, myStyle > const &mixing_ratio, yakl::Array< T, 2, myMem, myStyle > const &cloud_fraction, yakl::Array< T, 2, myMem, myStyle > const &dp, yakl::Array< T, 2, myMem, myStyle > &cloud_mass)
 
template<class S , class T >
void limit_to_bounds (S const &arr_in, T const lower, T const upper, S &arr_out)
 
template<class T , int myMem, int myStyle>
void compute_heating_rate (yakl::Array< T, 2, myMem, myStyle > const &flux_up, yakl::Array< T, 2, myMem, myStyle > const &flux_dn, yakl::Array< T, 2, myMem, myStyle > const &rho, yakl::Array< T, 2, myMem, myStyle > const &dz, yakl::Array< T, 2, myMem, myStyle > &heating_rate)
 
bool radiation_do (const int irad, const int nstep)
 

Variables

GasOpticsRRTMGP k_dist_sw
 
GasOpticsRRTMGP k_dist_lw
 
CloudOptics cloud_optics_sw
 
CloudOptics cloud_optics_lw
 
bool initialized = false
 
bool initialized_k = false
 

Function Documentation

◆ compute_aerocom_cloudtop()

void rrtmgp::compute_aerocom_cloudtop ( int  ncol,
int  nlay,
const real2d &  tmid,
const real2d &  pmid,
const real2d &  p_del,
const real2d &  z_del,
const real2d &  qc,
const real2d &  qi,
const real2d &  rel,
const real2d &  rei,
const real2d &  cldfrac_tot,
const real2d &  nc,
real1d &  T_mid_at_cldtop,
real1d &  p_mid_at_cldtop,
real1d &  cldfrac_ice_at_cldtop,
real1d &  cldfrac_liq_at_cldtop,
real1d &  cldfrac_tot_at_cldtop,
real1d &  cdnc_at_cldtop,
real1d &  eff_radius_qc_at_cldtop,
real1d &  eff_radius_qi_at_cldtop 
)
1088 {
1089  /* The goal of this routine is to calculate properties at cloud top
1090  * based on the AeroCom recommendation. See reference for routine
1091  * get_subcolumn_mask above, where equation 14 is used for the
1092  * maximum-random overlap assumption for subcolumn generation. We use
1093  * equation 13, the column counterpart.
1094  */
1095  // Set outputs to zero
1096  memset(T_mid_at_cldtop, 0.0);
1097  memset(p_mid_at_cldtop, 0.0);
1098  memset(cldfrac_ice_at_cldtop, 0.0);
1099  memset(cldfrac_liq_at_cldtop, 0.0);
1100  memset(cldfrac_tot_at_cldtop, 0.0);
1101  memset(cdnc_at_cldtop, 0.0);
1102  memset(eff_radius_qc_at_cldtop, 0.0);
1103  memset(eff_radius_qi_at_cldtop, 0.0);
1104 
1105  // Initialize the 1D "clear fraction" as 1 (totally clear)
1106  auto aerocom_clr = real1d("aerocom_clr", ncol);
1107  memset(aerocom_clr, 1.0);
1108 
1109  // TODO: move tunable constant to namelist
1110  constexpr real q_threshold = 0.0; // BAD_CONSTANT!
1111 
1112  // TODO: move tunable constant to namelist
1113  constexpr real cldfrac_tot_threshold = 0.001; // BAD_CONSTANT!
1114 
1115  // Loop over all columns in parallel
1116  parallel_for(SimpleBounds<1>(ncol), YAKL_LAMBDA(int icol)
1117  {
1118  // Loop over all layers in serial (due to accumulative
1119  // product), starting at 2 (second highest) layer because the
1120  // highest is assumed to have no clouds
1121  for(int ilay = 2; ilay <= nlay; ++ilay) {
1122  // Only do the calculation if certain conditions are met
1123  if((qc(icol, ilay) + qi(icol, ilay)) > q_threshold &&
1124  (cldfrac_tot(icol, ilay) > cldfrac_tot_threshold)) {
1125  /* PART I: Probabilistically determining cloud top */
1126  // Populate aerocom_tmp as the clear-sky fraction
1127  // probability of this level, where aerocom_clr is that of
1128  // the previous level
1129  auto aerocom_tmp = aerocom_clr(icol) *
1130  (1.0 - std::max(cldfrac_tot(icol, ilay - 1),
1131  cldfrac_tot(icol, ilay))) /
1132  (1.0 - std::min(cldfrac_tot(icol, ilay - 1),
1133  1.0 - cldfrac_tot_threshold));
1134  // Temporary variable for probability "weights"
1135  auto aerocom_wts = aerocom_clr(icol) - aerocom_tmp;
1136  // Temporary variable for liquid "phase"
1137  auto aerocom_phi = qc(icol, ilay) / (qc(icol, ilay) + qi(icol, ilay));
1138  /* PART II: The inferred properties */
1139  /* In general, converting a 3D property X to a 2D cloud-top
1140  * counterpart x follows: x(i) += X(i,k) * weights * Phase
1141  * but X and Phase are not always needed */
1142  // T_mid_at_cldtop
1143  T_mid_at_cldtop(icol) += tmid(icol, ilay) * aerocom_wts;
1144  // p_mid_at_cldtop
1145  p_mid_at_cldtop(icol) += pmid(icol, ilay) * aerocom_wts;
1146  // cldfrac_ice_at_cldtop
1147  cldfrac_ice_at_cldtop(icol) += (1.0 - aerocom_phi) * aerocom_wts;
1148  // cldfrac_liq_at_cldtop
1149  cldfrac_liq_at_cldtop(icol) += aerocom_phi * aerocom_wts;
1150  // cdnc_at_cldtop
1151  /* We need to convert nc from 1/mass to 1/volume first, and
1152  * from grid-mean to in-cloud, but after that, the
1153  * calculation follows the general logic */
1154  auto cdnc = nc(icol, ilay) * p_del(icol, ilay) /
1155  z_del(icol, ilay) / CONST_GRAV /
1156  cldfrac_tot(icol, ilay);
1157  cdnc_at_cldtop(icol) += cdnc * aerocom_phi * aerocom_wts;
1158  // eff_radius_qc_at_cldtop
1159  eff_radius_qc_at_cldtop(icol) += rel(icol, ilay) * aerocom_phi * aerocom_wts;
1160  // eff_radius_qi_at_cldtop
1161  eff_radius_qi_at_cldtop(icol) += rei(icol, ilay) * (1.0 - aerocom_phi) * aerocom_wts;
1162  // Reset aerocom_clr to aerocom_tmp to accumulate
1163  aerocom_clr(icol) = aerocom_tmp;
1164  }
1165  }
1166  // After the serial loop over levels, the cloudy fraction is
1167  // defined as (1 - aerocom_clr). This is true because
1168  // aerocom_clr is the result of accumulative probabilities
1169  // (their products)
1170  cldfrac_tot_at_cldtop(icol) = 1.0 - aerocom_clr(icol);
1171  });
1172 }
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:21
double real
Definition: ERF_OrbCosZenith.H:9
@ qc
Definition: ERF_SatAdj.H:36

◆ compute_band_by_band_surface_albedos()

void rrtmgp::compute_band_by_band_surface_albedos ( const int  ncol,
const int  nswbands,
real1d &  sfc_alb_dir_vis,
real1d &  sfc_alb_dir_nir,
real1d &  sfc_alb_dif_vis,
real1d &  sfc_alb_dif_nir,
real2d &  sfc_alb_dir,
real2d &  sfc_alb_dif 
)
276 {
277  /*
278  AMREX_ASSERT_WITH_MESSAGE(initialized, "ERROR: rrtmgp_initialize must be called before GasOpticsRRTMGP object can be used.");
279  */
280 
281  auto wavenumber_limits = k_dist_sw.get_band_lims_wavenumber();
282 
283  /*
284  AMREX_ASSERT_WITH_MESSAGE(yakl::intrinsics::size(wavenumber_limits, 1) == 2,
285  "Error! 1st dimension for wavenumber_limits should be 2.");
286  AMREX_ASSERT_WITH_MESSAGE(yakl::intrinsics::size(wavenumber_limits, 2) == nswbands,
287  "Error! 2nd dimension for wavenumber_limits should be " + std::to_string(nswbands) + " (nswbands).");
288  */
289 
290  // Loop over bands, and determine for each band whether it is broadly in the
291  // visible or infrared part of the spectrum (visible or "not visible")
292  parallel_for(SimpleBounds<2>(nswbands, ncol), YAKL_LAMBDA(const int ibnd, const int icol)
293  {
294  // Threshold between visible and infrared is 0.7 micron, or 14286 cm^-1.
295  const real visible_wavenumber_threshold = 14286;
296 
297  // Wavenumber is in the visible if it is above the visible wavenumber
298  // threshold, and in the infrared if it is below the threshold
299  const bool is_visible_wave1 = (wavenumber_limits(1, ibnd) > visible_wavenumber_threshold ? true : false);
300  const bool is_visible_wave2 = (wavenumber_limits(2, ibnd) > visible_wavenumber_threshold ? true : false);
301 
302  if (is_visible_wave1 && is_visible_wave2) {
303 
304  // Entire band is in the visible
305  sfc_alb_dir(icol,ibnd) = sfc_alb_dir_vis(icol);
306  sfc_alb_dif(icol,ibnd) = sfc_alb_dif_vis(icol);
307 
308  } else if (!is_visible_wave1 && !is_visible_wave2) {
309 
310  // Entire band is in the longwave (near-infrared)
311  sfc_alb_dir(icol,ibnd) = sfc_alb_dir_nir(icol);
312  sfc_alb_dif(icol,ibnd) = sfc_alb_dif_nir(icol);
313 
314  } else {
315 
316  // Band straddles the visible to near-infrared transition, so we take
317  // the albedo to be the average of the visible and near-infrared
318  // broadband albedos
319  sfc_alb_dir(icol,ibnd) = 0.5*(sfc_alb_dir_vis(icol) + sfc_alb_dir_nir(icol));
320  sfc_alb_dif(icol,ibnd) = 0.5*(sfc_alb_dif_vis(icol) + sfc_alb_dif_nir(icol));
321 
322  }
323  });
324 }
GasOpticsRRTMGP k_dist_sw
Definition: ERF_RRTMGP_Interface.cpp:26

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

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 real  pmin,
const real  pmax,
const real2d &  pmid,
const real3d &  cld_tau_gpt,
real1d &  cld_area 
)
1017 {
1018  // Subcolumn binary cld mask; if any layers with pressure between pmin and pmax are cloudy
1019  // then 2d subcol mask is 1, otherwise it is 0
1020  auto subcol_mask = real2d("subcol_mask", ncol, ngpt);
1021  memset(subcol_mask, 0);
1022  parallel_for(SimpleBounds<3>(ngpt, nlay, ncol), YAKL_LAMBDA(int igpt, int ilay, int icol)
1023  {
1024  // NOTE: using plev would need to assume level ordering (top to bottom or bottom to top), but
1025  // using play/pmid does not
1026  if (cld_tau_gpt(icol,ilay,igpt) > 0 && pmid(icol,ilay) >= pmin && pmid(icol,ilay) < pmax) {
1027  subcol_mask(icol,igpt) = 1;
1028  }
1029  });
1030  // Compute average over subcols to get cloud area
1031  auto ngpt_inv = 1.0 / ngpt;
1032  memset(cld_area, 0);
1033  parallel_for(SimpleBounds<1>(ncol), YAKL_LAMBDA(int icol)
1034  {
1035  // This loop needs to be serial because of the atomic reduction
1036  for (int igpt = 1; igpt <= ngpt; ++igpt) {
1037  cld_area(icol) += subcol_mask(icol,igpt) * ngpt_inv;
1038  }
1039  });
1040 }

◆ compute_heating_rate()

template<class T , int myMem, int myStyle>
void rrtmgp::compute_heating_rate ( yakl::Array< T, 2, myMem, myStyle > const &  flux_up,
yakl::Array< T, 2, myMem, myStyle > const &  flux_dn,
yakl::Array< T, 2, myMem, myStyle > const &  rho,
yakl::Array< T, 2, myMem, myStyle > const &  dz,
yakl::Array< T, 2, myMem, myStyle > &  heating_rate 
)
78 {
79  auto ncol = flux_up.dimension[0];
80  auto nlay = flux_up.dimension[1]-1;
81  yakl::fortran::parallel_for(yakl::fortran::SimpleBounds<2>(nlay,ncol), YAKL_LAMBDA(int ilay, int icol)
82  {
83  heating_rate(icol,ilay) = ( flux_up(icol,ilay+1) - flux_up(icol,ilay)
84  - flux_dn(icol,ilay+1) + flux_dn(icol,ilay) )
85  / (Cp_d * rho(icol,ilay) * dz(icol,ilay));
86 
87  /*
88  if (icol == 1) {
89  printf("Heating Rate %i %e %e %e %e %e %e\n",ilay,
90  flux_up(icol,ilay+1), flux_up(icol,ilay),
91  flux_dn(icol,ilay+1), flux_dn(icol,ilay),
92  rho(icol,ilay), dz(icol,ilay));
93  }
94  */
95  });
96 }
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:12
@ rho
Definition: ERF_Kessler.H:30

Referenced by Radiation::run_impl().

Here is the caller graph for this function:

◆ get_cloud_optics_lw()

OpticalProps1scl rrtmgp::get_cloud_optics_lw ( const int  ncol,
const int  nlay,
CloudOptics &  cloud_optics,
GasOpticsRRTMGP &  kdist,
real2d &  lwp,
real2d &  iwp,
real2d &  rel,
real2d &  rei 
)
82 {
83  // Initialize optics
84  OpticalProps1scl clouds;
85  clouds.init(kdist.get_band_lims_wavenumber());
86  clouds.alloc_1scl(ncol, nlay); // this is dumb, why do we need to init and alloc separately?!
87 
88  // Needed for consistency with all-sky example problem?
89  cloud_optics.set_ice_roughness(2);
90 
91  // Limit effective radii to be within bounds of lookup table
92  auto rel_limited = real2d("rel_limited", ncol, nlay);
93  auto rei_limited = real2d("rei_limited", ncol, nlay);
94  limit_to_bounds(rel, cloud_optics.radliq_lwr, cloud_optics.radliq_upr, rel_limited);
95  limit_to_bounds(rei, cloud_optics.radice_lwr, cloud_optics.radice_upr, rei_limited);
96 
97  // Calculate cloud optics
98  cloud_optics.cloud_optics(ncol, nlay, lwp, iwp, rel_limited, rei_limited, clouds);
99 
100  // Return optics
101  return clouds;
102 }
void limit_to_bounds(S const &arr_in, T const lower, T const upper, S &arr_out)
Definition: ERF_RRTMGP_Utils.H:49

Referenced by rrtmgp_main().

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

◆ get_cloud_optics_sw()

OpticalProps2str rrtmgp::get_cloud_optics_sw ( const int  ncol,
const int  nlay,
CloudOptics &  cloud_optics,
GasOpticsRRTMGP &  kdist,
real2d &  lwp,
real2d &  iwp,
real2d &  rel,
real2d &  rei 
)
50 {
51  // Initialize optics
52  OpticalProps2str clouds;
53  clouds.init(kdist.get_band_lims_wavenumber());
54  clouds.alloc_2str(ncol, nlay);
55 
56  // Needed for consistency with all-sky example problem?
57  cloud_optics.set_ice_roughness(2);
58 
59  // Limit effective radii to be within bounds of lookup table
60  auto rel_limited = real2d("rel_limited", ncol, nlay);
61  auto rei_limited = real2d("rei_limited", ncol, nlay);
62  limit_to_bounds(rel, cloud_optics.radliq_lwr, cloud_optics.radliq_upr, rel_limited);
63  limit_to_bounds(rei, cloud_optics.radice_lwr, cloud_optics.radice_upr, rei_limited);
64 
65  // Calculate cloud optics
66  cloud_optics.cloud_optics(ncol, nlay, lwp, iwp, rel_limited, rei_limited, clouds);
67 
68  // Return optics
69  return clouds;
70 }

Referenced by rrtmgp_main().

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

◆ get_subcolumn_mask()

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

Referenced by get_subsampled_clouds().

Here is the caller graph for this function:

◆ get_subsampled_clouds() [1/2]

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

◆ get_subsampled_clouds() [2/2]

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

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 ( OpticalProps &  kdist,
double  wavelength 
)
1054 {
1055  // Get wavelength bounds for all wavelength bands
1056  auto wavelength_bounds = kdist.get_band_lims_wavelength();
1057 
1058  // Find the band index for the specified wavelength
1059  // Note that bands are stored in wavenumber space, units of cm-1, so if we are passed wavelength
1060  // in units of meters, we need a conversion factor of 10^2
1061  int nbnds = kdist.get_nband();
1062  yakl::ScalarLiveOut<int> band_index(-1);
1063  parallel_for(SimpleBounds<1>(nbnds), YAKL_LAMBDA(int ibnd)
1064  {
1065  if (wavelength_bounds(1,ibnd) < wavelength_bounds(2,ibnd)) {
1066  if (wavelength_bounds(1,ibnd) <= wavelength * 1e2 && wavelength * 1e2 <= wavelength_bounds(2,ibnd)) {
1067  band_index = ibnd;
1068  }
1069  } else {
1070  if (wavelength_bounds(1,ibnd) >= wavelength * 1e2 && wavelength * 1e2 >= wavelength_bounds(2,ibnd)) {
1071  band_index = ibnd;
1072  }
1073  }
1074  });
1075  return band_index.hostRead();
1076 }

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 ( double  wavelength)
1048 { return get_wavelength_index(k_dist_lw, wavelength); }
GasOpticsRRTMGP k_dist_lw
Definition: ERF_RRTMGP_Interface.cpp:27
int get_wavelength_index(OpticalProps &kdist, double wavelength)
Definition: ERF_RRTMGP_Interface.cpp:1052
Here is the call graph for this function:

◆ get_wavelength_index_sw()

int rrtmgp::get_wavelength_index_sw ( double  wavelength)
1044 { return get_wavelength_index(k_dist_sw, wavelength); }
Here is the call graph for this function:

◆ limit_to_bounds()

template<class S , class T >
void rrtmgp::limit_to_bounds ( S const &  arr_in,
T const  lower,
T const  upper,
S &  arr_out 
)
53 {
54  yakl::c::parallel_for(arr_in.totElems(), YAKL_LAMBDA(int i)
55  {
56  arr_out.data()[i] = std::min(std::max(arr_in.data()[i], lower), upper);
57  });
58 }

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 T , int myMem, int myStyle>
void rrtmgp::mixing_ratio_to_cloud_mass ( yakl::Array< T, 2, myMem, myStyle > const &  mixing_ratio,
yakl::Array< T, 2, myMem, myStyle > const &  cloud_fraction,
yakl::Array< T, 2, myMem, myStyle > const &  dp,
yakl::Array< T, 2, myMem, myStyle > &  cloud_mass 
)
27 {
28  int ncol = mixing_ratio.dimension[0];
29  int nlay = mixing_ratio.dimension[1];
30  yakl::fortran::parallel_for(yakl::fortran::SimpleBounds<2>(nlay, ncol), YAKL_LAMBDA(int ilay, int icol)
31  {
32  // Compute in-cloud mixing ratio (mixing ratio of the cloudy part of the layer)
33  // NOTE: these thresholds (from E3SM) seem arbitrary, but included here for consistency
34  // This limits in-cloud mixing ratio to 0.005 kg/kg. According to note in cloud_diagnostics
35  // in EAM, this is consistent with limits in MG2. Is this true for P3?
36  if (cloud_fraction(icol,ilay) > 0) {
37  // Compute layer-integrated cloud mass (per unit area)
38  auto incloud_mixing_ratio = std::min(mixing_ratio(icol,ilay) / std::max(0.0001, cloud_fraction(icol,ilay)), 0.005);
39  cloud_mass(icol,ilay) = incloud_mixing_ratio * dp(icol,ilay) / CONST_GRAV;
40  } else {
41  cloud_mass(icol,ilay) = 0;
42  }
43  });
44 }

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
103 {
104  // If irad == 0, then never do radiation;
105  // Otherwise, we always call radiation at the first step,
106  // and afterwards we do radiation if the timestep is divisible
107  // by irad
108  if (irad == 0) {
109  return false;
110  } else {
111  return ( (nstep == 0) || (nstep % irad == 0) );
112  }
113 }

◆ rrtmgp_finalize()

void rrtmgp::rrtmgp_finalize ( )
258 {
259  initialized = false;
260  k_dist_sw.finalize();
261  k_dist_lw.finalize();
262  cloud_optics_sw.finalize(); //~CloudOptics();
263  cloud_optics_lw.finalize(); //~CloudOptics();
264 }
CloudOptics cloud_optics_sw
Definition: ERF_RRTMGP_Interface.cpp:34
CloudOptics cloud_optics_lw
Definition: ERF_RRTMGP_Interface.cpp:35
bool initialized
Definition: ERF_RRTMGP_Interface.cpp:37

Referenced by Radiation::finalize_impl().

Here is the caller graph for this function:

◆ rrtmgp_initialize()

void rrtmgp::rrtmgp_initialize ( GasConcs &  gas_concs,
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 
)
239 {
240  // Initialize YAKL
241  if (!yakl::isInitialized()) { yakl::init(); }
242 
243  // Load and initialize absorption coefficient data
244  load_and_init(k_dist_sw, coefficients_file_sw, gas_concs);
245  load_and_init(k_dist_lw, coefficients_file_lw, gas_concs);
246 
247  // Load and initialize cloud optical property look-up table information
248  load_cld_lutcoeff(cloud_optics_sw, cloud_optics_file_sw);
249  load_cld_lutcoeff(cloud_optics_lw, cloud_optics_file_lw);
250 
251  // We are now initialized!
252  initialized = true;
253 }

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,
GasOpticsRRTMGP &  k_dist,
real2d &  p_lay,
real2d &  t_lay,
real2d &  p_lev,
real2d &  t_lev,
GasConcs &  gas_concs,
OpticalProps1scl &  aerosol,
OpticalProps1scl &  clouds,
FluxesByband &  fluxes,
FluxesBroadband &  clnclrsky_fluxes,
FluxesBroadband &  clrsky_fluxes,
FluxesBroadband &  clnsky_fluxes,
const bool  extra_clnclrsky_diag,
const bool  extra_clnsky_diag 
)
889 {
890  // Problem size
891  int nbnd = k_dist.get_nband();
892 
893  // Associate local pointers for fluxes
894  auto& flux_up = fluxes.flux_up;
895  auto& flux_dn = fluxes.flux_dn;
896  auto& bnd_flux_up = fluxes.bnd_flux_up;
897  auto& bnd_flux_dn = fluxes.bnd_flux_dn;
898  auto& clnclrsky_flux_up = clnclrsky_fluxes.flux_up;
899  auto& clnclrsky_flux_dn = clnclrsky_fluxes.flux_dn;
900  auto& clrsky_flux_up = clrsky_fluxes.flux_up;
901  auto& clrsky_flux_dn = clrsky_fluxes.flux_dn;
902  auto& clnsky_flux_up = clnsky_fluxes.flux_up;
903  auto& clnsky_flux_dn = clnsky_fluxes.flux_dn;
904 
905  // Reset fluxes to zero
906  parallel_for(SimpleBounds<2>(nlay + 1, ncol), YAKL_LAMBDA(int ilev, int icol)
907  {
908  flux_up(icol, ilev) = 0;
909  flux_dn(icol, ilev) = 0;
910  clnclrsky_flux_up(icol, ilev) = 0;
911  clnclrsky_flux_dn(icol, ilev) = 0;
912  clrsky_flux_up(icol, ilev) = 0;
913  clrsky_flux_dn(icol, ilev) = 0;
914  clnsky_flux_up(icol, ilev) = 0;
915  clnsky_flux_dn(icol, ilev) = 0;
916  });
917  parallel_for(SimpleBounds<3>(nbnd, nlay + 1, ncol), YAKL_LAMBDA(int ibnd, int ilev, int icol)
918  {
919  bnd_flux_up(icol, ilev, ibnd) = 0;
920  bnd_flux_dn(icol, ilev, ibnd) = 0;
921  });
922 
923  // Allocate space for optical properties
924  OpticalProps1scl optics;
925  optics.alloc_1scl(ncol, nlay, k_dist);
926  OpticalProps1scl optics_no_aerosols;
927  if (extra_clnsky_diag) {
928  // Allocate space for optical properties (no aerosols)
929  optics_no_aerosols.alloc_1scl(ncol, nlay, k_dist);
930  }
931 
932  // Boundary conditions
933  SourceFuncLW lw_sources;
934  lw_sources.alloc(ncol, nlay, k_dist);
935  real1d t_sfc ("t_sfc" ,ncol);
936  real2d emis_sfc("emis_sfc",nbnd,ncol);
937 
938  // Surface temperature
939  auto p_lay_host = p_lay.createHostCopy();
940  bool top_at_1 = p_lay_host(1, 1) < p_lay_host(1, nlay);
941  parallel_for(SimpleBounds<1>(ncol), YAKL_LAMBDA(int icol)
942  {
943  t_sfc(icol) = t_lev(icol, merge(nlay+1, 1, top_at_1));
944  });
945  memset(emis_sfc , amrex::Real(0.98));
946 
947  // Get Gaussian quadrature weights
948  // TODO: move this crap out of userland!
949  // Weights and angle secants for first order (k=1) Gaussian quadrature.
950  // Values from Table 2, Clough et al, 1992, doi:10.1029/92JD01419
951  // after Abramowitz & Stegun 1972, page 921
952  int constexpr max_gauss_pts = 4;
953  realHost2d gauss_Ds_host ("gauss_Ds" ,max_gauss_pts,max_gauss_pts);
954  gauss_Ds_host(1,1) = 1.66_wp ; gauss_Ds_host(2,1) = 0._wp; gauss_Ds_host(3,1) = 0._wp; gauss_Ds_host(4,1) = 0._wp;
955  gauss_Ds_host(1,2) = 1.18350343_wp; gauss_Ds_host(2,2) = 2.81649655_wp; gauss_Ds_host(3,2) = 0._wp; gauss_Ds_host(4,2) = 0._wp;
956  gauss_Ds_host(1,3) = 1.09719858_wp; gauss_Ds_host(2,3) = 1.69338507_wp; gauss_Ds_host(3,3) = 4.70941630_wp; gauss_Ds_host(4,3) = 0._wp;
957  gauss_Ds_host(1,4) = 1.06056257_wp; gauss_Ds_host(2,4) = 1.38282560_wp; gauss_Ds_host(3,4) = 2.40148179_wp; gauss_Ds_host(4,4) = 7.15513024_wp;
958 
959  realHost2d gauss_wts_host("gauss_wts",max_gauss_pts,max_gauss_pts);
960  gauss_wts_host(1,1) = 0.5_wp ; gauss_wts_host(2,1) = 0._wp ; gauss_wts_host(3,1) = 0._wp ; gauss_wts_host(4,1) = 0._wp ;
961  gauss_wts_host(1,2) = 0.3180413817_wp; gauss_wts_host(2,2) = 0.1819586183_wp; gauss_wts_host(3,2) = 0._wp ; gauss_wts_host(4,2) = 0._wp ;
962  gauss_wts_host(1,3) = 0.2009319137_wp; gauss_wts_host(2,3) = 0.2292411064_wp; gauss_wts_host(3,3) = 0.0698269799_wp; gauss_wts_host(4,3) = 0._wp ;
963  gauss_wts_host(1,4) = 0.1355069134_wp; gauss_wts_host(2,4) = 0.2034645680_wp; gauss_wts_host(3,4) = 0.1298475476_wp; gauss_wts_host(4,4) = 0.0311809710_wp;
964 
965  real2d gauss_Ds ("gauss_Ds" ,max_gauss_pts,max_gauss_pts);
966  real2d gauss_wts("gauss_wts",max_gauss_pts,max_gauss_pts);
967  gauss_Ds_host .deep_copy_to(gauss_Ds );
968  gauss_wts_host.deep_copy_to(gauss_wts);
969 
970  // Limit temperatures for gas optics look-up tables
971  auto t_lay_limited = real2d("t_lay_limited", ncol, nlay);
972  auto t_lev_limited = real2d("t_lev_limited", ncol, nlay+1);
973  limit_to_bounds(t_lay, k_dist_lw.get_temp_min(), k_dist_lw.get_temp_max(), t_lay_limited);
974  limit_to_bounds(t_lev, k_dist_lw.get_temp_min(), k_dist_lw.get_temp_max(), t_lev_limited);
975 
976  // Do gas optics
977  k_dist.gas_optics(ncol, nlay, top_at_1, p_lay, p_lev, t_lay_limited, t_sfc, gas_concs, optics, lw_sources, real2d(), t_lev_limited);
978  if (extra_clnsky_diag) {
979  k_dist.gas_optics(ncol, nlay, top_at_1, p_lay, p_lev, t_lay_limited, t_sfc, gas_concs, optics_no_aerosols, lw_sources, real2d(), t_lev_limited);
980  }
981 
982  if (extra_clnclrsky_diag) {
983  // Compute clean-clear-sky fluxes before we add in aerosols and clouds
984  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics, top_at_1, lw_sources, emis_sfc, clnclrsky_fluxes);
985  }
986 
987  // Combine gas and aerosol optics
988  aerosol.increment(optics);
989 
990  // Compute clear-sky fluxes before we add in clouds
991  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics, top_at_1, lw_sources, emis_sfc, clrsky_fluxes);
992 
993  // Combine gas and cloud optics
994  clouds.increment(optics);
995 
996  // Compute allsky fluxes
997  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics, top_at_1, lw_sources, emis_sfc, fluxes);
998 
999  if (extra_clnsky_diag) {
1000  // First increment clouds in optics_no_aerosols
1001  clouds.increment(optics_no_aerosols);
1002  // Compute clean-sky fluxes
1003  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics_no_aerosols, top_at_1, lw_sources, emis_sfc, clnsky_fluxes);
1004  }
1005 }

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 &  p_lay,
real2d &  t_lay,
real2d &  p_lev,
real2d &  t_lev,
GasConcs &  gas_concs,
real2d &  sfc_alb_dir,
real2d &  sfc_alb_dif,
real1d &  mu0,
real2d &  lwp,
real2d &  iwp,
real2d &  rel,
real2d &  rei,
real2d &  cldfrac,
real3d &  aer_tau_sw,
real3d &  aer_ssa_sw,
real3d &  aer_asm_sw,
real3d &  aer_tau_lw,
real3d &  cld_tau_sw_bnd,
real3d &  cld_tau_lw_bnd,
real3d &  cld_tau_sw_gpt,
real3d &  cld_tau_lw_gpt,
real2d &  sw_flux_up,
real2d &  sw_flux_dn,
real2d &  sw_flux_dn_dir,
real2d &  lw_flux_up,
real2d &  lw_flux_dn,
real2d &  sw_clnclrsky_flux_up,
real2d &  sw_clnclrsky_flux_dn,
real2d &  sw_clnclrsky_flux_dn_dir,
real2d &  sw_clrsky_flux_up,
real2d &  sw_clrsky_flux_dn,
real2d &  sw_clrsky_flux_dn_dir,
real2d &  sw_clnsky_flux_up,
real2d &  sw_clnsky_flux_dn,
real2d &  sw_clnsky_flux_dn_dir,
real2d &  lw_clnclrsky_flux_up,
real2d &  lw_clnclrsky_flux_dn,
real2d &  lw_clrsky_flux_up,
real2d &  lw_clrsky_flux_dn,
real2d &  lw_clnsky_flux_up,
real2d &  lw_clnsky_flux_dn,
real3d &  sw_bnd_flux_up,
real3d &  sw_bnd_flux_dn,
real3d &  sw_bnd_flux_dn_dir,
real3d &  lw_bnd_flux_up,
real3d &  lw_bnd_flux_dn,
const real  tsi_scaling,
const bool  extra_clnclrsky_diag,
const bool  extra_clnsky_diag 
)
412 {
413  // Setup pointers to RRTMGP SW fluxes
414  FluxesByband fluxes_sw;
415  fluxes_sw.flux_up = sw_flux_up;
416  fluxes_sw.flux_dn = sw_flux_dn;
417  fluxes_sw.flux_dn_dir = sw_flux_dn_dir;
418  fluxes_sw.bnd_flux_up = sw_bnd_flux_up;
419  fluxes_sw.bnd_flux_dn = sw_bnd_flux_dn;
420  fluxes_sw.bnd_flux_dn_dir = sw_bnd_flux_dn_dir;
421  // Clean-clear-sky
422  FluxesBroadband clnclrsky_fluxes_sw;
423  clnclrsky_fluxes_sw.flux_up = sw_clnclrsky_flux_up;
424  clnclrsky_fluxes_sw.flux_dn = sw_clnclrsky_flux_dn;
425  clnclrsky_fluxes_sw.flux_dn_dir = sw_clnclrsky_flux_dn_dir;
426  // Clear-sky
427  FluxesBroadband clrsky_fluxes_sw;
428  clrsky_fluxes_sw.flux_up = sw_clrsky_flux_up;
429  clrsky_fluxes_sw.flux_dn = sw_clrsky_flux_dn;
430  clrsky_fluxes_sw.flux_dn_dir = sw_clrsky_flux_dn_dir;
431  // Clean-sky
432  FluxesBroadband clnsky_fluxes_sw;
433  clnsky_fluxes_sw.flux_up = sw_clnsky_flux_up;
434  clnsky_fluxes_sw.flux_dn = sw_clnsky_flux_dn;
435  clnsky_fluxes_sw.flux_dn_dir = sw_clnsky_flux_dn_dir;
436 
437  // Setup pointers to RRTMGP LW fluxes
438  FluxesByband fluxes_lw;
439  fluxes_lw.flux_up = lw_flux_up;
440  fluxes_lw.flux_dn = lw_flux_dn;
441  fluxes_lw.bnd_flux_up = lw_bnd_flux_up;
442  fluxes_lw.bnd_flux_dn = lw_bnd_flux_dn;
443  // Clean-clear-sky
444  FluxesBroadband clnclrsky_fluxes_lw;
445  clnclrsky_fluxes_lw.flux_up = lw_clnclrsky_flux_up;
446  clnclrsky_fluxes_lw.flux_dn = lw_clnclrsky_flux_dn;
447  // Clear-sky
448  FluxesBroadband clrsky_fluxes_lw;
449  clrsky_fluxes_lw.flux_up = lw_clrsky_flux_up;
450  clrsky_fluxes_lw.flux_dn = lw_clrsky_flux_dn;
451  // Clean-sky
452  FluxesBroadband clnsky_fluxes_lw;
453  clnsky_fluxes_lw.flux_up = lw_clnsky_flux_up;
454  clnsky_fluxes_lw.flux_dn = lw_clnsky_flux_dn;
455 
456  auto nswbands = k_dist_sw.get_nband();
457  auto nlwbands = k_dist_lw.get_nband();
458 
459  // Setup aerosol optical properties
460  OpticalProps2str aerosol_sw;
461  OpticalProps1scl aerosol_lw;
462  aerosol_sw.init(k_dist_sw.get_band_lims_wavenumber());
463  aerosol_sw.alloc_2str(ncol, nlay);
464  parallel_for(SimpleBounds<3>(nswbands,nlay,ncol) , YAKL_LAMBDA (int ibnd, int ilay, int icol)
465  {
466  aerosol_sw.tau(icol,ilay,ibnd) = aer_tau_sw(icol,ilay,ibnd);
467  aerosol_sw.ssa(icol,ilay,ibnd) = aer_ssa_sw(icol,ilay,ibnd);
468  aerosol_sw.g (icol,ilay,ibnd) = aer_asm_sw(icol,ilay,ibnd);
469  });
470  aerosol_lw.init(k_dist_lw.get_band_lims_wavenumber());
471  aerosol_lw.alloc_1scl(ncol, nlay);
472  parallel_for(SimpleBounds<3>(nlwbands,nlay,ncol) , YAKL_LAMBDA (int ibnd, int ilay, int icol)
473  {
474  aerosol_lw.tau(icol,ilay,ibnd) = aer_tau_lw(icol,ilay,ibnd);
475  });
476 
477  // Convert cloud physical properties to optical properties for input to RRTMGP
478  OpticalProps2str clouds_sw = get_cloud_optics_sw(ncol, nlay, cloud_optics_sw, k_dist_sw, lwp, iwp, rel, rei);
479  OpticalProps1scl clouds_lw = get_cloud_optics_lw(ncol, nlay, cloud_optics_lw, k_dist_lw, lwp, iwp, rel, rei);
480  clouds_sw.tau.deep_copy_to(cld_tau_sw_bnd);
481  clouds_lw.tau.deep_copy_to(cld_tau_lw_bnd);
482 
483  // Do subcolumn sampling to map bands -> gpoints based on cloud fraction and overlap assumption;
484  // This implements the Monte Carlo Independing Column Approximation by mapping only a single
485  // subcolumn (cloud state) to each gpoint.
486  auto nswgpts = k_dist_sw.get_ngpt();
487  auto clouds_sw_gpt = get_subsampled_clouds(ncol, nlay, nswbands, nswgpts, clouds_sw, k_dist_sw, cldfrac, p_lay);
488  // Longwave
489  auto nlwgpts = k_dist_lw.get_ngpt();
490  auto clouds_lw_gpt = get_subsampled_clouds(ncol, nlay, nlwbands, nlwgpts, clouds_lw, k_dist_lw, cldfrac, p_lay);
491 
492  // Copy cloud properties to outputs (is this needed, or can we just use pointers?)
493  // Alternatively, just compute and output a subcolumn cloud mask
494  parallel_for(SimpleBounds<3>(nswgpts, nlay, ncol), YAKL_LAMBDA (int igpt, int ilay, int icol)
495  {
496  cld_tau_sw_gpt(icol,ilay,igpt) = clouds_sw_gpt.tau(icol,ilay,igpt);
497  });
498  parallel_for(SimpleBounds<3>(nlwgpts, nlay, ncol), YAKL_LAMBDA (int igpt, int ilay, int icol)
499  {
500  cld_tau_lw_gpt(icol,ilay,igpt) = clouds_lw_gpt.tau(icol,ilay,igpt);
501  });
502 
503  // Do shortwave
504  rrtmgp_sw(ncol, nlay,
505  k_dist_sw, p_lay, t_lay, p_lev, t_lev, gas_concs,
506  sfc_alb_dir, sfc_alb_dif, mu0, aerosol_sw, clouds_sw_gpt,
507  fluxes_sw, clnclrsky_fluxes_sw, clrsky_fluxes_sw, clnsky_fluxes_sw,
508  tsi_scaling, extra_clnclrsky_diag, extra_clnsky_diag);
509 
510  // Do longwave
511  rrtmgp_lw(ncol, nlay,
512  k_dist_lw, p_lay, t_lay, p_lev, t_lev, gas_concs,
513  aerosol_lw, clouds_lw_gpt,
514  fluxes_lw, clnclrsky_fluxes_lw, clrsky_fluxes_lw, clnsky_fluxes_lw,
515  extra_clnclrsky_diag, extra_clnsky_diag);
516 
517 }
void rrtmgp_sw(const int ncol, const int nlay, GasOpticsRRTMGP &k_dist, real2d &p_lay, real2d &t_lay, real2d &p_lev, real2d &t_lev, GasConcs &gas_concs, real2d &sfc_alb_dir, real2d &sfc_alb_dif, real1d &mu0, OpticalProps2str &aerosol, OpticalProps2str &clouds, FluxesByband &fluxes, FluxesBroadband &clnclrsky_fluxes, FluxesBroadband &clrsky_fluxes, FluxesBroadband &clnsky_fluxes, const real tsi_scaling, const bool extra_clnclrsky_diag, const bool extra_clnsky_diag)
Definition: ERF_RRTMGP_Interface.cpp:594
OpticalProps1scl get_subsampled_clouds(const int ncol, const int nlay, const int nbnd, const int ngpt, OpticalProps1scl &cloud_optics, GasOpticsRRTMGP &kdist, real2d &cld, real2d &p_lay)
Definition: ERF_RRTMGP_Interface.cpp:172
void rrtmgp_lw(const int ncol, const int nlay, GasOpticsRRTMGP &k_dist, real2d &p_lay, real2d &t_lay, real2d &p_lev, real2d &t_lev, GasConcs &gas_concs, OpticalProps1scl &aerosol, OpticalProps1scl &clouds, FluxesByband &fluxes, FluxesBroadband &clnclrsky_fluxes, FluxesBroadband &clrsky_fluxes, FluxesBroadband &clnsky_fluxes, const bool extra_clnclrsky_diag, const bool extra_clnsky_diag)
Definition: ERF_RRTMGP_Interface.cpp:873
OpticalProps2str get_cloud_optics_sw(const int ncol, const int nlay, CloudOptics &cloud_optics, GasOpticsRRTMGP &kdist, real2d &lwp, real2d &iwp, real2d &rel, real2d &rei)
Definition: ERF_RRTMGP_Interface.cpp:42
OpticalProps1scl get_cloud_optics_lw(const int ncol, const int nlay, CloudOptics &cloud_optics, GasOpticsRRTMGP &kdist, real2d &lwp, real2d &iwp, real2d &rel, real2d &rei)
Definition: ERF_RRTMGP_Interface.cpp:74

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

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

CloudOptics rrtmgp::cloud_optics_lw

◆ cloud_optics_sw

CloudOptics rrtmgp::cloud_optics_sw

◆ initialized

bool rrtmgp::initialized = false

◆ initialized_k

bool rrtmgp::initialized_k = false

◆ k_dist_lw

GasOpticsRRTMGP rrtmgp::k_dist_lw

◆ k_dist_sw