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

Generated by: LCOV version 1.14