module modal_aero_wateruptake

!   RCE 07.04.13:  Adapted from MIRAGE2 code

use shr_kind_mod,     only: r8 => shr_kind_r8
use physconst,        only: pi, rhoh2o
use ppgrid,           only: pcols, pver
use physics_types,    only: physics_state
use physics_buffer,   only: physics_buffer_desc, pbuf_get_index, pbuf_old_tim_idx, pbuf_get_field

use wv_saturation,    only: qsat_water
use rad_constituents, only: rad_cnst_get_info, rad_cnst_get_aer_mmr, rad_cnst_get_aer_props, &
                            rad_cnst_get_mode_props, rad_cnst_get_mode_num
use cam_history,      only: addfld, add_default, phys_decomp, outfld
use cam_logfile,      only: iulog
use ref_pres,         only: top_lev => clim_modal_aero_top_lev
use phys_control,     only: phys_getopts
use cam_abortutils,   only: endrun

implicit none
private
save

public :: &
   modal_aero_wateruptake_init, &
   modal_aero_wateruptake_dr

public :: modal_aero_wateruptake_reg

real(r8), parameter :: third = 1._r8/3._r8
real(r8), parameter :: pi43  = pi*4.0_r8/3.0_r8


! Physics buffer indices
integer :: cld_idx        = 0
integer :: dgnum_idx      = 0
integer :: dgnumwet_idx   = 0
integer :: sulfeq_idx     = 0
integer :: wetdens_ap_idx = 0
integer :: qaerwat_idx    = 0

logical, public :: modal_strat_sulfate = .false.   ! If .true. then MAM sulfate surface area density used in stratospheric heterogeneous chemistry

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

subroutine modal_aero_wateruptake_reg()

  use physics_buffer,   only: pbuf_add_field, dtype_r8
  use rad_constituents, only: rad_cnst_get_info

   integer :: nmodes
   
   call rad_cnst_get_info(0, nmodes=nmodes)
   call pbuf_add_field('DGNUMWET',   'global',  dtype_r8, (/pcols, pver, nmodes/), dgnumwet_idx)
   call pbuf_add_field('WETDENS_AP', 'physpkg', dtype_r8, (/pcols, pver, nmodes/), wetdens_ap_idx)

   ! 1st order rate for direct conversion of strat. cloud water to precip (1/s)
   call pbuf_add_field('QAERWAT',    'physpkg', dtype_r8, (/pcols, pver, nmodes/), qaerwat_idx)  
   if (modal_strat_sulfate) then
     call pbuf_add_field('MAMH2SO4EQ', 'global',  dtype_r8, (/pcols, pver, nmodes/), sulfeq_idx)
   end if

end subroutine modal_aero_wateruptake_reg

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

subroutine modal_aero_wateruptake_init(pbuf2d)
   use time_manager,  only: is_first_step
   use physics_buffer,only: pbuf_set_field
   use infnan,       only : nan, assignment(=)

   type(physics_buffer_desc), pointer :: pbuf2d(:,:)
   real(r8) :: real_nan

   integer :: m, nmodes
   logical :: history_aerosol      ! Output the MAM aerosol variables and tendencies

   character(len=3) :: trnum       ! used to hold mode number (as characters)
   !----------------------------------------------------------------------------

   real_nan = nan
    
   cld_idx        = pbuf_get_index('CLD')    
   dgnum_idx      = pbuf_get_index('DGNUM')    

   ! assume for now that will compute wateruptake for climate list modes only

   call rad_cnst_get_info(0, nmodes=nmodes)

   do m = 1, nmodes
      write(trnum, '(i3.3)') m
      call addfld('dgnd_a'//trnum(2:3), 'm', pver, 'A', &
         'dry dgnum, interstitial, mode '//trnum(2:3), phys_decomp)
      call addfld('dgnw_a'//trnum(2:3), 'm', pver, 'A', &
         'wet dgnum, interstitial, mode '//trnum(2:3), phys_decomp)
      call addfld('wat_a'//trnum(3:3), 'm', pver, 'A', &
         'aerosol water, interstitial, mode '//trnum(2:3), phys_decomp)
      
      ! determine default variables
      call phys_getopts(history_aerosol_out = history_aerosol)

      if (history_aerosol) then  
         call add_default('dgnd_a'//trnum(2:3), 1, ' ')
         call add_default('dgnw_a'//trnum(2:3), 1, ' ')
         call add_default('wat_a'//trnum(3:3),  1, ' ')
      endif

   end do
   
   if (is_first_step()) then
      ! initialize fields in physics buffer
      call pbuf_set_field(pbuf2d, dgnumwet_idx, 0.0_r8)
      if (modal_strat_sulfate) then
      ! initialize fields in physics buffer to NaN (not a number) 
      ! so model will crash if used before initialization
         call pbuf_set_field(pbuf2d, sulfeq_idx, real_nan)
      endif
   endif

end subroutine modal_aero_wateruptake_init

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


subroutine modal_aero_wateruptake_dr(state, pbuf, list_idx_in, dgnumdry_m, dgnumwet_m, &
                                     qaerwat_m, wetdens_m)
!-----------------------------------------------------------------------
!
! CAM specific driver for modal aerosol water uptake code.
!
! *** N.B. *** The calculation has been enabled for diagnostic mode lists
!              via optional arguments.  If the list_idx arg is present then
!              all the optional args must be present.
!
!-----------------------------------------------------------------------

   use time_manager,  only: is_first_step
   use cam_history,   only: outfld, fieldname_len
   use tropopause,    only: tropopause_find, TROP_ALG_HYBSTOB, TROP_ALG_CLIMATE
   ! Arguments
   type(physics_state), target, intent(in)    :: state          ! Physics state variables
   type(physics_buffer_desc),   pointer       :: pbuf(:)        ! physics buffer

   integer,  optional,          intent(in)    :: list_idx_in
   real(r8), optional, target,  intent(in)    :: dgnumdry_m(:,:,:)
   real(r8), optional,          pointer       :: dgnumwet_m(:,:,:)
   real(r8), optional,          pointer       :: qaerwat_m(:,:,:)
   real(r8), optional,          pointer       :: wetdens_m(:,:,:)

   ! local variables

   integer  :: lchnk              ! chunk index
   integer  :: ncol               ! number of columns
   integer  :: list_idx           ! radiative constituents list index
   integer  :: stat

   integer :: i, k, l, m
   integer :: itim_old
   integer :: nmodes
   integer :: nspec
   integer :: tropLev(pcols)

   character(len=fieldname_len+3) :: fieldname

   real(r8), pointer :: h2ommr(:,:) ! specific humidity
   real(r8), pointer :: t(:,:)      ! temperatures (K)
   real(r8), pointer :: pmid(:,:)   ! layer pressure (Pa)
   real(r8), pointer :: raer(:,:)   ! aerosol species MRs (kg/kg and #/kg)

   real(r8), pointer :: cldn(:,:)      ! layer cloud fraction (0-1)
   real(r8), pointer :: dgncur_a(:,:,:)
   real(r8), pointer :: dgncur_awet(:,:,:)
   real(r8), pointer :: wetdens(:,:,:)
   real(r8), pointer :: qaerwat(:,:,:)

   real(r8), allocatable :: maer(:,:,:)      ! aerosol wet mass MR (including water) (kg/kg-air)
   real(r8), allocatable :: hygro(:,:,:)     ! volume-weighted mean hygroscopicity (--)
   real(r8), allocatable :: naer(:,:,:)      ! aerosol number MR (bounded!) (#/kg-air)
   real(r8), allocatable :: dryvol(:,:,:)    ! single-particle-mean dry volume (m3)
   real(r8), allocatable :: so4dryvol(:,:,:) ! single-particle-mean so4 dry volume (m3)
   real(r8), allocatable :: drymass(:,:,:)   ! single-particle-mean dry mass  (kg)
   real(r8), allocatable :: dryrad(:,:,:)    ! dry volume mean radius of aerosol (m)

   real(r8), allocatable :: wetrad(:,:,:)    ! wet radius of aerosol (m)
   real(r8), allocatable :: wetvol(:,:,:)    ! single-particle-mean wet volume (m3)
   real(r8), allocatable :: wtrvol(:,:,:)    ! single-particle-mean water volume in wet aerosol (m3)

   real(r8), allocatable :: rhcrystal(:)
   real(r8), allocatable :: rhdeliques(:)
   real(r8), allocatable :: specdens_1(:)

   real(r8), pointer :: sulfeq(:,:,:) ! H2SO4 equilibrium mixing ratios over particles (mol/mol)
   real(r8), allocatable :: wtpct(:,:,:)  ! sulfate aerosol composition, weight % H2SO4
   real(r8), allocatable :: sulden(:,:,:) ! sulfate aerosol mass density (g/cm3)
   
   real(r8) :: dryvolmr(pcols,pver)          ! volume MR for aerosol mode (m3/kg)
   real(r8) :: so4dryvolmr(pcols,pver)       ! volume MR for sulfate aerosol in mode (m3/kg)
   real(r8) :: specdens, so4specdens
   real(r8) :: spechygro, spechygro_1
   real(r8) :: duma, dumb
   real(r8) :: sigmag
   real(r8) :: alnsg
   real(r8) :: v2ncur_a
   real(r8) :: drydens               ! dry particle density  (kg/m^3)
   real(r8) :: rh(pcols,pver)        ! relative humidity (0-1)
   real(r8) :: dmean, qh2so4_equilib, wtpct_mode, sulden_mode

   real(r8) :: es(pcols)             ! saturation vapor pressure
   real(r8) :: qs(pcols)             ! saturation specific humidity

   character(len=3) :: trnum       ! used to hold mode number (as characters)
   character(len=32) :: spectype           
   !-----------------------------------------------------------------------

   lchnk = state%lchnk
   ncol = state%ncol

   list_idx = 0
   if (present(list_idx_in)) then
      list_idx = list_idx_in

      ! check that all optional args are present
      if (.not. present(dgnumdry_m)   .or. .not. present(dgnumwet_m) .or. &
          .not. present(qaerwat_m) .or. .not. present(wetdens_m)) then
         call endrun('modal_aero_wateruptake_dr called for'// &
                     'diagnostic list but required args not present')
      end if
   end if

   ! loop over all aerosol modes
   call rad_cnst_get_info(list_idx, nmodes=nmodes)

   if (modal_strat_sulfate) then
     call pbuf_get_field(pbuf, sulfeq_idx, sulfeq ) 
   endif

   allocate( &
      maer(pcols,pver,nmodes),     &
      hygro(pcols,pver,nmodes),    &
      naer(pcols,pver,nmodes),     &
      dryvol(pcols,pver,nmodes),   &
      so4dryvol(pcols,pver,nmodes),&
      drymass(pcols,pver,nmodes),  &
      dryrad(pcols,pver,nmodes),   &
      wetrad(pcols,pver,nmodes),   &
      wetvol(pcols,pver,nmodes),   &
      wtrvol(pcols,pver,nmodes),   &
      wtpct(pcols,pver,nmodes),    &
      sulden(pcols,pver,nmodes),   &
      rhcrystal(nmodes),           &
      rhdeliques(nmodes),          &
      specdens_1(nmodes)           )

   maer(:,:,:)      = 0._r8
   hygro(:,:,:)     = 0._r8
   so4dryvol(:,:,:) = 0._r8
   wtpct(:,:,:)     = 75._r8
   sulden(:,:,:)    = 1.923_r8

   if (list_idx == 0) then
      call pbuf_get_field(pbuf, dgnum_idx, dgncur_a )
      call pbuf_get_field(pbuf, dgnumwet_idx, dgncur_awet )
      if (is_first_step()) then
         dgncur_awet(:,:,:) = dgncur_a(:,:,:)
      end if
   else
      dgncur_a => dgnumdry_m
      dgncur_awet => dgnumwet_m
   end if

!-----------------------------------------------------------------------
! get tropopause level
!-----------------------------------------------------------------------
   if (modal_strat_sulfate) then
      call tropopause_find(state, tropLev, primary=TROP_ALG_HYBSTOB, backup=TROP_ALG_CLIMATE)
   endif

   h2ommr => state%q(:,:,1)
   t      => state%t
   pmid   => state%pmid

   do m = 1, nmodes

      dryvolmr(:,:) = 0._r8
      so4dryvolmr(:,:) = 0._r8

      ! get mode properties
      call rad_cnst_get_mode_props(list_idx, m, sigmag=sigmag,  &
         rhcrystal=rhcrystal(m), rhdeliques=rhdeliques(m))

      ! get mode info
      call rad_cnst_get_info(list_idx, m, nspec=nspec)

      do l = 1, nspec

         ! get species interstitial mixing ratio ('a')
         call rad_cnst_get_aer_mmr(list_idx, m, l, 'a', state, pbuf, raer)
         call rad_cnst_get_aer_props(list_idx, m, l, density_aer=specdens, &
                                     hygro_aer=spechygro, spectype=spectype)
                                     
         if (modal_strat_sulfate .and. (trim(spectype).eq.'sulfate')) then
            so4specdens=specdens
         end if
                                     

         if (l == 1) then
            ! save off these values to be used as defaults
            specdens_1(m)  = specdens
            spechygro_1    = spechygro
         end if

         do k = top_lev, pver
            do i = 1, ncol
               duma          = raer(i,k)     ! kg/kg air
               maer(i,k,m)   = maer(i,k,m) + duma
               dumb          = duma/specdens ! m3/kg air
               dryvolmr(i,k) = dryvolmr(i,k) + dumb
               if (modal_strat_sulfate .and. (trim(spectype).eq.'sulfate')) then
                  so4dryvolmr(i,k) = so4dryvolmr(i,k) + dumb
               end if
               hygro(i,k,m)  = hygro(i,k,m) + dumb*spechygro
            end do
         end do
      end do

      alnsg = log(sigmag)

      do k = top_lev, pver
         do i = 1, ncol

            if (dryvolmr(i,k) > 1.0e-30_r8) then
               hygro(i,k,m) = hygro(i,k,m)/dryvolmr(i,k)
            else
               hygro(i,k,m) = spechygro_1
            end if

            ! dry aerosol properties

            v2ncur_a = 1._r8 / ( (pi/6._r8)*(dgncur_a(i,k,m)**3._r8)*exp(4.5_r8*alnsg**2._r8) )
            ! naer = aerosol number (#/kg)
            naer(i,k,m) = dryvolmr(i,k)*v2ncur_a

            ! compute mean (1 particle) dry volume and mass for each mode
            ! old coding is replaced because the new (1/v2ncur_a) is equal to
            ! the mean particle volume
            ! also moletomass forces maer >= 1.0e-30, so (maer/dryvolmr)
            ! should never cause problems (but check for maer < 1.0e-31 anyway)
            if (maer(i,k,m) .gt. 1.0e-31_r8) then
               drydens = maer(i,k,m)/dryvolmr(i,k)        ! kg/m3 aerosol
            else
               drydens = 1.0_r8
            end if
            dryvol(i,k,m)   = 1.0_r8/v2ncur_a             ! m3/particle
            drymass(i,k,m)  = drydens*dryvol(i,k,m)       ! kg/particle
            dryrad(i,k,m)   = (dryvol(i,k,m)/pi43)**third ! m
         end do    ! i = 1, ncol
      end do    ! k = top_lev, pver

      if (modal_strat_sulfate) then
         do k = top_lev, pver
            do i = 1, ncol
               if (so4dryvolmr(i,k) .gt. 1.0e-31_r8) then
                  so4dryvol(i,k,m) = dryvol(i,k,m)*so4dryvolmr(i,k)/dryvolmr(i,k)
               else
                  so4dryvol(i,k,m) = 0.0_r8
               end if
               dmean = dgncur_awet(i,k,m)*exp(1.5_r8*alnsg**2)
               call calc_h2so4_equilib_mixrat( t(i,k), pmid(i,k), h2ommr(i,k), dmean, &
                                               qh2so4_equilib, wtpct_mode, sulden_mode )
               sulfeq(i,k,m)  = qh2so4_equilib
               wtpct(i,k,m)   = wtpct_mode
               sulden(i,k,m)  = sulden_mode                        
            end do    ! i = 1, ncol
         end do    ! k = top_lev, pver

         fieldname = ' '
         write(fieldname,fmt='(a,i1)') 'wtpct_a',m
         call outfld(fieldname,wtpct(1:ncol,1:pver,m), ncol, lchnk )

         fieldname = ' '
         write(fieldname,fmt='(a,i1)') 'sulfeq_a',m
         call outfld(fieldname,sulfeq(1:ncol,1:pver,m), ncol, lchnk )

         fieldname = ' '
         write(fieldname,fmt='(a,i1)') 'sulden_a',m
         call outfld(fieldname,sulden(1:ncol,1:pver,m), ncol, lchnk )

      end if
      
   end do    ! m = 1, nmodes

   ! relative humidity calc

   itim_old    =  pbuf_old_tim_idx()
   call pbuf_get_field(pbuf, cld_idx, cldn, start=(/1,1,itim_old/), kount=(/pcols,pver,1/) )

   do k = top_lev, pver
      call qsat_water(t(:ncol,k), pmid(:ncol,k), es(:ncol), qs(:ncol))
      do i = 1, ncol
         if (qs(i) > h2ommr(i,k)) then
            rh(i,k) = h2ommr(i,k)/qs(i)
         else
            rh(i,k) = 0.98_r8
         endif
         rh(i,k) = max(rh(i,k), 0.0_r8)
         rh(i,k) = min(rh(i,k), 0.98_r8)
         if (cldn(i,k) .lt. 1.0_r8) then
            rh(i,k) = (rh(i,k) - cldn(i,k)) / (1.0_r8 - cldn(i,k))  ! clear portion
         end if
         rh(i,k) = max(rh(i,k), 0.0_r8)
      end do
   end do

   call modal_aero_wateruptake_sub( &
      ncol, nmodes, rhcrystal, rhdeliques, dryrad, &
      hygro, rh, dryvol, so4dryvol, so4specdens, tropLev, &
      wetrad, wetvol, wtrvol, sulden, wtpct)

   if (list_idx == 0) then
      call pbuf_get_field(pbuf, wetdens_ap_idx, wetdens)
      call pbuf_get_field(pbuf, qaerwat_idx,    qaerwat)
   else
      allocate(wetdens(pcols,pver,nmodes), &
               qaerwat(pcols,pver,nmodes), stat=stat)
      if (stat > 0) then
         call endrun('modal_aero_wateruptake_dr: allocation FAILURE')
      end if

   end if

   do m = 1, nmodes

      do k = top_lev, pver
         do i = 1, ncol

            dgncur_awet(i,k,m) = dgncur_a(i,k,m) * (wetrad(i,k,m)/dryrad(i,k,m))
            qaerwat(i,k,m)     = rhoh2o*naer(i,k,m)*wtrvol(i,k,m)

            ! compute aerosol wet density (kg/m3)
            if (wetvol(i,k,m) > 1.0e-30_r8) then
               wetdens(i,k,m) = (drymass(i,k,m) + rhoh2o*wtrvol(i,k,m))/wetvol(i,k,m)
            else
               wetdens(i,k,m) = specdens_1(m)
            end if
         end do
      end do

   end do    ! modes

   if (list_idx == 0) then

      do m = 1, nmodes
         ! output to history
         write( trnum, '(i3.3)' ) m
         call outfld( 'wat_a'//trnum(3:3),  qaerwat(:,:,m),     pcols, lchnk)
         call outfld( 'dgnd_a'//trnum(2:3), dgncur_a(:,:,m),    pcols, lchnk)
         call outfld( 'dgnw_a'//trnum(2:3), dgncur_awet(:,:,m), pcols, lchnk)
      end do

   else

      ! for diagnostic calcs just return results
      dgnumwet_m => dgncur_awet
      qaerwat_m  => qaerwat
      wetdens_m  => wetdens

   end if
   
   deallocate( &
      maer,   hygro,  naer,   dryvol,    so4dryvol, drymass,    dryrad, &
      wetrad, wetvol, wtrvol, wtpct, sulden, rhcrystal, rhdeliques, specdens_1 )

end subroutine modal_aero_wateruptake_dr

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

subroutine modal_aero_wateruptake_sub( &
   ncol, nmodes, rhcrystal, rhdeliques, dryrad, &
   hygro, rh, dryvol, so4dryvol, so4specdens, troplev, &
   wetrad, wetvol, wtrvol, sulden, wtpct)

!-----------------------------------------------------------------------
!
! Purpose: Compute aerosol wet radius
!
! Method:  Kohler theory
!
! Author:  S. Ghan
!
!-----------------------------------------------------------------------

   
   ! Arguments
   integer, intent(in)  :: ncol                    ! number of columns
   integer, intent(in)  :: nmodes
   integer, intent(in)  :: troplev(:)

   real(r8), intent(in) :: rhcrystal(:)
   real(r8), intent(in) :: rhdeliques(:)
   real(r8), intent(in) :: dryrad(:,:,:)         ! dry volume mean radius of aerosol (m)
   real(r8), intent(in) :: hygro(:,:,:)          ! volume-weighted mean hygroscopicity (--)
   real(r8), intent(in) :: rh(:,:)               ! relative humidity (0-1)
   real(r8), intent(in) :: dryvol(:,:,:)         ! dry volume of single aerosol (m3)
   real(r8), intent(in) :: so4dryvol(:,:,:)      ! dry volume of sulfate in single aerosol (m3)
   real(r8), intent(in) :: so4specdens           ! mass density sulfate in single aerosol (kg/m3)
   real(r8), intent(in) :: wtpct(:,:,:)          ! sulfate aerosol composition, weight % H2SO4
   real(r8), intent(in) :: sulden(:,:,:)         ! sulfate aerosol mass density (g/cm3)

   real(r8), intent(out) :: wetrad(:,:,:)        ! wet radius of aerosol (m)
   real(r8), intent(out) :: wetvol(:,:,:)        ! single-particle-mean wet volume (m3)
   real(r8), intent(out) :: wtrvol(:,:,:)        ! single-particle-mean water volume in wet aerosol (m3)

   ! local variables

   integer :: i, k, m

   real(r8) :: hystfac                ! working variable for hysteresis
   !-----------------------------------------------------------------------


   ! loop over all aerosol modes
   do m = 1, nmodes

      hystfac = 1.0_r8 / max(1.0e-5_r8, (rhdeliques(m) - rhcrystal(m)))

      do k = top_lev, pver
         do i = 1, ncol

            if ( modal_strat_sulfate .and. (k<troplev(i))) then
               wetvol(i,k,m) = dryvol(i,k,m)-so4dryvol(i,k,m)
               wetvol(i,k,m) = wetvol(i,k,m)+so4dryvol(i,k,m)*so4specdens/sulden(i,k,m)/wtpct(i,k,m)/10._r8
               wetvol(i,k,m) = max(wetvol(i,k,m), dryvol(i,k,m))
               wetrad(i,k,m) = (wetvol(i,k,m)/pi43)**third
               wetrad(i,k,m) = max(wetrad(i,k,m), dryrad(i,k,m))
               wtrvol(i,k,m) = wetvol(i,k,m) - dryvol(i,k,m)
               wtrvol(i,k,m) = max(wtrvol(i,k,m), 0.0_r8)
            else
              ! compute wet radius for each mode
              call modal_aero_kohler(dryrad(i:i,k,m), hygro(i:i,k,m), rh(i:i,k), wetrad(i:i,k,m), 1)

              wetrad(i,k,m) = max(wetrad(i,k,m), dryrad(i,k,m))
              wetvol(i,k,m) = pi43*wetrad(i,k,m)**3
              wetvol(i,k,m) = max(wetvol(i,k,m), dryvol(i,k,m))
              wtrvol(i,k,m) = wetvol(i,k,m) - dryvol(i,k,m)
              wtrvol(i,k,m) = max(wtrvol(i,k,m), 0.0_r8)

              ! apply simple treatment of deliquesence/crystallization hysteresis
              ! for rhcrystal < rh < rhdeliques, aerosol water is a fraction of
              ! the "upper curve" value, and the fraction is a linear function of rh
              if (rh(i,k) < rhcrystal(m)) then
                 wetrad(i,k,m) = dryrad(i,k,m)
                 wetvol(i,k,m) = dryvol(i,k,m)
                 wtrvol(i,k,m) = 0.0_r8
              else if (rh(i,k) < rhdeliques(m)) then
                 wtrvol(i,k,m) = wtrvol(i,k,m)*hystfac*(rh(i,k) - rhcrystal(m))
                 wtrvol(i,k,m) = max(wtrvol(i,k,m), 0.0_r8)
                 wetvol(i,k,m) = dryvol(i,k,m) + wtrvol(i,k,m)
                 wetrad(i,k,m) = (wetvol(i,k,m)/pi43)**third
              end if
            end if

         end do  ! columns
      end do     ! levels

   end do ! modes

end subroutine modal_aero_wateruptake_sub

!-----------------------------------------------------------------------
      subroutine modal_aero_kohler(   &
          rdry_in, hygro, s, rwet_out, im )

! calculates equlibrium radius r of haze droplets as function of
! dry particle mass and relative humidity s using kohler solution
! given in pruppacher and klett (eqn 6-35)

! for multiple aerosol types, assumes an internal mixture of aerosols

      implicit none

! arguments
      integer :: im         ! number of grid points to be processed
      real(r8) :: rdry_in(:)    ! aerosol dry radius (m)
      real(r8) :: hygro(:)      ! aerosol volume-mean hygroscopicity (--)
      real(r8) :: s(:)          ! relative humidity (1 = saturated)
      real(r8) :: rwet_out(:)   ! aerosol wet radius (m)

! local variables
      integer, parameter :: imax=200
      integer :: i, n, nsol

      real(r8) :: a, b
      real(r8) :: p40(imax),p41(imax),p42(imax),p43(imax) ! coefficients of polynomial
      real(r8) :: p30(imax),p31(imax),p32(imax) ! coefficients of polynomial
      real(r8) :: p
      real(r8) :: r3, r4
      real(r8) :: r(im)         ! wet radius (microns)
      real(r8) :: rdry(imax)    ! radius of dry particle (microns)
      real(r8) :: ss            ! relative humidity (1 = saturated)
      real(r8) :: slog(imax)    ! log relative humidity
      real(r8) :: vol(imax)     ! total volume of particle (microns**3)
      real(r8) :: xi, xr

      complex(r8) :: cx4(4,imax),cx3(3,imax)

      real(r8), parameter :: eps = 1.e-4_r8
      real(r8), parameter :: mw = 18._r8
      real(r8), parameter :: pi = 3.14159_r8
      real(r8), parameter :: rhow = 1._r8
      real(r8), parameter :: surften = 76._r8
      real(r8), parameter :: tair = 273._r8
      real(r8), parameter :: third = 1._r8/3._r8
      real(r8), parameter :: ugascon = 8.3e7_r8


!     effect of organics on surface tension is neglected
      a=2.e4_r8*mw*surften/(ugascon*tair*rhow)

      do i=1,im
           rdry(i) = rdry_in(i)*1.0e6_r8   ! convert (m) to (microns)
           vol(i) = rdry(i)**3          ! vol is r**3, not volume
           b = vol(i)*hygro(i)

!          quartic
           ss=min(s(i),1._r8-eps)
           ss=max(ss,1.e-10_r8)
           slog(i)=log(ss)
           p43(i)=-a/slog(i)
           p42(i)=0._r8
           p41(i)=b/slog(i)-vol(i)
           p40(i)=a*vol(i)/slog(i)
!          cubic for rh=1
           p32(i)=0._r8
           p31(i)=-b/a
           p30(i)=-vol(i)
      end do


       do 100 i=1,im

!       if(vol(i).le.1.e-20)then
        if(vol(i).le.1.e-12_r8)then
           r(i)=rdry(i)
           go to 100
        endif

        p=abs(p31(i))/(rdry(i)*rdry(i))
        if(p.lt.eps)then
!          approximate solution for small particles
           r(i)=rdry(i)*(1._r8+p*third/(1._r8-slog(i)*rdry(i)/a))
        else
           call makoh_quartic(cx4(1,i),p43(i),p42(i),p41(i),p40(i),1)
!          find smallest real(r8) solution
           r(i)=1000._r8*rdry(i)
           nsol=0
           do n=1,4
              xr=real(cx4(n,i))
              xi=aimag(cx4(n,i))
              if(abs(xi).gt.abs(xr)*eps) cycle  
              if(xr.gt.r(i)) cycle  
              if(xr.lt.rdry(i)*(1._r8-eps)) cycle  
              if(xr.ne.xr) cycle  
              r(i)=xr
              nsol=n
           end do  
           if(nsol.eq.0)then
              write(iulog,*)   &
               'ccm kohlerc - no real(r8) solution found (quartic)'
              write(iulog,*)'roots =', (cx4(n,i),n=1,4)
              write(iulog,*)'p0-p3 =', p40(i), p41(i), p42(i), p43(i)
              write(iulog,*)'rh=',s(i)
              write(iulog,*)'setting radius to dry radius=',rdry(i)
              r(i)=rdry(i)
!             stop
           endif
        endif

        if(s(i).gt.1._r8-eps)then
!          save quartic solution at s=1-eps
           r4=r(i)
!          cubic for rh=1
           p=abs(p31(i))/(rdry(i)*rdry(i))
           if(p.lt.eps)then
              r(i)=rdry(i)*(1._r8+p*third)
           else
              call makoh_cubic(cx3,p32,p31,p30,im)
!             find smallest real(r8) solution
              r(i)=1000._r8*rdry(i)
              nsol=0
              do n=1,3
                 xr=real(cx3(n,i))
                 xi=aimag(cx3(n,i))
                 if(abs(xi).gt.abs(xr)*eps) cycle  
                 if(xr.gt.r(i)) cycle  
                 if(xr.lt.rdry(i)*(1._r8-eps)) cycle  
                 if(xr.ne.xr) cycle  
                 r(i)=xr
                 nsol=n
              end do  
              if(nsol.eq.0)then
                 write(iulog,*)   &
                  'ccm kohlerc - no real(r8) solution found (cubic)'
                 write(iulog,*)'roots =', (cx3(n,i),n=1,3)
                 write(iulog,*)'p0-p2 =', p30(i), p31(i), p32(i)
                 write(iulog,*)'rh=',s(i)
                 write(iulog,*)'setting radius to dry radius=',rdry(i)
                 r(i)=rdry(i)
!                stop
              endif
           endif
           r3=r(i)
!          now interpolate between quartic, cubic solutions
           r(i)=(r4*(1._r8-s(i))+r3*(s(i)-1._r8+eps))/eps
        endif

  100 continue

! bound and convert from microns to m
      do i=1,im
         r(i) = min(r(i),30._r8) ! upper bound based on 1 day lifetime
         rwet_out(i) = r(i)*1.e-6_r8
      end do

      return
      end subroutine modal_aero_kohler


!-----------------------------------------------------------------------
      subroutine makoh_cubic( cx, p2, p1, p0, im )
!
!     solves  x**3 + p2 x**2 + p1 x + p0 = 0
!     where p0, p1, p2 are real
!
      integer, parameter :: imx=200
      integer :: im
      real(r8) :: p0(imx), p1(imx), p2(imx)
      complex(r8) :: cx(3,imx)

      integer :: i
      real(r8) :: eps, q(imx), r(imx), sqrt3, third
      complex(r8) :: ci, cq, crad(imx), cw, cwsq, cy(imx), cz(imx)

      save eps
      data eps/1.e-20_r8/

      third=1._r8/3._r8
      ci=cmplx(0._r8,1._r8,r8)
      sqrt3=sqrt(3._r8)
      cw=0.5_r8*(-1+ci*sqrt3)
      cwsq=0.5_r8*(-1-ci*sqrt3)

      do i=1,im
      if(p1(i).eq.0._r8)then
!        completely insoluble particle
         cx(1,i)=(-p0(i))**third
         cx(2,i)=cx(1,i)
         cx(3,i)=cx(1,i)
      else
         q(i)=p1(i)/3._r8
         r(i)=p0(i)/2._r8
         crad(i)=r(i)*r(i)+q(i)*q(i)*q(i)
         crad(i)=sqrt(crad(i))

         cy(i)=r(i)-crad(i)
         if (abs(cy(i)).gt.eps) cy(i)=cy(i)**third
         cq=q(i)
         cz(i)=-cq/cy(i)

         cx(1,i)=-cy(i)-cz(i)
         cx(2,i)=-cw*cy(i)-cwsq*cz(i)
         cx(3,i)=-cwsq*cy(i)-cw*cz(i)
      endif
      enddo

      return
      end subroutine makoh_cubic


!-----------------------------------------------------------------------
      subroutine makoh_quartic( cx, p3, p2, p1, p0, im )

!     solves x**4 + p3 x**3 + p2 x**2 + p1 x + p0 = 0
!     where p0, p1, p2, p3 are real
!
      integer, parameter :: imx=200
      integer :: im
      real(r8) :: p0(imx), p1(imx), p2(imx), p3(imx)
      complex(r8) :: cx(4,imx)

      integer :: i
      real(r8) :: third, q(imx), r(imx)
      complex(r8) :: cb(imx), cb0(imx), cb1(imx),   &
                     crad(imx), cy(imx), czero


      czero=cmplx(0.0_r8,0.0_r8,r8)
      third=1._r8/3._r8

      do 10 i=1,im

      q(i)=-p2(i)*p2(i)/36._r8+(p3(i)*p1(i)-4*p0(i))/12._r8
      r(i)=-(p2(i)/6)**3+p2(i)*(p3(i)*p1(i)-4*p0(i))/48._r8   &
       +(4*p0(i)*p2(i)-p0(i)*p3(i)*p3(i)-p1(i)*p1(i))/16

      crad(i)=r(i)*r(i)+q(i)*q(i)*q(i)
      crad(i)=sqrt(crad(i))

      cb(i)=r(i)-crad(i)
      if(cb(i).eq.czero)then
!        insoluble particle
         cx(1,i)=(-p1(i))**third
         cx(2,i)=cx(1,i)
         cx(3,i)=cx(1,i)
         cx(4,i)=cx(1,i)
      else
         cb(i)=cb(i)**third

         cy(i)=-cb(i)+q(i)/cb(i)+p2(i)/6

         cb0(i)=sqrt(cy(i)*cy(i)-p0(i))
         cb1(i)=(p3(i)*cy(i)-p1(i))/(2*cb0(i))

         cb(i)=p3(i)/2+cb1(i)
         crad(i)=cb(i)*cb(i)-4*(cy(i)+cb0(i))
         crad(i)=sqrt(crad(i))
         cx(1,i)=(-cb(i)+crad(i))/2._r8
         cx(2,i)=(-cb(i)-crad(i))/2._r8

         cb(i)=p3(i)/2-cb1(i)
         crad(i)=cb(i)*cb(i)-4*(cy(i)-cb0(i))
         crad(i)=sqrt(crad(i))
         cx(3,i)=(-cb(i)+crad(i))/2._r8
         cx(4,i)=(-cb(i)-crad(i))/2._r8
      endif
   10 continue

      return
      end subroutine makoh_quartic

!----------------------------------------------------------------------
      subroutine calc_h2so4_equilib_mixrat( temp, pres, qh2o, dmean, &
                                            qh2so4_equilib, wtpct, sulden )

      implicit none

      real(r8), intent(in)  :: temp            ! temperature (K)
      real(r8), intent(in)  :: pres            ! pressure (Pa)
      real(r8), intent(in)  :: qh2o            ! water vapor specific humidity (kg/kg)
      real(r8), intent(in)  :: dmean           ! mean diameter of particles in each mode
      real(r8), intent(out) :: qh2so4_equilib  ! h2so4 saturation mixing ratios over the particles (mol/mol)
      real(r8), intent(out) :: wtpct           ! sulfate composition, weight % H2SO4
      real(r8), intent(out) :: sulden          ! sulfate aerosol mass density (g/cm3)
      
      ! Local declarations
      real(r8)            :: qh2o_kelvin ! water vapor specific humidity adjusted for Kelvin effect (kg/kg)
      real(r8)            :: wtpct_flat  ! sulfate composition over a flat surface, weight % H2SO4
      real(r8)            :: fk1, fk4, fk4_1, fk4_2 
      real(r8)            :: factor_kulm                     ! Kulmala correction terms
      real(r8)            :: en, t, sig1, sig2, frac, surf_tens, surf_tens_mode, dsigma_dwt
      real(r8)            :: den1, den2, sulfate_density, drho_dwt
      real(r8)            :: akelvin, expon, akas, rkelvinH2O, rkelvinH2O_a, rkelvinH2O_b
      real(r8)            :: sulfequil, r
      real(r8), parameter :: t0_kulm     = 340._r8           !  T0 set in the low end of the Ayers measurement range (338-445K)
      real(r8), parameter :: t_crit_kulm = 905._r8           !  Critical temperature = 1.5 * Boiling point
      real(r8), parameter :: fk0         = -10156._r8 / t0_kulm + 16.259_r8   !  Log(Kulmala correction factor)
      real(r8), parameter :: fk2         = 1._r8 / t0_kulm
      real(r8), parameter :: fk3         = 0.38_r8 / (t_crit_kulm - t0_kulm)   
      real(r8), parameter :: RGAS = 8.31430e+07_r8           ! ideal gas constant (erg/mol/K)
      real(r8), parameter :: wtmol_h2so4 =  98.078479_r8     ! molecular weight of sulphuric acid
      real(r8), parameter :: wtmol_h2o   =  18.015280_r8     ! molecular weight of water vapor
      real(r8), parameter :: wtmol_air   =  28.9644_r8       ! molecular weight of dry air
      real(r8)            :: gv_wt_abcd_en(6,95), gvh2ovp(95)
      real(r8)            :: dnwtp(46), dnc0(46), dnc1(46)
      real(r8)            :: stwtp(15), stc0(15), stc1(15)
      integer             :: i, k

        
      data stwtp/0._r8, 23.8141_r8, 38.0279_r8, 40.6856_r8, 45.335_r8, 52.9305_r8, &
         56.2735_r8, 59.8557_r8, 66.2364_r8, 73.103_r8, 79.432_r8, 85.9195_r8, &
         91.7444_r8, 97.6687_r8, 100._r8/

      data stc0/117.564_r8, 103.303_r8, 101.796_r8, 100.42_r8, 98.4993_r8, 91.8866_r8, &
         88.3033_r8, 86.5546_r8, 84.471_r8, 81.2939_r8, 79.3556_r8, 75.608_r8, &
         70.0777_r8, 63.7412_r8, 61.4591_r8 /

      data stc1/-0.153641_r8, -0.0982007_r8, -0.0872379_r8, -0.0818509_r8,           &
         -0.0746702_r8, -0.0522399_r8, -0.0407773_r8, -0.0357946_r8, -0.0317062_r8,   &
         -0.025825_r8, -0.0267212_r8, -0.0269204_r8, -0.0276187_r8, -0.0302094_r8,    &
         -0.0303081_r8 /
           
       
      data dnwtp / 0._r8, 1._r8, 5._r8, 10._r8, 20._r8, 25._r8, 30._r8, 35._r8, 40._r8,  &
         41._r8, 45._r8, 50._r8, 53._r8, 55._r8, 56._r8, 60._r8, 65._r8, 66._r8, 70._r8, &
         72._r8, 73._r8, 74._r8, 75._r8, 76._r8, 78._r8, 79._r8, 80._r8, 81._r8, 82._r8, &
         83._r8, 84._r8, 85._r8, 86._r8, 87._r8, 88._r8, 89._r8, 90._r8, 91._r8, 92._r8, &
         93._r8, 94._r8, 95._r8, 96._r8, 97._r8, 98._r8, 100._r8 /
         
      data dnc0 / 1._r8, 1.13185_r8, 1.17171_r8, 1.22164_r8, 1.3219_r8, 1.37209_r8,         &
         1.42185_r8, 1.4705_r8, 1.51767_r8, 1.52731_r8, 1.56584_r8, 1.61834_r8, 1.65191_r8, &
         1.6752_r8, 1.68708_r8, 1.7356_r8, 1.7997_r8, 1.81271_r8, 1.86696_r8, 1.89491_r8,   &
         1.9092_r8, 1.92395_r8, 1.93904_r8, 1.95438_r8, 1.98574_r8, 2.00151_r8, 2.01703_r8, &
         2.03234_r8, 2.04716_r8, 2.06082_r8, 2.07363_r8, 2.08461_r8, 2.09386_r8, 2.10143_r8,&
         2.10764_r8, 2.11283_r8, 2.11671_r8, 2.11938_r8, 2.12125_r8, 2.1219_r8, 2.12723_r8, &
         2.12654_r8, 2.12621_r8, 2.12561_r8, 2.12494_r8, 2.12093_r8 /
         
      data dnc1 / 0._r8,  -0.000435022_r8, -0.000479481_r8, -0.000531558_r8, -0.000622448_r8, &
         -0.000660866_r8, -0.000693492_r8, -0.000718251_r8, -0.000732869_r8, -0.000735755_r8, &
         -0.000744294_r8, -0.000761493_r8, -0.000774238_r8, -0.00078392_r8, -0.000788939_r8,  &
         -0.00080946_r8, -0.000839848_r8, -0.000845825_r8, -0.000874337_r8, -0.000890074_r8,  &
         -0.00089873_r8, -0.000908778_r8, -0.000920012_r8, -0.000932184_r8, -0.000959514_r8,  &
         -0.000974043_r8, -0.000988264_r8, -0.00100258_r8, -0.00101634_r8, -0.00102762_r8,    &
         -0.00103757_r8, -0.00104337_r8, -0.00104563_r8, -0.00104458_r8, -0.00104144_r8,      &
         -0.00103719_r8, -0.00103089_r8, -0.00102262_r8, -0.00101355_r8, -0.00100249_r8,      &
         -0.00100934_r8, -0.000998299_r8, -0.000990961_r8, -0.000985845_r8, -0.000984529_r8,  &
         -0.000989315_r8 /  

      ! Saturation vapor pressure of sulfuric acid
      !  
      ! Limit extrapolation at extreme temperatures
      t=min(max(temp,140._r8),450._r8)
      
      !!  Calculate the weight % H2SO4 composition of sulfate
      call calc_h2so4_wtpct(t, pres, qh2o, wtpct_flat)
                  
      !!  Calculate surface tension (erg/cm2) of sulfate of 
      !!  different compositions as a linear function of temperature.
      i = 2 ! minimum wt% is 29.517
      do while (wtpct_flat.gt.stwtp(i))
       i = i + 1
      end do
      sig1 = stc0(i-1) + stc1(i-1) * t
      sig2 = stc0(i)   + stc1(i) * t
      ! calculate derivative needed later for kelvin factor for h2o      
      dsigma_dwt = (sig2-sig1) / (stwtp(i)-stwtp(i-1))
      surf_tens = sig1 + dsigma_dwt*(wtpct_flat-stwtp(i))    
      
      !!  Calculate density (g/cm3) of sulfate of 
      !!  different compositions as a linear function of temperature.
      i = 6 ! minimum wt% is 29.517
      do while (wtpct_flat .gt. dnwtp(i))
        i = i + 1
      end do
      den1 = dnc0(i-1) + dnc1(i-1) * t
      den2 = dnc0(i)   + dnc1(i) * t
      ! Calculate derivative needed later for Kelvin factor for H2O      
      drho_dwt = (den2-den1) / (dnwtp(i)-dnwtp(i-1))
      sulfate_density = den1 + drho_dwt * (wtpct_flat-dnwtp(i-1))

      r=dmean*100._r8/2._r8 ! calcuate mode radius (cm) from diameter (m)

      ! Adjust for Kelvin effect for water
      rkelvinH2O_b = 1 + wtpct_flat * drho_dwt / &
         sulfate_density - 3._r8 * wtpct_flat * dsigma_dwt / (2._r8*surf_tens)

      rkelvinH2O_a = 2._r8 * wtmol_h2so4 * surf_tens / &
           (sulfate_density * RGAS * t * r)     

      rkelvinH2O = exp (rkelvinH2O_a*rkelvinH2O_b)

      qh2o_kelvin = qh2o/rkelvinH2O
      call calc_h2so4_wtpct(t, pres, qh2o_kelvin, wtpct)


      wtpct=max(wtpct,wtpct_flat)

      ! Parameterized fit to Giauque's (1959) enthalpies v. wt %:
      en = 4.184_r8 * (23624.8_r8 - 1.14208e8_r8 / ((wtpct - 105.318_r8)**2 + 4798.69_r8))
      en = max(en, 0.0_r8)

      !!  Calculate surface tension (erg/cm2) of sulfate of 
      !!  different compositions as a linear function of temperature.
      i = 2 ! minimum wt% is 29.517
      do while (wtpct.gt.stwtp(i))
       i=i+1
      end do
      sig1=stc0(i-1)+stc1(i-1)*t
      sig2=stc0(i)+stc1(i)*t
      frac=(stwtp(i)-wtpct)/(stwtp(i)-stwtp(i-1))
      surf_tens_mode=sig1*frac+sig2*(1.0_r8-frac)     

      !!  Calculate density (g/cm3) of sulfate of 
      !!  different compositions as a linear function of temperature.
      i = 6 ! minimum wt% is 29.517
      do while (wtpct .gt. dnwtp(i))
        i=i+1
      end do
      den1=dnc0(i-1)+dnc1(i-1)*t
      den2=dnc0(i)+dnc1(i)*t
      frac=(dnwtp(i)-wtpct)/(dnwtp(i)-dnwtp(i-1))
      sulden=den1*frac+den2*(1.0_r8-frac)

      ! Ayers' (1980) fit to sulfuric acid equilibrium vapor pressure:
      ! (Remember this is the log)
      ! SULFEQ=-10156/Temp+16.259-En/(8.3143*Temp)
      !
      ! Kulmala correction (J. CHEM. PHYS. V.93, No.1, 1 July 1990)
      fk1   = -1._r8 / t
      fk4_1 = log(t0_kulm / t)
      fk4_2 = t0_kulm / t
      fk4   = 1.0_r8 + fk4_1 - fk4_2
      factor_kulm = fk1 + fk2 + fk3 * fk4

      ! This is for pure H2SO4
      sulfequil = fk0 + 10156._r8 * factor_kulm

      ! Adjust for WTPCT composition:
      sulfequil = sulfequil - en / (8.3143_r8 * t)

      ! Take the exponential:
      sulfequil = exp(sulfequil)

      ! Convert atmospheres ==> Pa
      sulfequil = sulfequil * 1.01325e5_r8  

      ! Convert Pa ==> mol/mol
      sulfequil = sulfequil / pres

      ! Calculate Kelvin curvature factor for H2SO4 interactively with temperature:
      ! (g/mol)*(erg/cm2)/(K * g/cm3 * erg/mol/K) = cm
      akelvin = 2._r8 * wtmol_h2so4 * surf_tens_mode / (t * sulden * RGAS)

      expon = akelvin / r  ! divide by mode radius (cm) 
      expon = max(-100._r8, expon)
      expon = min(100._r8, expon)
      akas = exp( expon )
      qh2so4_equilib = sulfequil * akas ! reduce H2SO4 equilibrium mixing ratio by Kelvin curvature factor

      return
      end subroutine calc_h2so4_equilib_mixrat


!----------------------------------------------------------------------
      subroutine calc_h2so4_wtpct( temp, pres, qh2o, wtpct )
      
  !!  This function calculates the weight % H2SO4 composition of 
  !!  sulfate aerosol, using Tabazadeh et. al. (GRL, 1931, 1997).
  !!  Rated for T=185-260K, activity=0.01-1.0
  !!
  !!  Argument list input:   
  !!    temp = temperature (K)
  !!    pres = atmospheric pressure (Pa)
  !!    qh2o = water specific humidity (kg/kg)
  !!
  !!  Output:
  !!    wtpct = weight % H2SO4 in H2O/H2SO4 particle (0-100)
  !!
  !! @author Mike Mills
  !! @ version October 2013

      use wv_saturation, only: qsat_water
      
      implicit none

      real(r8), intent(in)  :: temp  ! temperature (K)
      real(r8), intent(in)  :: pres  ! pressure (Pa)
      real(r8), intent(in)  :: qh2o  ! water vapor specific humidity (kg/kg)
      real(r8), intent(out) :: wtpct ! sulfate weight % H2SO4 composition
      
      ! Local declarations
      real(r8)            :: atab1,btab1,ctab1,dtab1,atab2,btab2,ctab2,dtab2
      real(r8)            :: contl, conth, contt, conwtp
      real(r8)            :: activ
      real(r8)            :: es ! saturation vapor pressure over water (Pa) (dummy)
      real(r8)            :: qs ! saturation specific humidity over water (kg/kg)

      ! calculate saturation specific humidity over pure water, qs (kg/kg)
      call qsat_water(temp, pres, es, qs)
      
      !  Activity = water specific humidity (kg/kg) / equilibrium water (kg/kg)
      activ = qh2o/qs
       
      if (activ.lt.0.05_r8) then
        activ = max(activ,1.e-6_r8)    ! restrict minimum activity
        atab1 	= 12.37208932_r8	
        btab1 	= -0.16125516114_r8
        ctab1 	= -30.490657554_r8
        dtab1 	= -2.1133114241_r8
        atab2 	= 13.455394705_r8	
        btab2 	= -0.1921312255_r8
        ctab2 	= -34.285174607_r8
        dtab2 	= -1.7620073078_r8
      elseif (activ.ge.0.05_r8.and.activ.le.0.85_r8) then
        atab1 	= 11.820654354_r8
        btab1 	= -0.20786404244_r8
        ctab1 	= -4.807306373_r8
        dtab1 	= -5.1727540348_r8
        atab2 	= 12.891938068_r8	
        btab2 	= -0.23233847708_r8
        ctab2 	= -6.4261237757_r8
        dtab2 	= -4.9005471319_r8
      elseif (activ.gt.0.85_r8) then
        activ = min(activ,1._r8)      ! restrict maximum activity
        atab1 	= -180.06541028_r8
        btab1 	= -0.38601102592_r8
        ctab1 	= -93.317846778_r8
        dtab1 	= 273.88132245_r8
        atab2 	= -176.95814097_r8
        btab2 	= -0.36257048154_r8
        ctab2 	= -90.469744201_r8
        dtab2 	= 267.45509988_r8
      else
        write(iulog,*) 'calc_h2so4_wtpct: invalid activity: activ,qh2o,qs,temp,pres=',activ,qh2o,qs,temp,pres
        call endrun( 'calc_h2so4_wtpct error' )
        return
      endif

      contl = atab1*(activ**btab1)+ctab1*activ+dtab1
      conth = atab2*(activ**btab2)+ctab2*activ+dtab2

      contt = contl + (conth-contl) * ((temp -190._r8)/70._r8)
      conwtp = (contt*98._r8) + 1000._r8

      wtpct = (100._r8*contt*98._r8)/conwtp
      wtpct = min(max(wtpct,25._r8),100._r8) ! restrict between 1 and 100 %
      
      return
      end subroutine calc_h2so4_wtpct


!----------------------------------------------------------------------

   end module modal_aero_wateruptake