module aer_rad_props 2,11

!------------------------------------------------------------------------------------------------
! Converts aerosol masses to bulk optical properties for sw and lw radiation
! computations.  
!------------------------------------------------------------------------------------------------

use shr_kind_mod,     only: r8 => shr_kind_r8
use ppgrid,           only: pcols, pver, pverp
use physconst,        only: rga
use physics_types,    only: physics_state
use phys_buffer,      only: pbuf_size_max, pbuf_fld
use radconstants,     only: nrh, nswbands, nlwbands, idx_sw_diag, ot_length
use rad_constituents, only: rad_cnst_get_clim_info, rad_cnst_get_clim_aer, &
                            rad_cnst_get_clim_aer_props
use wv_saturation,    only: aqsat
use cam_history,      only: fieldname_len, addfld, phys_decomp, outfld, fillvalue, add_default

use abortutils,       only: endrun
use constituents,     only: pcnst

implicit none
private
save

public :: &
   aer_rad_props_init,        &
   aer_rad_props_get_clim_sw, & ! return SW optical props of total bulk aerosols
   aer_rad_props_get_clim_lw    ! return LW optical props of total bulk aerosols

! Private data
character(len=fieldname_len), pointer :: odv_names(:)  ! outfld names for visible OD


!==============================================================================
contains
!==============================================================================


subroutine aer_rad_props_init() 1,6


   integer                    :: i
   integer                    :: numaerosols  ! number of aerosols
   character(len=64), pointer :: aernames(:)  ! aerosol names
   !----------------------------------------------------------------------------


   call addfld ('AEROD_v ', '1', 1, 'A', &
      'Total Aerosol Optical Depth in visible band', phys_decomp, flag_xyfill=.true.)
   call add_default ('AEROD_v', 1, ' ')

   ! Contributions to AEROD_v from individual aerosols (climate species).

   ! number of aerosols in climate list
   call rad_cnst_get_clim_info(naero=numaerosols)

   ! get names of aerosols
   allocate(aernames(numaerosols))
   call rad_cnst_get_clim_info(aernames=aernames)

   ! create outfld names for visible OD and call addfld
   allocate(odv_names(numaerosols))
   do i = 1, numaerosols
      odv_names(i) = 'ODV_'//trim(aernames(i))
      call addfld (odv_names(i), '1', 1, 'A', &
         trim(aernames(i))//' optical depth in visible band', phys_decomp, flag_xyfill=.true.)
      call add_default (odv_names(i), 1, ' ')
   end do
   deallocate(aernames)

end subroutine aer_rad_props_init

!==============================================================================


subroutine aer_rad_props_get_clim_sw(state, pbuf, nnite, idxnite, & 1,47
                                     tau, tau_w, tau_w_g, tau_w_f,&
                                     diagnosticindex)

! return totaled (across all species) layer tau, omega, g, f 
! for all spectral interval for aerosols affecting the climate

   ! this use should be removed when the relative humidity calc is fixed
   use physconst, only: epsilo
#ifdef MODAL_AERO
   use modal_aero_data,   only: maxd_amode
   use modal_aer_opt,     only: modal_size_parameters, modal_aero_sw
   use modal_aer_opt,     only: ncoef
   use phys_buffer,       only: pbuf_get_fld_idx
#endif

   ! Arguments
   type(physics_state), intent(in) :: state
   type(pbuf_fld),      intent(in) :: pbuf(pbuf_size_max)
   integer,             intent(in) :: nnite                ! number of night columns
   integer,             intent(in) :: idxnite(:)           ! local column indices of night columns
   integer, optional,   intent(in) :: diagnosticindex      ! index (if present) to radiation diagnostic information

   real(r8), intent(out) :: tau    (pcols,0:pver,nswbands) ! aerosol extinction optical depth
   real(r8), intent(out) :: tau_w  (pcols,0:pver,nswbands) ! aerosol single scattering albedo * tau
   real(r8), intent(out) :: tau_w_g(pcols,0:pver,nswbands) ! aerosol assymetry parameter * tau * w
   real(r8), intent(out) :: tau_w_f(pcols,0:pver,nswbands) ! aerosol forward scattered fraction * tau * w

   ! Local variables

   integer :: ncol
   integer :: lchnk
   integer :: k, i    ! lev and daycolumn indices
   integer :: iswband ! sw band indices

   ! optical props for each aerosol
   ! hygroscopic
   real(r8), pointer :: h_ext(:,:)
   real(r8), pointer :: h_ssa(:,:)
   real(r8), pointer :: h_asm(:,:)
   ! non-hygroscopic
   real(r8), pointer :: n_ext(:)
   real(r8), pointer :: n_ssa(:)
   real(r8), pointer :: n_asm(:)
   real(r8), pointer :: n_scat(:)
   real(r8), pointer :: n_ascat(:)
   ! radius-dependent
   real(r8), pointer :: r_ext(:,:)    ! radius-dependent mass-specific extinction
   real(r8), pointer :: r_scat(:,:)
   real(r8), pointer :: r_ascat(:,:)
   real(r8), pointer :: r_mu(:)       ! log(radius) domain variable for r_ext, r_scat, r_ascat

   ! radiative properties for each aerosol
   real(r8) :: ta (pcols,pver,nswbands)
   real(r8) :: tw (pcols,pver,nswbands)
   real(r8) :: twf(pcols,pver,nswbands)
   real(r8) :: twg(pcols,pver,nswbands)

   ! aerosol masses
   real(r8), pointer :: aermmr(:,:)    ! mass mixing ratio of aerosols
   real(r8) :: mmr_to_mass(pcols,pver) ! conversion factor for mmr to mass
   real(r8) :: aermass(pcols,pver)     ! mass of aerosols
#ifdef MODAL_AERO
   real(r8), pointer, dimension(:,:,:) :: dgnumwet ! number mode diameter
   real(r8), pointer, dimension(:,:,:) :: qaerwat ! aerosol water
!   real(r8) :: raer(pcols, pver, pcnst)
   real(r8) :: radsurf(pcols,pver,maxd_amode) ! aerosol surface mode radius
   real(r8) :: logradsurf(pcols,pver,maxd_amode) ! log(aerosol surface mode radius)
   real(r8) :: cheb(ncoef,maxd_amode,pcols,pver)
   integer :: ifld
#endif

   ! for table lookup into rh grid
   real(r8) :: esat(pcols,pver)     ! saturation vapor pressure
   real(r8) :: qsat(pcols,pver)     ! saturation specific humidity
   real(r8) :: rh(pcols,pver)
   real(r8) :: rhtrunc(pcols,pver)
   real(r8) :: wrh(pcols,pver)
   integer  :: krh(pcols,pver)
 
   integer  :: iaerosol        ! aerosol index
   integer  :: numaerosols     ! number of aerosols in climate list
   integer  :: diag_idx = 0    ! idx for climate call
   character(len=ot_length) :: opticstype       ! hygro or nonhygro
   !-----------------------------------------------------------------------------

   ncol  = state%ncol
   lchnk = state%lchnk

   ! compute mixing ratio to mass conversion
   do k = 1, pver
      mmr_to_mass(:ncol,k) = rga * state%pdeldry(:ncol,k)
   enddo

   ! initialize to conditions that would cause failure
   tau     (:,:,:) = -100._r8
   tau_w   (:,:,:) = -100._r8
   tau_w_g (:,:,:) = -100._r8
   tau_w_f (:,:,:) = -100._r8

   ! top layer (ilev = 0) has no aerosol (ie tau = 0)
   ! also initialize rest of layers to accumulate od's
   tau    (1:ncol,:,:) = 0._r8
   tau_w  (1:ncol,:,:) = 0._r8
   tau_w_g(1:ncol,:,:) = 0._r8
   tau_w_f(1:ncol,:,:) = 0._r8

   ! calculate relative humidity for table lookup into rh grid
   call aqsat(state%t, state%pmid, esat, qsat, pcols, &
               ncol, pver, 1, pver)
   rh(1:ncol,1:pver) = state%q(1:ncol,1:pver,1) / qsat(1:ncol,1:pver)

   rhtrunc(1:ncol,1:pver) = min(rh(1:ncol,1:pver),1._r8)
   krh(1:ncol,1:pver) = min(floor( rhtrunc(1:ncol,1:pver) * nrh ) + 1, nrh - 1) ! index into rh mesh
   wrh(1:ncol,1:pver) = rhtrunc(1:ncol,1:pver) * nrh - krh(1:ncol,1:pver)       ! (-) weighting on left side values

   ! number of aerosols in climate list
   if (present(diagnosticindex)) then
       call rad_cnst_get_clim_info(naero=numaerosols, diagnosticindex=diagnosticindex)
       diag_idx = diagnosticindex
   else
       call rad_cnst_get_clim_info(naero=numaerosols)
       diag_idx = 0
   endif

#ifdef MODAL_AERO
   if (present(diagnosticindex)) then
      tau    (1:ncol,:,:) = 0._r8
      tau_w  (1:ncol,:,:) = 0._r8
      tau_w_g(1:ncol,:,:) = 0._r8
      tau_w_f(1:ncol,:,:) = 0._r8
   ! Loop over aerosols in climate list
      do iaerosol = 1, numaerosols
      ! get aerosol type
         call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype, diagnosticindex=diagnosticindex)
         if (trim(opticstype) == 'volcanic_radius') then

      ! get aerosol mass mixing ratio

            call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr, diagnosticindex=diagnosticindex)
            aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver)

     ! get optical properties for volcanic aerosols

            call rad_cnst_get_clim_aer_props(iaerosol, r_sw_ext=r_ext, r_sw_scat=r_scat, r_sw_ascat=r_ascat, mu=r_mu, diagnosticindex=diagnosticindex)
            call get_volcanic_radius_rad_props(lchnk, ncol, aermass, pbuf, r_ext, r_scat, r_ascat, r_mu, ta, tw, twg, twf)
            tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
            tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
            tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
            tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)
            call aer_vis_diag_out(lchnk, ncol, nnite, idxnite, iaerosol, ta(:,:,idx_sw_diag), diag_idx)
         end if
      end do
   else
      ifld = pbuf_get_fld_idx('DGNUMWET')
      dgnumwet => pbuf(ifld)%fld_ptr(1,1:pcols,1:pver,lchnk,1:maxd_amode)
      ifld  = pbuf_get_fld_idx( 'QAERWAT' )
      if ( associated(pbuf(ifld)%fld_ptr) ) then
         qaerwat => pbuf(ifld)%fld_ptr( 1, 1:pcols, 1:pver, lchnk, 1:maxd_amode )
      else
         call endrun( 'pbuf for QAERWAT not allocated in modal_size_parameters' )
      end if

      call modal_size_parameters(ncol, dgnumwet, radsurf, logradsurf, cheb)
!      raer(:,:,:)=state%q(:,:,:) ! for starters. later use rad_cnst_get_clim_aer
      do iswband=1,nswbands
         call modal_aero_sw(ncol, lchnk, iswband, nnite, idxnite, tau(1,0,iswband), tau_w(1,0,iswband), &
                  tau_w_g(1,0,iswband), tau_w_f(1,0,iswband), state%q, state, qaerwat, radsurf, logradsurf, cheb )
      end do

   ! Loop over aerosols in climate list
      do iaerosol = 1, numaerosols

      ! get aerosol type

         call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype)

         if (trim(opticstype) == 'volcanic_radius') then

      ! get aerosol mass mixing ratio

            call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr)
            aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver)

     ! get optical properties for volcanic aerosols
            call rad_cnst_get_clim_aer_props(iaerosol, r_sw_ext=r_ext, r_sw_scat=r_scat, r_sw_ascat=r_ascat, mu=r_mu )
            call get_volcanic_radius_rad_props(lchnk, ncol, aermass, pbuf, r_ext, r_scat, r_ascat, r_mu, ta, tw, twg, twf)
            tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
            tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
            tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
            tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)
         ! diagnostic output of individual aerosol optical properties
         ! currently implemented for climate list only
            call aer_vis_diag_out(lchnk, ncol, nnite, idxnite, iaerosol, ta(:,:,idx_sw_diag), diag_idx)
         end if
      end do
   end if
#else

   ! Loop over aerosols in climate list
   do iaerosol = 1, numaerosols

      ! get aerosol mass mixing ratio
      if (present(diagnosticindex)) then
          call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr, diagnosticindex=diagnosticindex)
      else
          call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr)
      endif
      aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver)

      ! get aerosol type
      if (present(diagnosticindex)) then
          call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype, diagnosticindex=diagnosticindex)
      else
          call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype)
      endif

      select case (trim(opticstype))
      case('hygro')
         ! get optical properties for hygroscopic aerosols
         if (present(diagnosticindex)) then
             call rad_cnst_get_clim_aer_props(iaerosol, sw_hygro_ext=h_ext, sw_hygro_ssa=h_ssa, sw_hygro_asm=h_asm, &
                                              diagnosticindex=diagnosticindex)
         else
             call rad_cnst_get_clim_aer_props(iaerosol, sw_hygro_ext=h_ext, sw_hygro_ssa=h_ssa, sw_hygro_asm=h_asm)
         endif
         call get_hygro_rad_props(ncol, krh, wrh, aermass, h_ext, h_ssa, h_asm, ta, tw, twg, twf)
         tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
         tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
         tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
         tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)

      case('hygroscopic','hygroscopi')
         ! get optical properties for hygroscopic aerosols
         if (present(diagnosticindex)) then
             call rad_cnst_get_clim_aer_props(iaerosol, sw_hygro_ext=h_ext, sw_hygro_ssa=h_ssa, sw_hygro_asm=h_asm, &
                                              diagnosticindex=diagnosticindex)
         else
             call rad_cnst_get_clim_aer_props(iaerosol, sw_hygro_ext=h_ext, sw_hygro_ssa=h_ssa, sw_hygro_asm=h_asm)
         endif
         call get_hygro_rad_props(ncol, krh, wrh, aermass, h_ext, h_ssa, h_asm, ta, tw, twg, twf)
         tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
         tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
         tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
         tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)

      case('nonhygro')
         ! get optical properties for non-hygroscopic aerosols
         if (present(diagnosticindex)) then
             call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_ssa=n_ssa, sw_nonhygro_asm=n_asm, &
                                              diagnosticindex=diagnosticindex)
         else
             call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_ssa=n_ssa, sw_nonhygro_asm=n_asm)
         endif

         call get_nonhygro_rad_props(ncol, aermass, n_ext, n_ssa, n_asm, ta, tw, twg, twf)
         tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
         tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
         tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
         tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)

      case('insoluble ')
         ! get optical properties for non-hygroscopic aerosols
         if (present(diagnosticindex)) then
             call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_ssa=n_ssa, sw_nonhygro_asm=n_asm, &
                                              diagnosticindex=diagnosticindex)
         else
             call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_ssa=n_ssa, sw_nonhygro_asm=n_asm)
         endif

         call get_nonhygro_rad_props(ncol, aermass, n_ext, n_ssa, n_asm, ta, tw, twg, twf)
         tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
         tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
         tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
         tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)

      case('volcanic')
         ! get optical properties for volcanic aerosols
         if (present(diagnosticindex)) then
             call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_scat=n_scat, sw_nonhygro_ascat=n_ascat,&
                                              diagnosticindex=diagnosticindex)
         else
             call rad_cnst_get_clim_aer_props(iaerosol, sw_nonhygro_ext=n_ext, sw_nonhygro_scat=n_scat, sw_nonhygro_ascat=n_ascat)
         endif

         call get_volcanic_rad_props(ncol, aermass, n_ext, n_scat, n_ascat, ta, tw, twg, twf)
         tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
         tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
         tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
         tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)

      case('volcanic_radius')
         ! get optical properties for volcanic aerosols
         if (present(diagnosticindex)) then
             call rad_cnst_get_clim_aer_props(iaerosol, r_sw_ext=r_ext, r_sw_scat=r_scat, r_sw_ascat=r_ascat, mu=r_mu,&
                                              diagnosticindex=diagnosticindex)
         else
             call rad_cnst_get_clim_aer_props(iaerosol, r_sw_ext=r_ext, r_sw_scat=r_scat, r_sw_ascat=r_ascat, mu=r_mu )
         endif

         call get_volcanic_radius_rad_props(lchnk, ncol, aermass, pbuf, r_ext, r_scat, r_ascat, r_mu, ta, tw, twg, twf)
         tau    (1:ncol,1:pver,:) = tau    (1:ncol,1:pver,:) + ta (1:ncol,:,:)
         tau_w  (1:ncol,1:pver,:) = tau_w  (1:ncol,1:pver,:) + tw (1:ncol,:,:)
         tau_w_g(1:ncol,1:pver,:) = tau_w_g(1:ncol,1:pver,:) + twg(1:ncol,:,:)
         tau_w_f(1:ncol,1:pver,:) = tau_w_f(1:ncol,1:pver,:) + twf(1:ncol,:,:)

      case('zero')
         ! no effect of "zero" aerosols, so update nothing
      case default
         call endrun('aer_rad_props_get_clim_sw: unsupported opticstype :'//trim(opticstype)//':')
      end select

      ! diagnostic output of individual aerosol optical properties
      ! currently implemented for climate list only
      call aer_vis_diag_out(lchnk, ncol, nnite, idxnite, iaerosol, ta(:,:,idx_sw_diag), diag_idx)

   enddo
#endif

   ! diagnostic output of total aerosol optical properties
   ! currently implemented for climate list only
   call aer_vis_diag_out(lchnk, ncol, nnite, idxnite, 0, tau(:,:,idx_sw_diag), diag_idx)

end subroutine aer_rad_props_get_clim_sw

!==============================================================================


subroutine aer_rad_props_get_clim_lw(state, pbuf, odap_aer, diagnosticindex) 1,32

   use physconst,     only: epsilo
   use radconstants,  only: ot_length
   use phys_buffer,   only: pbuf_get_fld_idx
#ifdef MODAL_AERO
   use modal_aer_opt, only: modal_aero_lw
#endif

! Purpose: Compute aerosol transmissions needed in absorptivity/
!    emissivity calculations

! lw extinction is the same representation for all 
! species.  If this changes, this routine will need to do something
! similar to the sw with routines like get_hygro_lw_abs

   ! Arguments
   type(physics_state), intent(in)  :: state
   type(pbuf_fld),      intent(in)  :: pbuf(pbuf_size_max)
   real(r8),            intent(out) :: odap_aer(pcols,pver,nlwbands) ! [fraction] absorption optical depth, per layer
   integer, optional,   intent(in)  :: diagnosticindex

   ! Local variables

   integer :: bnd_idx     ! LW band index
   integer :: i           ! column index
   integer :: k           ! lev index
   integer :: ncol        ! number of columns
   integer :: lchnk       ! chunk index
   integer :: iaerosol    ! index into aerosol list
   integer :: numaerosols ! index into aerosol list
   character(len=ot_length) :: opticstype       ! hygro or nonhygro


   ! optical props for each aerosol
   real(r8), pointer :: lw_abs(:)
   real(r8), pointer :: lw_hygro_abs(:,:)
   real(r8), pointer :: geometric_radius(:,:)
 
   ! volcanic lookup table
   real(r8), pointer :: r_lw_abs(:,:)  ! radius dependent mass-specific absorption coefficient
   real(r8), pointer :: r_mu(:)        ! log(geometric_mean_radius) domain samples of r_lw_abs(:,:)
   integer  :: idx                     ! index to pbuf for geometric radius
   real(r8) :: mu(pcols,pver)          ! log(geometric_radius)
   real(r8) :: r_mu_min, r_mu_max, wmu, mutrunc
   integer  :: nmu, kmu

   ! for table lookup into rh grid
   real(r8) :: esat(pcols,pver)     ! saturation vapor pressure
   real(r8) :: qsat(pcols,pver)     ! saturation specific humidity
   real(r8) :: rh(pcols,pver)
   real(r8) :: rhtrunc(pcols,pver)
   real(r8) :: wrh(pcols,pver)
   integer  :: krh(pcols,pver)

   ! aerosol (vertical) mass path and extinction
   ! aerosol masses
   real(r8), pointer :: aermmr(:,:)    ! mass mixing ratio of aerosols
   real(r8) :: mmr_to_mass(pcols,pver) ! conversion factor for mmr to mass
   real(r8) :: aermass(pcols,pver)     ! mass of aerosols
#ifdef MODAL_AERO
!   real(r8) :: raer(pcols, pver, pcnst)
#endif
   !-----------------------------------------------------------------------------

   ncol = state%ncol
   lchnk = state%lchnk

#ifdef MODAL_AERO
   if (present(diagnosticindex)) then
      odap_aer = 0._r8
! hack to get volcanic aerosols into mam
! number of aerosols in climate list
      call rad_cnst_get_clim_info(naero=numaerosols, diagnosticindex=diagnosticindex)

! Loop over aerosols in climate list

      do iaerosol = 1, numaerosols

         call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype, diagnosticindex=diagnosticindex)

         if (trim(opticstype) == 'volcanic_radius') then

! compute mixing ratio to mass conversion
            do k = 1, pver
               mmr_to_mass(:ncol,k) = rga * state%pdeldry(:ncol,k)
            end do
            
! compute optical depths odap_aer (summed over all aerosols)
! total optical depths from top (odap_aer_ttl)

! calculate relative humidity for table lookup into rh grid
            call aqsat(state%t, state%pmid, esat, qsat, pcols, &
                 ncol, pver, 1, pver)
            rh(1:ncol,1:pver) = state%q(1:ncol,1:pver,1) / qsat(1:ncol,1:pver)
            
            rhtrunc(1:ncol,1:pver) = min(rh(1:ncol,1:pver),1._r8)
            krh(1:ncol,1:pver) = min(floor( rhtrunc(1:ncol,1:pver) * nrh ) + 1, nrh - 1) ! index into rh mesh
            wrh(1:ncol,1:pver) = rhtrunc(1:ncol,1:pver) * nrh - krh(1:ncol,1:pver)       ! (-) weighting on left side values

      ! get aerosol mass mixing ratio

            call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr, diagnosticindex=diagnosticindex)
            aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver)

          ! get optical properties for hygroscopic aerosols
            call rad_cnst_get_clim_aer_props(iaerosol, r_lw_abs=r_lw_abs, mu=r_mu, diagnosticindex=diagnosticindex)

          ! get microphysical properties for volcanic aerosols
            idx = pbuf_get_fld_idx('VOLC_RAD_GEOM')
            geometric_radius => pbuf(idx)%fld_ptr( 1, 1:pcols, 1:pver, lchnk, 1)

          ! interpolate in radius
          ! caution: clip the table with no warning when outside bounds
            nmu = size(r_mu)
            r_mu_max = r_mu(nmu)
            r_mu_min = r_mu(1)
            do i = 1, ncol
               do k = 1, pver
                  if(geometric_radius(i,k) > 0.) then
                     mu(i,k) = log(geometric_radius(i,k))
                  else
                     mu(i,k) = 0.
                  endif
                  mutrunc = max(min(mu(i,k),r_mu_max),r_mu_min)
                  kmu = max(min(1 + (mutrunc-r_mu_min)/(r_mu_max-r_mu_min)*(nmu-1),nmu-1._r8),1._r8)
                  wmu = max(min( (mutrunc -r_mu(kmu)) / (r_mu(kmu+1) - r_mu(kmu)) ,1._r8),0._r8)
                  do bnd_idx = 1, nlwbands
                     odap_aer(i,k,bnd_idx) = odap_aer(i,k,bnd_idx) + &
                          aermass(i,k) * &
                          ((1._r8 - wmu) * r_lw_abs(bnd_idx, kmu  ) + &
                          (wmu) * r_lw_abs(bnd_idx, kmu+1))
                  end do
               enddo
            enddo
         end if
      enddo
   else
!      raer(:,:,:)=state%q(:,:,:) ! for starters. later use rad_cnst_get_clim_aer
      call modal_aero_lw(ncol,lchnk, pbuf, state%q, odap_aer, state%pdeldry)

! hack to get volcanic aerosols into mam
! number of aerosols in climate list
      call rad_cnst_get_clim_info(naero=numaerosols)

! Loop over aerosols in climate list

      do iaerosol = 1, numaerosols

         call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype)

         if (trim(opticstype) == 'volcanic_radius') then

! compute mixing ratio to mass conversion
            do k = 1, pver
               mmr_to_mass(:ncol,k) = rga * state%pdeldry(:ncol,k)
            end do
            
! compute optical depths odap_aer (summed over all aerosols)
! total optical depths from top (odap_aer_ttl)

! calculate relative humidity for table lookup into rh grid
            call aqsat(state%t, state%pmid, esat, qsat, pcols, &
                 ncol, pver, 1, pver)
            rh(1:ncol,1:pver) = state%q(1:ncol,1:pver,1) / qsat(1:ncol,1:pver)
            
            rhtrunc(1:ncol,1:pver) = min(rh(1:ncol,1:pver),1._r8)
            krh(1:ncol,1:pver) = min(floor( rhtrunc(1:ncol,1:pver) * nrh ) + 1, nrh - 1) ! index into rh mesh
            wrh(1:ncol,1:pver) = rhtrunc(1:ncol,1:pver) * nrh - krh(1:ncol,1:pver)       ! (-) weighting on left side values

      ! get aerosol mass mixing ratio

            call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr)
            aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver)

          ! get optical properties for hygroscopic aerosols
            call rad_cnst_get_clim_aer_props(iaerosol, r_lw_abs=r_lw_abs, mu=r_mu)

          ! get microphysical properties for volcanic aerosols
            idx = pbuf_get_fld_idx('VOLC_RAD_GEOM')
            geometric_radius => pbuf(idx)%fld_ptr( 1, 1:pcols, 1:pver, lchnk, 1)

          ! interpolate in radius
          ! caution: clip the table with no warning when outside bounds
            nmu = size(r_mu)
            r_mu_max = r_mu(nmu)
            r_mu_min = r_mu(1)
            do i = 1, ncol
               do k = 1, pver
                  if(geometric_radius(i,k) > 0.) then
                     mu(i,k) = log(geometric_radius(i,k))
                  else
                     mu(i,k) = 0.
                  endif
                  mutrunc = max(min(mu(i,k),r_mu_max),r_mu_min)
                  kmu = max(min(1 + (mutrunc-r_mu_min)/(r_mu_max-r_mu_min)*(nmu-1),nmu-1._r8),1._r8)
                  wmu = max(min( (mutrunc -r_mu(kmu)) / (r_mu(kmu+1) - r_mu(kmu)) ,1._r8),0._r8)
                  do bnd_idx = 1, nlwbands
                     odap_aer(i,k,bnd_idx) = odap_aer(i,k,bnd_idx) + &
                          aermass(i,k) * &
                          ((1._r8 - wmu) * r_lw_abs(bnd_idx, kmu  ) + &
                          (wmu) * r_lw_abs(bnd_idx, kmu+1))
                  end do
               enddo
            enddo
         end if
      enddo
   end if 
#else
   ! compute mixing ratio to mass conversion
   do k = 1, pver
      mmr_to_mass(:ncol,k) = rga * state%pdeldry(:ncol,k)
   end do

   ! compute optical depths odap_aer (summed over all aerosols)
   ! total optical depths from top (odap_aer_ttl)
   odap_aer = 0._r8

   ! calculate relative humidity for table lookup into rh grid
   call aqsat(state%t, state%pmid, esat, qsat, pcols, &
               ncol, pver, 1, pver)
   rh(1:ncol,1:pver) = state%q(1:ncol,1:pver,1) / qsat(1:ncol,1:pver)

   rhtrunc(1:ncol,1:pver) = min(rh(1:ncol,1:pver),1._r8)
   krh(1:ncol,1:pver) = min(floor( rhtrunc(1:ncol,1:pver) * nrh ) + 1, nrh - 1) ! index into rh mesh
   wrh(1:ncol,1:pver) = rhtrunc(1:ncol,1:pver) * nrh - krh(1:ncol,1:pver)       ! (-) weighting on left side values


   ! number of aerosols in climate list
   if (present(diagnosticindex)) then
      call rad_cnst_get_clim_info(naero=numaerosols, diagnosticindex=diagnosticindex)
   else
      call rad_cnst_get_clim_info(naero=numaerosols)
   endif

   ! Loop over aerosols in climate list
   do iaerosol = 1, numaerosols

      ! get aerosol mass mixing ratio
      if (present(diagnosticindex)) then
         call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr, diagnosticindex=diagnosticindex)
      else
         call rad_cnst_get_clim_aer(iaerosol, state, pbuf, aermmr)
      endif
      aermass(1:ncol,1:pver) = aermmr(1:ncol,1:pver) * mmr_to_mass(1:ncol,1:pver)

      if (present(diagnosticindex)) then
         call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype, diagnosticindex=diagnosticindex)
      else
         call rad_cnst_get_clim_aer_props(iaerosol, opticstype=opticstype)
      endif
      select case (trim(opticstype))
      case('hygroscopic')
          ! get optical properties for hygroscopic aerosols
          if (present(diagnosticindex)) then
              call rad_cnst_get_clim_aer_props(iaerosol, lw_hygro_ext=lw_hygro_abs, diagnosticindex=diagnosticindex)
          else
              call rad_cnst_get_clim_aer_props(iaerosol, lw_hygro_ext=lw_hygro_abs)
          endif
          do bnd_idx = 1, nlwbands
             do k = 1, pver
                do i = 1, ncol
                   odap_aer(i, k, bnd_idx) = odap_aer(i, k, bnd_idx) + &
                         aermass(i, k) * &
                            ((1 + wrh(i,k)) * lw_hygro_abs(krh(i,k)+1,bnd_idx) &
                                - wrh(i,k)  * lw_hygro_abs(krh(i,k),  bnd_idx))
                enddo
             enddo
          enddo
      case('insoluble','nonhygro','hygro','volcanic')
          ! get optical properties for hygroscopic aerosols
          if (present(diagnosticindex)) then
              call rad_cnst_get_clim_aer_props(iaerosol, lw_ext=lw_abs, diagnosticindex=diagnosticindex)
          else
              call rad_cnst_get_clim_aer_props(iaerosol, lw_ext=lw_abs)
          endif
          do bnd_idx = 1, nlwbands
             do k = 1, pver          
                do i = 1, ncol
                   odap_aer(i,k,bnd_idx) = odap_aer(i,k,bnd_idx) + lw_abs(bnd_idx)*aermass(i,k)
                end do
             end do
          end do

      case('volcanic_radius')
          ! get optical properties for hygroscopic aerosols
          if (present(diagnosticindex)) then
              call rad_cnst_get_clim_aer_props(iaerosol, r_lw_abs=r_lw_abs, mu=r_mu, diagnosticindex=diagnosticindex)
          else
              call rad_cnst_get_clim_aer_props(iaerosol, r_lw_abs=r_lw_abs, mu=r_mu)
          endif
          ! get microphysical properties for volcanic aerosols
          idx = pbuf_get_fld_idx('VOLC_RAD_GEOM')
          geometric_radius => pbuf(idx)%fld_ptr( 1, 1:pcols, 1:pver, lchnk, 1)

          ! interpolate in radius
          ! caution: clip the table with no warning when outside bounds
          nmu = size(r_mu)
          r_mu_max = r_mu(nmu)
          r_mu_min = r_mu(1)
          do i = 1, ncol
             do k = 1, pver
                if(geometric_radius(i,k) > 0.) then
                   mu(i,k) = log(geometric_radius(i,k))
                else
                   mu(i,k) = 0.
                endif
                mutrunc = max(min(mu(i,k),r_mu_max),r_mu_min)
                kmu = max(min(1 + (mutrunc-r_mu_min)/(r_mu_max-r_mu_min)*(nmu-1),nmu-1._r8),1._r8)
                wmu = max(min( (mutrunc -r_mu(kmu)) / (r_mu(kmu+1) - r_mu(kmu)) ,1._r8),0._r8)
                do bnd_idx = 1, nlwbands
                   odap_aer(i,k,bnd_idx) = odap_aer(i,k,bnd_idx) + &
                      aermass(i,k) * &
                      ((1._r8 - wmu) * r_lw_abs(bnd_idx, kmu  ) + &
                      (wmu) * r_lw_abs(bnd_idx, kmu+1))
                end do
             enddo
          enddo

      case('zero')
          ! zero aerosols types have no optical effect, so do nothing.
      case default
         call endrun('aer_rad_props_get_clim_lw: unsupported opticstype: '//trim(opticstype))
      end select
   end do
#endif

end subroutine aer_rad_props_get_clim_lw

!==============================================================================
! Private methods
!==============================================================================


subroutine get_hygro_rad_props(ncol, krh, wrh, mass, ext, ssa, asm, & 2
                               tau, tau_w, tau_w_g, tau_w_f)

   ! Arguments
   integer,  intent(in) :: ncol
   integer,  intent(in) :: krh(pcols,pver)  ! index for linear interpolation of optics on rh
   real(r8), intent(in) :: wrh(pcols,pver)  ! weight for linear interpolation of optics on rh
   real(r8), intent(in) :: mass(pcols,pver)
   real(r8), intent(in) :: ext(:,:)
   real(r8), intent(in) :: ssa(:,:)
   real(r8), intent(in) :: asm(:,:)

   real(r8), intent(out) :: tau    (pcols,pver,nswbands)
   real(r8), intent(out) :: tau_w  (pcols,pver,nswbands)
   real(r8), intent(out) :: tau_w_g(pcols,pver,nswbands)
   real(r8), intent(out) :: tau_w_f(pcols,pver,nswbands)

   ! Local variables
   real(r8) :: ext1, ssa1, asm1
   integer :: icol, ilev, iswband
   !-----------------------------------------------------------------------------

   do iswband = 1, nswbands
      do icol = 1, ncol
         do ilev = 1, pver
            ext1 = (1 + wrh(icol,ilev)) * ext(krh(icol,ilev)+1,iswband) &
                      - wrh(icol,ilev)  * ext(krh(icol,ilev),  iswband)
            ssa1 = (1 + wrh(icol,ilev)) * ssa(krh(icol,ilev)+1,iswband) &
                      - wrh(icol,ilev)  * ssa(krh(icol,ilev),  iswband)
            asm1 = (1 + wrh(icol,ilev)) * asm(krh(icol,ilev)+1,iswband) &
                      - wrh(icol,ilev)  * asm(krh(icol,ilev),  iswband)
  
            tau    (icol, ilev, iswband) = mass(icol, ilev) * ext1
            tau_w  (icol, ilev, iswband) = mass(icol, ilev) * ext1 * ssa1
            tau_w_g(icol, ilev, iswband) = mass(icol, ilev) * ext1 * ssa1 * asm1
            tau_w_f(icol, ilev, iswband) = mass(icol, ilev) * ext1 * ssa1 * asm1 * asm1
         enddo
      enddo
   enddo

end subroutine get_hygro_rad_props 

!==============================================================================
    

subroutine get_nonhygro_rad_props(ncol, mass, ext, ssa, asm, & 2
                                  tau, tau_w, tau_w_g, tau_w_f)

   ! Arguments
   integer,  intent(in) :: ncol
   real(r8), intent(in) :: mass(pcols, pver)
   real(r8), intent(in) :: ext(:)
   real(r8), intent(in) :: ssa(:)
   real(r8), intent(in) :: asm(:)

   real(r8), intent(out) :: tau    (pcols, pver, nswbands)
   real(r8), intent(out) :: tau_w  (pcols, pver, nswbands)
   real(r8), intent(out) :: tau_w_g(pcols, pver, nswbands)
   real(r8), intent(out) :: tau_w_f(pcols, pver, nswbands) 

   ! Local variables
   integer  :: iswband
   real(r8) :: ext1, ssa1, asm1
   !-----------------------------------------------------------------------------
   
   do iswband = 1, nswbands
      ext1 = ext(iswband)
      ssa1 = ssa(iswband)
      asm1 = asm(iswband)
      tau    (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1
      tau_w  (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1 * ssa1
      tau_w_g(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1 * ssa1 * asm1
      tau_w_f(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext1 * ssa1 * asm1 * asm1
   enddo

end subroutine get_nonhygro_rad_props

!==============================================================================
    

subroutine get_volcanic_radius_rad_props(lchnk, ncol, mass, pbuf, r_ext, r_scat, r_ascat, r_mu, & 3,2
                                  tau, tau_w, tau_w_g, tau_w_f)

   use phys_buffer,       only: pbuf_get_fld_idx, pbuf_size_max

   ! Arguments
   integer,  intent(in) :: lchnk
   integer,  intent(in) :: ncol
   real(r8), intent(in) :: mass(pcols, pver)
   type(pbuf_fld),      intent(in) :: pbuf(pbuf_size_max)
   real(r8), intent(in) :: r_ext(:,:)
   real(r8), intent(in) :: r_scat(:,:)
   real(r8), intent(in) :: r_ascat(:,:)
   real(r8), intent(in) :: r_mu(:) ! log(radius) domain of mass-specific optics

   real(r8), intent(out) :: tau    (pcols, pver, nswbands)
   real(r8), intent(out) :: tau_w  (pcols, pver, nswbands)
   real(r8), intent(out) :: tau_w_g(pcols, pver, nswbands)
   real(r8), intent(out) :: tau_w_f(pcols, pver, nswbands) 

   ! Local variables
   integer  :: iswband
   real(r8) :: g

   integer  :: idx                             ! index to radius in physics buffer
   real(r8), pointer :: geometric_radius(:,:)  ! geometric mean radius of volcanic aerosol
   real(r8) :: mu(pcols,pver)                  ! log(geometric mean radius of volcanic aerosol)
   integer  :: imu                             ! index into table values of log radius
   integer  :: kmu, nmu
   real(r8) :: wmu, mutrunc, r_mu_max, r_mu_min
 
   ! interpolated values from table
   real(r8) :: ext(nswbands)
   real(r8) :: scat(nswbands)
   real(r8) :: ascat(nswbands)

   integer :: i, k ! column level iterator
   !-----------------------------------------------------------------------------

   tau    =0._r8                 
   tau_w  =0._r8                 
   tau_w_g=0._r8                 
   tau_w_f=0._r8                  

   ! get microphysical properties for volcanic aerosols
   idx = pbuf_get_fld_idx('VOLC_RAD_GEOM')
   geometric_radius => pbuf(idx)%fld_ptr( 1, 1:pcols, 1:pver, lchnk, 1 )

   ! interpolate in radius
   ! caution: clip the table with no warning when outside bounds
   nmu = size(r_mu)
   r_mu_max = r_mu(nmu)
   r_mu_min = r_mu(1)
   do i = 1, ncol
      do k = 1, pver
         if(geometric_radius(i,k) > 0.) then
            mu(i,k) = log(geometric_radius(i,k))
         else
            mu(i,k) = 0.
         endif
         mutrunc = max(min(mu(i,k),r_mu_max),r_mu_min)
         kmu = max(min(1 + (mutrunc-r_mu_min)/(r_mu_max-r_mu_min)*(nmu-1),nmu-1._r8),1._r8)
         wmu = max(min( (mutrunc -r_mu(kmu)) / (r_mu(kmu+1) - r_mu(kmu)) ,1._r8),0._r8)
         do iswband = 1, nswbands
            ext(iswband) =  &
               ((1._r8 - wmu) * r_ext(iswband, kmu  ) + &
               (wmu) * r_ext(iswband, kmu+1))
            scat(iswband) =  &
               ((1._r8 - wmu) * r_scat(iswband, kmu  ) + &
               (wmu) * r_scat(iswband, kmu+1))
            ascat(iswband) =  &
               ((1._r8 - wmu) * r_ascat(iswband, kmu  ) + &
               (wmu) * r_ascat(iswband, kmu+1))
            if (scat(iswband).gt.0.) then
               g = ascat(iswband)/scat(iswband)
            else
               g=0._r8
            endif
            tau    (i,k,iswband) = mass(i,k) * ext(iswband)  
            tau_w  (i,k,iswband) = mass(i,k) * scat(iswband)  
            tau_w_g(i,k,iswband) = mass(i,k) * ascat(iswband)  
            tau_w_f(i,k,iswband) = mass(i,k) * g * ascat(iswband)  
         end do
      enddo
   enddo

end subroutine get_volcanic_radius_rad_props

!==============================================================================
    

subroutine get_volcanic_rad_props(ncol, mass, ext, scat, ascat, & 1
                                  tau, tau_w, tau_w_g, tau_w_f)

   ! Arguments
   integer,  intent(in) :: ncol
   real(r8), intent(in) :: mass(pcols, pver)
   real(r8), intent(in) :: ext(:)
   real(r8), intent(in) :: scat(:)
   real(r8), intent(in) :: ascat(:)

   real(r8), intent(out) :: tau    (pcols, pver, nswbands)
   real(r8), intent(out) :: tau_w  (pcols, pver, nswbands)
   real(r8), intent(out) :: tau_w_g(pcols, pver, nswbands)
   real(r8), intent(out) :: tau_w_f(pcols, pver, nswbands) 

   ! Local variables
   integer  :: iswband
   real(r8) :: g
   !-----------------------------------------------------------------------------
   
   do iswband = 1, nswbands
      if (scat(iswband).gt.0.) then
         g = ascat(iswband)/scat(iswband)
      else
         g=0._r8
      endif
      tau    (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ext(iswband) 
      tau_w  (1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * scat(iswband) 
      tau_w_g(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * ascat(iswband) 
      tau_w_f(1:ncol,1:pver,iswband) = mass(1:ncol,1:pver) * g * ascat(iswband) 
   enddo

end subroutine get_volcanic_rad_props

!==============================================================================


subroutine aer_vis_diag_out(lchnk, ncol, nnite, idxnite, iaer, tau, diag_idx) 4,2

   ! output aerosol optical depth for the visible band

   integer,          intent(in) :: lchnk
   integer,          intent(in) :: ncol           ! number of columns
   integer,          intent(in) :: nnite          ! number of night columns
   integer,          intent(in) :: idxnite(:)     ! local column indices of night columns
   integer,          intent(in) :: iaer           ! aerosol index -- if 0 then tau is a total for all aerosols
   real(r8),         intent(in) :: tau(:,:)       ! aerosol optical depth for the visible band
   integer,          intent(in) :: diag_idx       ! identifies whether the aerosol optics
                                                  ! is for the climate calc or a diagnostic calc
 
   ! Local variables
   integer  :: i
   real(r8) :: tmp(pcols)
   !-----------------------------------------------------------------------------

   ! currently only implemented for climate calc
   if (diag_idx > 0) return

   ! compute total column aerosol optical depth
   tmp(1:ncol) = sum(tau(1:ncol,:), 2)
   ! use fillvalue to indicate night columns
   do i = 1, nnite
      tmp(idxnite(i)) = fillvalue
   end do

   if (iaer > 0) then
      call outfld(odv_names(iaer), tmp, pcols, lchnk)
   else
      call outfld('AEROD_v', tmp, pcols, lchnk)
   end if

end subroutine aer_vis_diag_out

!==============================================================================

end module aer_rad_props