! This code is part of RRTM for GCM Applications - Parallel (RRTMGP) ! ! Contacts: Robert Pincus and Eli Mlawer ! email: rrtmgp@aer.com ! ! Copyright 2015-, Atmospheric and Environmental Research, ! Regents of the University of Colorado, Trustees of Columbia University. All right reserved. ! ! Use and duplication is permitted under the terms of the ! BSD 3-clause license, see http://opensource.org/licenses/BSD-3-Clause ! ------------------------------------------------------------------------------------------------- ! !> ## Class implementing the RRTMGP correlated-_k_ distribution !> !> Implements a class for computing spectrally-resolved gas optical properties and source functions !> given atmopsheric physical properties (profiles of temperature, pressure, and gas concentrations) !> The class must be initialized with data (provided as a netCDF file) before being used. !> !> Two variants apply to internal Planck sources (longwave radiation in the Earth's atmosphere) and to !> external stellar radiation (shortwave radiation in the Earth's atmosphere). !> The variant is chosen based on what information is supplied during initialization. ! (It might make more sense to define two sub-classes) ! ! ------------------------------------------------------------------------------------------------- module mo_gas_optics_rrtmgp use mo_rte_kind, only: wp, wl use mo_rte_config, only: check_extents, check_values use mo_rte_util_array, only: zero_array use mo_rte_util_array_validation, & only: any_vals_less_than, any_vals_outside, extents_are use mo_optical_props, only: ty_optical_props use mo_source_functions, only: ty_source_func_lw use mo_gas_optics_rrtmgp_kernels, & only: interpolation, compute_tau_absorption, compute_tau_rayleigh, compute_Planck_source use mo_gas_optics_constants, only: avogad, m_dry, m_h2o, grav use mo_gas_optics_util_string, only: lower_case, string_in_array, string_loc_in_array use mo_gas_concentrations, only: ty_gas_concs use mo_optical_props, only: ty_optical_props_arry, ty_optical_props_1scl, ty_optical_props_2str, ty_optical_props_nstr use mo_gas_optics, only: ty_gas_optics implicit none private real(wp), parameter :: pi = acos(-1._wp) ! ------------------------------------------------------------------------------------------------- type, extends(ty_gas_optics), public :: ty_gas_optics_rrtmgp private ! ! RRTMGP computes absorption in each band arising from ! two major species in each band, which are combined to make ! a relative mixing ratio eta and a total column amount (col_mix) ! contributions from zero or more minor species whose concentrations ! may be scaled by other components of the atmosphere ! ! Absorption coefficients are interpolated from tables on a pressure/temperature/(eta) grid ! ! ------------------------------------ ! Interpolation variables: Temperature and pressure grids ! real(wp), dimension(:), allocatable :: press_ref, press_ref_log, temp_ref ! ! Derived and stored for convenience: ! Min and max for temperature and pressure intepolation grids ! difference in ln pressure between consecutive reference levels ! log of reference pressure separating the lower and upper atmosphere ! real(wp) :: press_ref_min, press_ref_max, & temp_ref_min, temp_ref_max real(wp) :: press_ref_log_delta, temp_ref_delta, press_ref_trop_log ! ------------------------------------ ! Major absorbers ("key species") ! Each unique set of major species is called a flavor. ! ! Names and reference volume mixing ratios of major gases ! character(32), dimension(:), allocatable :: gas_names ! gas names real(wp), dimension(:,:,:), allocatable :: vmr_ref ! vmr_ref(lower or upper atmosphere, gas, temp) ! ! Which two gases are in each flavor? By index ! integer, dimension(:,:), allocatable :: flavor ! major species pair; (2,nflav) ! ! Which flavor for each g-point? One each for lower, upper atmosphere ! integer, dimension(:,:), allocatable :: gpoint_flavor ! flavor = gpoint_flavor(2, g-point) ! ! Major gas absorption coefficients ! real(wp), dimension(:,:,:,:), allocatable :: kmajor ! kmajor(g-point,eta,pressure,temperature) ! ! ------------------------------------ ! Minor species, independently for upper and lower atmospheres ! Array extents in the n_minor dimension will differ between upper and lower atmospheres ! Each contribution has starting and ending g-points ! integer, dimension(:,:), allocatable :: minor_limits_gpt_lower, & minor_limits_gpt_upper ! ! Minor gas contributions might be scaled by other gas amounts; if so we need to know ! the total density and whether the contribution is scaled by the partner gas ! or its complement (i.e. all other gases) ! Water vapor self- and foreign continua work like this, as do ! all collision-induced abosption pairs ! logical(wl), dimension(:), allocatable :: minor_scales_with_density_lower, & minor_scales_with_density_upper logical(wl), dimension(:), allocatable :: scale_by_complement_lower, scale_by_complement_upper integer, dimension(:), allocatable :: idx_minor_lower, idx_minor_upper integer, dimension(:), allocatable :: idx_minor_scaling_lower, idx_minor_scaling_upper ! ! Index into table of absorption coefficients ! integer, dimension(:), allocatable :: kminor_start_lower, kminor_start_upper ! ! The absorption coefficients themselves ! real(wp), dimension(:,:,:), allocatable :: kminor_lower, kminor_upper ! kminor_lower(n_minor,eta,temperature) ! ! ----------------------------------------------------------------------------------- ! ! Rayleigh scattering coefficients ! real(wp), dimension(:,:,:,:), allocatable :: krayl ! krayl(g-point,eta,temperature,upper/lower atmosphere) ! ! ----------------------------------------------------------------------------------- ! Planck function spectral mapping ! Allocated only when gas optics object is internal-source ! real(wp), dimension(:,:,:,:), allocatable :: planck_frac ! stored fraction of Planck irradiance in band for given g-point ! planck_frac(g-point, eta, pressure, temperature) real(wp), dimension(:,:), allocatable :: totplnk ! integrated Planck irradiance by band; (Planck temperatures,band) real(wp) :: totplnk_delta ! temperature steps in totplnk real(wp), dimension(:,:), allocatable :: optimal_angle_fit ! coefficients of linear function ! of vertical path clear-sky transmittance that is used to ! determine the secant of single angle used for the ! no-scattering calculation, ! optimal_angle_fit(coefficient, band) ! ----------------------------------------------------------------------------------- ! Solar source function spectral mapping with solar variability capability ! Allocated when gas optics object is external-source ! n-solar-terms: quiet sun, facular brightening and sunspot dimming components ! following the NRLSSI2 model of Coddington et al. 2016, doi:10.1175/BAMS-D-14-00265.1. ! real(wp), dimension(:), allocatable :: solar_source ! incoming solar irradiance, computed from other three terms (g-point) real(wp), dimension(:), allocatable :: solar_source_quiet ! incoming solar irradiance, quiet sun term (g-point) real(wp), dimension(:), allocatable :: solar_source_facular ! incoming solar irradiance, facular term (g-point) real(wp), dimension(:), allocatable :: solar_source_sunspot ! incoming solar irradiance, sunspot term (g-point) ! ! ----------------------------------------------------------------------------------- ! Ancillary ! ----------------------------------------------------------------------------------- ! Index into %gas_names -- is this a key species in any band? logical, dimension(:), allocatable :: is_key ! ----------------------------------------------------------------------------------- contains ! Type-bound procedures ! Public procedures ! public interface generic, public :: load => load_int, load_ext procedure, public :: source_is_internal procedure, public :: source_is_external procedure, public :: is_loaded procedure, public :: finalize procedure, public :: get_ngas procedure, public :: get_gases procedure, public :: get_press_min procedure, public :: get_press_max procedure, public :: get_temp_min procedure, public :: get_temp_max procedure, public :: compute_optimal_angles procedure, public :: set_solar_variability procedure, public :: set_tsi ! Internal procedures procedure, private :: load_int procedure, private :: load_ext procedure, public :: gas_optics_int procedure, public :: gas_optics_ext procedure, private :: check_key_species_present ! Interpolation table dimensions procedure, private :: get_nflav procedure, private :: get_neta procedure, private :: get_npres procedure, private :: get_ntemp procedure, private :: get_nPlanckTemp end type ty_gas_optics_rrtmgp ! ------------------------------------------------------------------------------------------------- ! !> col_dry is the number of molecules per cm-2 of dry air ! public :: get_col_dry ! Utility function, not type-bound contains ! -------------------------------------------------------------------------------------- ! ! Public procedures ! ! -------------------------------------------------------------------------------------- ! !> Two functions to define array sizes needed by gas_optics() ! pure function get_ngas(this) ! return the number of gases registered in the spectral configuration class(ty_gas_optics_rrtmgp), intent(in) :: this integer :: get_ngas get_ngas = size(this%gas_names) end function get_ngas !-------------------------------------------------------------------------------------------------------------------- ! !> return the number of distinct major gas pairs in the spectral bands (referred to as !> "flavors" - all bands have a flavor even if there is one or no major gas) ! pure function get_nflav(this) class(ty_gas_optics_rrtmgp), intent(in) :: this integer :: get_nflav get_nflav = size(this%flavor,dim=2) end function get_nflav !-------------------------------------------------------------------------------------------------------------------- ! !> Compute gas optical depth and Planck source functions, !> given temperature, pressure, and composition ! function gas_optics_int(this, & play, plev, tlay, tsfc, gas_desc, & optical_props, sources, & col_dry, tlev) result(error_msg) ! inputs class(ty_gas_optics_rrtmgp), intent(in) :: this real(wp), dimension(:,:), intent(in ) :: play, & !! layer pressures [Pa, mb]; (ncol,nlay) plev, & !! level pressures [Pa, mb]; (ncol,nlay+1) tlay !! layer temperatures [K]; (ncol,nlay) real(wp), dimension(:), intent(in ) :: tsfc !! surface skin temperatures [K]; (ncol) type(ty_gas_concs), intent(in ) :: gas_desc !! Gas volume mixing ratios ! output class(ty_optical_props_arry), & intent(inout) :: optical_props !! Optical properties class(ty_source_func_lw ), & intent(inout) :: sources !! Planck sources character(len=128) :: error_msg !! Empty if succssful ! Optional inputs real(wp), dimension(:,:), intent(in ), & optional, target :: col_dry, & !! Column dry amount; dim(ncol,nlay) tlev !! level temperatures [K]; (ncol,nlay+1) ! ---------------------------------------------------------- ! Local variables ! Interpolation coefficients for use in source function integer, dimension(size(play,dim=1), size(play,dim=2)) :: jtemp, jpress logical(wl), dimension(size(play,dim=1), size(play,dim=2)) :: tropo real(wp), dimension(2,2,2,size(play,dim=1),size(play,dim=2), get_nflav(this)) :: fmajor integer, dimension(2, size(play,dim=1),size(play,dim=2), get_nflav(this)) :: jeta integer :: ncol, nlay, ngpt, nband ! ---------------------------------------------------------- ncol = size(play,dim=1) nlay = size(play,dim=2) ngpt = this%get_ngpt() nband = this%get_nband() ! ! Gas optics ! !$acc enter data create(jtemp, jpress, tropo, fmajor, jeta) !$omp target enter data map(alloc:jtemp, jpress, tropo, fmajor, jeta) error_msg = compute_gas_taus(this, & ncol, nlay, ngpt, nband, & play, plev, tlay, gas_desc, & optical_props, & jtemp, jpress, jeta, tropo, fmajor, & col_dry) if(error_msg /= '') return ! ---------------------------------------------------------- ! ! External source -- check arrays sizes and values ! input data sizes and values ! !$acc enter data copyin(tsfc, tlev) ! Should be fine even if tlev is not supplied !$omp target enter data map(to:tsfc, tlev) if(check_extents) then if(.not. extents_are(tsfc, ncol)) & error_msg = "gas_optics(): array tsfc has wrong size" if(present(tlev)) then if(.not. extents_are(tlev, ncol, nlay+1)) & error_msg = "gas_optics(): array tlev has wrong size" end if ! ! output extents ! if(any([sources%get_ncol(), sources%get_nlay(), sources%get_ngpt()] /= [ncol, nlay, ngpt])) & error_msg = "gas_optics%gas_optics: source function arrays inconsistently sized" end if if(error_msg /= '') return if(check_values) then if(any_vals_outside(tsfc, this%temp_ref_min, this%temp_ref_max)) & error_msg = "gas_optics(): array tsfc has values outside range" if(present(tlev)) then if(any_vals_outside(tlev, this%temp_ref_min, this%temp_ref_max)) & error_msg = "gas_optics(): array tlev has values outside range" end if end if if(error_msg /= '') return ! ! Interpolate source function ! present status of optional argument should be passed to source() ! but nvfortran (and PGI Fortran before it) do not do so ! if(present(tlev)) then error_msg = source(this, & ncol, nlay, nband, ngpt, & play, plev, tlay, tsfc, & jtemp, jpress, jeta, tropo, fmajor, & sources, & tlev) !$acc exit data delete(tlev) !$omp target exit data map(release:tlev) else error_msg = source(this, & ncol, nlay, nband, ngpt, & play, plev, tlay, tsfc, & jtemp, jpress, jeta, tropo, fmajor, & sources) end if !$acc exit data delete(tsfc) !$omp target exit data map(release:tsfc) !$acc exit data delete(jtemp, jpress, tropo, fmajor, jeta) !$omp target exit data map(release:jtemp, jpress, tropo, fmajor, jeta) end function gas_optics_int !------------------------------------------------------------------------------------------ ! !> Compute gas optical depth given temperature, pressure, and composition !> Top-of-atmosphere stellar insolation is also reported ! function gas_optics_ext(this, & play, plev, tlay, gas_desc, & ! mandatory inputs optical_props, toa_src, & ! mandatory outputs col_dry) result(error_msg) ! optional input class(ty_gas_optics_rrtmgp), intent(in) :: this real(wp), dimension(:,:), intent(in ) :: play, & !! layer pressures [Pa, mb]; (ncol,nlay) plev, & !! level pressures [Pa, mb]; (ncol,nlay+1) tlay !! layer temperatures [K]; (ncol,nlay) type(ty_gas_concs), intent(in ) :: gas_desc !! Gas volume mixing ratios ! output class(ty_optical_props_arry), & intent(inout) :: optical_props real(wp), dimension(:,:), intent( out) :: toa_src !! Incoming solar irradiance(ncol,ngpt) character(len=128) :: error_msg !! Empty if successful ! Optional inputs real(wp), dimension(:,:), intent(in ), & optional, target :: col_dry ! Column dry amount; dim(ncol,nlay) ! ---------------------------------------------------------- ! Local variables ! Interpolation coefficients for use in source function integer, dimension(size(play,dim=1), size(play,dim=2)) :: jtemp, jpress logical(wl), dimension(size(play,dim=1), size(play,dim=2)) :: tropo real(wp), dimension(2,2,2,size(play,dim=1),size(play,dim=2), get_nflav(this)) :: fmajor integer, dimension(2, size(play,dim=1),size(play,dim=2), get_nflav(this)) :: jeta integer :: ncol, nlay, ngpt, nband, ngas, nflav integer :: igpt, icol ! ---------------------------------------------------------- ncol = size(play,dim=1) nlay = size(play,dim=2) ngpt = this%get_ngpt() nband = this%get_nband() ngas = this%get_ngas() nflav = get_nflav(this) ! ! Gas optics ! !$acc enter data create(jtemp, jpress, tropo, fmajor, jeta) !$omp target enter data map(alloc:jtemp, jpress, tropo, fmajor, jeta) error_msg = compute_gas_taus(this, & ncol, nlay, ngpt, nband, & play, plev, tlay, gas_desc, & optical_props, & jtemp, jpress, jeta, tropo, fmajor, & col_dry) !$acc exit data delete(jtemp, jpress, tropo, fmajor, jeta) !$omp target exit data map(release:jtemp, jpress, tropo, fmajor, jeta) if(error_msg /= '') return ! ---------------------------------------------------------- ! ! External source function is constant ! !$acc enter data create(toa_src) !$omp target enter data map(alloc:toa_src) if(check_extents) then if(.not. extents_are(toa_src, ncol, ngpt)) & error_msg = "gas_optics(): array toa_src has wrong size" end if if(error_msg /= '') return !$acc parallel loop collapse(2) !$omp target teams distribute parallel do simd collapse(2) do igpt = 1,ngpt do icol = 1,ncol toa_src(icol,igpt) = this%solar_source(igpt) end do end do !$acc exit data copyout(toa_src) !$omp target exit data map(from:toa_src) end function gas_optics_ext !------------------------------------------------------------------------------------------ ! ! Returns optical properties and interpolation coefficients ! function compute_gas_taus(this, & ncol, nlay, ngpt, nband, & play, plev, tlay, gas_desc, & optical_props, & jtemp, jpress, jeta, tropo, fmajor, & col_dry) result(error_msg) class(ty_gas_optics_rrtmgp), & intent(in ) :: this integer, intent(in ) :: ncol, nlay, ngpt, nband real(wp), dimension(:,:), intent(in ) :: play, & ! layer pressures [Pa, mb]; (ncol,nlay) plev, & ! level pressures [Pa, mb]; (ncol,nlay+1) tlay ! layer temperatures [K]; (ncol,nlay) type(ty_gas_concs), intent(in ) :: gas_desc ! Gas volume mixing ratios class(ty_optical_props_arry), intent(inout) :: optical_props !inout because components are allocated ! Interpolation coefficients for use in internal source function integer, dimension( ncol, nlay), intent( out) :: jtemp, jpress integer, dimension(2, ncol, nlay,get_nflav(this)), intent( out) :: jeta logical(wl), dimension( ncol, nlay), intent( out) :: tropo real(wp), dimension(2,2,2,ncol, nlay,get_nflav(this)), intent( out) :: fmajor character(len=128) :: error_msg ! Optional inputs real(wp), dimension(:,:), intent(in ), & optional, target :: col_dry ! Column dry amount; dim(ncol,nlay) ! ---------------------------------------------------------- ! Local variables real(wp), dimension(ncol,nlay,ngpt) :: tau, tau_rayleigh ! absorption, Rayleigh scattering optical depths ! Number of molecules per cm^2 real(wp), dimension(ncol,nlay), target :: col_dry_arr real(wp), dimension(:,:), pointer :: col_dry_wk ! ! Interpolation variables used in major gas but not elsewhere, so don't need exporting ! real(wp), dimension(ncol,nlay, this%get_ngas()) :: vmr ! volume mixing ratios real(wp), dimension(ncol,nlay,0:this%get_ngas()) :: col_gas ! column amounts for each gas, plus col_dry real(wp), dimension(2, ncol,nlay,get_nflav(this)) :: col_mix ! combination of major species's column amounts ! index(1) : reference temperature level ! index(2) : flavor ! index(3) : layer real(wp), dimension(2,2, ncol,nlay,get_nflav(this)) :: fminor ! interpolation fractions for minor species ! index(1) : reference eta level (temperature dependent) ! index(2) : reference temperature level ! index(3) : flavor ! index(4) : layer integer :: ngas, nflav, neta, npres, ntemp integer :: icol, ilay, igas integer :: idx_h2o ! index of water vapor integer :: nminorlower, nminorklower,nminorupper, nminorkupper logical :: use_rayl ! ---------------------------------------------------------- ! ! Error checking ! use_rayl = allocated(this%krayl) error_msg = '' ! Check for initialization if (.not. this%is_loaded()) then error_msg = 'ERROR: spectral configuration not loaded' return end if ! ! Check for presence of key species in ty_gas_concs; return error if any key species are not present ! error_msg = this%check_key_species_present(gas_desc) if (error_msg /= '') return ! ! Check input data sizes and values ! !$acc data copyin(play,plev,tlay) create( vmr,col_gas) !$omp target data map(to:play,plev,tlay) map(alloc:vmr,col_gas) if(check_extents) then if(.not. extents_are(play, ncol, nlay )) & error_msg = "gas_optics(): array play has wrong size" if(.not. extents_are(tlay, ncol, nlay )) & error_msg = "gas_optics(): array tlay has wrong size" if(.not. extents_are(plev, ncol, nlay+1)) & error_msg = "gas_optics(): array plev has wrong size" if(optical_props%get_ncol() /= ncol .or. & optical_props%get_nlay() /= nlay .or. & optical_props%get_ngpt() /= ngpt) & error_msg = "gas_optics(): optical properties have the wrong extents" if(present(col_dry)) then if(.not. extents_are(col_dry, ncol, nlay)) & error_msg = "gas_optics(): array col_dry has wrong size" end if end if if(error_msg == '') then if(check_values) then if(any_vals_outside(play, this%press_ref_min,this%press_ref_max)) & error_msg = "gas_optics(): array play has values outside range" if(any_vals_less_than(plev, 0._wp)) & error_msg = "gas_optics(): array plev has values outside range" if(any_vals_outside(tlay, this%temp_ref_min, this%temp_ref_max)) & error_msg = "gas_optics(): array tlay has values outside range" if(present(col_dry)) then if(any_vals_less_than(col_dry, 0._wp)) & error_msg = "gas_optics(): array col_dry has values outside range" end if end if end if ! ---------------------------------------------------------- if(error_msg == '') then ngas = this%get_ngas() nflav = get_nflav(this) neta = this%get_neta() npres = this%get_npres() ntemp = this%get_ntemp() ! number of minor contributors, total num absorption coeffs nminorlower = size(this%minor_scales_with_density_lower) nminorklower = size(this%kminor_lower, 3) nminorupper = size(this%minor_scales_with_density_upper) nminorkupper = size(this%kminor_upper, 3) ! ! Fill out the array of volume mixing ratios ! do igas = 1, ngas ! ! Get vmr if gas is provided in ty_gas_concs ! if (any (lower_case(this%gas_names(igas)) == gas_desc%get_gas_names())) then error_msg = gas_desc%get_vmr(this%gas_names(igas), vmr(:,:,igas)) endif end do end if if(error_msg == '') then ! ! Painful hacks to get code to compile with both the CCE-14 and Nvidia 21.3 compiler ! #ifdef _CRAYFTN !$acc enter data copyin(optical_props) #endif select type(optical_props) type is (ty_optical_props_1scl) #ifndef _CRAYFTN !$acc enter data copyin(optical_props) #endif !$acc enter data create( optical_props%tau) !$omp target enter data map(alloc:optical_props%tau) type is (ty_optical_props_2str) #ifndef _CRAYFTN !$acc enter data copyin(optical_props) #endif !$acc enter data create( optical_props%tau, optical_props%ssa, optical_props%g) !$omp target enter data map(alloc:optical_props%tau, optical_props%ssa, optical_props%g) type is (ty_optical_props_nstr) #ifndef _CRAYFTN !$acc enter data copyin(optical_props) #endif !$acc enter data create( optical_props%tau, optical_props%ssa, optical_props%p) !$omp target enter data map(alloc:optical_props%tau, optical_props%ssa, optical_props%p) end select ! ! Compute dry air column amounts (number of molecule per cm^2) if user hasn't provided them ! idx_h2o = string_loc_in_array('h2o', this%gas_names) if (present(col_dry)) then !$acc enter data copyin(col_dry) !$omp target enter data map(to:col_dry) col_dry_wk => col_dry else !$acc enter data create( col_dry_arr) !$omp target enter data map(alloc:col_dry_arr) col_dry_arr = get_col_dry(vmr(:,:,idx_h2o), plev) ! dry air column amounts computation col_dry_wk => col_dry_arr end if ! ! compute column gas amounts [molec/cm^2] ! !$acc parallel loop gang vector collapse(2) !$omp target teams distribute parallel do simd collapse(2) do ilay = 1, nlay do icol = 1, ncol col_gas(icol,ilay,0) = col_dry_wk(icol,ilay) end do end do !$acc parallel loop gang vector collapse(3) !$omp target teams distribute parallel do simd collapse(3) do igas = 1, ngas do ilay = 1, nlay do icol = 1, ncol col_gas(icol,ilay,igas) = vmr(icol,ilay,igas) * col_dry_wk(icol,ilay) end do end do end do ! ! ---- calculate gas optical depths ---- ! !$acc data copyout( jtemp, jpress, jeta, tropo, fmajor) create( col_mix, fminor) !$omp target data map(from:jtemp, jpress, jeta, tropo, fmajor) map(alloc:col_mix, fminor) call interpolation( & ncol,nlay, & ! problem dimensions ngas, nflav, neta, npres, ntemp, & ! interpolation dimensions this%flavor, & this%press_ref_log, & this%temp_ref, & this%press_ref_log_delta, & this%temp_ref_min, & this%temp_ref_delta, & this%press_ref_trop_log, & this%vmr_ref, & play, & tlay, & col_gas, & jtemp, & ! outputs fmajor,fminor,& col_mix, & tropo, & jeta,jpress) if (allocated(this%krayl)) then !$acc data copyin(this%gpoint_flavor, this%krayl) create(tau, tau_rayleigh) !$omp target data map(to:this%gpoint_flavor, this%krayl) map(alloc:tau, tau_rayleigh) call zero_array(ncol, nlay, ngpt, tau) call compute_tau_absorption( & ncol,nlay,nband,ngpt, & ! dimensions ngas,nflav,neta,npres,ntemp, & nminorlower, nminorklower, & ! number of minor contributors, total num absorption coeffs nminorupper, nminorkupper, & idx_h2o, & this%gpoint_flavor, & this%get_band_lims_gpoint(), & this%kmajor, & this%kminor_lower, & this%kminor_upper, & this%minor_limits_gpt_lower, & this%minor_limits_gpt_upper, & this%minor_scales_with_density_lower, & this%minor_scales_with_density_upper, & this%scale_by_complement_lower, & this%scale_by_complement_upper, & this%idx_minor_lower, & this%idx_minor_upper, & this%idx_minor_scaling_lower, & this%idx_minor_scaling_upper, & this%kminor_start_lower, & this%kminor_start_upper, & tropo, & col_mix,fmajor,fminor, & play,tlay,col_gas, & jeta,jtemp,jpress, & tau) call compute_tau_rayleigh( & !Rayleigh scattering optical depths ncol,nlay,nband,ngpt, & ngas,nflav,neta,npres,ntemp, & ! dimensions this%gpoint_flavor, & this%get_band_lims_gpoint(), & this%krayl, & ! inputs from object idx_h2o, col_dry_wk,col_gas, & fminor,jeta,tropo,jtemp, & ! local input tau_rayleigh) call combine_abs_and_rayleigh(tau, tau_rayleigh, optical_props) !$acc end data !$omp end target data else call zero_array(ncol, nlay, ngpt, optical_props%tau) call compute_tau_absorption( & ncol,nlay,nband,ngpt, & ! dimensions ngas,nflav,neta,npres,ntemp, & nminorlower, nminorklower, & ! number of minor contributors, total num absorption coeffs nminorupper, nminorkupper, & idx_h2o, & this%gpoint_flavor, & this%get_band_lims_gpoint(), & this%kmajor, & this%kminor_lower, & this%kminor_upper, & this%minor_limits_gpt_lower, & this%minor_limits_gpt_upper, & this%minor_scales_with_density_lower, & this%minor_scales_with_density_upper, & this%scale_by_complement_lower, & this%scale_by_complement_upper, & this%idx_minor_lower, & this%idx_minor_upper, & this%idx_minor_scaling_lower, & this%idx_minor_scaling_upper, & this%kminor_start_lower, & this%kminor_start_upper, & tropo, & col_mix,fmajor,fminor, & play,tlay,col_gas, & jeta,jtemp,jpress, & optical_props%tau) ! select type(optical_props) type is (ty_optical_props_2str) call zero_array(ncol, nlay, ngpt, optical_props%ssa) call zero_array(ncol, nlay, ngpt, optical_props%g) type is (ty_optical_props_nstr) call zero_array(ncol, nlay, ngpt, optical_props%ssa) call zero_array(optical_props%get_nmom(), & ncol, nlay, ngpt, optical_props%p) end select end if !$acc end data !$omp end target data if (present(col_dry)) then !$acc exit data delete( col_dry) !$omp target exit data map(release:col_dry) else !$acc exit data delete( col_dry_arr) !$omp target exit data map(release:col_dry_arr) end if select type(optical_props) type is (ty_optical_props_1scl) !$acc exit data copyout( optical_props%tau) !$omp target exit data map(from:optical_props%tau) type is (ty_optical_props_2str) !$acc exit data copyout( optical_props%tau, optical_props%ssa, optical_props%g) !$omp target exit data map(from:optical_props%tau, optical_props%ssa, optical_props%g) type is (ty_optical_props_nstr) !$acc exit data copyout( optical_props%tau, optical_props%ssa, optical_props%p) !$omp target exit data map(from:optical_props%tau, optical_props%ssa, optical_props%p) end select !$acc exit data delete(optical_props) end if !$acc end data !$omp end target data end function compute_gas_taus !------------------------------------------------------------------------------------------ ! !> Compute the spectral solar source function adjusted to account for solar variability !> following the NRLSSI2 model of Coddington et al. 2016, doi:10.1175/BAMS-D-14-00265.1. !> as specified by the facular brightening (mg_index) and sunspot dimming (sb_index) !> indices provided as input. !> !> Users provide the NRLSSI2 facular ("Bremen") index and sunspot ("SPOT67") index. !> Changing either of these indicies will change the total solar irradiance (TSI) !> Code in extensions/mo_solar_variability may be used to compute the value of these !> indices through an average solar cycle !> Users may also specify the TSI, either alone or in conjunction with the facular and sunspot indices ! !------------------------------------------------------------------------------------------ function set_solar_variability(this, & mg_index, sb_index, tsi) & result(error_msg) ! !! Updates the spectral distribution and, optionally, !! the integrated value of the solar source function !! Modifying either index will change the total solar irradiance ! class(ty_gas_optics_rrtmgp), intent(inout) :: this ! real(wp), intent(in) :: mg_index !! facular brightening index (NRLSSI2 facular "Bremen" index) real(wp), intent(in) :: sb_index !! sunspot dimming index (NRLSSI2 sunspot "SPOT67" index) real(wp), optional, intent(in) :: tsi !! total solar irradiance character(len=128) :: error_msg !! Empty if successful ! ---------------------------------------------------------- integer :: igpt real(wp), parameter :: a_offset = 0.1495954_wp real(wp), parameter :: b_offset = 0.00066696_wp ! ---------------------------------------------------------- error_msg = "" if(mg_index < 0._wp) error_msg = 'mg_index out of range' if(sb_index < 0._wp) error_msg = 'sb_index out of range' if(error_msg /= "") return ! ! Calculate solar source function for provided facular and sunspot indices ! !$acc parallel loop !$omp target teams distribute parallel do simd do igpt = 1, size(this%solar_source_quiet) this%solar_source(igpt) = this%solar_source_quiet(igpt) + & (mg_index - a_offset) * this%solar_source_facular(igpt) + & (sb_index - b_offset) * this%solar_source_sunspot(igpt) end do ! ! Scale solar source to input TSI value ! if (present(tsi)) error_msg = this%set_tsi(tsi) end function set_solar_variability !------------------------------------------------------------------------------------------ function set_tsi(this, tsi) result(error_msg) ! !> Scale the solar source function without changing the spectral distribution ! class(ty_gas_optics_rrtmgp), intent(inout) :: this real(wp), intent(in ) :: tsi !! user-specified total solar irradiance; character(len=128) :: error_msg !! Empty if successful real(wp) :: norm integer :: igpt, length ! ---------------------------------------------------------- error_msg = "" if(tsi < 0._wp) then error_msg = 'tsi out of range' else ! ! Scale the solar source function to the input tsi ! norm = 0._wp length = size(this%solar_source) !$acc parallel loop gang vector reduction(+:norm) !$omp target teams distribute parallel do simd reduction(+:norm) do igpt = 1, length norm = norm + this%solar_source(igpt) end do norm = 1._wp/norm !$acc parallel loop gang vector !$omp target teams distribute parallel do simd do igpt = 1, length this%solar_source(igpt) = this%solar_source(igpt) * tsi * norm end do end if end function set_tsi !------------------------------------------------------------------------------------------ ! ! Compute Planck source functions at layer centers and levels ! function source(this, & ncol, nlay, nbnd, ngpt, & play, plev, tlay, tsfc, & jtemp, jpress, jeta, tropo, fmajor, & sources, & ! Planck sources tlev) & ! optional input result(error_msg) ! inputs class(ty_gas_optics_rrtmgp), intent(in ) :: this integer, intent(in ) :: ncol, nlay, nbnd, ngpt real(wp), dimension(ncol,nlay), intent(in ) :: play ! layer pressures [Pa, mb] real(wp), dimension(ncol,nlay+1), intent(in ) :: plev ! level pressures [Pa, mb] real(wp), dimension(ncol,nlay), intent(in ) :: tlay ! layer temperatures [K] real(wp), dimension(ncol), intent(in ) :: tsfc ! surface skin temperatures [K] ! Interplation coefficients integer, dimension(ncol,nlay), intent(in ) :: jtemp, jpress logical(wl), dimension(ncol,nlay), intent(in ) :: tropo real(wp), dimension(2,2,2,ncol,nlay,get_nflav(this)), & intent(in ) :: fmajor integer, dimension(2, ncol,nlay,get_nflav(this)), & intent(in ) :: jeta class(ty_source_func_lw ), intent(inout) :: sources real(wp), dimension(ncol,nlay+1), intent(in ), & optional, target :: tlev ! level temperatures [K] character(len=128) :: error_msg ! ---------------------------------------------------------- logical(wl) :: top_at_1 integer :: icol, ilay ! Variables for temperature at layer edges [K] (ncol, nlay+1) real(wp), dimension( ncol,nlay+1), target :: tlev_arr real(wp), dimension(:,:), pointer :: tlev_wk ! ---------------------------------------------------------- error_msg = "" ! ! Source function needs temperature at interfaces/levels and at layer centers ! Allocate small local array for tlev unconditionally ! !$acc data copyin(sources) copyout( sources%lay_source, sources%lev_source) & !$acc copyout( sources%sfc_source, sources%sfc_source_Jac) & !$acc create(tlev_arr) !$omp target data map(from:sources%lay_source, sources%lev_source) & !$omp map(from:sources%sfc_source, sources%sfc_source_Jac) & !$omp map(alloc:tlev_arr) if (present(tlev)) then ! Users might have provided these tlev_wk => tlev else tlev_wk => tlev_arr ! ! Interpolate temperature to levels if not provided ! Interpolation and extrapolation at boundaries is weighted by pressure ! !$acc parallel loop gang vector !$omp target teams distribute parallel do simd do icol = 1, ncol tlev_arr(icol,1) = tlay(icol,1) & + (plev(icol,1)-play(icol,1))*(tlay(icol,2)-tlay(icol,1)) & / (play(icol,2)-play(icol,1)) tlev_arr(icol,nlay+1) = tlay(icol,nlay) & + (plev(icol,nlay+1)-play(icol,nlay))*(tlay(icol,nlay)-tlay(icol,nlay-1)) & / (play(icol,nlay)-play(icol,nlay-1)) end do !$acc parallel loop gang vector collapse(2) !$omp target teams distribute parallel do simd collapse(2) do ilay = 2, nlay do icol = 1, ncol tlev_arr(icol,ilay) = (play(icol,ilay-1)*tlay(icol,ilay-1)*(plev(icol,ilay )-play(icol,ilay)) & + play(icol,ilay )*tlay(icol,ilay )*(play(icol,ilay-1)-plev(icol,ilay))) / & (plev(icol,ilay)*(play(icol,ilay-1) - play(icol,ilay))) end do end do end if !------------------------------------------------------------------- ! Compute internal (Planck) source functions at layers and levels, ! which depend on mapping from spectral space that creates k-distribution. !$acc kernels copyout(top_at_1) !$omp target map(from:top_at_1) top_at_1 = play(1,1) < play(1, nlay) !$acc end kernels !$omp end target call compute_Planck_source(ncol, nlay, nbnd, ngpt, & get_nflav(this), this%get_neta(), this%get_npres(), this%get_ntemp(), this%get_nPlanckTemp(), & tlay, tlev_wk, tsfc, merge(nlay, 1, top_at_1), & fmajor, jeta, tropo, jtemp, jpress, & this%get_gpoint_bands(), this%get_band_lims_gpoint(), this%planck_frac, this%temp_ref_min,& this%totplnk_delta, this%totplnk, this%gpoint_flavor, & sources%sfc_source, sources%lay_source, sources%lev_source, & sources%sfc_source_Jac) !$acc end data !$omp end target data end function source !-------------------------------------------------------------------------------------------------------------------- ! ! Initialization ! !-------------------------------------------------------------------------------------------------------------------- ! Initialize object based on data read from netCDF file however the user desires. ! Rayleigh scattering tables may or may not be present; this is indicated with allocation status ! This interface is for the internal-sources object -- includes Plank functions and fractions ! function load_int(this, available_gases, gas_names, key_species, & band2gpt, band_lims_wavenum, & press_ref, press_ref_trop, temp_ref, & temp_ref_p, temp_ref_t, vmr_ref, & kmajor, kminor_lower, kminor_upper, & gas_minor,identifier_minor, & minor_gases_lower, minor_gases_upper, & minor_limits_gpt_lower, minor_limits_gpt_upper, & minor_scales_with_density_lower, & minor_scales_with_density_upper, & scaling_gas_lower, scaling_gas_upper, & scale_by_complement_lower, & scale_by_complement_upper, & kminor_start_lower, & kminor_start_upper, & totplnk, planck_frac, & rayl_lower, rayl_upper, & optimal_angle_fit) result(err_message) class(ty_gas_optics_rrtmgp), intent(inout) :: this class(ty_gas_concs), intent(in ) :: available_gases ! Which gases does the host model have available? character(len=*), dimension(:), intent(in ) :: gas_names integer, dimension(:,:,:), intent(in ) :: key_species integer, dimension(:,:), intent(in ) :: band2gpt real(wp), dimension(:,:), intent(in ) :: band_lims_wavenum real(wp), dimension(:), intent(in ) :: press_ref, temp_ref real(wp), intent(in ) :: press_ref_trop, temp_ref_p, temp_ref_t real(wp), dimension(:,:,:), intent(in ) :: vmr_ref real(wp), dimension(:,:,:,:), intent(in ) :: kmajor real(wp), dimension(:,:,:), intent(in ) :: kminor_lower, kminor_upper real(wp), dimension(:,:), intent(in ) :: totplnk real(wp), dimension(:,:,:,:), intent(in ) :: planck_frac real(wp), dimension(:,:,:), intent(in ), & allocatable :: rayl_lower, rayl_upper real(wp), dimension(:,:), intent(in ) :: optimal_angle_fit character(len=*), dimension(:), intent(in ) :: gas_minor,identifier_minor character(len=*), dimension(:), intent(in ) :: minor_gases_lower, & minor_gases_upper integer, dimension(:,:), intent(in ) :: minor_limits_gpt_lower, & minor_limits_gpt_upper logical(wl), dimension(:), intent(in ) :: minor_scales_with_density_lower, & minor_scales_with_density_upper character(len=*), dimension(:), intent(in ) :: scaling_gas_lower, & scaling_gas_upper logical(wl), dimension(:), intent(in ) :: scale_by_complement_lower,& scale_by_complement_upper integer, dimension(:), intent(in ) :: kminor_start_lower,& kminor_start_upper character(len = 128) :: err_message ! ---- !$acc enter data copyin(this) call this%finalize() err_message = init_abs_coeffs(this, & available_gases, & gas_names, key_species, & band2gpt, band_lims_wavenum, & press_ref, temp_ref, & press_ref_trop, temp_ref_p, temp_ref_t, & vmr_ref, & kmajor, kminor_lower, kminor_upper, & gas_minor,identifier_minor,& minor_gases_lower, minor_gases_upper, & minor_limits_gpt_lower, & minor_limits_gpt_upper, & minor_scales_with_density_lower, & minor_scales_with_density_upper, & scaling_gas_lower, scaling_gas_upper, & scale_by_complement_lower, & scale_by_complement_upper, & kminor_start_lower, & kminor_start_upper, & rayl_lower, rayl_upper) ! Planck function tables ! allocate(this%totplnk (size(totplnk, 1), size(totplnk, 2)), & this%planck_frac (size(planck_frac,4), size(planck_frac,2),size(planck_frac,3), size(planck_frac,1)), & this%optimal_angle_fit(size(optimal_angle_fit, 1), size(optimal_angle_fit, 2))) this%totplnk = totplnk ! this%planck_frac = planck_frac this%planck_frac = RESHAPE(planck_frac,(/size(planck_frac,4), size(planck_frac,2), & size(planck_frac,3), size(planck_frac,1)/),ORDER =(/4,2,3,1/)) this%optimal_angle_fit = optimal_angle_fit !$acc enter data copyin(this%totplnk, this%planck_frac, this%optimal_angle_fit) !$omp target enter data map(to:this%totplnk, this%planck_frac, this%optimal_angle_fit) ! Temperature steps for Planck function interpolation ! Assumes that temperature minimum and max are the same for the absorption coefficient grid and the ! Planck grid and the Planck grid is equally spaced this%totplnk_delta = (this%temp_ref_max-this%temp_ref_min) / (size(this%totplnk,dim=1)-1) end function load_int !-------------------------------------------------------------------------------------------------------------------- ! ! Initialize object based on data read from netCDF file however the user desires. ! Rayleigh scattering tables may or may not be present; this is indicated with allocation status ! This interface is for the external-sources object -- includes TOA source function table ! function load_ext(this, available_gases, gas_names, key_species, & band2gpt, band_lims_wavenum, & press_ref, press_ref_trop, temp_ref, & temp_ref_p, temp_ref_t, vmr_ref, & kmajor, kminor_lower, kminor_upper, & gas_minor,identifier_minor, & minor_gases_lower, minor_gases_upper, & minor_limits_gpt_lower, minor_limits_gpt_upper, & minor_scales_with_density_lower, & minor_scales_with_density_upper, & scaling_gas_lower, scaling_gas_upper, & scale_by_complement_lower, & scale_by_complement_upper, & kminor_start_lower, & kminor_start_upper, & solar_quiet, solar_facular, solar_sunspot, & tsi_default, mg_default, sb_default, & rayl_lower, rayl_upper) result(err_message) class(ty_gas_optics_rrtmgp), intent(inout) :: this class(ty_gas_concs), intent(in ) :: available_gases ! Which gases does the host model have available? character(len=*), & dimension(:), intent(in) :: gas_names integer, dimension(:,:,:), intent(in) :: key_species integer, dimension(:,:), intent(in) :: band2gpt real(wp), dimension(:,:), intent(in) :: band_lims_wavenum real(wp), dimension(:), intent(in) :: press_ref, temp_ref real(wp), intent(in) :: press_ref_trop, temp_ref_p, temp_ref_t real(wp), dimension(:,:,:), intent(in) :: vmr_ref real(wp), dimension(:,:,:,:), intent(in) :: kmajor real(wp), dimension(:,:,:), intent(in) :: kminor_lower, kminor_upper character(len=*), dimension(:), & intent(in) :: gas_minor, & identifier_minor character(len=*), dimension(:), & intent(in) :: minor_gases_lower, & minor_gases_upper integer, dimension(:,:), intent(in) :: & minor_limits_gpt_lower, & minor_limits_gpt_upper logical(wl), dimension(:), intent(in) :: & minor_scales_with_density_lower, & minor_scales_with_density_upper character(len=*),dimension(:),intent(in) :: & scaling_gas_lower, & scaling_gas_upper logical(wl), dimension(:), intent(in) :: & scale_by_complement_lower, & scale_by_complement_upper integer, dimension(:), intent(in) :: & kminor_start_lower, & kminor_start_upper real(wp), dimension(:), intent(in) :: solar_quiet, & solar_facular, & solar_sunspot real(wp), intent(in) :: tsi_default, & mg_default, sb_default real(wp), dimension(:,:,:), intent(in), & allocatable :: rayl_lower, rayl_upper character(len = 128) err_message integer :: ngpt ! ---- !$acc enter data copyin(this) call this%finalize() err_message = init_abs_coeffs(this, & available_gases, & gas_names, key_species, & band2gpt, band_lims_wavenum, & press_ref, temp_ref, & press_ref_trop, temp_ref_p, temp_ref_t, & vmr_ref, & kmajor, kminor_lower, kminor_upper, & gas_minor,identifier_minor, & minor_gases_lower, minor_gases_upper, & minor_limits_gpt_lower, & minor_limits_gpt_upper, & minor_scales_with_density_lower, & minor_scales_with_density_upper, & scaling_gas_lower, scaling_gas_upper, & scale_by_complement_lower, & scale_by_complement_upper, & kminor_start_lower, & kminor_start_upper, & rayl_lower, rayl_upper) if(err_message == "") then ! ! Spectral solar irradiance terms init ! ngpt = size(solar_quiet) allocate(this%solar_source_quiet(ngpt), this%solar_source_facular(ngpt), & this%solar_source_sunspot(ngpt), this%solar_source(ngpt)) !$acc enter data create( this%solar_source_quiet, this%solar_source_facular, this%solar_source_sunspot, this%solar_source) !$omp target enter data map(alloc:this%solar_source_quiet, this%solar_source_facular, this%solar_source_sunspot, this%solar_source) !$acc kernels !$omp target this%solar_source_quiet = solar_quiet this%solar_source_facular = solar_facular this%solar_source_sunspot = solar_sunspot !$acc end kernels !$omp end target err_message = this%set_solar_variability(mg_default, sb_default) endif end function load_ext !-------------------------------------------------------------------------------------------------------------------- ! ! Initialize absorption coefficient arrays, ! including Rayleigh scattering tables if provided (allocated) ! function init_abs_coeffs(this, & available_gases, & gas_names, key_species, & band2gpt, band_lims_wavenum, & press_ref, temp_ref, & press_ref_trop, temp_ref_p, temp_ref_t, & vmr_ref, & kmajor, kminor_lower, kminor_upper, & gas_minor,identifier_minor,& minor_gases_lower, minor_gases_upper, & minor_limits_gpt_lower, & minor_limits_gpt_upper, & minor_scales_with_density_lower, & minor_scales_with_density_upper, & scaling_gas_lower, scaling_gas_upper, & scale_by_complement_lower, & scale_by_complement_upper, & kminor_start_lower, & kminor_start_upper, & rayl_lower, rayl_upper) result(err_message) class(ty_gas_optics_rrtmgp), intent(inout) :: this class(ty_gas_concs), intent(in ) :: available_gases character(len=*), & dimension(:), intent(in) :: gas_names integer, dimension(:,:,:), intent(in) :: key_species integer, dimension(:,:), intent(in) :: band2gpt real(wp), dimension(:,:), intent(in) :: band_lims_wavenum real(wp), dimension(:), intent(in) :: press_ref, temp_ref real(wp), intent(in) :: press_ref_trop, temp_ref_p, temp_ref_t real(wp), dimension(:,:,:), intent(in) :: vmr_ref real(wp), dimension(:,:,:,:), intent(in) :: kmajor real(wp), dimension(:,:,:), intent(in) :: kminor_lower, kminor_upper character(len=*), dimension(:), & intent(in) :: gas_minor, & identifier_minor character(len=*), dimension(:), & intent(in) :: minor_gases_lower, & minor_gases_upper integer, dimension(:,:), intent(in) :: minor_limits_gpt_lower, & minor_limits_gpt_upper logical(wl), dimension(:), intent(in) :: minor_scales_with_density_lower, & minor_scales_with_density_upper character(len=*), dimension(:),& intent(in) :: scaling_gas_lower, & scaling_gas_upper logical(wl), dimension(:), intent(in) :: scale_by_complement_lower, & scale_by_complement_upper integer, dimension(:), intent(in) :: kminor_start_lower, & kminor_start_upper real(wp), dimension(:,:,:), intent(in), & allocatable :: rayl_lower, rayl_upper character(len=128) :: err_message ! -------------------------------------------------------------------------- logical, dimension(:), allocatable :: gas_is_present logical, dimension(:), allocatable :: key_species_present_init integer, dimension(:,:,:), allocatable :: key_species_red real(wp), dimension(:,:,:), allocatable :: vmr_ref_red character(len=256), & dimension(:), allocatable :: minor_gases_lower_red, & minor_gases_upper_red character(len=256), & dimension(:), allocatable :: scaling_gas_lower_red, & scaling_gas_upper_red integer :: i, j, idx integer :: ngas ! -------------------------------------- err_message = this%ty_optical_props%init(band_lims_wavenum, band2gpt) if(len_trim(err_message) /= 0) return ! ! Which gases known to the gas optics are present in the host model (available_gases)? ! ngas = size(gas_names) allocate(gas_is_present(ngas)) do i = 1, ngas ! Next line causes a compiler bug in gfortran 11.0.1 on Mac ARM ! Should replace gas_names with get_gas_names() and make gas_names private in ty_gas_concs gas_is_present(i) = string_in_array(gas_names(i), available_gases%gas_names) end do ! ! Now the number of gases is the union of those known to the k-distribution and provided ! by the host model ! ngas = count(gas_is_present) ! ! Initialize the gas optics object, keeping only those gases known to the ! gas optics and also present in the host model ! this%gas_names = pack(gas_names,mask=gas_is_present) ! Copy-ins below allocate(vmr_ref_red(size(vmr_ref,dim=1),0:ngas, & size(vmr_ref,dim=3))) ! Gas 0 is used in single-key species method, set to 1.0 (col_dry) vmr_ref_red(:,0,:) = vmr_ref(:,1,:) do i = 1, ngas idx = string_loc_in_array(this%gas_names(i), gas_names) vmr_ref_red(:,i,:) = vmr_ref(:,idx+1,:) enddo call move_alloc(vmr_ref_red, this%vmr_ref) !$acc enter data copyin(this%vmr_ref, this%gas_names) !$omp target enter data map(to:this%vmr_ref, this%gas_names) ! ! Reduce minor arrays so variables only contain minor gases that are available ! Reduce size of minor Arrays ! call reduce_minor_arrays(available_gases, & gas_minor,identifier_minor, & kminor_lower, & minor_gases_lower, & minor_limits_gpt_lower, & minor_scales_with_density_lower, & scaling_gas_lower, & scale_by_complement_lower, & kminor_start_lower, & this%kminor_lower, & minor_gases_lower_red, & this%minor_limits_gpt_lower, & this%minor_scales_with_density_lower, & scaling_gas_lower_red, & this%scale_by_complement_lower, & this%kminor_start_lower) call reduce_minor_arrays(available_gases, & gas_minor,identifier_minor,& kminor_upper, & minor_gases_upper, & minor_limits_gpt_upper, & minor_scales_with_density_upper, & scaling_gas_upper, & scale_by_complement_upper, & kminor_start_upper, & this%kminor_upper, & minor_gases_upper_red, & this%minor_limits_gpt_upper, & this%minor_scales_with_density_upper, & scaling_gas_upper_red, & this%scale_by_complement_upper, & this%kminor_start_upper) !$acc enter data copyin(this%minor_limits_gpt_lower, this%minor_limits_gpt_upper) !$omp target enter data map(to:this%minor_limits_gpt_lower, this%minor_limits_gpt_upper) !$acc enter data copyin(this%minor_scales_with_density_lower, this%minor_scales_with_density_upper) !$omp target enter data map(to:this%minor_scales_with_density_lower, this%minor_scales_with_density_upper) !$acc enter data copyin(this%scale_by_complement_lower, this%scale_by_complement_upper) !$omp target enter data map(to:this%scale_by_complement_lower, this%scale_by_complement_upper) !$acc enter data copyin(this%kminor_start_lower, this%kminor_start_upper) !$omp target enter data map(to:this%kminor_start_lower, this%kminor_start_upper) !$acc enter data copyin(this%kminor_lower, this%kminor_upper) !$omp target enter data map(to:this%kminor_lower, this%kminor_upper) ! Arrays not reduced by the presence, or lack thereof, of a gas allocate(this%press_ref(size(press_ref)), this%temp_ref(size(temp_ref)), & this%kmajor(size(kmajor,4),size(kmajor,2),size(kmajor,3),size(kmajor,1))) this%press_ref(:) = press_ref(:) this%temp_ref(:) = temp_ref(:) this%kmajor = RESHAPE(kmajor,(/size(kmajor,4),size(kmajor,2),size(kmajor,3),size(kmajor,1)/), ORDER= (/4,2,3,1/)) !$acc enter data copyin(this%press_ref, this%temp_ref, this%kmajor) !$omp target enter data map(to:this%press_ref, this%temp_ref, this%kmajor) if(allocated(rayl_lower) .neqv. allocated(rayl_upper)) then err_message = "rayl_lower and rayl_upper must have the same allocation status" return end if if (allocated(rayl_lower)) then allocate(this%krayl(size(rayl_lower,dim=3),size(rayl_lower,dim=2),size(rayl_lower,dim=1),2)) this%krayl(:,:,:,1) = RESHAPE(rayl_lower,(/size(rayl_lower,dim=3),size(rayl_lower,dim=2), & size(rayl_lower,dim=1)/),ORDER =(/3,2,1/)) this%krayl(:,:,:,2) = RESHAPE(rayl_upper,(/size(rayl_lower,dim=3),size(rayl_lower,dim=2), & size(rayl_lower,dim=1)/),ORDER =(/3,2,1/)) !$acc enter data copyin(this%krayl) !$omp target enter data map(to:this%krayl) end if ! ---- post processing ---- ! creates log reference pressure allocate(this%press_ref_log(size(this%press_ref))) this%press_ref_log(:) = log(this%press_ref(:)) !$acc enter data copyin(this%press_ref_log) !$omp target enter data map(to:this%press_ref_log) ! log scale of reference pressure this%press_ref_trop_log = log(press_ref_trop) ! Get index of gas (if present) for determining col_gas call create_idx_minor(this%gas_names, gas_minor, identifier_minor, minor_gases_lower_red, this%idx_minor_lower) call create_idx_minor(this%gas_names, gas_minor, identifier_minor, minor_gases_upper_red, this%idx_minor_upper) ! Get index of gas (if present) that has special treatment in density scaling call create_idx_minor_scaling(this%gas_names, scaling_gas_lower_red, this%idx_minor_scaling_lower) call create_idx_minor_scaling(this%gas_names, scaling_gas_upper_red, this%idx_minor_scaling_upper) !$acc enter data copyin(this%idx_minor_lower, this%idx_minor_upper) !$omp target enter data map(to:this%idx_minor_lower, this%idx_minor_upper) !$acc enter data copyin(this%idx_minor_scaling_lower, this%idx_minor_scaling_upper) !$omp target enter data map(to:this%idx_minor_scaling_lower, this%idx_minor_scaling_upper) ! create flavor list ! Reduce (remap) key_species list; checks that all key gases are present in incoming call create_key_species_reduce(gas_names,this%gas_names, & key_species,key_species_red,key_species_present_init) err_message = check_key_species_present_init(gas_names,key_species_present_init) if(len_trim(err_message) /= 0) return ! create flavor list call create_flavor(key_species_red, this%flavor) ! create gpoint_flavor list call create_gpoint_flavor(key_species_red, this%get_gpoint_bands(), this%flavor, this%gpoint_flavor) !Copy-ins at end of subroutine ! minimum, maximum reference temperature, pressure -- assumes low-to-high ordering ! for T, high-to-low ordering for p this%temp_ref_min = this%temp_ref (1) this%temp_ref_max = this%temp_ref (size(this%temp_ref)) this%press_ref_min = this%press_ref(size(this%press_ref)) this%press_ref_max = this%press_ref(1) ! creates press_ref_log, temp_ref_delta this%press_ref_log_delta = (log(this%press_ref_min)-log(this%press_ref_max))/(size(this%press_ref)-1) this%temp_ref_delta = (this%temp_ref_max-this%temp_ref_min)/(size(this%temp_ref)-1) ! Which species are key in one or more bands? ! this%flavor is an index into this%gas_names ! if (allocated(this%is_key)) deallocate(this%is_key) ! Shouldn't ever happen... allocate(this%is_key(this%get_ngas())) this%is_key(:) = .False. do j = 1, size(this%flavor, 2) do i = 1, size(this%flavor, 1) ! extents should be 2 if (this%flavor(i,j) /= 0) this%is_key(this%flavor(i,j)) = .true. end do end do !$acc enter data copyin(this%flavor, this%gpoint_flavor, this%is_key) !$omp target enter data map(to:this%flavor, this%gpoint_flavor, this%is_key) end function init_abs_coeffs ! ---------------------------------------------------------------------------------------------------- function check_key_species_present_init(gas_names, key_species_present_init) result(err_message) logical, dimension(:), intent(in) :: key_species_present_init character(len=*), dimension(:), intent(in) :: gas_names character(len=128) :: err_message integer :: i err_message='' do i = 1, size(key_species_present_init) if(.not. key_species_present_init(i)) & err_message = ' ' // trim(gas_names(i)) // trim(err_message) end do if(len_trim(err_message) > 0) err_message = "gas_optics: required gases" // trim(err_message) // " are not provided" end function check_key_species_present_init !------------------------------------------------------------------------------------------ ! ! Ensure that every key gas required by the k-distribution is ! present in the gas concentration object ! function check_key_species_present(this, gas_desc) result(error_msg) class(ty_gas_optics_rrtmgp), intent(in) :: this class(ty_gas_concs), intent(in) :: gas_desc character(len=128) :: error_msg ! Local variables character(len=32), dimension(count(this%is_key(:) )) :: key_gas_names integer :: igas ! -------------------------------------- error_msg = "" key_gas_names = pack(this%gas_names, mask=this%is_key) do igas = 1, size(key_gas_names) ! Next line causes a compiler bug in gfortran 11.0.1 on Mac ARM ! Should replace gas_names with get_gas_names() and make gas_names private in ty_gas_concs if(.not. string_in_array(key_gas_names(igas), gas_desc%gas_names)) & error_msg = ' ' // trim(lower_case(key_gas_names(igas))) // trim(error_msg) end do if(len_trim(error_msg) > 0) error_msg = "gas_optics: required gases" // trim(error_msg) // " are not provided" end function check_key_species_present !-------------------------------------------------------------------------------------------------------------------- ! ! Inquiry functions ! !-------------------------------------------------------------------------------------------------------------------- ! !> return true if initialized for internal sources/longwave, false otherwise ! pure function source_is_internal(this) class(ty_gas_optics_rrtmgp), intent(in) :: this logical :: source_is_internal source_is_internal = allocated(this%totplnk) .and. allocated(this%planck_frac) end function source_is_internal !-------------------------------------------------------------------------------------------------------------------- ! !> return true if initialized for external sources/shortwave, false otherwise ! pure function source_is_external(this) class(ty_gas_optics_rrtmgp), intent(in) :: this logical :: source_is_external source_is_external = allocated(this%solar_source) end function source_is_external !-------------------------------------------------------------------------------------------------------------------- ! !> return the names of the gases known to the k-distributions ! pure function get_gases(this) class(ty_gas_optics_rrtmgp), intent(in) :: this character(32), dimension(get_ngas(this)) :: get_gases !! names of the gases known to the k-distributions get_gases = this%gas_names end function get_gases !-------------------------------------------------------------------------------------------------------------------- ! !> return the minimum pressure on the interpolation grids ! pure function get_press_min(this) class(ty_gas_optics_rrtmgp), intent(in) :: this real(wp) :: get_press_min !! minimum pressure for which the k-dsitribution is valid get_press_min = this%press_ref_min end function get_press_min !-------------------------------------------------------------------------------------------------------------------- ! !> return the maximum pressure on the interpolation grids ! pure function get_press_max(this) class(ty_gas_optics_rrtmgp), intent(in) :: this real(wp) :: get_press_max !! maximum pressure for which the k-dsitribution is valid get_press_max = this%press_ref_max end function get_press_max !-------------------------------------------------------------------------------------------------------------------- ! !> return the minimum temparature on the interpolation grids ! pure function get_temp_min(this) class(ty_gas_optics_rrtmgp), intent(in) :: this real(wp) :: get_temp_min !! minimum temperature for which the k-dsitribution is valid get_temp_min = this%temp_ref_min end function get_temp_min !-------------------------------------------------------------------------------------------------------------------- ! !> return the maximum temparature on the interpolation grids ! pure function get_temp_max(this) class(ty_gas_optics_rrtmgp), intent(in) :: this real(wp) :: get_temp_max !! maximum temperature for which the k-dsitribution is valid get_temp_max = this%temp_ref_max end function get_temp_max !-------------------------------------------------------------------------------------------------------------------- ! !> Utility function, provided for user convenience !> computes column amounts of dry air using hydrostatic equation ! function get_col_dry(vmr_h2o, plev, latitude) result(col_dry) ! input real(wp), dimension(:,:), intent(in) :: vmr_h2o ! volume mixing ratio of water vapor to dry air; (ncol,nlay) real(wp), dimension(:,:), intent(in) :: plev ! Layer boundary pressures [Pa] (ncol,nlay+1) real(wp), dimension(:), optional, & intent(in) :: latitude ! Latitude [degrees] (ncol) ! output real(wp), dimension(size(plev,dim=1),size(plev,dim=2)-1) :: col_dry ! Column dry amount (ncol,nlay) ! ------------------------------------------------ ! first and second term of Helmert formula real(wp), parameter :: helmert1 = 9.80665_wp real(wp), parameter :: helmert2 = 0.02586_wp ! local variables real(wp), dimension(size(plev,dim=1)) :: g0 ! (ncol) real(wp):: delta_plev, m_air, fact integer :: ncol, nlev integer :: icol, ilev ! nlay = nlev-1 ! ------------------------------------------------ ncol = size(plev, dim=1) nlev = size(plev, dim=2) !$acc data create(g0) !$omp target data map(alloc:g0) if(present(latitude)) then ! A purely OpenACC implementation would probably compute g0 within the kernel below !$acc parallel loop !$omp target teams distribute parallel do simd do icol = 1, ncol g0(icol) = helmert1 - helmert2 * cos(2.0_wp * pi * latitude(icol) / 180.0_wp) ! acceleration due to gravity [m/s^2] end do else !$acc parallel loop !$omp target teams distribute parallel do simd do icol = 1, ncol g0(icol) = grav end do end if !$acc parallel loop gang vector collapse(2) copyin(plev,vmr_h2o) copyout(col_dry) !$omp target teams distribute parallel do simd collapse(2) map(to:plev,vmr_h2o) map(from:col_dry) do ilev = 1, nlev-1 do icol = 1, ncol delta_plev = abs(plev(icol,ilev) - plev(icol,ilev+1)) ! Get average mass of moist air per mole of moist air fact = 1._wp / (1.+vmr_h2o(icol,ilev)) m_air = (m_dry + m_h2o * vmr_h2o(icol,ilev)) * fact col_dry(icol,ilev) = 10._wp * delta_plev * avogad * fact/(1000._wp*m_air*100._wp*g0(icol)) end do end do !$acc end data !$omp end target data end function get_col_dry !-------------------------------------------------------------------------------------------------------------------- ! !> Compute a transport angle that minimizes flux errors at surface and TOA based on empirical fits ! function compute_optimal_angles(this, optical_props, optimal_angles) result(err_msg) ! input class(ty_gas_optics_rrtmgp), intent(in ) :: this class(ty_optical_props_arry), intent(in ) :: optical_props !! Optical properties real(wp), dimension(:,:), intent( out) :: optimal_angles !! Secant of optical transport angle character(len=128) :: err_msg !! Empty if successful !---------------------------- integer :: ncol, nlay, ngpt integer :: icol, ilay, igpt, bnd real(wp) :: t, trans_total #if defined _CRAYFTN && _RELEASE_MAJOR == 14 && _RELEASE_MINOR == 0 && _RELEASE_PATCHLEVEL == 3 # define CRAY_WORKAROUND #endif #ifdef CRAY_WORKAROUND integer, allocatable :: bands(:) #else integer :: bands(optical_props%get_ngpt()) #endif !---------------------------- ncol = optical_props%get_ncol() nlay = optical_props%get_nlay() ngpt = optical_props%get_ngpt() #ifdef CRAY_WORKAROUND allocate( bands(ngpt) ) ! In order to work with CCE 14 (it is also better software) #endif err_msg="" if(.not. this%gpoints_are_equal(optical_props)) & err_msg = "gas_optics%compute_optimal_angles: optical_props has different spectral discretization than gas_optics" if(.not. extents_are(optimal_angles, ncol, ngpt)) & err_msg = "gas_optics%compute_optimal_angles: optimal_angles different dimension (ncol)" if (err_msg /= "") return do igpt = 1, ngpt bands(igpt) = optical_props%convert_gpt2band(igpt) enddo ! ! column transmissivity ! !$acc parallel loop gang vector collapse(2) copyin(bands, optical_props, optical_props%tau) copyout(optimal_angles) !$omp target teams distribute parallel do simd collapse(2) map(to:bands, optical_props%tau) map(from:optimal_angles) do icol = 1, ncol do igpt = 1, ngpt ! ! Column transmissivity ! t = 0._wp trans_total = 0._wp do ilay = 1, nlay t = t + optical_props%tau(icol,ilay,igpt) end do trans_total = exp(-t) ! ! Optimal transport angle is a linear fit to column transmissivity ! optimal_angles(icol,igpt) = this%optimal_angle_fit(1,bands(igpt))*trans_total + & this%optimal_angle_fit(2,bands(igpt)) end do end do end function compute_optimal_angles !-------------------------------------------------------------------------------------------------------------------- ! ! Internal procedures ! !-------------------------------------------------------------------------------------------------------------------- pure function rewrite_key_species_pair(key_species_pair) ! (0,0) becomes (2,2) -- because absorption coefficients for these g-points will be 0. integer, dimension(2) :: rewrite_key_species_pair integer, dimension(2), intent(in) :: key_species_pair rewrite_key_species_pair = key_species_pair if (all(key_species_pair(:).eq.(/0,0/))) then rewrite_key_species_pair(:) = (/2,2/) end if end function ! --------------------------------------------------------------------------------------- ! true is key_species_pair exists in key_species_list pure function key_species_pair_exists(key_species_list, key_species_pair) logical :: key_species_pair_exists integer, dimension(:,:), intent(in) :: key_species_list integer, dimension(2), intent(in) :: key_species_pair integer :: i do i=1,size(key_species_list,dim=2) if (all(key_species_list(:,i).eq.key_species_pair(:))) then key_species_pair_exists = .true. return end if end do key_species_pair_exists = .false. end function key_species_pair_exists ! --------------------------------------------------------------------------------------- ! create flavor list -- ! an unordered array of extent (2,:) containing all possible pairs of key species ! used in either upper or lower atmos ! subroutine create_flavor(key_species, flavor) integer, dimension(:,:,:), intent(in) :: key_species integer, dimension(:,:), allocatable, intent(out) :: flavor integer, dimension(2,size(key_species,3)*2) :: key_species_list integer :: ibnd, iatm, i, iflavor ! prepare list of key_species i = 1 do ibnd=1,size(key_species,3) ! bands do iatm=1,size(key_species,2) ! upper/lower atmosphere key_species_list(:,i) = key_species(:,iatm,ibnd) i = i + 1 end do end do ! rewrite single key_species pairs do i=1,size(key_species_list,2) key_species_list(:,i) = rewrite_key_species_pair(key_species_list(:,i)) end do ! count unique key species pairs iflavor = 0 do i=1,size(key_species_list,2) if (.not.key_species_pair_exists(key_species_list(:,1:i-1),key_species_list(:,i))) then iflavor = iflavor + 1 end if end do ! fill flavors allocate(flavor(2,iflavor)) iflavor = 0 do i=1,size(key_species_list,2) if (.not.key_species_pair_exists(key_species_list(:,1:i-1),key_species_list(:,i))) then iflavor = iflavor + 1 flavor(:,iflavor) = key_species_list(:,i) end if end do end subroutine create_flavor ! --------------------------------------------------------------------------------------- ! ! create index list for extracting col_gas needed for minor gas optical depth calculations ! subroutine create_idx_minor(gas_names, & gas_minor, identifier_minor, minor_gases_atm, idx_minor_atm) character(len=*), dimension(:), intent(in) :: gas_names character(len=*), dimension(:), intent(in) :: & gas_minor, & identifier_minor character(len=*), dimension(:), intent(in) :: minor_gases_atm integer, dimension(:), allocatable, & intent(out) :: idx_minor_atm ! local integer :: imnr integer :: idx_mnr allocate(idx_minor_atm(size(minor_gases_atm,dim=1))) do imnr = 1, size(minor_gases_atm,dim=1) ! loop over minor absorbers in each band ! Find identifying string for minor species in list of possible identifiers (e.g. h2o_slf) idx_mnr = string_loc_in_array(minor_gases_atm(imnr), identifier_minor) ! Find name of gas associated with minor species identifier (e.g. h2o) idx_minor_atm(imnr) = string_loc_in_array(gas_minor(idx_mnr), gas_names) enddo end subroutine create_idx_minor ! --------------------------------------------------------------------------------------- ! ! create index for special treatment in density scaling of minor gases ! subroutine create_idx_minor_scaling(gas_names, & scaling_gas_atm, idx_minor_scaling_atm) character(len=*), dimension(:), intent(in) :: gas_names character(len=*), dimension(:), intent(in) :: scaling_gas_atm integer, dimension(:), allocatable, & intent(out) :: idx_minor_scaling_atm ! local integer :: imnr allocate(idx_minor_scaling_atm(size(scaling_gas_atm,dim=1))) do imnr = 1, size(scaling_gas_atm,dim=1) ! loop over minor absorbers in each band ! This will be -1 if there's no interacting gas idx_minor_scaling_atm(imnr) = string_loc_in_array(scaling_gas_atm(imnr), gas_names) enddo end subroutine create_idx_minor_scaling !-------------------------------------------------------------------------------------------------------------------- ! Is the object ready to use? ! pure function is_loaded(this) class(ty_gas_optics_rrtmgp), intent(in) :: this logical(wl) :: is_loaded is_loaded = allocated(this%kmajor) end function is_loaded !-------------------------------------------------------------------------------------------------------------------- ! ! Reset the object to un-initialized state ! subroutine finalize(this) class(ty_gas_optics_rrtmgp), intent(inout) :: this if(this%is_loaded()) then !$acc exit data delete(this%gas_names, this%vmr_ref, this%flavor) & !$acc delete(this%press_ref, this%press_ref_log, this%temp_ref) & !$acc delete(this%gpoint_flavor, this%kmajor) & !$acc delete(this%minor_limits_gpt_lower) & !$acc delete(this%minor_scales_with_density_lower, this%scale_by_complement_lower) & !$acc delete(this%idx_minor_lower, this%idx_minor_scaling_lower) & !$acc delete(this%kminor_start_lower, this%kminor_lower) & !$acc delete(this%minor_limits_gpt_upper) & !$acc delete(this%minor_scales_with_density_upper, this%scale_by_complement_upper) & !$acc delete(this%idx_minor_upper, this%idx_minor_scaling_upper) & !$acc delete(this%kminor_start_upper, this%kminor_upper) !$omp target exit data map(release:this%gas_names, this%vmr_ref, this%flavor) & !$omp map(release:this%press_ref, this%press_ref_log, this%temp_ref) !$omp map(release:this%gpoint_flavor, this%kmajor) & !$omp map(release:this%minor_limits_gpt_lower) & !$omp map(release:this%minor_scales_with_density_lower, this%scale_by_complement_lower) & !$omp map(release:this%idx_minor_lower, this%idx_minor_scaling_lower) & !$omp map(release:this%kminor_start_lower, this%kminor_lower) & !$omp map(release:this%minor_limits_gpt_upper) & !$omp map(release:this%minor_scales_with_density_upper, this%scale_by_complement_upper) & !$omp map(release:this%idx_minor_upper, this%idx_minor_scaling_upper) & !$omp map(release:this%kminor_start_upper, this%kminor_upper) deallocate(this%gas_names, this%vmr_ref, this%flavor, this%gpoint_flavor, this%kmajor) deallocate(this%press_ref, this%press_ref_log, this%temp_ref) deallocate(this%minor_limits_gpt_lower, & this%minor_scales_with_density_lower, this%scale_by_complement_lower, & this%idx_minor_lower, this%idx_minor_scaling_lower, this%kminor_start_lower, this%kminor_lower) deallocate(this%minor_limits_gpt_upper, & this%minor_scales_with_density_upper, this%scale_by_complement_upper, & this%idx_minor_upper, this%idx_minor_scaling_upper, this%kminor_start_upper, this%kminor_upper) if(allocated(this%krayl)) then !$acc exit data delete(this%krayl) !$omp target exit data map(release:this%krayl) deallocate(this%krayl) end if if(allocated(this%planck_frac)) then !$acc exit data delete(this%planck_frac, this%totplnk, this%optimal_angle_fit) !$omp target exit data map(release:this%planck_frac, this%totplnk, this%optimal_angle_fit) deallocate(this%planck_frac, this%totplnk, this%optimal_angle_fit) end if if(allocated(this%solar_source)) then !$acc exit data delete(this%solar_source, this%solar_source_quiet) & !$acc delete(this%solar_source_facular,this%solar_source_sunspot) !$omp target exit data map(release:this%solar_source, this%solar_source_quiet) !$omp map(release:this%solar_source_facular,this%solar_source_sunspot) deallocate(this%solar_source, & this%solar_source_quiet, this%solar_source_facular, this%solar_source_sunspot) end if !$acc exit data delete(this) !$omp target exit data map(release:this) end if end subroutine finalize ! --------------------------------------------------------------------------------------- subroutine create_key_species_reduce(gas_names,gas_names_red, & key_species,key_species_red,key_species_present_init) character(len=*), & dimension(:), intent(in) :: gas_names character(len=*), & dimension(:), intent(in) :: gas_names_red integer, dimension(:,:,:), intent(in) :: key_species integer, dimension(:,:,:), allocatable, intent(out) :: key_species_red logical, dimension(:), allocatable, intent(out) :: key_species_present_init integer :: ip, ia, it, np, na, nt np = size(key_species,dim=1) na = size(key_species,dim=2) nt = size(key_species,dim=3) allocate(key_species_red(size(key_species,dim=1), & size(key_species,dim=2), & size(key_species,dim=3))) allocate(key_species_present_init(size(gas_names))) key_species_present_init = .true. do ip = 1, np do ia = 1, na do it = 1, nt if (key_species(ip,ia,it) .ne. 0) then key_species_red(ip,ia,it) = string_loc_in_array(gas_names(key_species(ip,ia,it)),gas_names_red) if (key_species_red(ip,ia,it) .eq. -1) key_species_present_init(key_species(ip,ia,it)) = .false. else key_species_red(ip,ia,it) = key_species(ip,ia,it) endif enddo end do enddo end subroutine create_key_species_reduce ! --------------------------------------------------------------------------------------- subroutine reduce_minor_arrays(available_gases, & gas_minor,identifier_minor,& kminor_atm, & minor_gases_atm, & minor_limits_gpt_atm, & minor_scales_with_density_atm, & scaling_gas_atm, & scale_by_complement_atm, & kminor_start_atm, & kminor_atm_red, & minor_gases_atm_red, & minor_limits_gpt_atm_red, & minor_scales_with_density_atm_red, & scaling_gas_atm_red, & scale_by_complement_atm_red, & kminor_start_atm_red) class(ty_gas_concs), intent(in) :: available_gases real(wp), dimension(:,:,:), intent(in) :: kminor_atm character(len=*), dimension(:), intent(in) :: gas_minor, & identifier_minor character(len=*), dimension(:), intent(in) :: minor_gases_atm integer, dimension(:,:), intent(in) :: minor_limits_gpt_atm logical(wl), dimension(:), intent(in) :: minor_scales_with_density_atm character(len=*), dimension(:), intent(in) :: scaling_gas_atm logical(wl), dimension(:), intent(in) :: scale_by_complement_atm integer, dimension(:), intent(in) :: kminor_start_atm real(wp), dimension(:,:,:), allocatable, & intent(out) :: kminor_atm_red character(len=*), dimension(:), allocatable, & intent(out) :: minor_gases_atm_red integer, dimension(:,:), allocatable, & intent(out) :: minor_limits_gpt_atm_red logical(wl), dimension(:), allocatable, & intent(out) ::minor_scales_with_density_atm_red character(len=*), dimension(:), allocatable, & intent(out) ::scaling_gas_atm_red logical(wl), dimension(:), allocatable, intent(out) :: & scale_by_complement_atm_red integer, dimension(:), allocatable, intent(out) :: & kminor_start_atm_red ! Local variables integer :: i, j, ks integer :: idx_mnr, nm, tot_g, red_nm integer :: icnt, n_elim, ng logical, dimension(:), allocatable :: gas_is_present integer, dimension(:), allocatable :: indexes real(wp), dimension(:,:,:), allocatable :: kminor_atm_red_t nm = size(minor_gases_atm) tot_g=0 allocate(gas_is_present(nm)) do i = 1, size(minor_gases_atm) idx_mnr = string_loc_in_array(minor_gases_atm(i), identifier_minor) ! Next line causes a compiler bug in gfortran 11.0.1 on Mac ARM ! Should replace gas_names with get_gas_names() and make gas_names private in ty_gas_concs gas_is_present(i) = string_in_array(gas_minor(idx_mnr),available_gases%gas_names) if(gas_is_present(i)) then tot_g = tot_g + (minor_limits_gpt_atm(2,i)-minor_limits_gpt_atm(1,i)+1) endif enddo red_nm = count(gas_is_present) allocate(minor_gases_atm_red (red_nm),& minor_scales_with_density_atm_red(red_nm), & scaling_gas_atm_red (red_nm), & scale_by_complement_atm_red (red_nm), & kminor_start_atm_red (red_nm)) allocate(minor_limits_gpt_atm_red(2, red_nm)) allocate(kminor_atm_red_t(tot_g, size(kminor_atm,2), size(kminor_atm,3))) allocate(kminor_atm_red(size(kminor_atm,3),size(kminor_atm,2),tot_g)) if ((red_nm .eq. nm)) then ! Character data not allowed in OpenACC regions? minor_gases_atm_red = minor_gases_atm scaling_gas_atm_red = scaling_gas_atm kminor_atm_red_t = kminor_atm minor_limits_gpt_atm_red = minor_limits_gpt_atm minor_scales_with_density_atm_red = minor_scales_with_density_atm scale_by_complement_atm_red = scale_by_complement_atm kminor_start_atm_red = kminor_start_atm else allocate(indexes(red_nm)) ! Find the integer indexes for the gases that are present indexes = pack([(i, i = 1, size(minor_gases_atm))], mask=gas_is_present) minor_gases_atm_red = minor_gases_atm (indexes) scaling_gas_atm_red = scaling_gas_atm (indexes) minor_scales_with_density_atm_red = & minor_scales_with_density_atm(indexes) scale_by_complement_atm_red = & scale_by_complement_atm(indexes) kminor_start_atm_red = kminor_start_atm (indexes) icnt = 0 n_elim = 0 do i = 1, nm ng = minor_limits_gpt_atm(2,i)-minor_limits_gpt_atm(1,i)+1 if(gas_is_present(i)) then icnt = icnt + 1 minor_limits_gpt_atm_red(1:2,icnt) = minor_limits_gpt_atm(1:2,i) kminor_start_atm_red(icnt) = kminor_start_atm(i)-n_elim ks = kminor_start_atm_red(icnt) do j = 1, ng kminor_atm_red_t(kminor_start_atm_red(icnt)+j-1,:,:) = & kminor_atm(kminor_start_atm(i)+j-1,:,:) enddo else n_elim = n_elim + ng endif enddo endif kminor_atm_red = RESHAPE(kminor_atm_red_t,(/size(kminor_atm_red_t,dim=3), & size(kminor_atm_red_t,dim=2),size(kminor_atm_red_t,dim=1)/), ORDER=(/3,2,1/)) deallocate(kminor_atm_red_t) end subroutine reduce_minor_arrays ! --------------------------------------------------------------------------------------- ! returns flavor index; -1 if not found pure function key_species_pair2flavor(flavor, key_species_pair) integer :: key_species_pair2flavor integer, dimension(:,:), intent(in) :: flavor integer, dimension(2), intent(in) :: key_species_pair integer :: iflav do iflav=1,size(flavor,2) if (all(key_species_pair(:).eq.flavor(:,iflav))) then key_species_pair2flavor = iflav return end if end do key_species_pair2flavor = -1 end function key_species_pair2flavor ! --------------------------------------------------------------------------------------- ! ! create gpoint_flavor list ! a map pointing from each g-point to the corresponding entry in the "flavor list" ! subroutine create_gpoint_flavor(key_species, gpt2band, flavor, gpoint_flavor) integer, dimension(:,:,:), intent(in) :: key_species integer, dimension(:), intent(in) :: gpt2band integer, dimension(:,:), intent(in) :: flavor integer, dimension(:,:), intent(out), allocatable :: gpoint_flavor integer :: ngpt, igpt, iatm ngpt = size(gpt2band) allocate(gpoint_flavor(2,ngpt)) do igpt=1,ngpt do iatm=1,2 gpoint_flavor(iatm,igpt) = key_species_pair2flavor( & flavor, & rewrite_key_species_pair(key_species(:,iatm,gpt2band(igpt))) & ) end do end do end subroutine create_gpoint_flavor !-------------------------------------------------------------------------------------------------------------------- ! ! Utility function to combine optical depths from gas absorption and Rayleigh scattering ! It may be more efficient to combine scattering and absorption optical depths in place ! rather than storing and processing two large arrays ! subroutine combine_abs_and_rayleigh(tau, tau_rayleigh, optical_props) real(wp), dimension(:,:,:), intent(in ) :: tau real(wp), dimension(:,:,:), intent(in ) :: tau_rayleigh class(ty_optical_props_arry), intent(inout) :: optical_props integer :: icol, ilay, igpt, ncol, nlay, ngpt, nmom real(wp) :: t ncol = size(tau, 1) nlay = size(tau, 2) ngpt = size(tau, 3) select type(optical_props) type is (ty_optical_props_1scl) ! ! Extinction optical depth ! !$acc parallel loop gang vector collapse(3) default(present) !$omp target teams distribute parallel do simd collapse(3) do igpt = 1, ngpt do ilay = 1, nlay do icol = 1, ncol optical_props%tau(icol,ilay,igpt) = tau(icol,ilay,igpt) + & tau_rayleigh(icol,ilay,igpt) end do end do end do ! ! asymmetry factor or phase function moments ! type is (ty_optical_props_2str) ! ! Extinction optical depth and single scattering albedo ! !$acc parallel loop gang vector collapse(3) default(present) !$omp target teams distribute parallel do simd collapse(3) do igpt = 1, ngpt do ilay = 1, nlay do icol = 1, ncol t = tau(icol,ilay,igpt) + tau_rayleigh(icol,ilay,igpt) if(t > 2._wp * tiny(t)) then optical_props%ssa(icol,ilay,igpt) = tau_rayleigh(icol,ilay,igpt) / t else optical_props%ssa(icol,ilay,igpt) = 0._wp end if optical_props%tau(icol,ilay,igpt) = t end do end do end do call zero_array(ncol, nlay, ngpt, optical_props%g) type is (ty_optical_props_nstr) ! ! Extinction optical depth and single scattering albedo ! !$acc parallel loop gang vector collapse(3) default(present) !$omp target teams distribute parallel do simd collapse(3) do igpt = 1, ngpt do ilay = 1, nlay do icol = 1, ncol t = tau(icol,ilay,igpt) + tau_rayleigh(icol,ilay,igpt) if(t > 2._wp * tiny(t)) then optical_props%ssa(icol,ilay,igpt) = tau_rayleigh(icol,ilay,igpt) / t else optical_props%ssa(icol,ilay,igpt) = 0._wp end if optical_props%tau(icol,ilay,igpt) = t end do end do end do nmom = size(optical_props%p, 1) call zero_array(nmom, ncol, nlay, ngpt, optical_props%p) if(nmom >= 2) then !$acc parallel loop gang vector collapse(3) default(present) !$omp target teams distribute parallel do simd collapse(3) do igpt = 1, ngpt do ilay = 1, nlay do icol = 1, ncol optical_props%p(2,icol,ilay,igpt) = 0.1_wp end do end do end do end if end select end subroutine combine_abs_and_rayleigh !-------------------------------------------------------------------------------------------------------------------- ! Sizes of tables: pressure, temperate, eta (mixing fraction) ! Equivalent routines for the number of gases and flavors (get_ngas(), get_nflav()) are defined above because they're ! used in function defintions ! Table kmajor has dimensions (ngpt, neta, npres, ntemp) !-------------------------------------------------------------------------------------------------------------------- ! ! return extent of eta dimension ! pure function get_neta(this) class(ty_gas_optics_rrtmgp), intent(in) :: this integer :: get_neta get_neta = size(this%kmajor,dim=2) end function ! -------------------------------------------------------------------------------------- ! ! return the number of pressures in reference profile ! absorption coefficient table is one bigger since a pressure is repeated in upper/lower atmos ! pure function get_npres(this) class(ty_gas_optics_rrtmgp), intent(in) :: this integer :: get_npres get_npres = size(this%kmajor,dim=3)-1 end function get_npres ! -------------------------------------------------------------------------------------- ! ! return the number of temperatures ! pure function get_ntemp(this) class(ty_gas_optics_rrtmgp), intent(in) :: this integer :: get_ntemp get_ntemp = size(this%kmajor,dim=1) end function get_ntemp ! -------------------------------------------------------------------------------------- ! ! return the number of temperatures for Planck function ! pure function get_nPlanckTemp(this) class(ty_gas_optics_rrtmgp), intent(in) :: this integer :: get_nPlanckTemp get_nPlanckTemp = size(this%totplnk,dim=1) ! dimensions are Planck-temperature, band end function get_nPlanckTemp end module mo_gas_optics_rrtmgp