! This code is part of RRTM for GCM Applications - Parallel (RRTMGP) ! ! Contacts: Robert Pincus and Eli Mlawer ! email: rrtmgp@aer.com ! ! Copyright 2015-2019, Atmospheric and Environmental Research and ! Regents of the University of Colorado. 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 ! ------------------------------------------------------------------------------------------------- ! ! This module provides a simple implementation of sampling for the ! Monte Carlo Independent Pixel Approximation (McICA, doi:10.1029/2002jd003322) ! Cloud optical properties, defined by band and assumed homogenous within each cell (column/layer), ! are randomly sampled to preserve the mean cloud fraction and one of several possible overlap assumptions ! Users supply random numbers with order ngpt,nlay,ncol ! These are only accessed if cloud_fraction(icol,ilay) > 0 so many values don't need to be filled in ! ! Adapted by Dustin Swales on 8/11/2020 for use in UFS (NOAA-PSL/CU-CIRES) ! ! ------------------------------------------------------------------------------------------------- module rrtmgp_sampling use mo_rte_kind, only: wp, wl use mo_optical_props, only: ty_optical_props_arry, & ty_optical_props_1scl, & ty_optical_props_2str, & ty_optical_props_nstr implicit none private public :: draw_samples, sampled_mask contains ! ------------------------------------------------------------------------------------------------- ! ! Apply a T/F sampled cloud mask to cloud optical properties defined by band to produce ! McICA-sampled cloud optical properties ! ! ------------------------------------------------------------------------------------------------- function draw_samples(cloud_mask,do_twostream,clouds,clouds_sampled) result(error_msg) ! Inputs logical, dimension(:,:,:), intent(in ) :: cloud_mask ! Dimensions ncol,nlay,ngpt logical, intent(in ) :: do_twostream ! Do two-stream? class(ty_optical_props_arry), intent(in ) :: clouds ! Defined by band ! Outputs class(ty_optical_props_arry), intent(inout) :: clouds_sampled ! Defined by g-point character(len=128) :: error_msg ! Local variables integer :: ncol,nlay,nbnd,ngpt integer :: imom error_msg = "" ! Array extents ncol = clouds%get_ncol() nlay = clouds%get_nlay() nbnd = clouds%get_nband() ngpt = clouds_sampled%get_ngpt() ! Optical depth assignment works for 1scl, 2str (also nstr) call apply_cloud_mask(ncol,nlay,nbnd,ngpt,clouds_sampled%get_band_lims_gpoint(),cloud_mask,clouds%tau,clouds_sampled%tau) ! ! For 2-stream ! select type(clouds) type is (ty_optical_props_2str) select type(clouds_sampled) type is (ty_optical_props_2str) if (do_twostream) then call apply_cloud_mask(ncol,nlay,nbnd,ngpt,clouds_sampled%get_band_lims_gpoint(),cloud_mask,clouds%ssa,clouds_sampled%ssa) call apply_cloud_mask(ncol,nlay,nbnd,ngpt,clouds_sampled%get_band_lims_gpoint(),cloud_mask,clouds%g, clouds_sampled%g ) endif class default error_msg = "draw_samples: by-band and sampled cloud properties need to be the same variable type" end select end select end function draw_samples ! ------------------------------------------------------------------------------------------------- ! ! Generate a McICA-sampled cloud mask ! ! ------------------------------------------------------------------------------------------------- subroutine sampled_mask(randoms, cloud_frac, cloud_mask, overlap_param, randoms2) ! Inputs real(wp), dimension(:,:,:), intent(in ) :: randoms ! ngpt,nlay,ncol real(wp), dimension(:,:), intent(in ) :: cloud_frac ! ncol,nlay ! Outputs logical, dimension(:,:,:), intent(out) :: cloud_mask ! ncol,nlay,ngpt ! Inputs (optional) real(wp), dimension(:,:), intent(in ), optional :: overlap_param ! ncol,nlay-1 real(wp), dimension(:,:,:), intent(in ), optional :: randoms2 ! ngpt,nlay,ncol ! Local variables integer :: ncol, nlay, ngpt, icol, ilay, igpt integer :: cloud_lay_fst, cloud_lay_lst real(wp) :: rho real(wp), dimension(size(randoms,1)) :: local_rands logical, dimension(size(randoms,2)) :: cloud_mask_layer logical :: l_use_overlap_param = .false. logical :: l_use_second_rng = .false. character(len=128) :: error_msg ! Array dimensions ncol = size(randoms, 3) nlay = size(randoms, 2) ngpt = size(randoms, 1) ! Using cloud-overlap parameter (alpha)? if (present(overlap_param)) l_use_overlap_param = .true. ! Using a second RNG? if (present(randoms2)) l_use_second_rng = .true. ! Construct the cloud mask for each column do icol = 1, ncol cloud_mask_layer(1:nlay) = cloud_frac(icol,1:nlay) > 0._wp if(.not. any(cloud_mask_layer)) then cloud_mask(icol,1:nlay,1:ngpt) = .false. cycle end if cloud_lay_fst = findloc(cloud_mask_layer, .true., dim=1) cloud_lay_lst = findloc(cloud_mask_layer, .true., dim=1, back = .true.) cloud_mask(icol,1:cloud_lay_fst,1:ngpt) = .false. ilay = cloud_lay_fst local_rands(1:ngpt) = randoms(1:ngpt,cloud_lay_fst,icol) cloud_mask(icol,ilay,1:ngpt) = local_rands(1:ngpt) > (1._wp - cloud_frac(icol,ilay)) do ilay = cloud_lay_fst+1, cloud_lay_lst ! ################################################################################ ! Max-random overlap ! new random deviates if the adjacent layer isn't cloudy ! same random deviates if the adjacent layer is cloudy ! ################################################################################ if (.not. l_use_overlap_param) then if(cloud_mask_layer(ilay)) then if(.not. cloud_mask_layer(ilay-1)) local_rands(1:ngpt) = randoms(1:ngpt,ilay,icol) cloud_mask(icol,ilay,1:ngpt) = local_rands(1:ngpt) > (1._wp - cloud_frac(icol,ilay)) else cloud_mask(icol,ilay,1:ngpt) = .false. end if end if ! END COND: Maximum-random overlap ! ################################################################################ ! Exponential-random overlap ! new random deviates if the adjacent layer isn't cloudy ! correlated deviates if the adjacent layer is cloudy ! ################################################################################ if (l_use_overlap_param) then if(cloud_mask_layer(ilay)) then if(cloud_mask_layer(ilay-1)) then ! Create random deviates correlated between this layer and the previous layer ! (have to remove mean value before enforcing correlation). rho = overlap_param(icol,ilay-1) local_rands(1:ngpt) = rho*(local_rands(1:ngpt) -0.5_wp) + & sqrt(1._wp-rho*rho)*(randoms(1:ngpt,ilay,icol)-0.5_wp) + 0.5_wp else local_rands(1:ngpt) = randoms(1:ngpt,ilay,icol) end if cloud_mask(icol,ilay,1:ngpt) = local_rands(1:ngpt) > (1._wp - cloud_frac(icol,ilay)) endif endif ! END COND: Exponential/Exponential-random overlap ! ################################################################################ ! Exponential-decorrelation overlap ! new random deviates if the adjacent layer isn't cloudy ! correlated deviates if the adjacent layer is cloudy and decorrelation-length ! ################################################################################ if (l_use_overlap_param .and. l_use_second_rng) then where(randoms2(1:nGpt,iLay,iCol) .le. overlap_param(iCol,iLay-1)) cloud_mask(iCol,iLay,1:nGpt) = randoms(1:ngpt,iLay-1,iCol) > (1._wp - cloud_frac(iCol,iLay)) elsewhere cloud_mask(iCol,iLay,1:nGpt) = randoms(1:ngpt,iLay,iCol) > (1._wp - cloud_frac(iCol,iLay)) end where endif ! END COND: Exponential decorrelation-length end do ! END LOOP: Layers ! Set cloud-mask in layer below clouds to false cloud_mask(icol,cloud_lay_lst+1:nlay, 1:ngpt) = .false. end do ! END LOOP: Columns end subroutine sampled_mask ! ------------------------------------------------------------------------------------------------- ! ! Apply a true/false cloud mask to a homogeneous field ! This could be a kernel ! ! ------------------------------------------------------------------------------------------------- subroutine apply_cloud_mask(ncol,nlay,nbnd,ngpt,band_lims_gpt,cloud_mask,input_field,sampled_field) integer, intent(in ) :: ncol,nlay,nbnd,ngpt integer, dimension(2,nbnd), intent(in ) :: band_lims_gpt logical, dimension(ncol,nlay,ngpt), intent(in ) :: cloud_mask real(wp), dimension(ncol,nlay,nbnd), intent(in ) :: input_field real(wp), dimension(ncol,nlay,ngpt), intent(out) :: sampled_field integer :: icol,ilay,ibnd,igpt do ibnd = 1, nbnd do igpt = band_lims_gpt(1,ibnd), band_lims_gpt(2,ibnd) do ilay = 1, nlay sampled_field(1:ncol,ilay,igpt) = merge(input_field(1:ncol,ilay,ibnd), 0._wp, cloud_mask(1:ncol,ilay,igpt)) end do end do end do end subroutine apply_cloud_mask end module rrtmgp_sampling