ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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, real1d &t_sfc, real2d &emis_sfc, real1d &lw_src, 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, real1d &t_sfc, real2d &emis_sfc, real1d &lw_src, 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 
)
1103 {
1104  /* The goal of this routine is to calculate properties at cloud top
1105  * based on the AeroCom recommendation. See reference for routine
1106  * get_subcolumn_mask above, where equation 14 is used for the
1107  * maximum-random overlap assumption for subcolumn generation. We use
1108  * equation 13, the column counterpart.
1109  */
1110  // Set outputs to zero
1111  memset(T_mid_at_cldtop, 0.0);
1112  memset(p_mid_at_cldtop, 0.0);
1113  memset(cldfrac_ice_at_cldtop, 0.0);
1114  memset(cldfrac_liq_at_cldtop, 0.0);
1115  memset(cldfrac_tot_at_cldtop, 0.0);
1116  memset(cdnc_at_cldtop, 0.0);
1117  memset(eff_radius_qc_at_cldtop, 0.0);
1118  memset(eff_radius_qi_at_cldtop, 0.0);
1119 
1120  // Initialize the 1D "clear fraction" as 1 (totally clear)
1121  auto aerocom_clr = real1d("aerocom_clr", ncol);
1122  memset(aerocom_clr, 1.0);
1123 
1124  // TODO: move tunable constant to namelist
1125  constexpr real q_threshold = 0.0; // BAD_CONSTANT!
1126 
1127  // TODO: move tunable constant to namelist
1128  constexpr real cldfrac_tot_threshold = 0.001; // BAD_CONSTANT!
1129 
1130  // Loop over all columns in parallel
1131  parallel_for(SimpleBounds<1>(ncol), YAKL_LAMBDA(int icol)
1132  {
1133  // Loop over all layers in serial (due to accumulative
1134  // product), starting at 2 (second highest) layer because the
1135  // highest is assumed to have no clouds
1136  for(int ilay = 2; ilay <= nlay; ++ilay) {
1137  // Only do the calculation if certain conditions are met
1138  if((qc(icol, ilay) + qi(icol, ilay)) > q_threshold &&
1139  (cldfrac_tot(icol, ilay) > cldfrac_tot_threshold)) {
1140  /* PART I: Probabilistically determining cloud top */
1141  // Populate aerocom_tmp as the clear-sky fraction
1142  // probability of this level, where aerocom_clr is that of
1143  // the previous level
1144  auto aerocom_tmp = aerocom_clr(icol) *
1145  (1.0 - std::max(cldfrac_tot(icol, ilay - 1),
1146  cldfrac_tot(icol, ilay))) /
1147  (1.0 - std::min(cldfrac_tot(icol, ilay - 1),
1148  1.0 - cldfrac_tot_threshold));
1149  // Temporary variable for probability "weights"
1150  auto aerocom_wts = aerocom_clr(icol) - aerocom_tmp;
1151  // Temporary variable for liquid "phase"
1152  auto aerocom_phi = qc(icol, ilay) / (qc(icol, ilay) + qi(icol, ilay));
1153  /* PART II: The inferred properties */
1154  /* In general, converting a 3D property X to a 2D cloud-top
1155  * counterpart x follows: x(i) += X(i,k) * weights * Phase
1156  * but X and Phase are not always needed */
1157  // T_mid_at_cldtop
1158  T_mid_at_cldtop(icol) += tmid(icol, ilay) * aerocom_wts;
1159  // p_mid_at_cldtop
1160  p_mid_at_cldtop(icol) += pmid(icol, ilay) * aerocom_wts;
1161  // cldfrac_ice_at_cldtop
1162  cldfrac_ice_at_cldtop(icol) += (1.0 - aerocom_phi) * aerocom_wts;
1163  // cldfrac_liq_at_cldtop
1164  cldfrac_liq_at_cldtop(icol) += aerocom_phi * aerocom_wts;
1165  // cdnc_at_cldtop
1166  /* We need to convert nc from 1/mass to 1/volume first, and
1167  * from grid-mean to in-cloud, but after that, the
1168  * calculation follows the general logic */
1169  auto cdnc = nc(icol, ilay) * p_del(icol, ilay) /
1170  z_del(icol, ilay) / CONST_GRAV /
1171  cldfrac_tot(icol, ilay);
1172  cdnc_at_cldtop(icol) += cdnc * aerocom_phi * aerocom_wts;
1173  // eff_radius_qc_at_cldtop
1174  eff_radius_qc_at_cldtop(icol) += rel(icol, ilay) * aerocom_phi * aerocom_wts;
1175  // eff_radius_qi_at_cldtop
1176  eff_radius_qi_at_cldtop(icol) += rei(icol, ilay) * (1.0 - aerocom_phi) * aerocom_wts;
1177  // Reset aerocom_clr to aerocom_tmp to accumulate
1178  aerocom_clr(icol) = aerocom_tmp;
1179  }
1180  }
1181  // After the serial loop over levels, the cloudy fraction is
1182  // defined as (1 - aerocom_clr). This is true because
1183  // aerocom_clr is the result of accumulative probabilities
1184  // (their products)
1185  cldfrac_tot_at_cldtop(icol) = 1.0 - aerocom_clr(icol);
1186  });
1187 }
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:21
double real
Definition: ERF_OrbCosZenith.H:9
@ nc
Definition: ERF_Morrison.H:44
@ 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 
)
1032 {
1033  // Subcolumn binary cld mask; if any layers with pressure between pmin and pmax are cloudy
1034  // then 2d subcol mask is 1, otherwise it is 0
1035  auto subcol_mask = real2d("subcol_mask", ncol, ngpt);
1036  memset(subcol_mask, 0);
1037  parallel_for(SimpleBounds<3>(ngpt, nlay, ncol), YAKL_LAMBDA(int igpt, int ilay, int icol)
1038  {
1039  // NOTE: using plev would need to assume level ordering (top to bottom or bottom to top), but
1040  // using play/pmid does not
1041  if (cld_tau_gpt(icol,ilay,igpt) > 0 && pmid(icol,ilay) >= pmin && pmid(icol,ilay) < pmax) {
1042  subcol_mask(icol,igpt) = 1;
1043  }
1044  });
1045  // Compute average over subcols to get cloud area
1046  auto ngpt_inv = 1.0 / ngpt;
1047  memset(cld_area, 0);
1048  parallel_for(SimpleBounds<1>(ncol), YAKL_LAMBDA(int icol)
1049  {
1050  // This loop needs to be serial because of the atomic reduction
1051  for (int igpt = 1; igpt <= ngpt; ++igpt) {
1052  cld_area(icol) += subcol_mask(icol,igpt) * ngpt_inv;
1053  }
1054  });
1055 }

◆ 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) = -1.0 * ( 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:22

Referenced by Radiation::finalize_impl(), and 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 
)
528 {
529  // Routine will return subcolumn mask with values of 0 indicating no cloud, 1 indicating cloud
530  auto subcolumn_mask = int3d("subcolumn_mask", ncol, nlay, ngpt);
531 
532  // Subcolumn generators are a means for producing a variable x(i,j,k), where
533  //
534  // c(i,j,k) = 1 for x(i,j,k) > 1 - cldf(i,j)
535  // c(i,j,k) = 0 for x(i,j,k) <= 1 - cldf(i,j)
536  //
537  // I am going to call this "cldx" to be just slightly less ambiguous
538  auto cldx = real3d("cldx", ncol, nlay, ngpt);
539 
540  // Apply overlap assumption to set cldx
541  if (overlap_option == 0) { // Dummy mask, always cloudy
542  memset(cldx, 1);
543  } else { // Default case, maximum-random overlap
544  // Maximum-random overlap:
545  // Uses essentially the algorithm described in eq (14) in Raisanen et al. 2004,
546  // https://rmets.onlinelibrary.wiley.com/doi/epdf/10.1256/qj.03.99. Also the same
547  // algorithm used in RRTMG implementation of maximum-random overlap (see
548  // https://github.com/AER-RC/RRTMG_SW/blob/master/src/mcica_subcol_gen_sw.f90)
549  //
550  // First, fill cldx with random numbers. Need to use a unique seed for each column!
551  parallel_for(SimpleBounds<1>(ncol), YAKL_LAMBDA(int icol)
552  {
553  yakl::Random rand(seeds(icol));
554  for (int igpt = 1; igpt <= ngpt; igpt++) {
555  for (int ilay = 1; ilay <= nlay; ilay++) {
556  cldx(icol,ilay,igpt) = rand.genFP<real>();
557  }
558  }
559  });
560  // Step down columns and apply algorithm from eq (14)
561  parallel_for(SimpleBounds<2>(ngpt,ncol), YAKL_LAMBDA(int igpt, int icol)
562  {
563  for (int ilay = 2; 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) > 1.0 - 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, 1.0 - 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) * (1.0 - cldf(icol,ilay-1));
576  }
577  }
578  });
579  }
580 
581  // Use cldx array to create subcolumn mask
582  parallel_for(SimpleBounds<3>(ngpt,nlay,ncol), YAKL_LAMBDA(int igpt, int ilay, int icol)
583  {
584  if (cldx(icol,ilay,igpt) > 1.0 - cldf(icol,ilay)) {
585  subcolumn_mask(icol,ilay,igpt) = 1;
586  } else {
587  subcolumn_mask(icol,ilay,igpt) = 0;
588  }
589  });
590  return subcolumn_mask;
591 }

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:522
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 
)
1069 {
1070  // Get wavelength bounds for all wavelength bands
1071  auto wavelength_bounds = kdist.get_band_lims_wavelength();
1072 
1073  // Find the band index for the specified wavelength
1074  // Note that bands are stored in wavenumber space, units of cm-1, so if we are passed wavelength
1075  // in units of meters, we need a conversion factor of 10^2
1076  int nbnds = kdist.get_nband();
1077  yakl::ScalarLiveOut<int> band_index(-1);
1078  parallel_for(SimpleBounds<1>(nbnds), YAKL_LAMBDA(int ibnd)
1079  {
1080  if (wavelength_bounds(1,ibnd) < wavelength_bounds(2,ibnd)) {
1081  if (wavelength_bounds(1,ibnd) <= wavelength * 1e2 && wavelength * 1e2 <= wavelength_bounds(2,ibnd)) {
1082  band_index = ibnd;
1083  }
1084  } else {
1085  if (wavelength_bounds(1,ibnd) >= wavelength * 1e2 && wavelength * 1e2 >= wavelength_bounds(2,ibnd)) {
1086  band_index = ibnd;
1087  }
1088  }
1089  });
1090  return band_index.hostRead();
1091 }

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)
1063 { 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:1067
Here is the call graph for this function:

◆ get_wavelength_index_sw()

int rrtmgp::get_wavelength_index_sw ( double  wavelength)
1059 { 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,
real1d &  t_sfc,
real2d &  emis_sfc,
real1d &  lw_src,
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 
)
893 {
894  // Problem size
895  int nbnd = k_dist.get_nband();
896 
897  // Associate local pointers for fluxes
898  auto& flux_up = fluxes.flux_up;
899  auto& flux_dn = fluxes.flux_dn;
900  auto& bnd_flux_up = fluxes.bnd_flux_up;
901  auto& bnd_flux_dn = fluxes.bnd_flux_dn;
902  auto& clnclrsky_flux_up = clnclrsky_fluxes.flux_up;
903  auto& clnclrsky_flux_dn = clnclrsky_fluxes.flux_dn;
904  auto& clrsky_flux_up = clrsky_fluxes.flux_up;
905  auto& clrsky_flux_dn = clrsky_fluxes.flux_dn;
906  auto& clnsky_flux_up = clnsky_fluxes.flux_up;
907  auto& clnsky_flux_dn = clnsky_fluxes.flux_dn;
908 
909  // Reset fluxes to zero
910  parallel_for(SimpleBounds<2>(nlay + 1, ncol), YAKL_LAMBDA(int ilev, int icol)
911  {
912  flux_up(icol, ilev) = 0;
913  flux_dn(icol, ilev) = 0;
914  clnclrsky_flux_up(icol, ilev) = 0;
915  clnclrsky_flux_dn(icol, ilev) = 0;
916  clrsky_flux_up(icol, ilev) = 0;
917  clrsky_flux_dn(icol, ilev) = 0;
918  clnsky_flux_up(icol, ilev) = 0;
919  clnsky_flux_dn(icol, ilev) = 0;
920  });
921  parallel_for(SimpleBounds<3>(nbnd, nlay + 1, ncol), YAKL_LAMBDA(int ibnd, int ilev, int icol)
922  {
923  bnd_flux_up(icol, ilev, ibnd) = 0;
924  bnd_flux_dn(icol, ilev, ibnd) = 0;
925  });
926 
927  // Allocate space for optical properties
928  OpticalProps1scl optics;
929  optics.alloc_1scl(ncol, nlay, k_dist);
930  OpticalProps1scl optics_no_aerosols;
931  if (extra_clnsky_diag) {
932  // Allocate space for optical properties (no aerosols)
933  optics_no_aerosols.alloc_1scl(ncol, nlay, k_dist);
934  }
935 
936  // Boundary conditions
937  SourceFuncLW lw_sources;
938  lw_sources.alloc(ncol, nlay, k_dist);
939  // set the LW source
940  // TODO: figure out how to do this correctly?
941  YAKL_SCOPE(d_lw_src, lw_sources.sfc_source);
942  //parallel_for(SimpleBounds<2>(ncol,m_nlwgpts), YAKL_LAMBDA(int icol, int igpt)
943  parallel_for(SimpleBounds<1>(ncol), YAKL_LAMBDA(int icol)
944  {
945  d_lw_src(icol, 1) = lw_src(icol);
946  });
947  //real1d t_sfc ("t_sfc" ,ncol);
948 
949  // Surface temperature
950  auto p_lay_host = p_lay.createHostCopy();
951  bool top_at_1 = p_lay_host(1, 1) < p_lay_host(1, nlay);
952  real2d emis_sfc_T("emis_sfc",nbnd,ncol);
953  //memset(emis_sfc , amrex::Real(0.98));
954  // RRTMGP assumes surface albedos have a screwy dimension ordering
955  // for some strange reason, so we need to transpose these; also do
956  // daytime subsetting in the same kernel
957  parallel_for(SimpleBounds<2>(nbnd,ncol), YAKL_LAMBDA(int ibnd, int icol)
958  {
959  emis_sfc_T(ibnd,icol) = emis_sfc(icol,ibnd);
960  });
961 
962  // Get Gaussian quadrature weights
963  // TODO: move this crap out of userland!
964  // Weights and angle secants for first order (k=1) Gaussian quadrature.
965  // Values from Table 2, Clough et al, 1992, doi:10.1029/92JD01419
966  // after Abramowitz & Stegun 1972, page 921
967  int constexpr max_gauss_pts = 4;
968  realHost2d gauss_Ds_host ("gauss_Ds" ,max_gauss_pts,max_gauss_pts);
969  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;
970  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;
971  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;
972  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;
973 
974  realHost2d gauss_wts_host("gauss_wts",max_gauss_pts,max_gauss_pts);
975  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 ;
976  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 ;
977  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 ;
978  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;
979 
980  real2d gauss_Ds ("gauss_Ds" ,max_gauss_pts,max_gauss_pts);
981  real2d gauss_wts("gauss_wts",max_gauss_pts,max_gauss_pts);
982  gauss_Ds_host .deep_copy_to(gauss_Ds );
983  gauss_wts_host.deep_copy_to(gauss_wts);
984 
985  // Limit temperatures for gas optics look-up tables
986  auto t_lay_limited = real2d("t_lay_limited", ncol, nlay);
987  auto t_lev_limited = real2d("t_lev_limited", ncol, nlay+1);
988  limit_to_bounds(t_lay, k_dist_lw.get_temp_min(), k_dist_lw.get_temp_max(), t_lay_limited);
989  limit_to_bounds(t_lev, k_dist_lw.get_temp_min(), k_dist_lw.get_temp_max(), t_lev_limited);
990 
991  // Do gas optics
992  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);
993  if (extra_clnsky_diag) {
994  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);
995  }
996 
997  if (extra_clnclrsky_diag) {
998  // Compute clean-clear-sky fluxes before we add in aerosols and clouds
999  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics, top_at_1, lw_sources, emis_sfc_T, clnclrsky_fluxes);
1000  }
1001 
1002  // Combine gas and aerosol optics
1003  aerosol.increment(optics);
1004 
1005  // Compute clear-sky fluxes before we add in clouds
1006  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics, top_at_1, lw_sources, emis_sfc_T, clrsky_fluxes);
1007 
1008  // Combine gas and cloud optics
1009  clouds.increment(optics);
1010 
1011  // Compute allsky fluxes
1012  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics, top_at_1, lw_sources, emis_sfc_T, fluxes);
1013 
1014  if (extra_clnsky_diag) {
1015  // First increment clouds in optics_no_aerosols
1016  clouds.increment(optics_no_aerosols);
1017  // Compute clean-sky fluxes
1018  rte_lw(max_gauss_pts, gauss_Ds, gauss_wts, optics_no_aerosols, top_at_1, lw_sources, emis_sfc_T, clnsky_fluxes);
1019  }
1020 }

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,
real1d &  t_sfc,
real2d &  emis_sfc,
real1d &  lw_src,
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 
)
413 {
414  // Setup pointers to RRTMGP SW fluxes
415  FluxesByband fluxes_sw;
416  fluxes_sw.flux_up = sw_flux_up;
417  fluxes_sw.flux_dn = sw_flux_dn;
418  fluxes_sw.flux_dn_dir = sw_flux_dn_dir;
419  fluxes_sw.bnd_flux_up = sw_bnd_flux_up;
420  fluxes_sw.bnd_flux_dn = sw_bnd_flux_dn;
421  fluxes_sw.bnd_flux_dn_dir = sw_bnd_flux_dn_dir;
422  // Clean-clear-sky
423  FluxesBroadband clnclrsky_fluxes_sw;
424  clnclrsky_fluxes_sw.flux_up = sw_clnclrsky_flux_up;
425  clnclrsky_fluxes_sw.flux_dn = sw_clnclrsky_flux_dn;
426  clnclrsky_fluxes_sw.flux_dn_dir = sw_clnclrsky_flux_dn_dir;
427  // Clear-sky
428  FluxesBroadband clrsky_fluxes_sw;
429  clrsky_fluxes_sw.flux_up = sw_clrsky_flux_up;
430  clrsky_fluxes_sw.flux_dn = sw_clrsky_flux_dn;
431  clrsky_fluxes_sw.flux_dn_dir = sw_clrsky_flux_dn_dir;
432  // Clean-sky
433  FluxesBroadband clnsky_fluxes_sw;
434  clnsky_fluxes_sw.flux_up = sw_clnsky_flux_up;
435  clnsky_fluxes_sw.flux_dn = sw_clnsky_flux_dn;
436  clnsky_fluxes_sw.flux_dn_dir = sw_clnsky_flux_dn_dir;
437 
438  // Setup pointers to RRTMGP LW fluxes
439  FluxesByband fluxes_lw;
440  fluxes_lw.flux_up = lw_flux_up;
441  fluxes_lw.flux_dn = lw_flux_dn;
442  fluxes_lw.bnd_flux_up = lw_bnd_flux_up;
443  fluxes_lw.bnd_flux_dn = lw_bnd_flux_dn;
444  // Clean-clear-sky
445  FluxesBroadband clnclrsky_fluxes_lw;
446  clnclrsky_fluxes_lw.flux_up = lw_clnclrsky_flux_up;
447  clnclrsky_fluxes_lw.flux_dn = lw_clnclrsky_flux_dn;
448  // Clear-sky
449  FluxesBroadband clrsky_fluxes_lw;
450  clrsky_fluxes_lw.flux_up = lw_clrsky_flux_up;
451  clrsky_fluxes_lw.flux_dn = lw_clrsky_flux_dn;
452  // Clean-sky
453  FluxesBroadband clnsky_fluxes_lw;
454  clnsky_fluxes_lw.flux_up = lw_clnsky_flux_up;
455  clnsky_fluxes_lw.flux_dn = lw_clnsky_flux_dn;
456 
457  auto nswbands = k_dist_sw.get_nband();
458  auto nlwbands = k_dist_lw.get_nband();
459 
460  // Setup aerosol optical properties
461  OpticalProps2str aerosol_sw;
462  OpticalProps1scl aerosol_lw;
463  aerosol_sw.init(k_dist_sw.get_band_lims_wavenumber());
464  aerosol_sw.alloc_2str(ncol, nlay);
465  parallel_for(SimpleBounds<3>(nswbands,nlay,ncol) , YAKL_LAMBDA (int ibnd, int ilay, int icol)
466  {
467  aerosol_sw.tau(icol,ilay,ibnd) = aer_tau_sw(icol,ilay,ibnd);
468  aerosol_sw.ssa(icol,ilay,ibnd) = aer_ssa_sw(icol,ilay,ibnd);
469  aerosol_sw.g (icol,ilay,ibnd) = aer_asm_sw(icol,ilay,ibnd);
470  });
471  aerosol_lw.init(k_dist_lw.get_band_lims_wavenumber());
472  aerosol_lw.alloc_1scl(ncol, nlay);
473  parallel_for(SimpleBounds<3>(nlwbands,nlay,ncol) , YAKL_LAMBDA (int ibnd, int ilay, int icol)
474  {
475  aerosol_lw.tau(icol,ilay,ibnd) = aer_tau_lw(icol,ilay,ibnd);
476  });
477 
478  // Convert cloud physical properties to optical properties for input to RRTMGP
479  OpticalProps2str clouds_sw = get_cloud_optics_sw(ncol, nlay, cloud_optics_sw, k_dist_sw, lwp, iwp, rel, rei);
480  OpticalProps1scl clouds_lw = get_cloud_optics_lw(ncol, nlay, cloud_optics_lw, k_dist_lw, lwp, iwp, rel, rei);
481  clouds_sw.tau.deep_copy_to(cld_tau_sw_bnd);
482  clouds_lw.tau.deep_copy_to(cld_tau_lw_bnd);
483 
484  // Do subcolumn sampling to map bands -> gpoints based on cloud fraction and overlap assumption;
485  // This implements the Monte Carlo Independing Column Approximation by mapping only a single
486  // subcolumn (cloud state) to each gpoint.
487  auto nswgpts = k_dist_sw.get_ngpt();
488  auto clouds_sw_gpt = get_subsampled_clouds(ncol, nlay, nswbands, nswgpts, clouds_sw, k_dist_sw, cldfrac, p_lay);
489  // Longwave
490  auto nlwgpts = k_dist_lw.get_ngpt();
491  auto clouds_lw_gpt = get_subsampled_clouds(ncol, nlay, nlwbands, nlwgpts, clouds_lw, k_dist_lw, cldfrac, p_lay);
492 
493  // Copy cloud properties to outputs (is this needed, or can we just use pointers?)
494  // Alternatively, just compute and output a subcolumn cloud mask
495  parallel_for(SimpleBounds<3>(nswgpts, nlay, ncol), YAKL_LAMBDA (int igpt, int ilay, int icol)
496  {
497  cld_tau_sw_gpt(icol,ilay,igpt) = clouds_sw_gpt.tau(icol,ilay,igpt);
498  });
499  parallel_for(SimpleBounds<3>(nlwgpts, nlay, ncol), YAKL_LAMBDA (int igpt, int ilay, int icol)
500  {
501  cld_tau_lw_gpt(icol,ilay,igpt) = clouds_lw_gpt.tau(icol,ilay,igpt);
502  });
503 
504  // Do shortwave
505  rrtmgp_sw(ncol, nlay,
506  k_dist_sw, 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, p_lay, t_lay, p_lev, t_lev, t_sfc, emis_sfc, lw_src, gas_concs,
514  aerosol_lw, clouds_lw_gpt,
515  fluxes_lw, clnclrsky_fluxes_lw, clrsky_fluxes_lw, clnsky_fluxes_lw,
516  extra_clnclrsky_diag, extra_clnsky_diag);
517 
518 }
void rrtmgp_lw(const int ncol, const int nlay, GasOpticsRRTMGP &k_dist, real2d &p_lay, real2d &t_lay, real2d &p_lev, real2d &t_lev, real1d &t_sfc, real2d &emis_sfc, real1d &lw_src, 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:874
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:595
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
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 
)
615 {
616  // Get problem sizes
617  int nbnd = k_dist.get_nband();
618  int ngpt = k_dist.get_ngpt();
619  int ngas = gas_concs.get_num_gases();
620 
621  // Associate local pointers for fluxes
622  auto& flux_up = fluxes.flux_up;
623  auto& flux_dn = fluxes.flux_dn;
624  auto& flux_dn_dir = fluxes.flux_dn_dir;
625  auto& bnd_flux_up = fluxes.bnd_flux_up;
626  auto& bnd_flux_dn = fluxes.bnd_flux_dn;
627  auto& bnd_flux_dn_dir = fluxes.bnd_flux_dn_dir;
628  auto& clnclrsky_flux_up = clnclrsky_fluxes.flux_up;
629  auto& clnclrsky_flux_dn = clnclrsky_fluxes.flux_dn;
630  auto& clnclrsky_flux_dn_dir = clnclrsky_fluxes.flux_dn_dir;
631  auto& clrsky_flux_up = clrsky_fluxes.flux_up;
632  auto& clrsky_flux_dn = clrsky_fluxes.flux_dn;
633  auto& clrsky_flux_dn_dir = clrsky_fluxes.flux_dn_dir;
634  auto& clnsky_flux_up = clnsky_fluxes.flux_up;
635  auto& clnsky_flux_dn = clnsky_fluxes.flux_dn;
636  auto& clnsky_flux_dn_dir = clnsky_fluxes.flux_dn_dir;
637 
638  // Reset fluxes to zero
639  parallel_for(SimpleBounds<2>(nlay+1,ncol), YAKL_LAMBDA(int ilev, int icol)
640  {
641  flux_up (icol,ilev) = 0;
642  flux_dn (icol,ilev) = 0;
643  flux_dn_dir(icol,ilev) = 0;
644  clnclrsky_flux_up (icol,ilev) = 0;
645  clnclrsky_flux_dn (icol,ilev) = 0;
646  clnclrsky_flux_dn_dir(icol,ilev) = 0;
647  clrsky_flux_up (icol,ilev) = 0;
648  clrsky_flux_dn (icol,ilev) = 0;
649  clrsky_flux_dn_dir(icol,ilev) = 0;
650  clnsky_flux_up (icol,ilev) = 0;
651  clnsky_flux_dn (icol,ilev) = 0;
652  clnsky_flux_dn_dir(icol,ilev) = 0;
653  });
654  parallel_for(SimpleBounds<3>(nbnd,nlay+1,ncol), YAKL_LAMBDA(int ibnd, int ilev, int icol)
655  {
656  bnd_flux_up (icol,ilev,ibnd) = 0;
657  bnd_flux_dn (icol,ilev,ibnd) = 0;
658  bnd_flux_dn_dir(icol,ilev,ibnd) = 0;
659  });
660 
661  // Get daytime indices
662  auto dayIndices = int1d("dayIndices", ncol);
663  memset(dayIndices, -1);
664  // Loop below has to be done on host, so create host copies
665  // TODO: there is probably a way to do this on the device
666  auto dayIndices_h = dayIndices.createHostCopy();
667  auto mu0_h = mu0.createHostCopy();
668  int nday = 0;
669  for (int icol = 1; icol <= ncol; icol++) {
670  if (mu0_h(icol) > 0) {
671  nday++;
672  dayIndices_h(nday) = icol;
673  }
674  }
675 
676  // Copy data back to the device
677  dayIndices_h.deep_copy_to(dayIndices);
678  if (nday == 0) {
679  // No daytime columns in this chunk, skip the rest of this routine
680  return;
681  }
682 
683  // Subset mu0
684  auto mu0_day = real1d("mu0_day", nday);
685  parallel_for(SimpleBounds<1>(nday), YAKL_LAMBDA(int iday)
686  {
687  mu0_day(iday) = mu0(dayIndices(iday));
688  });
689 
690  // subset state variables
691  auto p_lay_day = real2d("p_lay_day", nday, nlay);
692  auto t_lay_day = real2d("t_lay_day", nday, nlay);
693  parallel_for(SimpleBounds<2>(nlay,nday), YAKL_LAMBDA(int ilay, int iday)
694  {
695  p_lay_day(iday,ilay) = p_lay(dayIndices(iday),ilay);
696  t_lay_day(iday,ilay) = t_lay(dayIndices(iday),ilay);
697  });
698  auto p_lev_day = real2d("p_lev_day", nday, nlay+1);
699  auto t_lev_day = real2d("t_lev_day", nday, nlay+1);
700  parallel_for(SimpleBounds<2>(nlay+1,nday), YAKL_LAMBDA(int ilev, int iday)
701  {
702  p_lev_day(iday,ilev) = p_lev(dayIndices(iday),ilev);
703  t_lev_day(iday,ilev) = t_lev(dayIndices(iday),ilev);
704  });
705 
706  // Subset gases
707  auto gas_names = gas_concs.get_gas_names();
708  GasConcs gas_concs_day;
709  gas_concs_day.init(gas_names, nday, nlay);
710  for (int igas = 0; igas < ngas; igas++) {
711  auto vmr_day = real2d("vmr_day", nday, nlay);
712  auto vmr = real2d("vmr" , ncol, nlay);
713  gas_concs.get_vmr(gas_names[igas], vmr);
714  parallel_for(SimpleBounds<2>(nlay,nday), YAKL_LAMBDA(int ilay, int iday)
715  {
716  vmr_day(iday,ilay) = vmr(dayIndices(iday),ilay);
717  });
718  gas_concs_day.set_vmr(gas_names[igas], vmr_day);
719  }
720 
721  // Subset aerosol optics
722  OpticalProps2str aerosol_day;
723  aerosol_day.init(k_dist.get_band_lims_wavenumber());
724  aerosol_day.alloc_2str(nday, nlay);
725  parallel_for(SimpleBounds<3>(nbnd,nlay,nday), YAKL_LAMBDA(int ibnd, int ilay, int iday)
726  {
727  aerosol_day.tau(iday,ilay,ibnd) = aerosol.tau(dayIndices(iday),ilay,ibnd);
728  aerosol_day.ssa(iday,ilay,ibnd) = aerosol.ssa(dayIndices(iday),ilay,ibnd);
729  aerosol_day.g (iday,ilay,ibnd) = aerosol.g (dayIndices(iday),ilay,ibnd);
730  });
731 
732  // Subset cloud optics
733  // TODO: nbnd -> ngpt once we pass sub-sampled cloud state
734  OpticalProps2str clouds_day;
735  clouds_day.init(k_dist.get_band_lims_wavenumber(), k_dist.get_band_lims_gpoint());
736  clouds_day.alloc_2str(nday, nlay);
737  parallel_for(SimpleBounds<3>(ngpt,nlay,nday), YAKL_LAMBDA(int igpt, int ilay, int iday)
738  {
739  clouds_day.tau(iday,ilay,igpt) = clouds.tau(dayIndices(iday),ilay,igpt);
740  clouds_day.ssa(iday,ilay,igpt) = clouds.ssa(dayIndices(iday),ilay,igpt);
741  clouds_day.g (iday,ilay,igpt) = clouds.g (dayIndices(iday),ilay,igpt);
742  });
743 
744  // RRTMGP assumes surface albedos have a screwy dimension ordering
745  // for some strange reason, so we need to transpose these; also do
746  // daytime subsetting in the same kernel
747  real2d sfc_alb_dir_T("sfc_alb_dir", nbnd, nday);
748  real2d sfc_alb_dif_T("sfc_alb_dif", nbnd, nday);
749  parallel_for(SimpleBounds<2>(nbnd,nday), YAKL_LAMBDA(int ibnd, int icol)
750  {
751  sfc_alb_dir_T(ibnd,icol) = sfc_alb_dir(dayIndices(icol),ibnd);
752  sfc_alb_dif_T(ibnd,icol) = sfc_alb_dif(dayIndices(icol),ibnd);
753  });
754 
755  // Temporaries we need for daytime-only fluxes
756  auto flux_up_day = real2d("flux_up_day", nday, nlay+1);
757  auto flux_dn_day = real2d("flux_dn_day", nday, nlay+1);
758  auto flux_dn_dir_day = real2d("flux_dn_dir_day", nday, nlay+1);
759  auto bnd_flux_up_day = real3d("bnd_flux_up_day", nday, nlay+1, nbnd);
760  auto bnd_flux_dn_day = real3d("bnd_flux_dn_day", nday, nlay+1, nbnd);
761  auto bnd_flux_dn_dir_day = real3d("bnd_flux_dn_dir_day", nday, nlay+1, nbnd);
762  FluxesByband fluxes_day;
763  fluxes_day.flux_up = flux_up_day;
764  fluxes_day.flux_dn = flux_dn_day;
765  fluxes_day.flux_dn_dir = flux_dn_dir_day;
766  fluxes_day.bnd_flux_up = bnd_flux_up_day;
767  fluxes_day.bnd_flux_dn = bnd_flux_dn_day;
768  fluxes_day.bnd_flux_dn_dir = bnd_flux_dn_dir_day;
769 
770  // Allocate space for optical properties
771  OpticalProps2str optics;
772  optics.alloc_2str(nday, nlay, k_dist);
773 
774  OpticalProps2str optics_no_aerosols;
775  if (extra_clnsky_diag) {
776  // Allocate space for optical properties (no aerosols)
777  optics_no_aerosols.alloc_2str(nday, nlay, k_dist);
778  }
779 
780  // Limit temperatures for gas optics look-up tables
781  auto t_lay_limited = real2d("t_lay_limited", nday, nlay);
782  limit_to_bounds(t_lay_day, k_dist_sw.get_temp_min(), k_dist_sw.get_temp_max(), t_lay_limited);
783 
784  // Do gas optics
785  real2d toa_flux("toa_flux", nday, ngpt);
786  auto p_lay_host = p_lay.createHostCopy();
787  bool top_at_1 = p_lay_host(1, 1) < p_lay_host(1, nlay);
788 
789  k_dist.gas_optics(nday, nlay, top_at_1, p_lay_day, p_lev_day,
790  t_lay_limited, gas_concs_day, optics, toa_flux);
791  if (extra_clnsky_diag) {
792  k_dist.gas_optics(nday, nlay, top_at_1, p_lay_day, p_lev_day,
793  t_lay_limited, gas_concs_day, optics_no_aerosols, toa_flux);
794  }
795 
796  // Apply tsi_scaling
797  parallel_for(SimpleBounds<2>(ngpt,nday), YAKL_LAMBDA(int igpt, int iday)
798  {
799  toa_flux(iday,igpt) = tsi_scaling * toa_flux(iday,igpt);
800  });
801 
802  if (extra_clnclrsky_diag) {
803  // Compute clear-clean-sky (just gas) fluxes on daytime columns
804  rte_sw(optics, top_at_1, mu0_day, toa_flux, sfc_alb_dir_T, sfc_alb_dif_T, fluxes_day);
805  // Expand daytime fluxes to all columns
806  parallel_for(SimpleBounds<2>(nlay+1,nday), YAKL_LAMBDA(int ilev, int iday)
807  {
808  int icol = dayIndices(iday);
809  clnclrsky_flux_up (icol,ilev) = flux_up_day (iday,ilev);
810  clnclrsky_flux_dn (icol,ilev) = flux_dn_day (iday,ilev);
811  clnclrsky_flux_dn_dir(icol,ilev) = flux_dn_dir_day(iday,ilev);
812  });
813  }
814 
815  // Combine gas and aerosol optics
816  aerosol_day.delta_scale();
817  aerosol_day.increment(optics);
818 
819  // Compute clearsky (gas + aerosol) fluxes on daytime columns
820  rte_sw(optics, top_at_1, mu0_day, toa_flux, sfc_alb_dir_T, sfc_alb_dif_T, fluxes_day);
821 
822  // Expand daytime fluxes to all columns
823  parallel_for(SimpleBounds<2>(nlay+1,nday), YAKL_LAMBDA(int ilev, int iday)
824  {
825  int icol = dayIndices(iday);
826  clrsky_flux_up (icol,ilev) = flux_up_day (iday,ilev);
827  clrsky_flux_dn (icol,ilev) = flux_dn_day (iday,ilev);
828  clrsky_flux_dn_dir(icol,ilev) = flux_dn_dir_day(iday,ilev);
829  });
830 
831  // Now merge in cloud optics and do allsky calculations
832 
833  // Combine gas and cloud optics
834  clouds_day.delta_scale();
835  clouds_day.increment(optics);
836 
837  // Compute fluxes on daytime columns
838  rte_sw(optics, top_at_1, mu0_day, toa_flux, sfc_alb_dir_T, sfc_alb_dif_T, fluxes_day);
839 
840  // Expand daytime fluxes to all columns
841  parallel_for(SimpleBounds<2>(nlay+1,nday), YAKL_LAMBDA(int ilev, int iday)
842  {
843  int icol = dayIndices(iday);
844  flux_up (icol,ilev) = flux_up_day (iday,ilev);
845  flux_dn (icol,ilev) = flux_dn_day (iday,ilev);
846  flux_dn_dir(icol,ilev) = flux_dn_dir_day(iday,ilev);
847  });
848  parallel_for(SimpleBounds<3>(nbnd,nlay+1,nday), YAKL_LAMBDA(int ibnd, int ilev, int iday)
849  {
850  int icol = dayIndices(iday);
851  bnd_flux_up (icol,ilev,ibnd) = bnd_flux_up_day (iday,ilev,ibnd);
852  bnd_flux_dn (icol,ilev,ibnd) = bnd_flux_dn_day (iday,ilev,ibnd);
853  bnd_flux_dn_dir(icol,ilev,ibnd) = bnd_flux_dn_dir_day(iday,ilev,ibnd);
854  });
855 
856  if (extra_clnsky_diag) {
857  // First increment clouds in optics_no_aerosols
858  clouds_day.increment(optics_no_aerosols);
859  // Compute cleansky (gas + clouds) fluxes on daytime columns
860  rte_sw(optics_no_aerosols, top_at_1, mu0_day, toa_flux, sfc_alb_dir_T, sfc_alb_dif_T, fluxes_day);
861  // Expand daytime fluxes to all columns
862  parallel_for(SimpleBounds<2>(nlay+1,nday), YAKL_LAMBDA(int ilev, int iday)
863  {
864  int icol = dayIndices(iday);
865  clnsky_flux_up (icol,ilev) = flux_up_day (iday,ilev);
866  clnsky_flux_dn (icol,ilev) = flux_dn_day (iday,ilev);
867  clnsky_flux_dn_dir(icol,ilev) = flux_dn_dir_day(iday,ilev);
868  });
869  }
870 }

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