LCOV - code coverage report
Current view: | top level - physics/cam - aer_rad_props.F90 (source / functions) |  |  | Hit | Total | Coverage
Test: | coverage.info |  | Lines: | 89 | 245 | 36.3 %
Date: | 2025-01-13 21:54:50 |  | Functions: | 4 | 8 | 50.0 %

              Line data    Source code
           1             : module aer_rad_props
           2             :
           3             : !------------------------------------------------------------------------------------------------
           4             : ! Converts aerosol masses to bulk optical properties for sw and lw radiation
           5             : ! computations.
           6             : !------------------------------------------------------------------------------------------------
           7             :
           8             : use shr_kind_mod,     only: r8 => shr_kind_r8
           9             : use ppgrid,           only: pcols, pver
          10             : use physconst,        only: rga
          11             : use physics_types,    only: physics_state
          12             :
          13             : use physics_buffer,   only: physics_buffer_desc
          14             : use radconstants,     only: nswbands, nlwbands, idx_sw_diag
          15             : use phys_prop,        only: nrh, ot_length
          16             : use rad_constituents, only: rad_cnst_get_info, rad_cnst_get_aer_mmr, &
          17             :                             rad_cnst_get_aer_props
          18             : use wv_saturation,    only: qsat
          19             : use aerosol_optics_cam,only: aerosol_optics_cam_init, aerosol_optics_cam_sw, aerosol_optics_cam_lw
          20             : use cam_history,      only: fieldname_len, addfld, outfld, add_default, horiz_only
          21             : use cam_history_support, only : fillvalue
          22             : ! Placed here due to PGI bug.
          23             : use ref_pres,         only: clim_modal_aero_top_lev
          24             :
          25             : use cam_abortutils,   only: endrun
          26             :
          27             : implicit none
          28             : private
          29             : save
          30             :
          31             : integer :: top_lev = 1
          32             :
          33             : public :: &
          34             :    aer_rad_props_init,        &
          35             :    aer_rad_props_sw,          & ! return SW optical props of aerosols
          36             :    aer_rad_props_lw             ! return LW optical props of aerosols
          37             :
          38             : ! Private data
          39             : character(len=fieldname_len), pointer :: odv_names(:)  ! outfld names for visible OD
          40             :
          41             :
          42             : !==============================================================================
          43             : contains
          44             : !==============================================================================
          45             :
          46        1536 : subroutine aer_rad_props_init()
          47             :    use phys_control, only: phys_getopts
          48             :
          49             :
          50             :    integer                    :: i
          51             :    integer                    :: numaerosols  ! number of aerosols
          52        1536 :    character(len=64), pointer :: aernames(:)  ! aerosol names
          53             :    logical                    :: history_amwg         ! output the variables used by the AMWG diag package
          54             :    logical                    :: history_aero_optics  ! Output aerosol optics diagnostics
          55             :    logical                    :: history_dust         ! Output dust diagnostics
          56             :    logical                    :: prog_modal_aero      ! Prognostic modal aerosols present
          57             :    integer                    :: nmodes          ! number of aerosol modes
          58             :
          59             :    !----------------------------------------------------------------------------
          60             :
          61             :    call phys_getopts( history_aero_optics_out    = history_aero_optics, &
          62             :                       history_amwg_out           = history_amwg,    &
          63             :                       history_dust_out           = history_dust,    &
          64        1536 :                       prog_modal_aero_out        = prog_modal_aero )
          65             :
          66             :    ! Limit modal aerosols with top_lev here.
          67        1536 :    if (prog_modal_aero) top_lev = clim_modal_aero_top_lev
          68             :
          69             :    call addfld ('AEROD_v ', horiz_only, 'A', '1', &
          70        1536 :       'Total Aerosol Optical Depth in visible band', flag_xyfill=.true.)
          71             :
          72             :    call addfld ('AODvstrt', horiz_only, 'A', '1', &
          73        1536 :       'Stratospheric Aerosol Optical Depth in visible band', flag_xyfill=.true.)
          74             :
          75             :    ! Contributions to AEROD_v from individual aerosols (climate species).
          76             :
          77             :    ! number of bulk aerosols in climate list
          78        1536 :    call rad_cnst_get_info(0, naero=numaerosols)
          79             :
          80             :    ! get names of bulk aerosols
          81        3072 :    allocate(aernames(numaerosols))
          82        1536 :    call rad_cnst_get_info(0, aernames=aernames, nmodes=nmodes)
          83             :
          84             :    ! diagnostic output for bulk aerosols
          85             :    ! create outfld names for visible OD
          86        3072 :    allocate(odv_names(numaerosols))
          87        1536 :    do i = 1, numaerosols
          88           0 :       odv_names(i) = 'ODV_'//trim(aernames(i))
          89           0 :       call addfld (odv_names(i), horiz_only, 'A', '1', &
          90        1536 :          trim(aernames(i))//' optical depth in visible band', flag_xyfill=.true.)
          91             :    end do
          92             :
          93             :    ! Determine default fields
          94        1536 :    if (history_amwg .or. history_dust ) then
          95        1536 :       call add_default ('AEROD_v', 1, ' ')
          96             :    endif
          97             :
          98        1536 :    if ( history_aero_optics ) then
          99           0 :       call add_default ('AEROD_v', 1, ' ')
         100           0 :       do i = 1, numaerosols
         101           0 :          odv_names(i) = 'ODV_'//trim(aernames(i))
         102           0 :          call add_default (odv_names(i), 1, ' ')
         103             :       end do
         104             :     endif
         105             :
         106        1536 :     if (nmodes > 0) then
         107           0 :        call aerosol_optics_cam_init()
         108             :     end if
         109             :
         110        1536 :    deallocate(aernames)
         111             :
         112        1536 : end subroutine aer_rad_props_init
         113             :
         114             : !==============================================================================
         115             :
         116      749232 : subroutine aer_rad_props_sw(list_idx, state, pbuf,  nnite, idxnite, &
         117             :                             tau, tau_w, tau_w_g, tau_w_f)
         118             :
         119             :    ! Return bulk layer tau, omega, g, f for all spectral intervals.
         120             :
         121             :    use physics_buffer, only : physics_buffer_desc
         122             :    use tropopause,     only : tropopause_find_cam
         123             :    ! Arguments
         124             :    integer,             intent(in) :: list_idx      ! index of the climate or a diagnostic list
         125             :    type(physics_state), intent(in), target :: state
         126             :
         127             :    type(physics_buffer_desc), pointer :: pbuf(:)
         128             :    integer,             intent(in) :: nnite                ! number of night columns
         129             :    integer,             intent(in) :: idxnite(:)           ! local column indices of night columns
         130             :
         131             :    real(r8), intent(out) :: tau    (pcols,0:pver,nswbands) ! aerosol extinction optical depth
         132             :    real(r8), intent(out) :: tau_w  (pcols,0:pver,nswbands) ! aerosol single scattering albedo * tau
         133             :    real(r8), intent(out) :: tau_w_g(pcols,0:pver,nswbands) ! aerosol asymmetry parameter * tau * w
         134             :    real(r8), intent(out) :: tau_w_f(pcols,0:pver,nswbands) ! aerosol forward scattered fraction * tau * w
         135             :
         136             :    ! Local variables
         137             :
         138             :    integer :: ncol
         139             :    integer :: lchnk
         140             :    integer :: k       ! index
         141             :    integer :: troplev(pcols)
         142             :
         143             :    ! optical props for each aerosol
         144             :    ! hygroscopic
         145      749232 :    real(r8), pointer :: h_ext(:,:)
         146      749232 :    real(r8), pointer :: h_ssa(:,:)
         147      749232 :    real(r8), pointer :: h_asm(:,:)
         148             :    ! non-hygroscopic
         149      749232 :    real(r8), pointer :: n_ext(:)
         150      749232 :    real(r8), pointer :: n_ssa(:)
         151      749232 :    real(r8), pointer :: n_asm(:)
         152      749232 :    real(r8), pointer :: n_scat(:)
         153      749232 :    real(r8), pointer :: n_ascat(:)
         154             :    ! radius-dependent
         155      749232 :    real(r8), pointer :: r_ext(:,:)    ! radius-dependent mass-specific extinction
         156      749232 :    real(r8), pointer :: r_scat(:,:)
         157      749232 :    real(r8), pointer :: r_ascat(:,:)
         158      749232 :    real(r8), pointer :: r_mu(:)       ! log(radius) domain variable for r_ext, r_scat, r_ascat
         159             :
         160             :    ! radiative properties for each aerosol
         161             :    real(r8) :: ta (pcols,pver,nswbands)
         162             :    real(r8) :: tw (pcols,pver,nswbands)
         163             :    real(r8) :: twf(pcols,pver,nswbands)
         164             :    real(r8) :: twg(pcols,pver,nswbands)
         165             :
         166             :    ! aerosol masses
         167      749232 :    real(r8), pointer :: aermmr(:,:)    ! mass mixing ratio of aerosols
         168             :    real(r8) :: mmr_to_mass(pcols,pver) ! conversion factor for mmr to mass
         169             :    real(r8) :: aermass(pcols,pver)     ! mass of aerosols
         170             :
         171             :    ! for table lookup into rh grid
         172             :    real(r8) :: es(pcols,pver)     ! saturation vapor pressure
         173             :    real(r8) :: qs(pcols,pver)     ! saturation specific humidity
         174             :    real(r8) :: rh(pcols,pver)
         175             :    real(r8) :: rhtrunc(pcols,pver)
         176             :    real(r8) :: wrh(pcols,pver)
         177             :    integer  :: krh(pcols,pver)
         178             :
         179             :    integer  :: numaerosols     ! number of bulk aerosols in climate/diagnostic list
         180             :    integer  :: nmodes          ! number of aerosol modes in climate/diagnostic list
         181             :    integer  :: iaerosol        ! index into bulk aerosol list
         182             :
         183             :    character(len=ot_length) :: opticstype       ! hygro or nonhygro
         184             :    character(len=16) :: pbuf_fld
         185             :    !-----------------------------------------------------------------------------
         186             :
         187      749232 :    ncol  = state%ncol
         188      749232 :    lchnk = state%lchnk
         189             :
         190             :    ! compute mixing ratio to mass conversion
         191    20229264 :    do k = 1, pver
         192   326020464 :       mmr_to_mass(:ncol,k) = rga * state%pdeldry(:ncol,k)
         193             :    enddo
         194             :
         195             :    ! initialize to conditions that would cause failure
         196  6549036912 :    tau     (:,:,:) = -100._r8
         197  6549036912 :    tau_w   (:,:,:) = -100._r8
         198  6549036912 :    tau_w_g (:,:,:) = -100._r8
         199  6549036912 :    tau_w_f (:,:,:) = -100._r8
         200             :
         201             :    ! top layer (ilev = 0) has no aerosol (ie tau = 0)
         202             :    ! also initialize rest of layers to accumulate od's
         203  6432836256 :    tau    (1:ncol,:,:) = 0._r8
         204  6432836256 :    tau_w  (1:ncol,:,:) = 0._r8
         205  6432836256 :    tau_w_g(1:ncol,:,:) = 0._r8
         206  6432836256 :    tau_w_f(1:ncol,:,:) = 0._r8
         207             :
         208             :    ! calculate relative humidity for table lookup into rh grid
         209    20229264 :    do k = 1, pver
         210    20229264 :       call qsat(state%t(1:ncol,k), state%pmid(1:ncol,k), es(1:ncol,k), qs(1:ncol,k), ncol)
         211             :    end do
         212   326020464 :    rh(1:ncol,1:pver) = state%q(1:ncol,1:pver,1) / qs(1:ncol,1:pver)
         213             :
         214   326020464 :    rhtrunc(1:ncol,1:pver) = min(rh(1:ncol,1:pver),1._r8)
         215   326020464 :    krh(1:ncol,1:pver) = min(floor( rhtrunc(1:ncol,1:pver) * nrh ) + 1, nrh - 1) ! index into rh mesh
         216   326020464 :    wrh(1:ncol,1:pver) = rhtrunc(1:ncol,1:pver) * nrh - krh(1:ncol,1:pver)       ! (-) weighting on left side values
         217             :
         218             :    ! get number of bulk aerosols and number of modes in current list
         219      749232 :    call rad_cnst_get_info(list_idx, naero=numaerosols, nmodes=nmodes)
         220             :
         221             :    ! Contributions from modal aerosols.
         222      749232 :    if (nmodes > 0) then
         223             :       call aerosol_optics_cam_sw(list_idx, state, pbuf, nnite, idxnite, &
         224           0 :                                  tau, tau_w, tau_w_g, tau_w_f)
         225             :    else
         226  6432836256 :       tau    (1:ncol,:,:) = 0._r8
         227  6432836256 :       tau_w  (1:ncol,:,:) = 0._r8
         228  6432836256 :       tau_w_g(1:ncol,:,:) = 0._r8
         229  6432836256 :       tau_w_f(1:ncol,:,:) = 0._r8
         230             :    end if
         231             :
         232             :    !REMOVECAM - no longer need this when CAM is retired and pcols no longer exists
         233      749232 :    troplev = 0
         234             :    !REMOVECAM_END
         235      749232 :    call tropopause_find_cam(state, troplev)
         236             :
         237             :    ! Contributions from bulk aerosols.
         238      749232 :    do iaerosol = 1, numaerosols
         239             :
         240             :       ! get bulk aerosol mass mixing ratio
         241           0 :       call rad_cnst_get_aer_mmr(list_idx, iaerosol, state, pbuf, aermmr)
         242           0 :       aermass(1:ncol,1:top_lev-1) = 0._r8
         243           0 :       aermass(1:ncol,top_lev:pver) = aermmr(1:ncol,top_lev:pver) * mmr_to_mass(1:ncol,top_lev:pver)
         244             :
         245             :       ! get optics type
         246           0 :       call rad_cnst_get_aer_props(list_idx, iaerosol, opticstype=opticstype)
         247             :
         248           0 :       select case (trim(opticstype))
         249             :       case('hygro','hygroscopic','hygroscopi')
         250             :          ! get optical properties for hygroscopic aerosols
         251           0 :          call rad_cnst_get_aer_props(list_idx, iaerosol, sw_hygro_ext=h_ext, sw_hygro_ssa=h_ssa, sw_hygro_asm=h_asm)
         252           0 :          call get_hygro_rad_props(ncol, krh, wrh, aermass, h_ext, h_ssa, h_asm, ta, tw, twg, twf)
         253           0 :          tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
         254           0 :          tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
         255           0 :          tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
         256           0 :          tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)
         257             :
         258             :       case('nonhygro','insoluble ')
         259             :          ! get optical properties for non-hygroscopic aerosols
         260             :          call rad_cnst_get_aer_props(list_idx, iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_ssa=n_ssa, &
         261           0 :                                      sw_nonhygro_asm=n_asm)
         262             :
         263           0 :          call get_nonhygro_rad_props(ncol, aermass, n_ext, n_ssa, n_asm, ta, tw, twg, twf)
         264           0 :          tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
         265           0 :          tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
         266           0 :          tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
         267           0 :          tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)
         268             :
         269             :       case('volcanic')
         270             :          ! get optical properties for volcanic aerosols
         271             :          call rad_cnst_get_aer_props(list_idx, iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_scat=n_scat, &
         272           0 :                                      sw_nonhygro_ascat=n_ascat)
         273             :
         274           0 :          call get_volcanic_rad_props(ncol, aermass, n_ext, n_scat, n_ascat, ta, tw, twg, twf)
         275           0 :          tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
         276           0 :          tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
         277           0 :          tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
         278           0 :          tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)
         279             :
         280             :       case('volcanic_radius','volcanic_radius1','volcanic_radius2','volcanic_radius3')
         281           0 :          pbuf_fld = 'VOLC_RAD_GEOM '
         282           0 :          if (len_trim(opticstype)>15) then
         283           0 :             pbuf_fld = trim(pbuf_fld)//opticstype(16:16)
         284             :          endif
         285             :          ! get optical properties for volcanic aerosols
         286           0 :          call rad_cnst_get_aer_props(list_idx, iaerosol, r_sw_ext=r_ext, r_sw_scat=r_scat, r_sw_ascat=r_ascat, mu=r_mu)
         287           0 :          call get_volcanic_radius_rad_props(ncol, aermass, pbuf_fld, pbuf, r_ext, r_scat, r_ascat, r_mu, ta, tw, twg, twf)
         288           0 :          tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
         289           0 :          tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
         290           0 :          tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
         291           0 :          tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)
         292             :
         293             :       case('zero')
         294             :          ! no effect of "zero" aerosols, so update nothing
         295             :       case default
         296           0 :          call endrun('aer_rad_props_sw: unsupported opticstype :'//trim(opticstype)//':')
         297             :       end select
         298             :
         299             :       ! diagnostic output of individual aerosol optical properties
         300             :       ! currently implemented for climate list only
         301      749232 :       call aer_vis_diag_out(lchnk, ncol, nnite, idxnite, iaerosol, ta(:,:,idx_sw_diag), list_idx, troplev)
         302             :
         303             :    enddo
         304             :
         305             :    ! diagnostic output of total aerosol optical properties
         306             :    ! currently implemented for climate list only
         307      749232 :    call aer_vis_diag_out(lchnk, ncol, nnite, idxnite, 0, tau(:,:,idx_sw_diag), list_idx, troplev)
         308             :
         309     1498464 : end subroutine aer_rad_props_sw
         310             :
         311             : !==============================================================================
         312             :
         313       68112 : subroutine aer_rad_props_lw(list_idx, state, pbuf,  odap_aer)
         314             :
         315             :    ! Purpose: Compute aerosol transmissions needed in absorptivity/
         316             :    !    emissivity calculations
         317             :
         318             :    ! lw extinction is the same representation for all
         319             :    ! species.  If this changes, this routine will need to do something
         320             :    ! similar to the sw with routines like get_hygro_lw_abs
         321             :
         322      749232 :    use physics_buffer, only : pbuf_get_field, pbuf_get_index, physics_buffer_desc
         323             :
         324             :    ! Arguments
         325             :    integer,             intent(in)  :: list_idx                      ! index of the climate or a diagnostic list
         326             :    type(physics_state), intent(in), target :: state
         327             :
         328             :    type(physics_buffer_desc), pointer :: pbuf(:)
         329             :    real(r8),            intent(out) :: odap_aer(pcols,pver,nlwbands) ! [fraction] absorption optical depth, per layer
         330             :
         331             :    ! Local variables
         332             :
         333             :    integer :: bnd_idx     ! LW band index
         334             :    integer :: i           ! column index
         335             :    integer :: k           ! lev index
         336             :    integer :: ncol        ! number of columns
         337             :    integer :: numaerosols ! number of bulk aerosols in climate/diagnostic list
         338             :    integer :: nmodes      ! number of aerosol modes in climate/diagnostic list
         339             :    integer :: iaerosol    ! index into bulk aerosol list
         340             :    character(len=ot_length) :: opticstype       ! hygro or nonhygro
         341             :
         342             :    ! optical props for each aerosol
         343       68112 :    real(r8), pointer :: lw_abs(:)
         344       68112 :    real(r8), pointer :: lw_hygro_abs(:,:)
         345       68112 :    real(r8), pointer :: geometric_radius(:,:)
         346             :
         347             :    ! volcanic lookup table
         348       68112 :    real(r8), pointer :: r_lw_abs(:,:)  ! radius dependent mass-specific absorption coefficient
         349       68112 :    real(r8), pointer :: r_mu(:)        ! log(geometric_mean_radius) domain samples of r_lw_abs(:,:)
         350             :    integer  :: idx                     ! index to pbuf for geometric radius
         351             :    real(r8) :: mu(pcols,pver)          ! log(geometric_radius)
         352             :    real(r8) :: r_mu_min, r_mu_max, wmu, mutrunc
         353             :    integer  :: nmu, kmu
         354             :
         355             :    ! for table lookup into rh grid
         356             :    real(r8) :: es(pcols,pver)     ! saturation vapor pressure
         357             :    real(r8) :: qs(pcols,pver)     ! saturation specific humidity
         358             :    real(r8) :: rh(pcols,pver)
         359             :    real(r8) :: rhtrunc(pcols,pver)
         360             :    real(r8) :: wrh(pcols,pver)
         361             :    integer  :: krh(pcols,pver)
         362             :
         363             :    ! aerosol (vertical) mass path and extinction
         364             :    ! aerosol masses
         365       68112 :    real(r8), pointer :: aermmr(:,:)    ! mass mixing ratio of aerosols
         366             :    real(r8) :: mmr_to_mass(pcols,pver) ! conversion factor for mmr to mass
         367             :    real(r8) :: aermass(pcols,pver)     ! mass of aerosols
         368             :
         369             :    character(len=16) :: pbuf_fld
         370             :    !-----------------------------------------------------------------------------
         371             :
         372       68112 :    ncol = state%ncol
         373             :
         374             :    ! get number of bulk aerosols and number of modes in current list
         375       68112 :    call rad_cnst_get_info(list_idx, naero=numaerosols, nmodes=nmodes)
         376             :
         377             :    ! Contributions from modal aerosols.
         378       68112 :    if (nmodes > 0) then
         379           0 :       call aerosol_optics_cam_lw(list_idx, state, pbuf, odap_aer)
         380             :    else
         381       68112 :       odap_aer = 0._r8
         382             :    end if
         383             :
         384             :    ! Contributions from bulk aerosols.
         385       68112 :    if (numaerosols > 0) then
         386             :
         387             :       ! compute mixing ratio to mass conversion
         388           0 :       do k = 1, pver
         389           0 :          mmr_to_mass(:ncol,k) = rga * state%pdeldry(:ncol,k)
         390             :       end do
         391             :
         392             :       ! calculate relative humidity for table lookup into rh grid
         393           0 :       do k = 1, pver
         394           0 :          call qsat(state%t(1:ncol,k), state%pmid(1:ncol,k), es(1:ncol,k), qs(1:ncol,k), ncol)
         395             :       end do
         396           0 :       rh(1:ncol,1:pver) = state%q(1:ncol,1:pver,1) / qs(1:ncol,1:pver)
         397             :
         398           0 :       rhtrunc(1:ncol,1:pver) = min(rh(1:ncol,1:pver),1._r8)
         399           0 :       krh(1:ncol,1:pver) = min(floor( rhtrunc(1:ncol,1:pver) * nrh ) + 1, nrh - 1) ! index into rh mesh
         400           0 :       wrh(1:ncol,1:pver) = rhtrunc(1:ncol,1:pver) * nrh - krh(1:ncol,1:pver)       ! (-) weighting on left side values
         401             :
         402             :    end if
         403             :
         404             :    ! Loop over bulk aerosols in list.
         405       68112 :    do iaerosol = 1, numaerosols
         406             :
         407             :       ! get aerosol mass mixing ratio
         408           0 :       call rad_cnst_get_aer_mmr(list_idx, iaerosol, state, pbuf,  aermmr)
         409           0 :       aermass(1:ncol,1:top_lev-1) = 0._r8
         410           0 :       aermass(1:ncol,top_lev:pver) = aermmr(1:ncol,top_lev:pver) * mmr_to_mass(1:ncol,top_lev:pver)
         411             :
         412             :       ! get optics type
         413           0 :       call rad_cnst_get_aer_props(list_idx, iaerosol, opticstype=opticstype)
         414       68112 :       select case (trim(opticstype))
         415             :       case('hygroscopic')
         416             :           ! get optical properties for hygroscopic aerosols
         417           0 :          call rad_cnst_get_aer_props(list_idx, iaerosol, lw_hygro_ext=lw_hygro_abs)
         418           0 :          do bnd_idx = 1, nlwbands
         419           0 :             do k = 1, pver
         420           0 :                do i = 1, ncol
         421           0 :                   odap_aer(i, k, bnd_idx) = odap_aer(i, k, bnd_idx) + &
         422             :                        aermass(i, k) * &
         423           0 :                        ((1 + wrh(i,k)) * lw_hygro_abs(krh(i,k)+1,bnd_idx) &
         424           0 :                        - wrh(i,k)  * lw_hygro_abs(krh(i,k),  bnd_idx))
         425             :                end do
         426             :             end do
         427             :          end do
         428             :       case('insoluble','nonhygro','hygro','volcanic')
         429             :           ! get optical properties for hygroscopic aerosols
         430           0 :          call rad_cnst_get_aer_props(list_idx, iaerosol, lw_ext=lw_abs)
         431           0 :          do bnd_idx = 1, nlwbands
         432           0 :             do k = 1, pver
         433           0 :                do i = 1, ncol
         434           0 :                   odap_aer(i,k,bnd_idx) = odap_aer(i,k,bnd_idx) + lw_abs(bnd_idx)*aermass(i,k)
         435             :                end do
         436             :             end do
         437             :          end do
         438             :
         439             :       case('volcanic_radius','volcanic_radius1','volcanic_radius2','volcanic_radius3')
         440           0 :          pbuf_fld = 'VOLC_RAD_GEOM '
         441           0 :          if (len_trim(opticstype)>15) then
         442           0 :             pbuf_fld = trim(pbuf_fld)//opticstype(16:16)
         443             :          endif
         444             :
         445             :          ! get optical properties for hygroscopic aerosols
         446           0 :          call rad_cnst_get_aer_props(list_idx, iaerosol, r_lw_abs=r_lw_abs, mu=r_mu)
         447             :          ! get microphysical properties for volcanic aerosols
         448           0 :          idx = pbuf_get_index(pbuf_fld)
         449           0 :          call pbuf_get_field(pbuf, idx, geometric_radius )
         450             :
         451             :          ! interpolate in radius
         452             :          ! caution: clip the table with no warning when outside bounds
         453           0 :          nmu = size(r_mu)
         454           0 :          r_mu_max = r_mu(nmu)
         455           0 :          r_mu_min = r_mu(1)
         456           0 :          do i = 1, ncol
         457           0 :             do k = 1, pver
         458           0 :                if(geometric_radius(i,k) > 0._r8) then
         459           0 :                   mu(i,k) = log(geometric_radius(i,k))
         460             :                else
         461           0 :                   mu(i,k) = 0._r8
         462             :                endif
         463           0 :                mutrunc = max(min(mu(i,k),r_mu_max),r_mu_min)
         464           0 :                kmu = max(min(1 + (mutrunc-r_mu_min)/(r_mu_max-r_mu_min)*(nmu-1),nmu-1._r8),1._r8)
         465           0 :                wmu = max(min( (mutrunc -r_mu(kmu)) / (r_mu(kmu+1) - r_mu(kmu)) ,1._r8),0._r8)
         466           0 :                do bnd_idx = 1, nlwbands
         467           0 :                   odap_aer(i,k,bnd_idx) = odap_aer(i,k,bnd_idx) + &
         468             :                      aermass(i,k) * &
         469           0 :                      ((1._r8 - wmu) * r_lw_abs(bnd_idx, kmu  ) + &
         470           0 :                      (wmu) * r_lw_abs(bnd_idx, kmu+1))
         471             :                end do
         472             :             end do
         473             :          end do
         474             :
         475             :        case('zero')
         476             :           ! zero aerosols types have no optical effect, so do nothing.
         477             :        case default
         478           0 :           call endrun('aer_rad_props_lw: unsupported opticstype: '//trim(opticstype))
         479             :        end select
         480             :     end do
         481             :
         482      136224 : end subroutine aer_rad_props_lw
         483             :
         484             : !==============================================================================
         485             : ! Private methods
         486             : !==============================================================================
         487             :
         488           0 : subroutine get_hygro_rad_props(ncol, krh, wrh, mass, ext, ssa, asm, &
         489             :                                tau, tau_w, tau_w_g, tau_w_f)
         490             :
         491             :    ! Arguments
         492             :    integer,  intent(in) :: ncol
         493             :    integer,  intent(in) :: krh(pcols,pver)  ! index for linear interpolation of optics on rh
         494             :    real(r8), intent(in) :: wrh(pcols,pver)  ! weight for linear interpolation of optics on rh
         495             :    real(r8), intent(in) :: mass(pcols,pver)
         496             :    real(r8), intent(in) :: ext(:,:)
         497             :    real(r8), intent(in) :: ssa(:,:)
         498             :    real(r8), intent(in) :: asm(:,:)
         499             :
         500             :    real(r8), intent(out) :: tau    (pcols,pver,nswbands)
         501             :    real(r8), intent(out) :: tau_w  (pcols,pver,nswbands)
         502             :    real(r8), intent(out) :: tau_w_g(pcols,pver,nswbands)
         503             :    real(r8), intent(out) :: tau_w_f(pcols,pver,nswbands)
         504             :
         505             :    ! Local variables
         506             :    real(r8) :: ext1, ssa1, asm1
         507             :    integer :: icol, ilev, iswband
         508             :    !-----------------------------------------------------------------------------
         509             :
         510           0 :    do iswband = 1, nswbands
         511           0 :       do icol = 1, ncol
         512           0 :          do ilev = 1, pver
         513           0 :             ext1 = (1 + wrh(icol,ilev)) * ext(krh(icol,ilev)+1,iswband) &
         514           0 :                       - wrh(icol,ilev)  * ext(krh(icol,ilev),  iswband)
         515           0 :             ssa1 = (1 + wrh(icol,ilev)) * ssa(krh(icol,ilev)+1,iswband) &
         516           0 :                       - wrh(icol,ilev)  * ssa(krh(icol,ilev),  iswband)
         517           0 :             asm1 = (1 + wrh(icol,ilev)) * asm(krh(icol,ilev)+1,iswband) &
         518           0 :                       - wrh(icol,ilev)  * asm(krh(icol,ilev),  iswband)
         519             :
         520           0 :             tau    (icol, ilev, iswband) = mass(icol, ilev) * ext1
         521           0 :             tau_w  (icol, ilev, iswband) = mass(icol, ilev) * ext1 * ssa1
         522           0 :             tau_w_g(icol, ilev, iswband) = mass(icol, ilev) * ext1 * ssa1 * asm1
         523           0 :             tau_w_f(icol, ilev, iswband) = mass(icol, ilev) * ext1 * ssa1 * asm1 * asm1
         524             :          enddo
         525             :       enddo
         526             :    enddo
         527             :
         528       68112 : end subroutine get_hygro_rad_props
         529             :
         530             : !==============================================================================
         531             :
         532           0 : subroutine get_nonhygro_rad_props(ncol, mass, ext, ssa, asm, &
         533             :                                   tau, tau_w, tau_w_g, tau_w_f)
         534             :
         535             :    ! Arguments
         536             :    integer,  intent(in) :: ncol
         537             :    real(r8), intent(in) :: mass(pcols, pver)
         538             :    real(r8), intent(in) :: ext(:)
         539             :    real(r8), intent(in) :: ssa(:)
         540             :    real(r8), intent(in) :: asm(:)
         541             :
         542             :    real(r8), intent(out) :: tau    (pcols, pver, nswbands)
         543             :    real(r8), intent(out) :: tau_w  (pcols, pver, nswbands)
         544             :    real(r8), intent(out) :: tau_w_g(pcols, pver, nswbands)
         545             :    real(r8), intent(out) :: tau_w_f(pcols, pver, nswbands)
         546             :
         547             :    ! Local variables
         548             :    integer  :: iswband
         549             :    real(r8) :: ext1, ssa1, asm1
         550             :    !-----------------------------------------------------------------------------
         551             :
         552           0 :    do iswband = 1, nswbands
         553           0 :       ext1 = ext(iswband)
         554           0 :       ssa1 = ssa(iswband)
         555           0 :       asm1 = asm(iswband)
         556           0 :       tau    (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1
         557           0 :       tau_w  (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1 * ssa1
         558           0 :       tau_w_g(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1 * ssa1 * asm1
         559           0 :       tau_w_f(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1 * ssa1 * asm1 * asm1
         560             :    enddo
         561             :
         562           0 : end subroutine get_nonhygro_rad_props
         563             :
         564             : !==============================================================================
         565             :
         566           0 : subroutine get_volcanic_radius_rad_props(ncol, mass, pbuf_radius_name, pbuf,  r_ext, r_scat, r_ascat, r_mu, &
         567             :                                   tau, tau_w, tau_w_g, tau_w_f)
         568             :
         569             :
         570             :    use physics_buffer, only : pbuf_get_field, pbuf_get_index
         571             :
         572             :    ! Arguments
         573             :    integer,  intent(in) :: ncol
         574             :    real(r8), intent(in) :: mass(pcols, pver)
         575             :    character(len=*) :: pbuf_radius_name
         576             :    type(physics_buffer_desc), pointer :: pbuf(:)
         577             :    real(r8), intent(in) :: r_ext(:,:)
         578             :    real(r8), intent(in) :: r_scat(:,:)
         579             :    real(r8), intent(in) :: r_ascat(:,:)
         580             :    real(r8), intent(in) :: r_mu(:) ! log(radius) domain of mass-specific optics
         581             :
         582             :    real(r8), intent(out) :: tau    (pcols, pver, nswbands)
         583             :    real(r8), intent(out) :: tau_w  (pcols, pver, nswbands)
         584             :    real(r8), intent(out) :: tau_w_g(pcols, pver, nswbands)
         585             :    real(r8), intent(out) :: tau_w_f(pcols, pver, nswbands)
         586             :
         587             :    ! Local variables
         588             :    integer  :: iswband
         589             :    real(r8) :: g
         590             :
         591             :    integer  :: idx                             ! index to radius in physics buffer
         592           0 :    real(r8), pointer :: geometric_radius(:,:)  ! geometric mean radius of volcanic aerosol
         593             :    real(r8) :: mu(pcols,pver)                  ! log(geometric mean radius of volcanic aerosol)
         594             :    integer  :: kmu, nmu
         595             :    real(r8) :: wmu, mutrunc, r_mu_max, r_mu_min
         596             :
         597             :    ! interpolated values from table
         598             :    real(r8) :: ext(nswbands)
         599             :    real(r8) :: scat(nswbands)
         600             :    real(r8) :: ascat(nswbands)
         601             :
         602             :    integer :: i, k ! column level iterator
         603             :    !-----------------------------------------------------------------------------
         604             :
         605           0 :    tau    =0._r8
         606           0 :    tau_w  =0._r8
         607           0 :    tau_w_g=0._r8
         608           0 :    tau_w_f=0._r8
         609             :
         610             :    ! get microphysical properties for volcanic aerosols
         611           0 :    idx = pbuf_get_index(pbuf_radius_name)
         612           0 :    call pbuf_get_field(pbuf, idx, geometric_radius )
         613             :
         614             :    ! interpolate in radius
         615             :    ! caution: clip the table with no warning when outside bounds
         616           0 :    nmu = size(r_mu)
         617           0 :    r_mu_max = r_mu(nmu)
         618           0 :    r_mu_min = r_mu(1)
         619           0 :    do i = 1, ncol
         620           0 :       do k = 1, pver
         621           0 :          if(geometric_radius(i,k) > 0._r8) then
         622           0 :             mu(i,k) = log(geometric_radius(i,k))
         623             :          else
         624           0 :             mu(i,k) = 0._r8
         625             :          endif
         626           0 :          mutrunc = max(min(mu(i,k),r_mu_max),r_mu_min)
         627           0 :          kmu = max(min(1 + (mutrunc-r_mu_min)/(r_mu_max-r_mu_min)*(nmu-1),nmu-1._r8),1._r8)
         628           0 :          wmu = max(min( (mutrunc -r_mu(kmu)) / (r_mu(kmu+1) - r_mu(kmu)) ,1._r8),0._r8)
         629           0 :          do iswband = 1, nswbands
         630           0 :             ext(iswband) =  &
         631           0 :                ((1._r8 - wmu) * r_ext(iswband, kmu  ) + &
         632           0 :                (wmu) * r_ext(iswband, kmu+1))
         633             :             scat(iswband) =  &
         634           0 :                ((1._r8 - wmu) * r_scat(iswband, kmu  ) + &
         635           0 :                (wmu) * r_scat(iswband, kmu+1))
         636             :             ascat(iswband) =  &
         637           0 :                ((1._r8 - wmu) * r_ascat(iswband, kmu  ) + &
         638           0 :                (wmu) * r_ascat(iswband, kmu+1))
         639           0 :             if (scat(iswband).gt.0._r8) then
         640           0 :                g = ascat(iswband)/scat(iswband)
         641             :             else
         642             :                g=0._r8
         643             :             endif
         644           0 :             tau    (i,k,iswband) = mass(i,k) * ext(iswband)
         645           0 :             tau_w  (i,k,iswband) = mass(i,k) * scat(iswband)
         646           0 :             tau_w_g(i,k,iswband) = mass(i,k) * ascat(iswband)
         647           0 :             tau_w_f(i,k,iswband) = mass(i,k) * g * ascat(iswband)
         648             :          end do
         649             :       enddo
         650             :    enddo
         651             :
         652           0 : end subroutine get_volcanic_radius_rad_props
         653             :
         654             : !==============================================================================
         655             :
         656           0 : subroutine get_volcanic_rad_props(ncol, mass, ext, scat, ascat, &
         657             :                                   tau, tau_w, tau_w_g, tau_w_f)
         658             :
         659             :    ! Arguments
         660             :    integer,  intent(in) :: ncol
         661             :    real(r8), intent(in) :: mass(pcols, pver)
         662             :    real(r8), intent(in) :: ext(:)
         663             :    real(r8), intent(in) :: scat(:)
         664             :    real(r8), intent(in) :: ascat(:)
         665             :
         666             :    real(r8), intent(out) :: tau    (pcols, pver, nswbands)
         667             :    real(r8), intent(out) :: tau_w  (pcols, pver, nswbands)
         668             :    real(r8), intent(out) :: tau_w_g(pcols, pver, nswbands)
         669             :    real(r8), intent(out) :: tau_w_f(pcols, pver, nswbands)
         670             :
         671             :    ! Local variables
         672             :    integer  :: iswband
         673             :    real(r8) :: g
         674             :    !-----------------------------------------------------------------------------
         675             :
         676           0 :    do iswband = 1, nswbands
         677           0 :       if (scat(iswband).gt.0._r8) then
         678           0 :          g = ascat(iswband)/scat(iswband)
         679             :       else
         680             :          g=0._r8
         681             :       endif
         682           0 :       tau    (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext(iswband)
         683           0 :       tau_w  (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * scat(iswband)
         684           0 :       tau_w_g(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ascat(iswband)
         685           0 :       tau_w_f(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * g * ascat(iswband)
         686             :    enddo
         687             :
         688           0 : end subroutine get_volcanic_rad_props
         689             :
         690             : !==============================================================================
         691             :
         692      749232 : subroutine aer_vis_diag_out(lchnk, ncol, nnite, idxnite, iaer, tau, diag_idx, troplev)
         693             :
         694             :    ! output aerosol optical depth for the visible band
         695             :
         696             :    integer,          intent(in) :: lchnk
         697             :    integer,          intent(in) :: ncol           ! number of columns
         698             :    integer,          intent(in) :: nnite          ! number of night columns
         699             :    integer,          intent(in) :: idxnite(:)     ! local column indices of night columns
         700             :    integer,          intent(in) :: iaer           ! aerosol index -- if 0 then tau is a total for all aerosols
         701             :    real(r8),         intent(in) :: tau(:,:)       ! aerosol optical depth for the visible band
         702             :    integer,          intent(in) :: diag_idx       ! identifies whether the aerosol optics
         703             :                                                   ! is for the climate calc or a diagnostic calc
         704             :    integer,          intent(in) :: troplev(:)     ! tropopause level
         705             :
         706             :    ! Local variables
         707             :    integer  :: i
         708             :    real(r8) :: tmp(pcols), tmp2(pcols)
         709             :    !-----------------------------------------------------------------------------
         710             :
         711             :    ! currently only implemented for climate calc
         712      749232 :    if (diag_idx > 0) return
         713             :
         714             :    ! compute total column aerosol optical depth
         715   330062832 :    tmp(1:ncol) = sum(tau(1:ncol,:), 2)
         716             :    ! use fillvalue to indicate night columns
         717     6629832 :    do i = 1, nnite
         718     6629832 :       tmp(idxnite(i)) = fillvalue
         719             :    end do
         720             :
         721      749232 :    if (iaer > 0) then
         722           0 :       call outfld(odv_names(iaer), tmp, pcols, lchnk)
         723             :    else
         724      749232 :       call outfld('AEROD_v', tmp, pcols, lchnk)
         725    12510432 :       do i = 1, ncol
         726   146391260 :         tmp2(i) = sum(tau(i,:troplev(i)))
         727             :       end do
         728      749232 :       call outfld('AODvstrt', tmp2, pcols, lchnk)
         729             :    end if
         730             :
         731             : end subroutine aer_vis_diag_out
         732             :
         733             : !==============================================================================
         734             :
         735             : end module aer_rad_props

Generated by: LCOV version 1.14