module aer_rad_props 2,11 !------------------------------------------------------------------------------------------------ ! Converts aerosol masses to bulk optical properties for sw and lw radiation ! computations. !------------------------------------------------------------------------------------------------ use shr_kind_mod, only: r8 => shr_kind_r8 use ppgrid, only: pcols, pver, pverp use physconst, only: rga use physics_types, only: physics_state use phys_buffer, only: pbuf_size_max, pbuf_fld use radconstants, only: nrh, nswbands, nlwbands, idx_sw_diag, ot_length use rad_constituents, only: rad_cnst_get_clim_info, rad_cnst_get_clim_aer, & rad_cnst_get_clim_aer_props use wv_saturation, only: aqsat use cam_history, only: fieldname_len, addfld, phys_decomp, outfld, fillvalue, add_default use abortutils, only: endrun use constituents, only: pcnst implicit none private save public :: & aer_rad_props_init, & aer_rad_props_get_clim_sw, & ! return SW optical props of total bulk aerosols aer_rad_props_get_clim_lw ! return LW optical props of total bulk aerosols ! Private data character(len=fieldname_len), pointer :: odv_names(:) ! outfld names for visible OD !============================================================================== contains !============================================================================== subroutine aer_rad_props_init() 1,6 integer :: i integer :: numaerosols ! number of aerosols character(len=64), pointer :: aernames(:) ! aerosol names !---------------------------------------------------------------------------- call addfld ('AEROD_v ', '1', 1, 'A', & 'Total Aerosol Optical Depth in visible band', phys_decomp, flag_xyfill=.true.) call add_default ('AEROD_v', 1, ' ') ! Contributions to AEROD_v from individual aerosols (climate species). ! number of aerosols in climate list call rad_cnst_get_clim_info(naero=numaerosols) ! get names of aerosols allocate(aernames(numaerosols)) call rad_cnst_get_clim_info(aernames=aernames) ! create outfld names for visible OD and call addfld allocate(odv_names(numaerosols)) do i = 1, numaerosols odv_names(i) = 'ODV_'//trim(aernames(i)) call addfld (odv_names(i), '1', 1, 'A', & trim(aernames(i))//' optical depth in visible band', phys_decomp, flag_xyfill=.true.) call add_default (odv_names(i), 1, ' ') end do deallocate(aernames) end subroutine aer_rad_props_init !============================================================================== subroutine aer_rad_props_get_clim_sw(state, pbuf, nnite, idxnite, & 1,47 tau, tau_w, tau_w_g, tau_w_f,& diagnosticindex) ! return totaled (across all species) layer tau, omega, g, f ! for all spectral interval for aerosols affecting the climate ! this use should be removed when the relative humidity calc is fixed use physconst, only: epsilo #ifdef MODAL_AERO use modal_aero_data, only: maxd_amode use modal_aer_opt, only: modal_size_parameters, modal_aero_sw use modal_aer_opt, only: ncoef use phys_buffer, only: pbuf_get_fld_idx #endif ! Arguments type(physics_state), intent(in) :: state type(pbuf_fld), intent(in) :: pbuf(pbuf_size_max) integer, intent(in) :: nnite ! number of night columns integer, intent(in) :: idxnite(:) ! local column indices of night columns integer, optional, intent(in) :: diagnosticindex ! index (if present) to radiation diagnostic information real(r8), intent(out) :: tau (pcols,0:pver,nswbands) ! aerosol extinction optical depth real(r8), intent(out) :: tau_w (pcols,0:pver,nswbands) ! aerosol single scattering albedo * tau real(r8), intent(out) :: tau_w_g(pcols,0:pver,nswbands) ! aerosol assymetry parameter * tau * w real(r8), intent(out) :: tau_w_f(pcols,0:pver,nswbands) ! aerosol forward scattered fraction * tau * w ! Local variables integer :: ncol integer :: lchnk integer :: k, i ! lev and daycolumn indices integer :: iswband ! sw band indices ! optical props for each aerosol ! hygroscopic real(r8), pointer :: h_ext(:,:) real(r8), pointer :: h_ssa(:,:) real(r8), pointer :: h_asm(:,:) ! non-hygroscopic real(r8), pointer :: n_ext(:) real(r8), pointer :: n_ssa(:) real(r8), pointer :: n_asm(:) real(r8), pointer :: n_scat(:) real(r8), pointer :: n_ascat(:) ! radius-dependent real(r8), pointer :: r_ext(:,:) ! radius-dependent mass-specific extinction real(r8), pointer :: r_scat(:,:) real(r8), pointer :: r_ascat(:,:) real(r8), pointer :: r_mu(:) ! log(radius) domain variable for r_ext, r_scat, r_ascat ! radiative properties for each aerosol real(r8) :: ta (pcols,pver,nswbands) real(r8) :: tw (pcols,pver,nswbands) real(r8) :: twf(pcols,pver,nswbands) real(r8) :: twg(pcols,pver,nswbands) ! aerosol masses real(r8), pointer :: aermmr(:,:) ! mass mixing ratio of aerosols real(r8) :: mmr_to_mass(pcols,pver) ! conversion factor for mmr to mass real(r8) :: aermass(pcols,pver) ! mass of aerosols #ifdef MODAL_AERO real(r8), pointer, dimension(:,:,:) :: dgnumwet ! number mode diameter real(r8), pointer, dimension(:,:,:) :: qaerwat ! aerosol water ! real(r8) :: raer(pcols, pver, pcnst) real(r8) :: radsurf(pcols,pver,maxd_amode) ! aerosol surface mode radius real(r8) :: logradsurf(pcols,pver,maxd_amode) ! log(aerosol surface mode radius) real(r8) :: cheb(ncoef,maxd_amode,pcols,pver) integer :: ifld #endif ! for table lookup into rh grid real(r8) :: esat(pcols,pver) ! saturation vapor pressure real(r8) :: qsat(pcols,pver) ! saturation specific humidity real(r8) :: rh(pcols,pver) real(r8) :: rhtrunc(pcols,pver) real(r8) :: wrh(pcols,pver) integer :: krh(pcols,pver) integer :: iaerosol ! aerosol index integer :: numaerosols ! number of aerosols in climate list integer :: diag_idx = 0 ! idx for climate call character(len=ot_length) :: opticstype ! hygro or nonhygro !----------------------------------------------------------------------------- ncol = state%ncol lchnk = state%lchnk ! compute mixing ratio to mass conversion do k = 1, pver mmr_to_mass(:ncol,k) = rga * state%pdeldry(:ncol,k) enddo ! initialize to conditions that would cause failure tau (:,:,:) = -100._r8 tau_w (:,:,:) = -100._r8 tau_w_g (:,:,:) = -100._r8 tau_w_f (:,:,:) = -100._r8 ! top layer (ilev = 0) has no aerosol (ie tau = 0) ! also initialize rest of layers to accumulate od's tau (1:ncol,:,:) = 0._r8 tau_w (1:ncol,:,:) = 0._r8 tau_w_g(1:ncol,:,:) = 0._r8 tau_w_f(1:ncol,:,:) = 0._r8 ! calculate relative humidity for table lookup into rh grid call aqsat(state%t, state%pmid, esat, qsat, pcols, & ncol, pver, 1, pver) rh(1:ncol,1:pver) = state%q(1:ncol,1:pver,1) / qsat(1:ncol,1:pver) rhtrunc(1:ncol,1:pver) = min(rh(1:ncol,1:pver),1._r8) krh(1:ncol,1:pver) = min(floor( rhtrunc(1:ncol,1:pver) * nrh ) + 1, nrh - 1) ! index into rh mesh wrh(1:ncol,1:pver) = rhtrunc(1:ncol,1:pver) * nrh - krh(1:ncol,1:pver) ! (-) weighting on left side values ! number of aerosols in climate list if (present(diagnosticindex)) then call rad_cnst_get_clim_info(naero=numaerosols, diagnosticindex=diagnosticindex) diag_idx = diagnosticindex else call rad_cnst_get_clim_info(naero=numaerosols) diag_idx = 0 endif #ifdef MODAL_AERO if (present(diagnosticindex)) then tau (1:ncol,:,:) = 0._r8 tau_w (1:ncol,:,:) = 0._r8 tau_w_g(1:ncol,:,:) = 0._r8 tau_w_f(1:ncol,:,:) = 0._r8 ! Loop over aerosols in climate list do iaerosol = 1, numaerosols ! get aerosol type call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype, diagnosticindex=diagnosticindex) if (trim(opticstype) == 'volcanic_radius') then ! get aerosol mass mixing ratio call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr, diagnosticindex=diagnosticindex) aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver) ! get optical properties for volcanic aerosols call rad_cnst_get_clim_aer_props(iaerosol, r_sw_ext=r_ext, r_sw_scat=r_scat, r_sw_ascat=r_ascat, mu=r_mu, diagnosticindex=diagnosticindex) call get_volcanic_radius_rad_props(lchnk, ncol, aermass, pbuf, r_ext, r_scat, r_ascat, r_mu, ta, tw, twg, twf) tau (1:ncol,1:pver,:) = tau (1:ncol,1:pver,:) + ta (1:ncol,:,:) tau_w (1:ncol,1:pver,:) = tau_w (1:ncol,1:pver,:) + tw (1:ncol,:,:) tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:) tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:) call aer_vis_diag_out(lchnk, ncol, nnite, idxnite, iaerosol, ta(:,:,idx_sw_diag), diag_idx) end if end do else ifld = pbuf_get_fld_idx('DGNUMWET') dgnumwet => pbuf(ifld)%fld_ptr(1,1:pcols,1:pver,lchnk,1:maxd_amode) ifld = pbuf_get_fld_idx( 'QAERWAT' ) if ( associated(pbuf(ifld)%fld_ptr) ) then qaerwat => pbuf(ifld)%fld_ptr( 1, 1:pcols, 1:pver, lchnk, 1:maxd_amode ) else call endrun( 'pbuf for QAERWAT not allocated in modal_size_parameters' ) end if call modal_size_parameters(ncol, dgnumwet, radsurf, logradsurf, cheb) ! raer(:,:,:)=state%q(:,:,:) ! for starters. later use rad_cnst_get_clim_aer do iswband=1,nswbands call modal_aero_sw(ncol, lchnk, iswband, nnite, idxnite, tau(1,0,iswband), tau_w(1,0,iswband), & tau_w_g(1,0,iswband), tau_w_f(1,0,iswband), state%q, state, qaerwat, radsurf, logradsurf, cheb ) end do ! Loop over aerosols in climate list do iaerosol = 1, numaerosols ! get aerosol type call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype) if (trim(opticstype) == 'volcanic_radius') then ! get aerosol mass mixing ratio call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr) aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver) ! get optical properties for volcanic aerosols call rad_cnst_get_clim_aer_props(iaerosol, r_sw_ext=r_ext, r_sw_scat=r_scat, r_sw_ascat=r_ascat, mu=r_mu ) call get_volcanic_radius_rad_props(lchnk, ncol, aermass, pbuf, r_ext, r_scat, r_ascat, r_mu, ta, tw, twg, twf) tau (1:ncol,1:pver,:) = tau (1:ncol,1:pver,:) + ta (1:ncol,:,:) tau_w (1:ncol,1:pver,:) = tau_w (1:ncol,1:pver,:) + tw (1:ncol,:,:) tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:) tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:) ! diagnostic output of individual aerosol optical properties ! currently implemented for climate list only call aer_vis_diag_out(lchnk, ncol, nnite, idxnite, iaerosol, ta(:,:,idx_sw_diag), diag_idx) end if end do end if #else ! Loop over aerosols in climate list do iaerosol = 1, numaerosols ! get aerosol mass mixing ratio if (present(diagnosticindex)) then call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr, diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr) endif aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver) ! get aerosol type if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype, diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype) endif select case (trim(opticstype)) case('hygro') ! get optical properties for hygroscopic aerosols if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, sw_hygro_ext=h_ext, sw_hygro_ssa=h_ssa, sw_hygro_asm=h_asm, & diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, sw_hygro_ext=h_ext, sw_hygro_ssa=h_ssa, sw_hygro_asm=h_asm) endif call get_hygro_rad_props(ncol, krh, wrh, aermass, h_ext, h_ssa, h_asm, ta, tw, twg, twf) tau (1:ncol,1:pver,:) = tau (1:ncol,1:pver,:) + ta (1:ncol,:,:) tau_w (1:ncol,1:pver,:) = tau_w (1:ncol,1:pver,:) + tw (1:ncol,:,:) tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:) tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:) case('hygroscopic','hygroscopi') ! get optical properties for hygroscopic aerosols if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, sw_hygro_ext=h_ext, sw_hygro_ssa=h_ssa, sw_hygro_asm=h_asm, & diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, sw_hygro_ext=h_ext, sw_hygro_ssa=h_ssa, sw_hygro_asm=h_asm) endif call get_hygro_rad_props(ncol, krh, wrh, aermass, h_ext, h_ssa, h_asm, ta, tw, twg, twf) tau (1:ncol,1:pver,:) = tau (1:ncol,1:pver,:) + ta (1:ncol,:,:) tau_w (1:ncol,1:pver,:) = tau_w (1:ncol,1:pver,:) + tw (1:ncol,:,:) tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:) tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:) case('nonhygro') ! get optical properties for non-hygroscopic aerosols if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_ssa=n_ssa, sw_nonhygro_asm=n_asm, & diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_ssa=n_ssa, sw_nonhygro_asm=n_asm) endif call get_nonhygro_rad_props(ncol, aermass, n_ext, n_ssa, n_asm, ta, tw, twg, twf) tau (1:ncol,1:pver,:) = tau (1:ncol,1:pver,:) + ta (1:ncol,:,:) tau_w (1:ncol,1:pver,:) = tau_w (1:ncol,1:pver,:) + tw (1:ncol,:,:) tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:) tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:) case('insoluble ') ! get optical properties for non-hygroscopic aerosols if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_ssa=n_ssa, sw_nonhygro_asm=n_asm, & diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_ssa=n_ssa, sw_nonhygro_asm=n_asm) endif call get_nonhygro_rad_props(ncol, aermass, n_ext, n_ssa, n_asm, ta, tw, twg, twf) tau (1:ncol,1:pver,:) = tau (1:ncol,1:pver,:) + ta (1:ncol,:,:) tau_w (1:ncol,1:pver,:) = tau_w (1:ncol,1:pver,:) + tw (1:ncol,:,:) tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:) tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:) case('volcanic') ! get optical properties for volcanic aerosols if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_scat=n_scat, sw_nonhygro_ascat=n_ascat,& diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_scat=n_scat, sw_nonhygro_ascat=n_ascat) endif call get_volcanic_rad_props(ncol, aermass, n_ext, n_scat, n_ascat, ta, tw, twg, twf) tau (1:ncol,1:pver,:) = tau (1:ncol,1:pver,:) + ta (1:ncol,:,:) tau_w (1:ncol,1:pver,:) = tau_w (1:ncol,1:pver,:) + tw (1:ncol,:,:) tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:) tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:) case('volcanic_radius') ! get optical properties for volcanic aerosols if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, r_sw_ext=r_ext, r_sw_scat=r_scat, r_sw_ascat=r_ascat, mu=r_mu,& diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, r_sw_ext=r_ext, r_sw_scat=r_scat, r_sw_ascat=r_ascat, mu=r_mu ) endif call get_volcanic_radius_rad_props(lchnk, ncol, aermass, pbuf, r_ext, r_scat, r_ascat, r_mu, ta, tw, twg, twf) tau (1:ncol,1:pver,:) = tau (1:ncol,1:pver,:) + ta (1:ncol,:,:) tau_w (1:ncol,1:pver,:) = tau_w (1:ncol,1:pver,:) + tw (1:ncol,:,:) tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:) tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:) case('zero') ! no effect of "zero" aerosols, so update nothing case default call endrun('aer_rad_props_get_clim_sw: unsupported opticstype :'//trim(opticstype)//':') end select ! diagnostic output of individual aerosol optical properties ! currently implemented for climate list only call aer_vis_diag_out(lchnk, ncol, nnite, idxnite, iaerosol, ta(:,:,idx_sw_diag), diag_idx) enddo #endif ! diagnostic output of total aerosol optical properties ! currently implemented for climate list only call aer_vis_diag_out(lchnk, ncol, nnite, idxnite, 0, tau(:,:,idx_sw_diag), diag_idx) end subroutine aer_rad_props_get_clim_sw !============================================================================== subroutine aer_rad_props_get_clim_lw(state, pbuf, odap_aer, diagnosticindex) 1,32 use physconst, only: epsilo use radconstants, only: ot_length use phys_buffer, only: pbuf_get_fld_idx #ifdef MODAL_AERO use modal_aer_opt, only: modal_aero_lw #endif ! Purpose: Compute aerosol transmissions needed in absorptivity/ ! emissivity calculations ! lw extinction is the same representation for all ! species. If this changes, this routine will need to do something ! similar to the sw with routines like get_hygro_lw_abs ! Arguments type(physics_state), intent(in) :: state type(pbuf_fld), intent(in) :: pbuf(pbuf_size_max) real(r8), intent(out) :: odap_aer(pcols,pver,nlwbands) ! [fraction] absorption optical depth, per layer integer, optional, intent(in) :: diagnosticindex ! Local variables integer :: bnd_idx ! LW band index integer :: i ! column index integer :: k ! lev index integer :: ncol ! number of columns integer :: lchnk ! chunk index integer :: iaerosol ! index into aerosol list integer :: numaerosols ! index into aerosol list character(len=ot_length) :: opticstype ! hygro or nonhygro ! optical props for each aerosol real(r8), pointer :: lw_abs(:) real(r8), pointer :: lw_hygro_abs(:,:) real(r8), pointer :: geometric_radius(:,:) ! volcanic lookup table real(r8), pointer :: r_lw_abs(:,:) ! radius dependent mass-specific absorption coefficient real(r8), pointer :: r_mu(:) ! log(geometric_mean_radius) domain samples of r_lw_abs(:,:) integer :: idx ! index to pbuf for geometric radius real(r8) :: mu(pcols,pver) ! log(geometric_radius) real(r8) :: r_mu_min, r_mu_max, wmu, mutrunc integer :: nmu, kmu ! for table lookup into rh grid real(r8) :: esat(pcols,pver) ! saturation vapor pressure real(r8) :: qsat(pcols,pver) ! saturation specific humidity real(r8) :: rh(pcols,pver) real(r8) :: rhtrunc(pcols,pver) real(r8) :: wrh(pcols,pver) integer :: krh(pcols,pver) ! aerosol (vertical) mass path and extinction ! aerosol masses real(r8), pointer :: aermmr(:,:) ! mass mixing ratio of aerosols real(r8) :: mmr_to_mass(pcols,pver) ! conversion factor for mmr to mass real(r8) :: aermass(pcols,pver) ! mass of aerosols #ifdef MODAL_AERO ! real(r8) :: raer(pcols, pver, pcnst) #endif !----------------------------------------------------------------------------- ncol = state%ncol lchnk = state%lchnk #ifdef MODAL_AERO if (present(diagnosticindex)) then odap_aer = 0._r8 ! hack to get volcanic aerosols into mam ! number of aerosols in climate list call rad_cnst_get_clim_info(naero=numaerosols, diagnosticindex=diagnosticindex) ! Loop over aerosols in climate list do iaerosol = 1, numaerosols call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype, diagnosticindex=diagnosticindex) if (trim(opticstype) == 'volcanic_radius') then ! compute mixing ratio to mass conversion do k = 1, pver mmr_to_mass(:ncol,k) = rga * state%pdeldry(:ncol,k) end do ! compute optical depths odap_aer (summed over all aerosols) ! total optical depths from top (odap_aer_ttl) ! calculate relative humidity for table lookup into rh grid call aqsat(state%t, state%pmid, esat, qsat, pcols, & ncol, pver, 1, pver) rh(1:ncol,1:pver) = state%q(1:ncol,1:pver,1) / qsat(1:ncol,1:pver) rhtrunc(1:ncol,1:pver) = min(rh(1:ncol,1:pver),1._r8) krh(1:ncol,1:pver) = min(floor( rhtrunc(1:ncol,1:pver) * nrh ) + 1, nrh - 1) ! index into rh mesh wrh(1:ncol,1:pver) = rhtrunc(1:ncol,1:pver) * nrh - krh(1:ncol,1:pver) ! (-) weighting on left side values ! get aerosol mass mixing ratio call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr, diagnosticindex=diagnosticindex) aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver) ! get optical properties for hygroscopic aerosols call rad_cnst_get_clim_aer_props(iaerosol, r_lw_abs=r_lw_abs, mu=r_mu, diagnosticindex=diagnosticindex) ! get microphysical properties for volcanic aerosols idx = pbuf_get_fld_idx('VOLC_RAD_GEOM') geometric_radius => pbuf(idx)%fld_ptr( 1, 1:pcols, 1:pver, lchnk, 1) ! interpolate in radius ! caution: clip the table with no warning when outside bounds nmu = size(r_mu) r_mu_max = r_mu(nmu) r_mu_min = r_mu(1) do i = 1, ncol do k = 1, pver if(geometric_radius(i,k) > 0.) then mu(i,k) = log(geometric_radius(i,k)) else mu(i,k) = 0. endif mutrunc = max(min(mu(i,k),r_mu_max),r_mu_min) kmu = max(min(1 + (mutrunc-r_mu_min)/(r_mu_max-r_mu_min)*(nmu-1),nmu-1._r8),1._r8) wmu = max(min( (mutrunc -r_mu(kmu)) / (r_mu(kmu+1) - r_mu(kmu)) ,1._r8),0._r8) do bnd_idx = 1, nlwbands odap_aer(i,k,bnd_idx) = odap_aer(i,k,bnd_idx) + & aermass(i,k) * & ((1._r8 - wmu) * r_lw_abs(bnd_idx, kmu ) + & (wmu) * r_lw_abs(bnd_idx, kmu+1)) end do enddo enddo end if enddo else ! raer(:,:,:)=state%q(:,:,:) ! for starters. later use rad_cnst_get_clim_aer call modal_aero_lw(ncol,lchnk, pbuf, state%q, odap_aer, state%pdeldry) ! hack to get volcanic aerosols into mam ! number of aerosols in climate list call rad_cnst_get_clim_info(naero=numaerosols) ! Loop over aerosols in climate list do iaerosol = 1, numaerosols call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype) if (trim(opticstype) == 'volcanic_radius') then ! compute mixing ratio to mass conversion do k = 1, pver mmr_to_mass(:ncol,k) = rga * state%pdeldry(:ncol,k) end do ! compute optical depths odap_aer (summed over all aerosols) ! total optical depths from top (odap_aer_ttl) ! calculate relative humidity for table lookup into rh grid call aqsat(state%t, state%pmid, esat, qsat, pcols, & ncol, pver, 1, pver) rh(1:ncol,1:pver) = state%q(1:ncol,1:pver,1) / qsat(1:ncol,1:pver) rhtrunc(1:ncol,1:pver) = min(rh(1:ncol,1:pver),1._r8) krh(1:ncol,1:pver) = min(floor( rhtrunc(1:ncol,1:pver) * nrh ) + 1, nrh - 1) ! index into rh mesh wrh(1:ncol,1:pver) = rhtrunc(1:ncol,1:pver) * nrh - krh(1:ncol,1:pver) ! (-) weighting on left side values ! get aerosol mass mixing ratio call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr) aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver) ! get optical properties for hygroscopic aerosols call rad_cnst_get_clim_aer_props(iaerosol, r_lw_abs=r_lw_abs, mu=r_mu) ! get microphysical properties for volcanic aerosols idx = pbuf_get_fld_idx('VOLC_RAD_GEOM') geometric_radius => pbuf(idx)%fld_ptr( 1, 1:pcols, 1:pver, lchnk, 1) ! interpolate in radius ! caution: clip the table with no warning when outside bounds nmu = size(r_mu) r_mu_max = r_mu(nmu) r_mu_min = r_mu(1) do i = 1, ncol do k = 1, pver if(geometric_radius(i,k) > 0.) then mu(i,k) = log(geometric_radius(i,k)) else mu(i,k) = 0. endif mutrunc = max(min(mu(i,k),r_mu_max),r_mu_min) kmu = max(min(1 + (mutrunc-r_mu_min)/(r_mu_max-r_mu_min)*(nmu-1),nmu-1._r8),1._r8) wmu = max(min( (mutrunc -r_mu(kmu)) / (r_mu(kmu+1) - r_mu(kmu)) ,1._r8),0._r8) do bnd_idx = 1, nlwbands odap_aer(i,k,bnd_idx) = odap_aer(i,k,bnd_idx) + & aermass(i,k) * & ((1._r8 - wmu) * r_lw_abs(bnd_idx, kmu ) + & (wmu) * r_lw_abs(bnd_idx, kmu+1)) end do enddo enddo end if enddo end if #else ! compute mixing ratio to mass conversion do k = 1, pver mmr_to_mass(:ncol,k) = rga * state%pdeldry(:ncol,k) end do ! compute optical depths odap_aer (summed over all aerosols) ! total optical depths from top (odap_aer_ttl) odap_aer = 0._r8 ! calculate relative humidity for table lookup into rh grid call aqsat(state%t, state%pmid, esat, qsat, pcols, & ncol, pver, 1, pver) rh(1:ncol,1:pver) = state%q(1:ncol,1:pver,1) / qsat(1:ncol,1:pver) rhtrunc(1:ncol,1:pver) = min(rh(1:ncol,1:pver),1._r8) krh(1:ncol,1:pver) = min(floor( rhtrunc(1:ncol,1:pver) * nrh ) + 1, nrh - 1) ! index into rh mesh wrh(1:ncol,1:pver) = rhtrunc(1:ncol,1:pver) * nrh - krh(1:ncol,1:pver) ! (-) weighting on left side values ! number of aerosols in climate list if (present(diagnosticindex)) then call rad_cnst_get_clim_info(naero=numaerosols, diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_info(naero=numaerosols) endif ! Loop over aerosols in climate list do iaerosol = 1, numaerosols ! get aerosol mass mixing ratio if (present(diagnosticindex)) then call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr, diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr) endif aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver) if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype, diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype) endif select case (trim(opticstype)) case('hygroscopic') ! get optical properties for hygroscopic aerosols if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, lw_hygro_ext=lw_hygro_abs, diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, lw_hygro_ext=lw_hygro_abs) endif do bnd_idx = 1, nlwbands do k = 1, pver do i = 1, ncol odap_aer(i, k, bnd_idx) = odap_aer(i, k, bnd_idx) + & aermass(i, k) * & ((1 + wrh(i,k)) * lw_hygro_abs(krh(i,k)+1,bnd_idx) & - wrh(i,k) * lw_hygro_abs(krh(i,k), bnd_idx)) enddo enddo enddo case('insoluble','nonhygro','hygro','volcanic') ! get optical properties for hygroscopic aerosols if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, lw_ext=lw_abs, diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, lw_ext=lw_abs) endif do bnd_idx = 1, nlwbands do k = 1, pver do i = 1, ncol odap_aer(i,k,bnd_idx) = odap_aer(i,k,bnd_idx) + lw_abs(bnd_idx)*aermass(i,k) end do end do end do case('volcanic_radius') ! get optical properties for hygroscopic aerosols if (present(diagnosticindex)) then call rad_cnst_get_clim_aer_props(iaerosol, r_lw_abs=r_lw_abs, mu=r_mu, diagnosticindex=diagnosticindex) else call rad_cnst_get_clim_aer_props(iaerosol, r_lw_abs=r_lw_abs, mu=r_mu) endif ! get microphysical properties for volcanic aerosols idx = pbuf_get_fld_idx('VOLC_RAD_GEOM') geometric_radius => pbuf(idx)%fld_ptr( 1, 1:pcols, 1:pver, lchnk, 1) ! interpolate in radius ! caution: clip the table with no warning when outside bounds nmu = size(r_mu) r_mu_max = r_mu(nmu) r_mu_min = r_mu(1) do i = 1, ncol do k = 1, pver if(geometric_radius(i,k) > 0.) then mu(i,k) = log(geometric_radius(i,k)) else mu(i,k) = 0. endif mutrunc = max(min(mu(i,k),r_mu_max),r_mu_min) kmu = max(min(1 + (mutrunc-r_mu_min)/(r_mu_max-r_mu_min)*(nmu-1),nmu-1._r8),1._r8) wmu = max(min( (mutrunc -r_mu(kmu)) / (r_mu(kmu+1) - r_mu(kmu)) ,1._r8),0._r8) do bnd_idx = 1, nlwbands odap_aer(i,k,bnd_idx) = odap_aer(i,k,bnd_idx) + & aermass(i,k) * & ((1._r8 - wmu) * r_lw_abs(bnd_idx, kmu ) + & (wmu) * r_lw_abs(bnd_idx, kmu+1)) end do enddo enddo case('zero') ! zero aerosols types have no optical effect, so do nothing. case default call endrun('aer_rad_props_get_clim_lw: unsupported opticstype: '//trim(opticstype)) end select end do #endif end subroutine aer_rad_props_get_clim_lw !============================================================================== ! Private methods !============================================================================== subroutine get_hygro_rad_props(ncol, krh, wrh, mass, ext, ssa, asm, & 2 tau, tau_w, tau_w_g, tau_w_f) ! Arguments integer, intent(in) :: ncol integer, intent(in) :: krh(pcols,pver) ! index for linear interpolation of optics on rh real(r8), intent(in) :: wrh(pcols,pver) ! weight for linear interpolation of optics on rh real(r8), intent(in) :: mass(pcols,pver) real(r8), intent(in) :: ext(:,:) real(r8), intent(in) :: ssa(:,:) real(r8), intent(in) :: asm(:,:) real(r8), intent(out) :: tau (pcols,pver,nswbands) real(r8), intent(out) :: tau_w (pcols,pver,nswbands) real(r8), intent(out) :: tau_w_g(pcols,pver,nswbands) real(r8), intent(out) :: tau_w_f(pcols,pver,nswbands) ! Local variables real(r8) :: ext1, ssa1, asm1 integer :: icol, ilev, iswband !----------------------------------------------------------------------------- do iswband = 1, nswbands do icol = 1, ncol do ilev = 1, pver ext1 = (1 + wrh(icol,ilev)) * ext(krh(icol,ilev)+1,iswband) & - wrh(icol,ilev) * ext(krh(icol,ilev), iswband) ssa1 = (1 + wrh(icol,ilev)) * ssa(krh(icol,ilev)+1,iswband) & - wrh(icol,ilev) * ssa(krh(icol,ilev), iswband) asm1 = (1 + wrh(icol,ilev)) * asm(krh(icol,ilev)+1,iswband) & - wrh(icol,ilev) * asm(krh(icol,ilev), iswband) tau (icol, ilev, iswband) = mass(icol, ilev) * ext1 tau_w (icol, ilev, iswband) = mass(icol, ilev) * ext1 * ssa1 tau_w_g(icol, ilev, iswband) = mass(icol, ilev) * ext1 * ssa1 * asm1 tau_w_f(icol, ilev, iswband) = mass(icol, ilev) * ext1 * ssa1 * asm1 * asm1 enddo enddo enddo end subroutine get_hygro_rad_props !============================================================================== subroutine get_nonhygro_rad_props(ncol, mass, ext, ssa, asm, & 2 tau, tau_w, tau_w_g, tau_w_f) ! Arguments integer, intent(in) :: ncol real(r8), intent(in) :: mass(pcols, pver) real(r8), intent(in) :: ext(:) real(r8), intent(in) :: ssa(:) real(r8), intent(in) :: asm(:) real(r8), intent(out) :: tau (pcols, pver, nswbands) real(r8), intent(out) :: tau_w (pcols, pver, nswbands) real(r8), intent(out) :: tau_w_g(pcols, pver, nswbands) real(r8), intent(out) :: tau_w_f(pcols, pver, nswbands) ! Local variables integer :: iswband real(r8) :: ext1, ssa1, asm1 !----------------------------------------------------------------------------- do iswband = 1, nswbands ext1 = ext(iswband) ssa1 = ssa(iswband) asm1 = asm(iswband) tau (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1 tau_w (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1 * ssa1 tau_w_g(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1 * ssa1 * asm1 tau_w_f(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1 * ssa1 * asm1 * asm1 enddo end subroutine get_nonhygro_rad_props !============================================================================== subroutine get_volcanic_radius_rad_props(lchnk, ncol, mass, pbuf, r_ext, r_scat, r_ascat, r_mu, & 3,2 tau, tau_w, tau_w_g, tau_w_f) use phys_buffer, only: pbuf_get_fld_idx, pbuf_size_max ! Arguments integer, intent(in) :: lchnk integer, intent(in) :: ncol real(r8), intent(in) :: mass(pcols, pver) type(pbuf_fld), intent(in) :: pbuf(pbuf_size_max) real(r8), intent(in) :: r_ext(:,:) real(r8), intent(in) :: r_scat(:,:) real(r8), intent(in) :: r_ascat(:,:) real(r8), intent(in) :: r_mu(:) ! log(radius) domain of mass-specific optics real(r8), intent(out) :: tau (pcols, pver, nswbands) real(r8), intent(out) :: tau_w (pcols, pver, nswbands) real(r8), intent(out) :: tau_w_g(pcols, pver, nswbands) real(r8), intent(out) :: tau_w_f(pcols, pver, nswbands) ! Local variables integer :: iswband real(r8) :: g integer :: idx ! index to radius in physics buffer real(r8), pointer :: geometric_radius(:,:) ! geometric mean radius of volcanic aerosol real(r8) :: mu(pcols,pver) ! log(geometric mean radius of volcanic aerosol) integer :: imu ! index into table values of log radius integer :: kmu, nmu real(r8) :: wmu, mutrunc, r_mu_max, r_mu_min ! interpolated values from table real(r8) :: ext(nswbands) real(r8) :: scat(nswbands) real(r8) :: ascat(nswbands) integer :: i, k ! column level iterator !----------------------------------------------------------------------------- tau =0._r8 tau_w =0._r8 tau_w_g=0._r8 tau_w_f=0._r8 ! get microphysical properties for volcanic aerosols idx = pbuf_get_fld_idx('VOLC_RAD_GEOM') geometric_radius => pbuf(idx)%fld_ptr( 1, 1:pcols, 1:pver, lchnk, 1 ) ! interpolate in radius ! caution: clip the table with no warning when outside bounds nmu = size(r_mu) r_mu_max = r_mu(nmu) r_mu_min = r_mu(1) do i = 1, ncol do k = 1, pver if(geometric_radius(i,k) > 0.) then mu(i,k) = log(geometric_radius(i,k)) else mu(i,k) = 0. endif mutrunc = max(min(mu(i,k),r_mu_max),r_mu_min) kmu = max(min(1 + (mutrunc-r_mu_min)/(r_mu_max-r_mu_min)*(nmu-1),nmu-1._r8),1._r8) wmu = max(min( (mutrunc -r_mu(kmu)) / (r_mu(kmu+1) - r_mu(kmu)) ,1._r8),0._r8) do iswband = 1, nswbands ext(iswband) = & ((1._r8 - wmu) * r_ext(iswband, kmu ) + & (wmu) * r_ext(iswband, kmu+1)) scat(iswband) = & ((1._r8 - wmu) * r_scat(iswband, kmu ) + & (wmu) * r_scat(iswband, kmu+1)) ascat(iswband) = & ((1._r8 - wmu) * r_ascat(iswband, kmu ) + & (wmu) * r_ascat(iswband, kmu+1)) if (scat(iswband).gt.0.) then g = ascat(iswband)/scat(iswband) else g=0._r8 endif tau (i,k,iswband) = mass(i,k) * ext(iswband) tau_w (i,k,iswband) = mass(i,k) * scat(iswband) tau_w_g(i,k,iswband) = mass(i,k) * ascat(iswband) tau_w_f(i,k,iswband) = mass(i,k) * g * ascat(iswband) end do enddo enddo end subroutine get_volcanic_radius_rad_props !============================================================================== subroutine get_volcanic_rad_props(ncol, mass, ext, scat, ascat, & 1 tau, tau_w, tau_w_g, tau_w_f) ! Arguments integer, intent(in) :: ncol real(r8), intent(in) :: mass(pcols, pver) real(r8), intent(in) :: ext(:) real(r8), intent(in) :: scat(:) real(r8), intent(in) :: ascat(:) real(r8), intent(out) :: tau (pcols, pver, nswbands) real(r8), intent(out) :: tau_w (pcols, pver, nswbands) real(r8), intent(out) :: tau_w_g(pcols, pver, nswbands) real(r8), intent(out) :: tau_w_f(pcols, pver, nswbands) ! Local variables integer :: iswband real(r8) :: g !----------------------------------------------------------------------------- do iswband = 1, nswbands if (scat(iswband).gt.0.) then g = ascat(iswband)/scat(iswband) else g=0._r8 endif tau (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext(iswband) tau_w (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * scat(iswband) tau_w_g(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ascat(iswband) tau_w_f(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * g * ascat(iswband) enddo end subroutine get_volcanic_rad_props !============================================================================== subroutine aer_vis_diag_out(lchnk, ncol, nnite, idxnite, iaer, tau, diag_idx) 4,2 ! output aerosol optical depth for the visible band integer, intent(in) :: lchnk integer, intent(in) :: ncol ! number of columns integer, intent(in) :: nnite ! number of night columns integer, intent(in) :: idxnite(:) ! local column indices of night columns integer, intent(in) :: iaer ! aerosol index -- if 0 then tau is a total for all aerosols real(r8), intent(in) :: tau(:,:) ! aerosol optical depth for the visible band integer, intent(in) :: diag_idx ! identifies whether the aerosol optics ! is for the climate calc or a diagnostic calc ! Local variables integer :: i real(r8) :: tmp(pcols) !----------------------------------------------------------------------------- ! currently only implemented for climate calc if (diag_idx > 0) return ! compute total column aerosol optical depth tmp(1:ncol) = sum(tau(1:ncol,:), 2) ! use fillvalue to indicate night columns do i = 1, nnite tmp(idxnite(i)) = fillvalue end do if (iaer > 0) then call outfld(odv_names(iaer), tmp, pcols, lchnk) else call outfld('AEROD_v', tmp, pcols, lchnk) end if end subroutine aer_vis_diag_out !============================================================================== end module aer_rad_props