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 use cam_history, only: addfld, add_default, 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, & modal_aero_wateruptake_sub, & modal_aero_kohler 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 integer :: hygro_idx = 0 integer :: dryvol_idx = 0 integer :: dryrad_idx = 0 integer :: drymass_idx = 0 integer :: so4dryvol_idx = 0 integer :: naer_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') hygro_idx = pbuf_get_index('HYGRO') dryvol_idx = pbuf_get_index('DRYVOL') dryrad_idx = pbuf_get_index('DRYRAD') drymass_idx = pbuf_get_index('DRYMASS') so4dryvol_idx = pbuf_get_index('SO4DRYVOL') naer_idx = pbuf_get_index('NAER') ! 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), (/ 'lev' /), 'A', 'm', & 'dry dgnum, interstitial, mode '//trnum(2:3)) call addfld('dgnw_a'//trnum(2:3), (/ 'lev' /), 'A', 'm', & 'wet dgnum, interstitial, mode '//trnum(2:3)) call addfld('wat_a'//trnum(3:3), (/ 'lev' /), 'A', 'm', & 'aerosol water, interstitial, mode '//trnum(2:3)) ! 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, hygro_m, dryvol_m, dryrad_m, drymass_m,& so4dryvol_m, naer_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, pointer :: dgnumdry_m(:,:,:) real(r8), optional, pointer :: dgnumwet_m(:,:,:) real(r8), optional, pointer :: qaerwat_m(:,:,:) real(r8), optional, pointer :: wetdens_m(:,:,:) real(r8), optional, pointer :: hygro_m(:,:,:) real(r8), optional, pointer :: dryvol_m(:,:,:) real(r8), optional, pointer :: dryrad_m(:,:,:) real(r8), optional, pointer :: drymass_m(:,:,:) real(r8), optional, pointer :: so4dryvol_m(:,:,:) real(r8), optional, pointer :: naer_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 :: 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), pointer :: maer(:,:,:) ! aerosol wet mass MR (including water) (kg/kg-air) real(r8), pointer :: hygro(:,:,:) ! volume-weighted mean hygroscopicity (--) real(r8), pointer :: naer(:,:,:) ! aerosol number MR (bounded!) (#/kg-air) real(r8), pointer :: dryvol(:,:,:) ! single-particle-mean dry volume (m3) real(r8), pointer :: so4dryvol(:,:,:) ! single-particle-mean so4 dry volume (m3) real(r8), pointer :: drymass(:,:,:) ! single-particle-mean dry mass (kg) real(r8), pointer :: 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) :: specdens, so4specdens real(r8) :: sigmag real(r8) :: alnsg 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 ! arrays for diagnostic calculations must be associated if (.not. associated(dgnumdry_m) .or. .not. associated(dgnumwet_m) .or. & .not. associated(qaerwat_m) .or. .not. associated(wetdens_m)) then call endrun('modal_aero_wateruptake_dr called for'// & 'diagnostic list but required args not associated') end if if (modal_strat_sulfate) then call endrun('modal_aero_wateruptake_dr cannot be called with optional arguments and'// & ' having modal_strat_sulfate set to true') 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( & 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) ) 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 ) call pbuf_get_field(pbuf, wetdens_ap_idx, wetdens) call pbuf_get_field(pbuf, qaerwat_idx, qaerwat) call pbuf_get_field(pbuf, hygro_idx, hygro) call pbuf_get_field(pbuf, dryvol_idx, dryvol) call pbuf_get_field(pbuf, dryrad_idx, dryrad) call pbuf_get_field(pbuf, drymass_idx, drymass) call pbuf_get_field(pbuf, so4dryvol_idx, so4dryvol) call pbuf_get_field(pbuf, naer_idx, naer) if (is_first_step()) then dgncur_awet(:,:,:) = dgncur_a(:,:,:) end if else dgncur_a => dgnumdry_m dgncur_awet => dgnumwet_m qaerwat => qaerwat_m wetdens => wetdens_m hygro => hygro_m dryvol => dryvol_m dryrad => dryrad_m drymass => drymass_m so4dryvol => so4dryvol_m naer => naer_m end if if (modal_strat_sulfate) then ! get tropopause level 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 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_props(list_idx, m, l, density_aer=specdens, & 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 end if end do alnsg = log(sigmag) if (modal_strat_sulfate) then do k = top_lev, pver do i = 1, ncol 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) qaerwat = 0.0_r8 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 end if deallocate( & 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 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