LCOV - code coverage report
Current view: top level - chemistry/utils - modal_aero_wateruptake.F90 (source / functions) Hit Total Coverage
Test: coverage.info Lines: 232 441 52.6 %
Date: 2025-03-13 18:42:46 Functions: 6 9 66.7 %

          Line data    Source code
       1             : module modal_aero_wateruptake
       2             : 
       3             : !   RCE 07.04.13:  Adapted from MIRAGE2 code
       4             : 
       5             : use shr_kind_mod,     only: r8 => shr_kind_r8
       6             : use physconst,        only: pi, rhoh2o, rair
       7             : use ppgrid,           only: pcols, pver
       8             : use physics_types,    only: physics_state
       9             : use physics_buffer,   only: physics_buffer_desc, pbuf_get_index, pbuf_old_tim_idx, pbuf_get_field
      10             : 
      11             : use wv_saturation,    only: qsat_water
      12             : use rad_constituents, only: rad_cnst_get_info, rad_cnst_get_aer_mmr, rad_cnst_get_aer_props, &
      13             :                             rad_cnst_get_mode_props
      14             : use cam_history,      only: addfld, add_default, outfld, horiz_only
      15             : use cam_logfile,      only: iulog
      16             : use ref_pres,         only: top_lev => clim_modal_aero_top_lev
      17             : use phys_control,     only: phys_getopts
      18             : use cam_abortutils,   only: endrun
      19             : 
      20             : implicit none
      21             : private
      22             : save
      23             : 
      24             : public :: &
      25             :    modal_aero_wateruptake_init, &
      26             :    modal_aero_wateruptake_dr,   &
      27             :    modal_aero_wateruptake_sub,  &
      28             :    modal_aero_kohler
      29             : 
      30             : public :: modal_aero_wateruptake_reg
      31             : 
      32             : real(r8), parameter :: third = 1._r8/3._r8
      33             : real(r8), parameter :: pi43  = pi*4.0_r8/3.0_r8
      34             : 
      35             : 
      36             : ! Physics buffer indices
      37             : integer :: cld_idx        = 0
      38             : integer :: dgnum_idx      = 0
      39             : integer :: dgnumwet_idx   = 0
      40             : integer :: sulfeq_idx     = 0
      41             : integer :: wetdens_ap_idx = 0
      42             : integer :: qaerwat_idx    = 0
      43             : integer :: hygro_idx      = 0
      44             : integer :: dryvol_idx     = 0
      45             : integer :: dryrad_idx     = 0
      46             : integer :: drymass_idx    = 0
      47             : integer :: so4dryvol_idx  = 0
      48             : integer :: naer_idx       = 0
      49             : 
      50             : 
      51             : logical, public :: modal_strat_sulfate = .false.   ! If .true. then MAM sulfate surface area density used in stratospheric heterogeneous chemistry
      52             : 
      53             : !===============================================================================
      54             : contains
      55             : !===============================================================================
      56             : 
      57        1536 : subroutine modal_aero_wateruptake_reg()
      58             : 
      59             :   use physics_buffer,   only: pbuf_add_field, dtype_r8
      60             :   use rad_constituents, only: rad_cnst_get_info
      61             : 
      62             :    integer :: nmodes
      63             :    
      64        1536 :    call rad_cnst_get_info(0, nmodes=nmodes)
      65        6144 :    call pbuf_add_field('DGNUMWET',   'global',  dtype_r8, (/pcols, pver, nmodes/), dgnumwet_idx)
      66        6144 :    call pbuf_add_field('WETDENS_AP', 'physpkg', dtype_r8, (/pcols, pver, nmodes/), wetdens_ap_idx)
      67             : 
      68             :    ! 1st order rate for direct conversion of strat. cloud water to precip (1/s)
      69        6144 :    call pbuf_add_field('QAERWAT',    'physpkg', dtype_r8, (/pcols, pver, nmodes/), qaerwat_idx)  
      70             : 
      71        1536 :    if (modal_strat_sulfate) then
      72           0 :       call pbuf_add_field('MAMH2SO4EQ', 'global',  dtype_r8, (/pcols, pver, nmodes/), sulfeq_idx)
      73             :    end if
      74             : 
      75             : 
      76        1536 : end subroutine modal_aero_wateruptake_reg
      77             : 
      78             : !===============================================================================
      79             : !===============================================================================
      80             : 
      81        1536 : subroutine modal_aero_wateruptake_init(pbuf2d)
      82        1536 :    use time_manager,  only: is_first_step
      83             :    use physics_buffer,only: pbuf_set_field
      84             :    use infnan,       only : nan, assignment(=)
      85             : 
      86             :    type(physics_buffer_desc), pointer :: pbuf2d(:,:)
      87             :    real(r8) :: real_nan
      88             : 
      89             :    integer :: m, nmodes
      90             :    logical :: history_aerosol      ! Output the MAM aerosol variables and tendencies
      91             : 
      92             :    character(len=3) :: trnum       ! used to hold mode number (as characters)
      93             :    !----------------------------------------------------------------------------
      94             : 
      95        1536 :    real_nan = nan
      96             :     
      97        1536 :    cld_idx        = pbuf_get_index('CLD')    
      98        1536 :    dgnum_idx      = pbuf_get_index('DGNUM')    
      99             : 
     100        1536 :    hygro_idx      = pbuf_get_index('HYGRO')    
     101        1536 :    dryvol_idx     = pbuf_get_index('DRYVOL')    
     102        1536 :    dryrad_idx     = pbuf_get_index('DRYRAD')    
     103        1536 :    drymass_idx    = pbuf_get_index('DRYMASS')    
     104        1536 :    so4dryvol_idx  = pbuf_get_index('SO4DRYVOL')    
     105        1536 :    naer_idx       = pbuf_get_index('NAER')    
     106             : 
     107             :    ! assume for now that will compute wateruptake for climate list modes only
     108             : 
     109        1536 :    call rad_cnst_get_info(0, nmodes=nmodes)
     110             : 
     111        7680 :    do m = 1, nmodes
     112        6144 :       write(trnum, '(i3.3)') m
     113             :       call addfld('dgnd_a'//trnum(2:3), (/ 'lev' /), 'A', 'm', &
     114       12288 :          'dry dgnum, interstitial, mode '//trnum(2:3))
     115             :       call addfld('dgnw_a'//trnum(2:3), (/ 'lev' /), 'A', 'm', &
     116       12288 :          'wet dgnum, interstitial, mode '//trnum(2:3))
     117             :       call addfld('wat_a'//trnum(3:3),  (/ 'lev' /), 'A', 'm', &
     118       12288 :          'aerosol water, interstitial, mode '//trnum(2:3))
     119             :       
     120             :       ! determine default variables
     121        6144 :       call phys_getopts(history_aerosol_out = history_aerosol)
     122             : 
     123        7680 :       if (history_aerosol) then  
     124           0 :          call add_default('dgnd_a'//trnum(2:3), 1, ' ')
     125           0 :          call add_default('dgnw_a'//trnum(2:3), 1, ' ')
     126           0 :          call add_default('wat_a'//trnum(3:3),  1, ' ')
     127             :       endif
     128             : 
     129             :    end do
     130             :    
     131        3072 :    call addfld('PM25',     (/ 'lev' /), 'A', 'kg/m3', 'PM2.5 concentration')
     132        1536 :    call addfld('PM25_SRF', horiz_only,  'A', 'kg/m3', 'surface PM2.5 concentration')
     133             : 
     134        1536 :    if (is_first_step()) then
     135             :       ! initialize fields in physics buffer
     136         768 :       call pbuf_set_field(pbuf2d, dgnumwet_idx, 0.0_r8)
     137         768 :       if (modal_strat_sulfate) then
     138             :       ! initialize fields in physics buffer to NaN (not a number) 
     139             :       ! so model will crash if used before initialization
     140           0 :          call pbuf_set_field(pbuf2d, sulfeq_idx, real_nan)
     141             :       endif
     142             :    endif
     143             : 
     144        1536 : end subroutine modal_aero_wateruptake_init
     145             : 
     146             : !===============================================================================
     147             : 
     148             : 
     149       61920 : subroutine modal_aero_wateruptake_dr(state, pbuf, list_idx_in, dgnumdry_m, dgnumwet_m, &
     150             :                                      qaerwat_m, wetdens_m, hygro_m, dryvol_m, dryrad_m, drymass_m,&
     151             :                                      so4dryvol_m, naer_m)
     152             : !-----------------------------------------------------------------------
     153             : !
     154             : ! CAM specific driver for modal aerosol water uptake code.
     155             : !
     156             : ! *** N.B. *** The calculation has been enabled for diagnostic mode lists
     157             : !              via optional arguments.  If the list_idx arg is present then
     158             : !              all the optional args must be present.
     159             : !
     160             : !-----------------------------------------------------------------------
     161             : 
     162        1536 :    use time_manager,  only: is_first_step
     163             :    use cam_history,   only: outfld, fieldname_len
     164             :    use tropopause,    only: tropopause_find, TROP_ALG_HYBSTOB, TROP_ALG_CLIMATE
     165             :    ! Arguments
     166             :    type(physics_state), target, intent(in)    :: state          ! Physics state variables
     167             :    type(physics_buffer_desc),   pointer       :: pbuf(:)        ! physics buffer
     168             : 
     169             :    integer,  optional,          intent(in)    :: list_idx_in
     170             :    real(r8), optional,          pointer       :: dgnumdry_m(:,:,:)
     171             :    real(r8), optional,          pointer       :: dgnumwet_m(:,:,:)
     172             :    real(r8), optional,          pointer       :: qaerwat_m(:,:,:)
     173             :    real(r8), optional,          pointer       :: wetdens_m(:,:,:)
     174             :    real(r8), optional,          pointer       :: hygro_m(:,:,:)
     175             :    real(r8), optional,          pointer       :: dryvol_m(:,:,:)
     176             :    real(r8), optional,          pointer       :: dryrad_m(:,:,:)
     177             :    real(r8), optional,          pointer       :: drymass_m(:,:,:)
     178             :    real(r8), optional,          pointer       :: so4dryvol_m(:,:,:)
     179             :    real(r8), optional,          pointer       :: naer_m(:,:,:)
     180             : 
     181             :    ! local variables
     182             : 
     183             :    integer  :: lchnk              ! chunk index
     184             :    integer  :: ncol               ! number of columns
     185             :    integer  :: list_idx           ! radiative constituents list index
     186             :    integer  :: stat
     187             : 
     188             :    integer :: i, k, l, m
     189             :    integer :: itim_old
     190             :    integer :: nmodes
     191             :    integer :: nspec
     192             :    integer :: tropLev(pcols)
     193             : 
     194             :    character(len=fieldname_len+3) :: fieldname
     195             : 
     196       61920 :    real(r8), pointer :: h2ommr(:,:) ! specific humidity
     197       61920 :    real(r8), pointer :: t(:,:)      ! temperatures (K)
     198       61920 :    real(r8), pointer :: pmid(:,:)   ! layer pressure (Pa)
     199             : 
     200       61920 :    real(r8), pointer :: cldn(:,:)      ! layer cloud fraction (0-1)
     201       61920 :    real(r8), pointer :: dgncur_a(:,:,:)
     202       61920 :    real(r8), pointer :: dgncur_awet(:,:,:)
     203       61920 :    real(r8), pointer :: wetdens(:,:,:)
     204       61920 :    real(r8), pointer :: qaerwat(:,:,:)
     205             : 
     206       61920 :    real(r8), pointer :: raer(:,:)        ! aerosol species MRs (kg/kg)
     207       61920 :    real(r8), pointer :: maer(:,:,:)      ! accumulated aerosol mode MRs
     208       61920 :    real(r8), pointer :: hygro(:,:,:)     ! volume-weighted mean hygroscopicity (--)
     209       61920 :    real(r8), pointer :: naer(:,:,:)      ! aerosol number MR (bounded!) (#/kg-air)
     210       61920 :    real(r8), pointer :: dryvol(:,:,:)    ! single-particle-mean dry volume (m3)
     211       61920 :    real(r8), pointer :: so4dryvol(:,:,:) ! single-particle-mean so4 dry volume (m3)
     212       61920 :    real(r8), pointer :: drymass(:,:,:)   ! single-particle-mean dry mass  (kg)
     213       61920 :    real(r8), pointer :: dryrad(:,:,:)    ! dry volume mean radius of aerosol (m)
     214             : 
     215       61920 :    real(r8), allocatable :: wetrad(:,:,:)    ! wet radius of aerosol (m)
     216       61920 :    real(r8), allocatable :: wetvol(:,:,:)    ! single-particle-mean wet volume (m3)
     217       61920 :    real(r8), allocatable :: wtrvol(:,:,:)    ! single-particle-mean water volume in wet aerosol (m3)
     218             : 
     219       61920 :    real(r8), allocatable :: rhcrystal(:)
     220       61920 :    real(r8), allocatable :: rhdeliques(:)
     221       61920 :    real(r8), allocatable :: specdens_1(:)
     222             : 
     223       61920 :    real(r8), pointer :: sulfeq(:,:,:) ! H2SO4 equilibrium mixing ratios over particles (mol/mol)
     224       61920 :    real(r8), allocatable :: wtpct(:,:,:)  ! sulfate aerosol composition, weight % H2SO4
     225       61920 :    real(r8), allocatable :: sulden(:,:,:) ! sulfate aerosol mass density (g/cm3)
     226             :    
     227             :    real(r8) :: specdens, so4specdens
     228             :    real(r8) :: sigmag
     229       61920 :    real(r8), allocatable :: alnsg(:)
     230             :    real(r8) :: rh(pcols,pver)        ! relative humidity (0-1)
     231             :    real(r8) :: dmean, qh2so4_equilib, wtpct_mode, sulden_mode
     232             : 
     233             :    real(r8) :: es(pcols)             ! saturation vapor pressure
     234             :    real(r8) :: qs(pcols)             ! saturation specific humidity
     235             : 
     236             :    real(r8) :: pm25(pcols,pver)      ! PM2.5 diagnostics     
     237             :    real(r8) :: rhoair(pcols,pver) 
     238             : 
     239             :    character(len=3) :: trnum       ! used to hold mode number (as characters)
     240             :    character(len=32) :: spectype
     241             :    !-----------------------------------------------------------------------
     242             : 
     243       61920 :    lchnk = state%lchnk
     244       61920 :    ncol = state%ncol
     245             : 
     246       61920 :    list_idx = 0
     247       61920 :    if (present(list_idx_in)) then
     248           0 :       list_idx = list_idx_in
     249             : 
     250             :       ! check that all optional args are present
     251             :       if (.not. present(dgnumdry_m) .or. .not. present(dgnumwet_m) .or. &
     252           0 :           .not. present(qaerwat_m)  .or. .not. present(wetdens_m)) then
     253             :          call endrun('modal_aero_wateruptake_dr called for'// &
     254           0 :                      'diagnostic list but required args not present')
     255             :       end if
     256             : 
     257             :       ! arrays for diagnostic calculations must be associated
     258             :       if (.not. associated(dgnumdry_m) .or. .not. associated(dgnumwet_m) .or. &
     259           0 :           .not. associated(qaerwat_m)  .or. .not. associated(wetdens_m)) then
     260             :          call endrun('modal_aero_wateruptake_dr called for'// &
     261           0 :                      'diagnostic list but required args not associated')
     262             :       end if
     263             : 
     264           0 :       if (modal_strat_sulfate) then
     265             :          call endrun('modal_aero_wateruptake_dr cannot be called with optional arguments and'// &
     266           0 :                      ' having modal_strat_sulfate set to true')
     267             :       end if
     268             :    end if
     269             : 
     270             :    ! loop over all aerosol modes
     271       61920 :    call rad_cnst_get_info(list_idx, nmodes=nmodes)
     272             : 
     273       61920 :    if (modal_strat_sulfate) then
     274           0 :      call pbuf_get_field(pbuf,  sulfeq_idx, sulfeq ) 
     275             :    endif
     276             : 
     277             :    allocate( &
     278           0 :       wetrad(pcols,pver,nmodes),   &
     279           0 :       wetvol(pcols,pver,nmodes),   &
     280           0 :       wtrvol(pcols,pver,nmodes),   &
     281           0 :       wtpct(pcols,pver,nmodes),    &
     282           0 :       sulden(pcols,pver,nmodes),   &
     283           0 :       rhcrystal(nmodes),           &
     284           0 :       rhdeliques(nmodes),          &
     285             :       specdens_1(nmodes),          &
     286      743040 :       alnsg(nmodes)           )
     287             : 
     288   391891680 :    wtpct(:,:,:)     = 75._r8
     289   391891680 :    sulden(:,:,:)    = 1.923_r8
     290             : 
     291       61920 :    if (list_idx == 0) then
     292       61920 :       call pbuf_get_field(pbuf, dgnum_idx,      dgncur_a )
     293       61920 :       call pbuf_get_field(pbuf, dgnumwet_idx,   dgncur_awet )
     294       61920 :       call pbuf_get_field(pbuf, wetdens_ap_idx, wetdens)
     295       61920 :       call pbuf_get_field(pbuf, qaerwat_idx,    qaerwat)
     296       61920 :       call pbuf_get_field(pbuf, hygro_idx,       hygro)
     297       61920 :       call pbuf_get_field(pbuf, dryvol_idx,      dryvol)
     298       61920 :       call pbuf_get_field(pbuf, dryrad_idx,      dryrad)
     299       61920 :       call pbuf_get_field(pbuf, drymass_idx,     drymass)
     300       61920 :       call pbuf_get_field(pbuf, so4dryvol_idx,   so4dryvol)
     301       61920 :       call pbuf_get_field(pbuf, naer_idx,        naer)
     302             : 
     303       61920 :       if (is_first_step()) then
     304    78372144 :          dgncur_awet(:,:,:) = dgncur_a(:,:,:)
     305             :       end if
     306             :    else
     307           0 :       dgncur_a    => dgnumdry_m
     308           0 :       dgncur_awet => dgnumwet_m
     309           0 :       qaerwat     => qaerwat_m
     310           0 :       wetdens     => wetdens_m
     311           0 :       hygro       => hygro_m
     312           0 :       dryvol      => dryvol_m
     313           0 :       dryrad      => dryrad_m
     314           0 :       drymass     => drymass_m
     315           0 :       so4dryvol   => so4dryvol_m
     316           0 :       naer        => naer_m
     317             :    end if
     318             : 
     319       61920 :    if (modal_strat_sulfate) then
     320             :       ! get tropopause level
     321           0 :       call tropopause_find(state, tropLev, primary=TROP_ALG_HYBSTOB, backup=TROP_ALG_CLIMATE)
     322             :    endif
     323             : 
     324       61920 :    h2ommr => state%q(:,:,1)
     325       61920 :    t      => state%t
     326       61920 :    pmid   => state%pmid
     327             : 
     328      185760 :    allocate( maer(pcols,pver,nmodes))
     329   391891680 :    maer(:,:,:) = 0._r8
     330             : 
     331      309600 :    do m = 1, nmodes
     332             : 
     333             :       call rad_cnst_get_mode_props(list_idx, m, sigmag=sigmag, &
     334      247680 :          rhcrystal=rhcrystal(m), rhdeliques=rhdeliques(m))
     335             : 
     336             :       ! get mode info
     337      247680 :       call rad_cnst_get_info(list_idx, m, nspec=nspec)
     338             : 
     339     1176480 :       do l = 1, nspec
     340             : 
     341             :          ! accumulate the aerosol masses of each mode
     342      928800 :          call rad_cnst_get_aer_mmr(0, m, l, 'a', state, pbuf, raer)
     343  2885565600 :          maer(:ncol,:,m)= maer(:ncol,:,m) + raer(:ncol,:)
     344             :             
     345             :          ! get species interstitial mixing ratio ('a')
     346             :          call rad_cnst_get_aer_props(list_idx, m, l, density_aer=specdens, &
     347      928800 :                                      spectype=spectype)
     348             : 
     349      928800 :          if (modal_strat_sulfate .and. (trim(spectype).eq.'sulfate')) then
     350           0 :             so4specdens=specdens
     351             :          end if
     352             : 
     353     2105280 :          if (l == 1) then
     354             :             ! save off these values to be used as defaults
     355      247680 :             specdens_1(m) = specdens
     356             :          end if
     357             : 
     358             :       end do
     359             : 
     360      247680 :       alnsg(m) = log(sigmag)
     361             : 
     362      557280 :       if (modal_strat_sulfate) then
     363           0 :          do k = top_lev, pver
     364           0 :             do i = 1, ncol
     365           0 :                dmean = dgncur_awet(i,k,m)*exp(1.5_r8*alnsg(m)**2)
     366           0 :                call calc_h2so4_equilib_mixrat( t(i,k), pmid(i,k), h2ommr(i,k), dmean, &
     367           0 :                                                qh2so4_equilib, wtpct_mode, sulden_mode )
     368           0 :                sulfeq(i,k,m)  = qh2so4_equilib
     369           0 :                wtpct(i,k,m)   = wtpct_mode
     370           0 :                sulden(i,k,m)  = sulden_mode                        
     371             :             end do    ! i = 1, ncol
     372             :          end do    ! k = top_lev, pver
     373             :  
     374           0 :           fieldname = ' '
     375           0 :           write(fieldname,fmt='(a,i1)') 'wtpct_a',m
     376           0 :           call outfld(fieldname,wtpct(1:ncol,1:pver,m), ncol, lchnk )
     377             :  
     378           0 :           fieldname = ' '
     379           0 :           write(fieldname,fmt='(a,i1)') 'sulfeq_a',m
     380           0 :           call outfld(fieldname,sulfeq(1:ncol,1:pver,m), ncol, lchnk )
     381             :  
     382           0 :           fieldname = ' '
     383           0 :           write(fieldname,fmt='(a,i1)') 'sulden_a',m
     384           0 :           call outfld(fieldname,sulden(1:ncol,1:pver,m), ncol, lchnk )
     385             :  
     386             :        end if
     387             :       
     388             :     end do    ! m = 1, nmodes
     389             : 
     390             :    ! relative humidity calc
     391             : 
     392       61920 :    itim_old    =  pbuf_old_tim_idx()
     393      247680 :    call pbuf_get_field(pbuf, cld_idx, cldn, start=(/1,1,itim_old/), kount=(/pcols,pver,1/) )
     394             : 
     395     5820480 :    do k = top_lev, pver
     396     5758560 :       call qsat_water(t(1:ncol,k), pmid(1:ncol,k), es(1:ncol), qs(1:ncol), ncol)
     397    96216480 :       do i = 1, ncol
     398    90396000 :          if (qs(i) > h2ommr(i,k)) then
     399    88785055 :             rh(i,k) = h2ommr(i,k)/qs(i)
     400             :          else
     401     1610945 :             rh(i,k) = 0.98_r8
     402             :          endif
     403    90396000 :          rh(i,k) = max(rh(i,k), 0.0_r8)
     404    90396000 :          rh(i,k) = min(rh(i,k), 0.98_r8)
     405    90396000 :          if (cldn(i,k) .lt. 1.0_r8) then
     406    89676082 :             rh(i,k) = (rh(i,k) - cldn(i,k)) / (1.0_r8 - cldn(i,k))  ! clear portion
     407             :          end if
     408    90396000 :          rh(i,k) = max(rh(i,k), 0.0_r8)
     409    96154560 :          rhoair(i,k) = pmid(i,k)/(rair*t(i,k))
     410             :       end do
     411             :    end do
     412             : 
     413             :    call modal_aero_wateruptake_sub( &
     414             :       ncol, nmodes, rhcrystal, rhdeliques, dryrad, &
     415             :       hygro, rh, dryvol, so4dryvol, so4specdens, tropLev, &
     416       61920 :       wetrad, wetvol, wtrvol, sulden, wtpct)
     417             : 
     418   391891680 :    qaerwat = 0.0_r8
     419             : 
     420      309600 :    do m = 1, nmodes
     421             : 
     422    23343840 :       do k = top_lev, pver
     423   384865920 :          do i = 1, ncol
     424             : 
     425   361584000 :             dgncur_awet(i,k,m) = dgncur_a(i,k,m) * (wetrad(i,k,m)/dryrad(i,k,m))
     426   361584000 :             qaerwat(i,k,m)     = rhoh2o*naer(i,k,m)*wtrvol(i,k,m)
     427             : 
     428             :             ! compute aerosol wet density (kg/m3)
     429   384618240 :             if (wetvol(i,k,m) > 1.0e-30_r8) then
     430   361584000 :                wetdens(i,k,m) = (drymass(i,k,m) + rhoh2o*wtrvol(i,k,m))/wetvol(i,k,m)
     431             :             else
     432           0 :                wetdens(i,k,m) = specdens_1(m)
     433             :             end if
     434             :          end do
     435             :       end do
     436             : 
     437             :    end do    ! modes
     438             : 
     439       61920 :    if (list_idx == 0) then
     440             : 
     441       61920 :       pm25(:,:)=0._r8
     442             : 
     443      309600 :       do m = 1, nmodes
     444             :          ! output to history
     445      247680 :          write( trnum, '(i3.3)' ) m
     446      247680 :          call outfld( 'wat_a'//trnum(3:3),  qaerwat(:,:,m),     pcols, lchnk)
     447      247680 :          call outfld( 'dgnd_a'//trnum(2:3), dgncur_a(:,:,m),    pcols, lchnk)
     448      247680 :          call outfld( 'dgnw_a'//trnum(2:3), dgncur_awet(:,:,m), pcols, lchnk)
     449             : 
     450             :          ! calculate PM2.5 diagnostics -- dgncur_a is zero above top_lev
     451    23343840 :          do k = top_lev, pver
     452   384865920 :             do i=1,ncol
     453  1084752000 :                pm25(i,k) = pm25(i,k)+maer(i,k,m)*(1._r8-(0.5_r8 - 0.5_r8*erf(log(2.5e-6_r8/dgncur_a(i,k,m))/ &
     454  1469370240 :                                                  (2._r8**0.5_r8*alnsg(m)))))*rhoair(i,k)
     455             :             end do
     456             :          end do
     457             :       end do
     458             : 
     459       61920 :       call outfld('PM25',     pm25(:,:),    pcols, lchnk)
     460       61920 :       call outfld('PM25_SRF', pm25(:,pver), pcols, lchnk)
     461             : 
     462             :    end if
     463             : 
     464       61920 :    deallocate(maer, alnsg)
     465           0 :    deallocate( &
     466       61920 :       wetrad, wetvol, wtrvol, wtpct, sulden, rhcrystal, rhdeliques, specdens_1 )
     467      123840 : end subroutine modal_aero_wateruptake_dr
     468             : 
     469             : !===============================================================================
     470             : 
     471       61920 : subroutine modal_aero_wateruptake_sub( &
     472       61920 :    ncol, nmodes, rhcrystal, rhdeliques, dryrad, &
     473      185760 :    hygro, rh, dryvol, so4dryvol, so4specdens, troplev, &
     474      123840 :    wetrad, wetvol, wtrvol, sulden, wtpct)
     475             : 
     476             : !-----------------------------------------------------------------------
     477             : !
     478             : ! Purpose: Compute aerosol wet radius
     479             : !
     480             : ! Method:  Kohler theory
     481             : !
     482             : ! Author:  S. Ghan
     483             : !
     484             : !-----------------------------------------------------------------------
     485             : 
     486             :    
     487             :    ! Arguments
     488             :    integer, intent(in)  :: ncol                    ! number of columns
     489             :    integer, intent(in)  :: nmodes
     490             :    integer, intent(in)  :: troplev(:)
     491             : 
     492             :    real(r8), intent(in) :: rhcrystal(:)
     493             :    real(r8), intent(in) :: rhdeliques(:)
     494             :    real(r8), intent(in) :: dryrad(:,:,:)         ! dry volume mean radius of aerosol (m)
     495             :    real(r8), intent(in) :: hygro(:,:,:)          ! volume-weighted mean hygroscopicity (--)
     496             :    real(r8), intent(in) :: rh(:,:)               ! relative humidity (0-1)
     497             :    real(r8), intent(in) :: dryvol(:,:,:)         ! dry volume of single aerosol (m3)
     498             :    real(r8), intent(in) :: so4dryvol(:,:,:)      ! dry volume of sulfate in single aerosol (m3)
     499             :    real(r8), intent(in) :: so4specdens           ! mass density sulfate in single aerosol (kg/m3)
     500             :    real(r8), intent(in) :: wtpct(:,:,:)          ! sulfate aerosol composition, weight % H2SO4
     501             :    real(r8), intent(in) :: sulden(:,:,:)         ! sulfate aerosol mass density (g/cm3)
     502             : 
     503             :    real(r8), intent(out) :: wetrad(:,:,:)        ! wet radius of aerosol (m)
     504             :    real(r8), intent(out) :: wetvol(:,:,:)        ! single-particle-mean wet volume (m3)
     505             :    real(r8), intent(out) :: wtrvol(:,:,:)        ! single-particle-mean water volume in wet aerosol (m3)
     506             : 
     507             :    ! local variables
     508             : 
     509             :    integer :: i, k, m
     510             : 
     511             :    real(r8) :: hystfac                ! working variable for hysteresis
     512             :    !-----------------------------------------------------------------------
     513             : 
     514             : 
     515             :    ! loop over all aerosol modes
     516      309600 :    do m = 1, nmodes
     517             : 
     518      247680 :       hystfac = 1.0_r8 / max(1.0e-5_r8, (rhdeliques(m) - rhcrystal(m)))
     519             : 
     520    23343840 :       do k = top_lev, pver
     521   384865920 :          do i = 1, ncol
     522             : 
     523   384618240 :             if ( modal_strat_sulfate .and. (k<troplev(i))) then
     524           0 :                wetvol(i,k,m) = dryvol(i,k,m)-so4dryvol(i,k,m)
     525           0 :                wetvol(i,k,m) = wetvol(i,k,m)+so4dryvol(i,k,m)*so4specdens/sulden(i,k,m)/wtpct(i,k,m)/10._r8
     526           0 :                wetvol(i,k,m) = max(wetvol(i,k,m), dryvol(i,k,m))
     527           0 :                wetrad(i,k,m) = (wetvol(i,k,m)/pi43)**third
     528           0 :                wetrad(i,k,m) = max(wetrad(i,k,m), dryrad(i,k,m))
     529           0 :                wtrvol(i,k,m) = wetvol(i,k,m) - dryvol(i,k,m)
     530           0 :                wtrvol(i,k,m) = max(wtrvol(i,k,m), 0.0_r8)
     531             :             else
     532             :               ! compute wet radius for each mode
     533   361584000 :               call modal_aero_kohler(dryrad(i:i,k,m), hygro(i:i,k,m), rh(i:i,k), wetrad(i:i,k,m), 1)
     534             : 
     535   361584000 :               wetrad(i,k,m) = max(wetrad(i,k,m), dryrad(i,k,m))
     536   361584000 :               wetvol(i,k,m) = pi43*wetrad(i,k,m)**3
     537   361584000 :               wetvol(i,k,m) = max(wetvol(i,k,m), dryvol(i,k,m))
     538   361584000 :               wtrvol(i,k,m) = wetvol(i,k,m) - dryvol(i,k,m)
     539   361584000 :               wtrvol(i,k,m) = max(wtrvol(i,k,m), 0.0_r8)
     540             : 
     541             :               ! apply simple treatment of deliquesence/crystallization hysteresis
     542             :               ! for rhcrystal < rh < rhdeliques, aerosol water is a fraction of
     543             :               ! the "upper curve" value, and the fraction is a linear function of rh
     544   361584000 :               if (rh(i,k) < rhcrystal(m)) then
     545   278012608 :                  wetrad(i,k,m) = dryrad(i,k,m)
     546   278012608 :                  wetvol(i,k,m) = dryvol(i,k,m)
     547   278012608 :                  wtrvol(i,k,m) = 0.0_r8
     548    83571392 :               else if (rh(i,k) < rhdeliques(m)) then
     549    50497732 :                  wtrvol(i,k,m) = wtrvol(i,k,m)*hystfac*(rh(i,k) - rhcrystal(m))
     550    50497732 :                  wtrvol(i,k,m) = max(wtrvol(i,k,m), 0.0_r8)
     551    50497732 :                  wetvol(i,k,m) = dryvol(i,k,m) + wtrvol(i,k,m)
     552    50497732 :                  wetrad(i,k,m) = (wetvol(i,k,m)/pi43)**third
     553             :               end if
     554             :             end if
     555             : 
     556             :          end do  ! columns
     557             :       end do     ! levels
     558             : 
     559             :    end do ! modes
     560             : 
     561       61920 : end subroutine modal_aero_wateruptake_sub
     562             : 
     563             : !-----------------------------------------------------------------------
     564   361584000 :       subroutine modal_aero_kohler(   &
     565   361584000 :           rdry_in, hygro, s, rwet_out, im )
     566             : 
     567             : ! calculates equlibrium radius r of haze droplets as function of
     568             : ! dry particle mass and relative humidity s using kohler solution
     569             : ! given in pruppacher and klett (eqn 6-35)
     570             : 
     571             : ! for multiple aerosol types, assumes an internal mixture of aerosols
     572             : 
     573             :       implicit none
     574             : 
     575             : ! arguments
     576             :       integer :: im         ! number of grid points to be processed
     577             :       real(r8) :: rdry_in(:)    ! aerosol dry radius (m)
     578             :       real(r8) :: hygro(:)      ! aerosol volume-mean hygroscopicity (--)
     579             :       real(r8) :: s(:)          ! relative humidity (1 = saturated)
     580             :       real(r8) :: rwet_out(:)   ! aerosol wet radius (m)
     581             : 
     582             : ! local variables
     583             :       integer, parameter :: imax=200
     584             :       integer :: i, n, nsol
     585             : 
     586             :       real(r8) :: a, b
     587             :       real(r8) :: p40(imax),p41(imax),p42(imax),p43(imax) ! coefficients of polynomial
     588             :       real(r8) :: p30(imax),p31(imax),p32(imax) ! coefficients of polynomial
     589             :       real(r8) :: p
     590             :       real(r8) :: r3, r4
     591   723168000 :       real(r8) :: r(im)         ! wet radius (microns)
     592             :       real(r8) :: rdry(imax)    ! radius of dry particle (microns)
     593             :       real(r8) :: ss            ! relative humidity (1 = saturated)
     594             :       real(r8) :: slog(imax)    ! log relative humidity
     595             :       real(r8) :: vol(imax)     ! total volume of particle (microns**3)
     596             :       real(r8) :: xi, xr
     597             : 
     598             :       complex(r8) :: cx4(4,imax),cx3(3,imax)
     599             : 
     600             :       real(r8), parameter :: eps = 1.e-4_r8
     601             :       real(r8), parameter :: mw = 18._r8
     602             :       real(r8), parameter :: pi = 3.14159_r8
     603             :       real(r8), parameter :: rhow = 1._r8
     604             :       real(r8), parameter :: surften = 76._r8
     605             :       real(r8), parameter :: tair = 273._r8
     606             :       real(r8), parameter :: third = 1._r8/3._r8
     607             :       real(r8), parameter :: ugascon = 8.3e7_r8
     608             : 
     609             : 
     610             : !     effect of organics on surface tension is neglected
     611   361584000 :       a=2.e4_r8*mw*surften/(ugascon*tair*rhow)
     612             : 
     613   723168000 :       do i=1,im
     614   361584000 :            rdry(i) = rdry_in(i)*1.0e6_r8   ! convert (m) to (microns)
     615   361584000 :            vol(i) = rdry(i)**3          ! vol is r**3, not volume
     616   361584000 :            b = vol(i)*hygro(i)
     617             : 
     618             : !          quartic
     619   361584000 :            ss=min(s(i),1._r8-eps)
     620   361584000 :            ss=max(ss,1.e-10_r8)
     621   361584000 :            slog(i)=log(ss)
     622   361584000 :            p43(i)=-a/slog(i)
     623   361584000 :            p42(i)=0._r8
     624   361584000 :            p41(i)=b/slog(i)-vol(i)
     625   361584000 :            p40(i)=a*vol(i)/slog(i)
     626             : !          cubic for rh=1
     627   361584000 :            p32(i)=0._r8
     628   361584000 :            p31(i)=-b/a
     629   723168000 :            p30(i)=-vol(i)
     630             :       end do
     631             : 
     632             : 
     633   723168000 :        do 100 i=1,im
     634             : 
     635             : !       if(vol(i).le.1.e-20)then
     636   361584000 :         if(vol(i).le.1.e-12_r8)then
     637           0 :            r(i)=rdry(i)
     638           0 :            go to 100
     639             :         endif
     640             : 
     641   361584000 :         p=abs(p31(i))/(rdry(i)*rdry(i))
     642   361584000 :         if(p.lt.eps)then
     643             : !          approximate solution for small particles
     644    90443636 :            r(i)=rdry(i)*(1._r8+p*third/(1._r8-slog(i)*rdry(i)/a))
     645             :         else
     646   271140364 :            call makoh_quartic(cx4(1,i),p43(i),p42(i),p41(i),p40(i),1)
     647             : !          find smallest real(r8) solution
     648   271140364 :            r(i)=1000._r8*rdry(i)
     649   271140364 :            nsol=0
     650  1355701820 :            do n=1,4
     651  1084561456 :               xr=real(cx4(n,i))
     652  1084561456 :               xi=aimag(cx4(n,i))
     653  1084561456 :               if(abs(xi).gt.abs(xr)*eps) cycle  
     654   542488700 :               if(xr.gt.r(i)) cycle  
     655   542488700 :               if(xr.lt.rdry(i)*(1._r8-eps)) cycle  
     656   271140364 :               if(xr.ne.xr) cycle  
     657   271140364 :               r(i)=xr
     658  1355701820 :               nsol=n
     659             :            end do  
     660   271140364 :            if(nsol.eq.0)then
     661             :               write(iulog,*)   &
     662           0 :                'ccm kohlerc - no real(r8) solution found (quartic)'
     663           0 :               write(iulog,*)'roots =', (cx4(n,i),n=1,4)
     664           0 :               write(iulog,*)'p0-p3 =', p40(i), p41(i), p42(i), p43(i)
     665           0 :               write(iulog,*)'rh=',s(i)
     666           0 :               write(iulog,*)'setting radius to dry radius=',rdry(i)
     667           0 :               r(i)=rdry(i)
     668             : !             stop
     669             :            endif
     670             :         endif
     671             : 
     672   361584000 :         if(s(i).gt.1._r8-eps)then
     673             : !          save quartic solution at s=1-eps
     674           0 :            r4=r(i)
     675             : !          cubic for rh=1
     676           0 :            p=abs(p31(i))/(rdry(i)*rdry(i))
     677           0 :            if(p.lt.eps)then
     678           0 :               r(i)=rdry(i)*(1._r8+p*third)
     679             :            else
     680           0 :               call makoh_cubic(cx3,p32,p31,p30,im)
     681             : !             find smallest real(r8) solution
     682           0 :               r(i)=1000._r8*rdry(i)
     683           0 :               nsol=0
     684           0 :               do n=1,3
     685           0 :                  xr=real(cx3(n,i))
     686           0 :                  xi=aimag(cx3(n,i))
     687           0 :                  if(abs(xi).gt.abs(xr)*eps) cycle  
     688           0 :                  if(xr.gt.r(i)) cycle  
     689           0 :                  if(xr.lt.rdry(i)*(1._r8-eps)) cycle  
     690           0 :                  if(xr.ne.xr) cycle  
     691           0 :                  r(i)=xr
     692           0 :                  nsol=n
     693             :               end do  
     694           0 :               if(nsol.eq.0)then
     695             :                  write(iulog,*)   &
     696           0 :                   'ccm kohlerc - no real(r8) solution found (cubic)'
     697           0 :                  write(iulog,*)'roots =', (cx3(n,i),n=1,3)
     698           0 :                  write(iulog,*)'p0-p2 =', p30(i), p31(i), p32(i)
     699           0 :                  write(iulog,*)'rh=',s(i)
     700           0 :                  write(iulog,*)'setting radius to dry radius=',rdry(i)
     701           0 :                  r(i)=rdry(i)
     702             : !                stop
     703             :               endif
     704             :            endif
     705           0 :            r3=r(i)
     706             : !          now interpolate between quartic, cubic solutions
     707           0 :            r(i)=(r4*(1._r8-s(i))+r3*(s(i)-1._r8+eps))/eps
     708             :         endif
     709             : 
     710   361584000 :   100 continue
     711             : 
     712             : ! bound and convert from microns to m
     713   723168000 :       do i=1,im
     714   361584000 :          r(i) = min(r(i),30._r8) ! upper bound based on 1 day lifetime
     715   723168000 :          rwet_out(i) = r(i)*1.e-6_r8
     716             :       end do
     717             : 
     718   361584000 :       return
     719             :       end subroutine modal_aero_kohler
     720             : 
     721             : 
     722             : !-----------------------------------------------------------------------
     723           0 :       subroutine makoh_cubic( cx, p2, p1, p0, im )
     724             : !
     725             : !     solves  x**3 + p2 x**2 + p1 x + p0 = 0
     726             : !     where p0, p1, p2 are real
     727             : !
     728             :       integer, parameter :: imx=200
     729             :       integer :: im
     730             :       real(r8) :: p0(imx), p1(imx), p2(imx)
     731             :       complex(r8) :: cx(3,imx)
     732             : 
     733             :       integer :: i
     734             :       real(r8) :: eps, q(imx), r(imx), sqrt3, third
     735             :       complex(r8) :: ci, cq, crad(imx), cw, cwsq, cy(imx), cz(imx)
     736             : 
     737             :       save eps
     738             :       data eps/1.e-20_r8/
     739             : 
     740           0 :       third=1._r8/3._r8
     741           0 :       ci=cmplx(0._r8,1._r8,r8)
     742           0 :       sqrt3=sqrt(3._r8)
     743           0 :       cw=0.5_r8*(-1+ci*sqrt3)
     744           0 :       cwsq=0.5_r8*(-1-ci*sqrt3)
     745             : 
     746           0 :       do i=1,im
     747           0 :       if(p1(i).eq.0._r8)then
     748             : !        completely insoluble particle
     749           0 :          cx(1,i)=(-p0(i))**third
     750           0 :          cx(2,i)=cx(1,i)
     751           0 :          cx(3,i)=cx(1,i)
     752             :       else
     753           0 :          q(i)=p1(i)/3._r8
     754           0 :          r(i)=p0(i)/2._r8
     755           0 :          crad(i)=r(i)*r(i)+q(i)*q(i)*q(i)
     756           0 :          crad(i)=sqrt(crad(i))
     757             : 
     758           0 :          cy(i)=r(i)-crad(i)
     759           0 :          if (abs(cy(i)).gt.eps) cy(i)=cy(i)**third
     760           0 :          cq=q(i)
     761           0 :          cz(i)=-cq/cy(i)
     762             : 
     763           0 :          cx(1,i)=-cy(i)-cz(i)
     764           0 :          cx(2,i)=-cw*cy(i)-cwsq*cz(i)
     765           0 :          cx(3,i)=-cwsq*cy(i)-cw*cz(i)
     766             :       endif
     767             :       enddo
     768             : 
     769           0 :       return
     770             :       end subroutine makoh_cubic
     771             : 
     772             : 
     773             : !-----------------------------------------------------------------------
     774   271140364 :       subroutine makoh_quartic( cx, p3, p2, p1, p0, im )
     775             : 
     776             : !     solves x**4 + p3 x**3 + p2 x**2 + p1 x + p0 = 0
     777             : !     where p0, p1, p2, p3 are real
     778             : !
     779             :       integer, parameter :: imx=200
     780             :       integer :: im
     781             :       real(r8) :: p0(imx), p1(imx), p2(imx), p3(imx)
     782             :       complex(r8) :: cx(4,imx)
     783             : 
     784             :       integer :: i
     785             :       real(r8) :: third, q(imx), r(imx)
     786             :       complex(r8) :: cb(imx), cb0(imx), cb1(imx),   &
     787             :                      crad(imx), cy(imx), czero
     788             : 
     789             : 
     790   271140364 :       czero=cmplx(0.0_r8,0.0_r8,r8)
     791   271140364 :       third=1._r8/3._r8
     792             : 
     793   542280728 :       do 10 i=1,im
     794             : 
     795   271140364 :       q(i)=-p2(i)*p2(i)/36._r8+(p3(i)*p1(i)-4*p0(i))/12._r8
     796             :       r(i)=-(p2(i)/6)**3+p2(i)*(p3(i)*p1(i)-4*p0(i))/48._r8   &
     797   271140364 :        +(4*p0(i)*p2(i)-p0(i)*p3(i)*p3(i)-p1(i)*p1(i))/16
     798             : 
     799   271140364 :       crad(i)=r(i)*r(i)+q(i)*q(i)*q(i)
     800   271140364 :       crad(i)=sqrt(crad(i))
     801             : 
     802   271140364 :       cb(i)=r(i)-crad(i)
     803   271140364 :       if(cb(i).eq.czero)then
     804             : !        insoluble particle
     805           0 :          cx(1,i)=(-p1(i))**third
     806           0 :          cx(2,i)=cx(1,i)
     807           0 :          cx(3,i)=cx(1,i)
     808           0 :          cx(4,i)=cx(1,i)
     809             :       else
     810   271140364 :          cb(i)=cb(i)**third
     811             : 
     812   271140364 :          cy(i)=-cb(i)+q(i)/cb(i)+p2(i)/6
     813             : 
     814   271140364 :          cb0(i)=sqrt(cy(i)*cy(i)-p0(i))
     815   271140364 :          cb1(i)=(p3(i)*cy(i)-p1(i))/(2*cb0(i))
     816             : 
     817   271140364 :          cb(i)=p3(i)/2+cb1(i)
     818   271140364 :          crad(i)=cb(i)*cb(i)-4*(cy(i)+cb0(i))
     819   271140364 :          crad(i)=sqrt(crad(i))
     820   271140364 :          cx(1,i)=(-cb(i)+crad(i))/2._r8
     821   271140364 :          cx(2,i)=(-cb(i)-crad(i))/2._r8
     822             : 
     823   271140364 :          cb(i)=p3(i)/2-cb1(i)
     824   271140364 :          crad(i)=cb(i)*cb(i)-4*(cy(i)-cb0(i))
     825   271140364 :          crad(i)=sqrt(crad(i))
     826   271140364 :          cx(3,i)=(-cb(i)+crad(i))/2._r8
     827   271140364 :          cx(4,i)=(-cb(i)-crad(i))/2._r8
     828             :       endif
     829   271140364 :    10 continue
     830             : 
     831   271140364 :       return
     832             :       end subroutine makoh_quartic
     833             : 
     834             : !----------------------------------------------------------------------
     835           0 :       subroutine calc_h2so4_equilib_mixrat( temp, pres, qh2o, dmean, &
     836             :                                             qh2so4_equilib, wtpct, sulden )
     837             : 
     838             :       implicit none
     839             : 
     840             :       real(r8), intent(in)  :: temp            ! temperature (K)
     841             :       real(r8), intent(in)  :: pres            ! pressure (Pa)
     842             :       real(r8), intent(in)  :: qh2o            ! water vapor specific humidity (kg/kg)
     843             :       real(r8), intent(in)  :: dmean           ! mean diameter of particles in each mode
     844             :       real(r8), intent(out) :: qh2so4_equilib  ! h2so4 saturation mixing ratios over the particles (mol/mol)
     845             :       real(r8), intent(out) :: wtpct           ! sulfate composition, weight % H2SO4
     846             :       real(r8), intent(out) :: sulden          ! sulfate aerosol mass density (g/cm3)
     847             :       
     848             :       ! Local declarations
     849             :       real(r8)            :: qh2o_kelvin ! water vapor specific humidity adjusted for Kelvin effect (kg/kg)
     850             :       real(r8)            :: wtpct_flat  ! sulfate composition over a flat surface, weight % H2SO4
     851             :       real(r8)            :: fk1, fk4, fk4_1, fk4_2 
     852             :       real(r8)            :: factor_kulm                     ! Kulmala correction terms
     853             :       real(r8)            :: en, t, sig1, sig2, frac, surf_tens, surf_tens_mode, dsigma_dwt
     854             :       real(r8)            :: den1, den2, sulfate_density, drho_dwt
     855             :       real(r8)            :: akelvin, expon, akas, rkelvinH2O, rkelvinH2O_a, rkelvinH2O_b
     856             :       real(r8)            :: sulfequil, r
     857             :       real(r8), parameter :: t0_kulm     = 340._r8           !  T0 set in the low end of the Ayers measurement range (338-445K)
     858             :       real(r8), parameter :: t_crit_kulm = 905._r8           !  Critical temperature = 1.5 * Boiling point
     859             :       real(r8), parameter :: fk0         = -10156._r8 / t0_kulm + 16.259_r8   !  Log(Kulmala correction factor)
     860             :       real(r8), parameter :: fk2         = 1._r8 / t0_kulm
     861             :       real(r8), parameter :: fk3         = 0.38_r8 / (t_crit_kulm - t0_kulm)   
     862             :       real(r8), parameter :: RGAS = 8.31430e+07_r8           ! ideal gas constant (erg/mol/K)
     863             :       real(r8), parameter :: wtmol_h2so4 =  98.078479_r8     ! molecular weight of sulphuric acid
     864             :       real(r8), parameter :: wtmol_h2o   =  18.015280_r8     ! molecular weight of water vapor
     865             :       real(r8), parameter :: wtmol_air   =  28.9644_r8       ! molecular weight of dry air
     866             :       real(r8)            :: gv_wt_abcd_en(6,95), gvh2ovp(95)
     867             :       real(r8)            :: dnwtp(46), dnc0(46), dnc1(46)
     868             :       real(r8)            :: stwtp(15), stc0(15), stc1(15)
     869             :       integer             :: i, k
     870             : 
     871             :         
     872             :       data stwtp/0._r8, 23.8141_r8, 38.0279_r8, 40.6856_r8, 45.335_r8, 52.9305_r8, &
     873             :          56.2735_r8, 59.8557_r8, 66.2364_r8, 73.103_r8, 79.432_r8, 85.9195_r8, &
     874             :          91.7444_r8, 97.6687_r8, 100._r8/
     875             : 
     876             :       data stc0/117.564_r8, 103.303_r8, 101.796_r8, 100.42_r8, 98.4993_r8, 91.8866_r8, &
     877             :          88.3033_r8, 86.5546_r8, 84.471_r8, 81.2939_r8, 79.3556_r8, 75.608_r8, &
     878             :          70.0777_r8, 63.7412_r8, 61.4591_r8 /
     879             : 
     880             :       data stc1/-0.153641_r8, -0.0982007_r8, -0.0872379_r8, -0.0818509_r8,           &
     881             :          -0.0746702_r8, -0.0522399_r8, -0.0407773_r8, -0.0357946_r8, -0.0317062_r8,   &
     882             :          -0.025825_r8, -0.0267212_r8, -0.0269204_r8, -0.0276187_r8, -0.0302094_r8,    &
     883             :          -0.0303081_r8 /
     884             :            
     885             :        
     886             :       data dnwtp / 0._r8, 1._r8, 5._r8, 10._r8, 20._r8, 25._r8, 30._r8, 35._r8, 40._r8,  &
     887             :          41._r8, 45._r8, 50._r8, 53._r8, 55._r8, 56._r8, 60._r8, 65._r8, 66._r8, 70._r8, &
     888             :          72._r8, 73._r8, 74._r8, 75._r8, 76._r8, 78._r8, 79._r8, 80._r8, 81._r8, 82._r8, &
     889             :          83._r8, 84._r8, 85._r8, 86._r8, 87._r8, 88._r8, 89._r8, 90._r8, 91._r8, 92._r8, &
     890             :          93._r8, 94._r8, 95._r8, 96._r8, 97._r8, 98._r8, 100._r8 /
     891             :          
     892             :       data dnc0 / 1._r8, 1.13185_r8, 1.17171_r8, 1.22164_r8, 1.3219_r8, 1.37209_r8,         &
     893             :          1.42185_r8, 1.4705_r8, 1.51767_r8, 1.52731_r8, 1.56584_r8, 1.61834_r8, 1.65191_r8, &
     894             :          1.6752_r8, 1.68708_r8, 1.7356_r8, 1.7997_r8, 1.81271_r8, 1.86696_r8, 1.89491_r8,   &
     895             :          1.9092_r8, 1.92395_r8, 1.93904_r8, 1.95438_r8, 1.98574_r8, 2.00151_r8, 2.01703_r8, &
     896             :          2.03234_r8, 2.04716_r8, 2.06082_r8, 2.07363_r8, 2.08461_r8, 2.09386_r8, 2.10143_r8,&
     897             :          2.10764_r8, 2.11283_r8, 2.11671_r8, 2.11938_r8, 2.12125_r8, 2.1219_r8, 2.12723_r8, &
     898             :          2.12654_r8, 2.12621_r8, 2.12561_r8, 2.12494_r8, 2.12093_r8 /
     899             :          
     900             :       data dnc1 / 0._r8,  -0.000435022_r8, -0.000479481_r8, -0.000531558_r8, -0.000622448_r8, &
     901             :          -0.000660866_r8, -0.000693492_r8, -0.000718251_r8, -0.000732869_r8, -0.000735755_r8, &
     902             :          -0.000744294_r8, -0.000761493_r8, -0.000774238_r8, -0.00078392_r8, -0.000788939_r8,  &
     903             :          -0.00080946_r8, -0.000839848_r8, -0.000845825_r8, -0.000874337_r8, -0.000890074_r8,  &
     904             :          -0.00089873_r8, -0.000908778_r8, -0.000920012_r8, -0.000932184_r8, -0.000959514_r8,  &
     905             :          -0.000974043_r8, -0.000988264_r8, -0.00100258_r8, -0.00101634_r8, -0.00102762_r8,    &
     906             :          -0.00103757_r8, -0.00104337_r8, -0.00104563_r8, -0.00104458_r8, -0.00104144_r8,      &
     907             :          -0.00103719_r8, -0.00103089_r8, -0.00102262_r8, -0.00101355_r8, -0.00100249_r8,      &
     908             :          -0.00100934_r8, -0.000998299_r8, -0.000990961_r8, -0.000985845_r8, -0.000984529_r8,  &
     909             :          -0.000989315_r8 /  
     910             : 
     911             :       ! Saturation vapor pressure of sulfuric acid
     912             :       !  
     913             :       ! Limit extrapolation at extreme temperatures
     914           0 :       t=min(max(temp,140._r8),450._r8)
     915             :       
     916             :       !!  Calculate the weight % H2SO4 composition of sulfate
     917           0 :       call calc_h2so4_wtpct(t, pres, qh2o, wtpct_flat)
     918             :                   
     919             :       !!  Calculate surface tension (erg/cm2) of sulfate of 
     920             :       !!  different compositions as a linear function of temperature.
     921           0 :       i = 2 ! minimum wt% is 29.517
     922           0 :       do while (wtpct_flat.gt.stwtp(i))
     923           0 :        i = i + 1
     924             :       end do
     925           0 :       sig1 = stc0(i-1) + stc1(i-1) * t
     926           0 :       sig2 = stc0(i)   + stc1(i) * t
     927             :       ! calculate derivative needed later for kelvin factor for h2o      
     928           0 :       dsigma_dwt = (sig2-sig1) / (stwtp(i)-stwtp(i-1))
     929           0 :       surf_tens = sig1 + dsigma_dwt*(wtpct_flat-stwtp(i))    
     930             :       
     931             :       !!  Calculate density (g/cm3) of sulfate of 
     932             :       !!  different compositions as a linear function of temperature.
     933           0 :       i = 6 ! minimum wt% is 29.517
     934           0 :       do while (wtpct_flat .gt. dnwtp(i))
     935           0 :         i = i + 1
     936             :       end do
     937           0 :       den1 = dnc0(i-1) + dnc1(i-1) * t
     938           0 :       den2 = dnc0(i)   + dnc1(i) * t
     939             :       ! Calculate derivative needed later for Kelvin factor for H2O      
     940           0 :       drho_dwt = (den2-den1) / (dnwtp(i)-dnwtp(i-1))
     941           0 :       sulfate_density = den1 + drho_dwt * (wtpct_flat-dnwtp(i-1))
     942             : 
     943           0 :       r=dmean*100._r8/2._r8 ! calcuate mode radius (cm) from diameter (m)
     944             : 
     945             :       ! Adjust for Kelvin effect for water
     946             :       rkelvinH2O_b = 1 + wtpct_flat * drho_dwt / &
     947           0 :          sulfate_density - 3._r8 * wtpct_flat * dsigma_dwt / (2._r8*surf_tens)
     948             : 
     949             :       rkelvinH2O_a = 2._r8 * wtmol_h2so4 * surf_tens / &
     950           0 :            (sulfate_density * RGAS * t * r)     
     951             : 
     952           0 :       rkelvinH2O = exp (rkelvinH2O_a*rkelvinH2O_b)
     953             : 
     954           0 :       qh2o_kelvin = qh2o/rkelvinH2O
     955           0 :       call calc_h2so4_wtpct(t, pres, qh2o_kelvin, wtpct)
     956             : 
     957             : 
     958           0 :       wtpct=max(wtpct,wtpct_flat)
     959             : 
     960             :       ! Parameterized fit to Giauque's (1959) enthalpies v. wt %:
     961           0 :       en = 4.184_r8 * (23624.8_r8 - 1.14208e8_r8 / ((wtpct - 105.318_r8)**2 + 4798.69_r8))
     962           0 :       en = max(en, 0.0_r8)
     963             : 
     964             :       !!  Calculate surface tension (erg/cm2) of sulfate of 
     965             :       !!  different compositions as a linear function of temperature.
     966           0 :       i = 2 ! minimum wt% is 29.517
     967           0 :       do while (wtpct.gt.stwtp(i))
     968           0 :        i=i+1
     969             :       end do
     970           0 :       sig1=stc0(i-1)+stc1(i-1)*t
     971           0 :       sig2=stc0(i)+stc1(i)*t
     972           0 :       frac=(stwtp(i)-wtpct)/(stwtp(i)-stwtp(i-1))
     973           0 :       surf_tens_mode=sig1*frac+sig2*(1.0_r8-frac)     
     974             : 
     975             :       !!  Calculate density (g/cm3) of sulfate of 
     976             :       !!  different compositions as a linear function of temperature.
     977           0 :       i = 6 ! minimum wt% is 29.517
     978           0 :       do while (wtpct .gt. dnwtp(i))
     979           0 :         i=i+1
     980             :       end do
     981           0 :       den1=dnc0(i-1)+dnc1(i-1)*t
     982           0 :       den2=dnc0(i)+dnc1(i)*t
     983           0 :       frac=(dnwtp(i)-wtpct)/(dnwtp(i)-dnwtp(i-1))
     984           0 :       sulden=den1*frac+den2*(1.0_r8-frac)
     985             : 
     986             :       ! Ayers' (1980) fit to sulfuric acid equilibrium vapor pressure:
     987             :       ! (Remember this is the log)
     988             :       ! SULFEQ=-10156/Temp+16.259-En/(8.3143*Temp)
     989             :       !
     990             :       ! Kulmala correction (J. CHEM. PHYS. V.93, No.1, 1 July 1990)
     991           0 :       fk1   = -1._r8 / t
     992           0 :       fk4_1 = log(t0_kulm / t)
     993           0 :       fk4_2 = t0_kulm / t
     994           0 :       fk4   = 1.0_r8 + fk4_1 - fk4_2
     995           0 :       factor_kulm = fk1 + fk2 + fk3 * fk4
     996             : 
     997             :       ! This is for pure H2SO4
     998           0 :       sulfequil = fk0 + 10156._r8 * factor_kulm
     999             : 
    1000             :       ! Adjust for WTPCT composition:
    1001           0 :       sulfequil = sulfequil - en / (8.3143_r8 * t)
    1002             : 
    1003             :       ! Take the exponential:
    1004           0 :       sulfequil = exp(sulfequil)
    1005             : 
    1006             :       ! Convert atmospheres ==> Pa
    1007           0 :       sulfequil = sulfequil * 1.01325e5_r8  
    1008             : 
    1009             :       ! Convert Pa ==> mol/mol
    1010           0 :       sulfequil = sulfequil / pres
    1011             : 
    1012             :       ! Calculate Kelvin curvature factor for H2SO4 interactively with temperature:
    1013             :       ! (g/mol)*(erg/cm2)/(K * g/cm3 * erg/mol/K) = cm
    1014           0 :       akelvin = 2._r8 * wtmol_h2so4 * surf_tens_mode / (t * sulden * RGAS)
    1015             : 
    1016           0 :       expon = akelvin / r  ! divide by mode radius (cm) 
    1017           0 :       expon = max(-100._r8, expon)
    1018           0 :       expon = min(100._r8, expon)
    1019           0 :       akas = exp( expon )
    1020           0 :       qh2so4_equilib = sulfequil * akas ! reduce H2SO4 equilibrium mixing ratio by Kelvin curvature factor
    1021             : 
    1022           0 :       return
    1023             :       end subroutine calc_h2so4_equilib_mixrat
    1024             : 
    1025             : 
    1026             : !----------------------------------------------------------------------
    1027           0 :       subroutine calc_h2so4_wtpct( temp, pres, qh2o, wtpct )
    1028             :       
    1029             :   !!  This function calculates the weight % H2SO4 composition of 
    1030             :   !!  sulfate aerosol, using Tabazadeh et. al. (GRL, 1931, 1997).
    1031             :   !!  Rated for T=185-260K, activity=0.01-1.0
    1032             :   !!
    1033             :   !!  Argument list input:   
    1034             :   !!    temp = temperature (K)
    1035             :   !!    pres = atmospheric pressure (Pa)
    1036             :   !!    qh2o = water specific humidity (kg/kg)
    1037             :   !!
    1038             :   !!  Output:
    1039             :   !!    wtpct = weight % H2SO4 in H2O/H2SO4 particle (0-100)
    1040             :   !!
    1041             :   !! @author Mike Mills
    1042             :   !! @ version October 2013
    1043             : 
    1044             :       use wv_saturation, only: qsat_water
    1045             :       
    1046             :       implicit none
    1047             : 
    1048             :       real(r8), intent(in)  :: temp  ! temperature (K)
    1049             :       real(r8), intent(in)  :: pres  ! pressure (Pa)
    1050             :       real(r8), intent(in)  :: qh2o  ! water vapor specific humidity (kg/kg)
    1051             :       real(r8), intent(out) :: wtpct ! sulfate weight % H2SO4 composition
    1052             :       
    1053             :       ! Local declarations
    1054             :       real(r8)            :: atab1,btab1,ctab1,dtab1,atab2,btab2,ctab2,dtab2
    1055             :       real(r8)            :: contl, conth, contt, conwtp
    1056             :       real(r8)            :: activ
    1057             :       real(r8)            :: es ! saturation vapor pressure over water (Pa) (dummy)
    1058             :       real(r8)            :: qs ! saturation specific humidity over water (kg/kg)
    1059             : 
    1060             :       ! calculate saturation specific humidity over pure water, qs (kg/kg)
    1061           0 :       call qsat_water(temp, pres, es, qs)
    1062             :       
    1063             :       !  Activity = water specific humidity (kg/kg) / equilibrium water (kg/kg)
    1064           0 :       activ = qh2o/qs
    1065             :        
    1066           0 :       if (activ.lt.0.05_r8) then
    1067           0 :         activ = max(activ,1.e-6_r8)    ! restrict minimum activity
    1068           0 :         atab1   = 12.37208932_r8        
    1069           0 :         btab1   = -0.16125516114_r8
    1070           0 :         ctab1   = -30.490657554_r8
    1071           0 :         dtab1   = -2.1133114241_r8
    1072           0 :         atab2   = 13.455394705_r8       
    1073           0 :         btab2   = -0.1921312255_r8
    1074           0 :         ctab2   = -34.285174607_r8
    1075           0 :         dtab2   = -1.7620073078_r8
    1076           0 :       elseif (activ.ge.0.05_r8.and.activ.le.0.85_r8) then
    1077             :         atab1   = 11.820654354_r8
    1078             :         btab1   = -0.20786404244_r8
    1079             :         ctab1   = -4.807306373_r8
    1080             :         dtab1   = -5.1727540348_r8
    1081             :         atab2   = 12.891938068_r8       
    1082             :         btab2   = -0.23233847708_r8
    1083             :         ctab2   = -6.4261237757_r8
    1084             :         dtab2   = -4.9005471319_r8
    1085           0 :       elseif (activ.gt.0.85_r8) then
    1086           0 :         activ = min(activ,1._r8)      ! restrict maximum activity
    1087           0 :         atab1   = -180.06541028_r8
    1088           0 :         btab1   = -0.38601102592_r8
    1089           0 :         ctab1   = -93.317846778_r8
    1090           0 :         dtab1   = 273.88132245_r8
    1091           0 :         atab2   = -176.95814097_r8
    1092           0 :         btab2   = -0.36257048154_r8
    1093           0 :         ctab2   = -90.469744201_r8
    1094           0 :         dtab2   = 267.45509988_r8
    1095             :       else
    1096           0 :         write(iulog,*) 'calc_h2so4_wtpct: invalid activity: activ,qh2o,qs,temp,pres=',activ,qh2o,qs,temp,pres
    1097           0 :         call endrun( 'calc_h2so4_wtpct error' )
    1098           0 :         return
    1099             :       endif
    1100             : 
    1101           0 :       contl = atab1*(activ**btab1)+ctab1*activ+dtab1
    1102           0 :       conth = atab2*(activ**btab2)+ctab2*activ+dtab2
    1103             : 
    1104           0 :       contt = contl + (conth-contl) * ((temp -190._r8)/70._r8)
    1105           0 :       conwtp = (contt*98._r8) + 1000._r8
    1106             : 
    1107           0 :       wtpct = (100._r8*contt*98._r8)/conwtp
    1108           0 :       wtpct = min(max(wtpct,25._r8),100._r8) ! restrict between 1 and 100 %
    1109             :       
    1110           0 :       return
    1111             :       end subroutine calc_h2so4_wtpct
    1112             : 
    1113             : 
    1114             : !----------------------------------------------------------------------
    1115             : 
    1116             :    end module modal_aero_wateruptake
    1117             : 
    1118             : 

Generated by: LCOV version 1.14