#define WRF_PORT #define MODAL_AERO ! Updated to CESM1.0.3 (CAM5.1.01) by Balwinder.Singh@pnnl.gov module ndrop 3,4 #ifdef MODAL_AERO use shr_kind_mod, only: r8 => shr_kind_r8 #ifndef WRF_PORT use abortutils, only: endrun use modal_aero_data, only: ntot_amode, qqcw_get_field use cam_logfile, only: iulog #else use module_cam_support, only: endrun, iulog use modal_aero_data, only: ntot_amode #endif implicit none private save public activate_init, dropmixnuc real(r8) :: npv(ntot_amode) ! number per volume concentration real(r8) :: alogsig(ntot_amode) ! natl log of geometric standard dev of aerosol real(r8) :: exp45logsig(ntot_amode) real(r8) :: argfactor(ntot_amode) real(r8) :: f1(ntot_amode),f2(ntot_amode) ! abdul-razzak functions of width real(r8) :: t0 ! reference temperature real(r8) :: aten real(r8) :: surften ! surface tension of water w/respect to air (N/m) real(r8) :: alogten,alog2,alog3,alogaten real(r8) :: third, twothird, sixth, zero real(r8) :: sq2, sqpi, pi type qqcw_type real(r8), pointer :: fldcw(:,:) end type qqcw_type contains subroutine dropmixnuc(lchnk, ncol, ncldwtr,tendnd, temp,omega, & 1,32 pmid,pint,pdel,rpdel,zm,kvh,wsub,cldn,cldo, & raer, cflx, raertend, dtmicro & #ifdef WRF_PORT , qqcw_inout & #endif ) ! vertical diffusion and nucleation of cloud droplets ! assume cloud presence controlled by cloud fraction ! doesn't distinguish between warm, cold clouds #ifndef WRF_PORT use ppgrid, only: pcols, pver, pverp #else use module_cam_support, only: pcols, pver, pverp #endif use physconst, only: gravit, rhoh2o, latvap, cpair, epsilo, rair, mwh2o, r_universal #ifndef WRF_PORT use time_manager, only: get_nstep use constituents, only: pcnst, qmin, cnst_get_ind, cnst_name #else use module_cam_support, only: pcnst => pcnst_mp use constituents, only: cnst_get_ind, cnst_name #endif use error_function, only: erf ! use aerosol_radiation_interface, only: collect_sw_aer_masses, aer_mass #ifndef WRF_PORT use modal_aero_data use cam_history, only: fieldname_len use phys_grid, only: get_lat_all_p, get_lon_all_p use physics_types, only: physics_state use cam_history, only: outfld #else use modal_aero_data, only: numptrcw_amode => numptrcw_amode_mp, & nspec_amode => nspec_amode_mp, lmassptrcw_amode => lmassptrcw_amode_mp, & numptr_amode => numptr_amode_mp , lmassptr_amode => lmassptr_amode_mp, & cnst_name_cw => cnst_name_cw_mp use module_cam_support, only: fieldname_len, outfld #endif implicit none ! input integer, intent(in) :: lchnk ! chunk identifier integer, intent(in) :: ncol ! number of columns ! type(physics_state), intent(in) :: state ! Physics state variables real(r8), intent(in) :: dtmicro ! time step for microphysics (s) real(r8), intent(in) :: temp(pcols,pver) ! temperature (K) real(r8), intent(inout) :: ncldwtr(pcols,pver) ! droplet number concentration (#/kg) real(r8), intent(inout) :: tendnd(pcols,pver) ! change in droplet number concentration (#/kg/s) real(r8), intent(in) :: omega(pcols,pver) ! vertical velocity (Pa/s) real(r8), intent(in) :: pmid(pcols,pver) ! mid-level pressure (Pa) real(r8), intent(in) :: pint(pcols,pverp) ! pressure at layer interfaces (Pa) real(r8), intent(in) :: pdel(pcols,pver) ! pressure thickess of layer (Pa) real(r8), intent(in) :: rpdel(pcols,pver) ! inverse of pressure thickess of layer (/Pa) real(r8), intent(in) :: zm(pcols,pver) ! geopotential height of level (m) real(r8), intent(in) :: kvh(pcols,pverp) ! vertical diffusivity (m2/s) real(r8), intent(in) :: wsub(pcols,pver) ! subgrid vertical velocity real(r8), intent(in) :: cldo(pcols,pver) ! cloud fraction on previous time step real(r8), intent(in) :: cldn(pcols,pver) ! cloud fraction ! output real(r8), intent(in) :: raer(pcols,pver,pcnst) ! aerosol mass, number mixing ratios real(r8), intent(in) :: cflx(pcols,pcnst) ! constituent flux from surface real(r8), intent(out) :: raertend(pcols,pver,pcnst) ! tendency of aerosol mass, number mixing ratios #ifdef WRF_PORT real(r8), target, intent(inout) :: qqcw_inout(pcols,pver,pcnst) ! cloud-borne aerosol #endif !--------------------Local storage------------------------------------- ! type(qqcw_type) :: QQCW(pcnst) real(r8) depvel(pcols,pcnst)! deposition velocity for droplets (m/s) real(r8) qcld(pver) ! cloud droplet number mixing ratio (#/kg) real(r8) wtke(pcols,pver) ! turbulent vertical velocity at base of layer k (m/s) real(r8) wtke_cen(pcols,pver) ! turbulent vertical velocity at center of layer k (m/s) real(r8) zn(pver) ! g/pdel (m2/g) for layer real(r8) zs(pver) ! inverse of distance between levels (m) real(r8), parameter :: zkmin=0.01_r8,zkmax=100._r8 real(r8) w(pcols,pver) ! vertical velocity (m/s) real(r8) cs(pcols,pver) ! air density (kg/m3) real(r8) dz(pcols,pver) ! geometric thickness of layers (m) real(r8) zero,flxconv ! convergence of flux into lowest layer real(r8) wdiab ! diabatic vertical velocity real(r8), parameter :: wmixmin = 0.1 ! minimum turbulence vertical velocity (m/s) ! real(r8), parameter :: wmixmin = 0.2 ! minimum turbulence vertical velocity (m/s) ! real(r8), parameter :: wmixmin = 1.0 ! minimum turbulence vertical velocity (m/s) real(r8) qncld(pver) ! droplet number nucleated on cloud boundaries real(r8) ekd(pver) ! diffusivity for droplets (m2/s) real(r8) ekk(0:pver) ! density*diffusivity for droplets (kg/m3 m2/s) real(r8) srcn(pver) ! droplet source rate (/s) real(r8), parameter :: sq2pi=2.5066283_r8 real(r8) dtinv logical top ! true if cloud top, false if cloud base or new cloud integer km1,kp1 real(r8) wbar,wmix,wmin,wmax real(r8) dum,dumc real(r8) dact real(r8) fluxntot ! (#/cm2/s) real(r8) fac_srflx real(r8) surfrate(pcnst) ! surface exchange rate (/s) real(r8) surfratemax ! max surfrate for all species treated here real(r8) dtmin,tinv,dtt integer nsubmix,nsubmix_bnd integer i,k,m,n real(r8) tempr4 ! temperature real(r8) dtmix real(r8) alogarg integer nstep real(r8) pi integer nnew,nsav,ntemp real(r8) overlapp(pver),overlapm(pver) ! cloud overlap real(r8) ekkp(pver),ekkm(pver) ! zn*zs*density*diffusivity integer count_submix(100) save count_submix real(r8) kvhmax save kvhmax real(r8) nsource(pcols,pver) ! droplet number source (#/kg/s) real(r8) ndropmix(pcols,pver) ! droplet number mixing (#/kg/s) real(r8) ndropcol(pcols) ! column droplet number (#/m2) integer lnum, lnumcw, lmass, lmasscw, lphase, & lsfc, lsfccw, lsig, lspec, ltype, lwater integer ntype(ntot_amode) real(r8) na(pcols),va(pcols),hy(pcols) real(r8) naermod(ntot_amode) ! (/m3) real(r8) sigmag(ntot_amode) ! geometric standard deviation of aerosol size dist real(r8) hygro(ntot_amode) ! hygroscopicity of aerosol mode real(r8) vaerosol(ntot_amode) ! interstit+activated aerosol volume conc (cm3/cm3) real(r8) raercol(pver,pcnst,2) ! aerosol mass, number mixing ratios real(r8) raercol_cw(pver,pcnst,2) ! cloud-borne aerosol mass, number mixing ratios real(r8) surfrate_cw(pcnst) ! surface exchange rate for cloud-borne aerosol (/s) real(r8) source(pver) ! real(r8) fn(ntot_amode) ! activation fraction for aerosol number real(r8) fm(ntot_amode) ! activation fraction for aerosol mass real(r8) fluxn(ntot_amode) ! number activation fraction flux (cm/s) real(r8) fluxm(ntot_amode) ! mass activation fraction flux (cm/s) real(r8) sum,sumcw,flux,fluxcw ! note: activation fraction fluxes are defined as ! fluxn = [flux of activated aero. number into cloud (#/cm2/s)] ! / [aero. number conc. in updraft, just below cloudbase (#/cm3)] real(r8) nact(pver,ntot_amode) ! fractional aero. number activation rate (/s) real(r8) mact(pver,ntot_amode) ! fractional aero. mass activation rate (/s) real(r8) sigmag_amode_cur(ntot_amode) ! sigmag for current grid cell save sigmag_amode_cur real(r8) :: qqcwtend(pcols,pver,pcnst) ! tendency of cloudborne aerosol mass, number mixing ratios real(r8) :: coltend(pcols) ! column tendency real(r8) :: tmpa integer :: lc character(len=fieldname_len) :: tmpname character(len=fieldname_len+3) :: fieldname real(r8) :: csbot(pver) ! air density at bottom (interface) of layer (kg/m3) real(r8) :: csbot_cscen(pver) ! csbot(i)/cs(i,k) real(r8) :: flux_fullact(pver) ! 100% activation fraction flux (cm/s) real(r8) :: taumix_internal_pver_inv ! 1/(internal mixing time scale for k=pver) (1/s) real(r8) dactn,taunuc,damp real(r8) :: cldo_tmp, cldn_tmp real(r8) :: tau_cld_regenerate logical cldinterior integer ixndrop, l real(r8) naer_tot,naer(pcols) integer, parameter :: psat=6 ! number of supersaturations to calc ccn concentration real(r8) :: supersat(psat)= & ! supersaturation (%) to determine ccn concentration (/0.02,0.05,0.1,0.2,0.5,1.0/) real(r8) ccn(pcols,pver,psat) ! number conc of aerosols activated at supersat character(len=8), dimension(psat) :: ccn_name(psat)= & (/'CCN1','CCN2','CCN3','CCN4','CCN5','CCN6'/) real(r8) arg integer phase ! phase of aerosol arg = 1.0_r8 if (abs(0.8427_r8-erf(arg))/0.8427_r8>0.001_r8) then write (iulog,*) 'erf(1.0) = ',ERF(arg) #ifdef WRF_PORT call wrf_message(iulog) #endif call endrun('dropmixnuc: Error function error') endif arg = 0.0 if (erf(arg) /= 0.0) then write (iulog,*) 'erf(0.0) = ',erf(arg) #ifdef WRF_PORT call wrf_message(iulog) #endif write (iulog,*) 'dropmixnuc: Error function error' #ifdef WRF_PORT call wrf_message(iulog) #endif call endrun('dropmixnuc: Error function error') endif zero=0._r8 pi = 4._r8*atan(1.0_r8) dtinv=1./dtmicro #ifndef WRF_PORT nstep = get_nstep() #endif ! call t_startf('load_aerosol') call cnst_get_ind('NUMLIQ', ixndrop) depvel(:,:) = 0.0_r8 ! droplet number is done in pkg_cld_sediment, aerosols in mz_aerosols_intr ! depvel(:,ixndrop) = 0.1_r8 ! prescribed here rather than getting it from MIRAGE depvel_part ! call t_stopf('load_aerosol') do m=1,ntot_amode #ifndef WRF_PORT QQCW(numptrcw_amode(m))%fldcw => qqcw_get_field(numptrcw_amode(m),lchnk) #else QQCW(numptrcw_amode(m))%fldcw => qqcw_inout(:,:,numptrcw_amode(m)) #endif do l=1,nspec_amode(m) #ifndef WRF_PORT QQCW(lmassptrcw_amode(l,m))%fldcw => qqcw_get_field(lmassptrcw_amode(l,m),lchnk) #else QQCW(lmassptrcw_amode(l,m))%fldcw => qqcw_inout(:,:,lmassptrcw_amode(l,m)) #endif end do end do ! start loop over columns overall_main_i_loop: & do i=1,ncol ! load number nucleated into qcld on cloud boundaries ! initialization for current i ......................................... ! call t_startf('calc_wtke') do k=1,pver-1 zs(k)=1._r8/(zm(i,k)-zm(i,k+1)) enddo zs(pver)=zs(pver-1) do k=1,pver qcld(k)=ncldwtr(i,k) qncld(k)=0._r8 srcn(k)=0._r8 cs(i,k)=pmid(i,k)/(rair*temp(i,k)) ! air density (kg/m3) dz(i,k)=1._r8/(cs(i,k)*gravit*rpdel(i,k)) ! layer thickness in m w(i,k)=-1._r8*omega(i,k)/(cs(i,k)*gravit) ! large-scale vertical velocity m/s do m=1,ntot_amode nact(k,m)=0._r8 mact(k,m)=0._r8 enddo zn(k)=gravit*rpdel(i,k) kvhmax=max(kvh(i,k),kvhmax) if(k<pver)then ekd(k)=kvh(i,k+1) ekd(k)=max(ekd(k),zkmin) ekd(k)=min(ekd(k),zkmax) csbot(k)=2.0_r8*pint(i,k+1)/(rair*(temp(i,k)+temp(i,k+1))) csbot_cscen(k) = csbot(k)/cs(i,k) else ekd(k)=0._r8 csbot(k)=cs(i,k) csbot_cscen(k) = 1.0_r8 endif ! diagnose subgrid vertical velocity from diffusivity if(k.eq.pver)then wtke(i,k)=sq2pi*depvel(i,ixndrop) ! wtke(i,k)=sq2pi*kvh(i,k+1) wtke(i,k)=max(wtke(i,k),wmixmin) else wtke(i,k)=sq2pi*ekd(k)/dz(i,k) endif ! get sub-grid vertical velocity from diff coef. ! following morrison et al. 2005, JAS ! assume mixing length of 30 m ! rce-comment - define wtke at layer centers for new-cloud activation ! and at layer boundaries for old-cloud activation !++ag ! wtke_cen(i,k)=0.5_r8*(kvh(i,k)+kvh(i,k+1))/30._r8 ! wtke(i,k)=kvh(i,k+1)/30._r8 wtke_cen(i,k)=wsub(i,k) wtke(i,k)=wsub(i,k) !--ag wtke_cen(i,k)=max(wtke_cen(i,k),wmixmin) wtke(i,k)=max(wtke(i,k),wmixmin) nsource(i,k)=0._r8 enddo surfratemax = 0.0_r8 nsav=1 nnew=2 surfrate(ixndrop)=depvel(i,ixndrop)/dz(i,pver) surfratemax = max( surfratemax, surfrate(ixndrop) ) do m=1,ntot_amode lnum=numptr_amode(m) lnumcw=numptrcw_amode(m) if(lnum>0)then surfrate(lnum)=depvel(i,lnum)/dz(i,pver) ! surfrate(lnumcw)=surfrate(ixndrop) surfrate_cw(lnumcw)=surfrate(ixndrop) surfratemax = max( surfratemax, surfrate(lnum) ) ! raercol(:,lnumcw,nsav)=raer(i,:,lnumcw) raercol_cw(:,lnumcw,nsav)=qqcw(lnumcw)%fldcw(i,:) ! use cloud-borne aerosol array raercol(:,lnum,nsav)=raer(i,:,lnum) endif do l=1,nspec_amode(m) lmass=lmassptr_amode(l,m) lmasscw=lmassptrcw_amode(l,m) surfrate(lmass)=depvel(i,lmass)/dz(i,pver) ! surfrate(lmasscw)=surfrate(ixndrop) surfrate_cw(lmasscw)=surfrate(ixndrop) surfratemax = max( surfratemax, surfrate(lmass) ) ! raercol(:,lmasscw,nsav)=raer(i,:,lmasscw) raercol_cw(:,lmasscw,nsav)=qqcw(lmasscw)%fldcw(i,:) ! use cloud-borne aerosol array raercol(:,lmass,nsav)=raer(i,:,lmass) enddo ! lwater=lwaterptr_amode(m) ! surfrate(lwater)=depvel(i,lwater)/dz(i,pver) ! surfratemax = max( surfratemax, surfrate(lwater) ) ! raercol(:,lwater,nsav)=raer(i,:,lwater) enddo ! call t_stopf('calc_wtke') ! droplet nucleation/aerosol activation ! tau_cld_regenerate = time scale for regeneration of cloudy air ! by (horizontal) exchange with clear air tau_cld_regenerate = 3600.0_r8 * 3.0_r8 ! k-loop for growing/shrinking cloud calcs ............................. grow_shrink_main_k_loop: & do k=1,pver km1=max0(k-1,1) kp1=min0(k+1,pver) ! shrinking cloud ...................................................... ! treat the reduction of cloud fraction from when cldn(i,k) < cldo(i,k) ! and also dissipate the portion of the cloud that will be regenerated cldo_tmp = cldo(i,k) cldn_tmp = cldn(i,k) * exp( -dtmicro/tau_cld_regenerate ) ! alternate formulation ! cldn_tmp = cldn(i,k) * max( 0.0_r8, (1.0_r8-dtmicro/tau_cld_regenerate) ) if(cldn_tmp.lt.cldo_tmp)then ! droplet loss in decaying cloud !++ sungsup nsource(i,k)=nsource(i,k)+qcld(k)*(cldn_tmp-cldo_tmp)/cldo_tmp*dtinv qcld(k)=qcld(k)*(1.+(cldn_tmp-cldo_tmp)/cldo_tmp) !-- sungsup ! convert activated aerosol to interstitial in decaying cloud dumc=(cldn_tmp-cldo_tmp)/cldo_tmp do m=1,ntot_amode lnum=numptr_amode(m) lnumcw=numptrcw_amode(m) if(lnum.gt.0)then dact=raercol_cw(k,lnumcw,nsav)*dumc ! raercol(k,lnumcw,nsav)=raercol(k,lnumcw,nsav)+dact raercol_cw(k,lnumcw,nsav)=raercol_cw(k,lnumcw,nsav)+dact ! cloud-borne aerosol raercol(k,lnum,nsav)=raercol(k,lnum,nsav)-dact endif do l=1,nspec_amode(m) lmass=lmassptr_amode(l,m) lmasscw=lmassptrcw_amode(l,m) dact=raercol_cw(k,lmasscw,nsav)*dumc ! raercol(k,lmasscw,nsav)=raercol(k,lmasscw,nsav)+dact raercol_cw(k,lmasscw,nsav)=raercol_cw(k,lmasscw,nsav)+dact ! cloud-borne aerosol raercol(k,lmass,nsav)=raercol(k,lmass,nsav)-dact enddo enddo endif ! growing cloud ...................................................... ! treat the increase of cloud fraction from when cldn(i,k) > cldo(i,k) ! and also regenerate part of the cloud cldo_tmp = cldn_tmp cldn_tmp = cldn(i,k) if(cldn_tmp-cldo_tmp.gt.0.01)then ! wmix=wtke(i,k) ! wbar=wtke(i,k) ! rce-comment - use wtke at layer centers for new-cloud activation wbar=wtke_cen(i,k) wmix=0._r8 wmin=0._r8 wmax=10._r8 wdiab=0 ! load aerosol properties, assuming external mixtures phase=1 ! interstitial do m=1,ntot_amode call loadaer(raer,qqcw,i,i,k,m,cs,npv(m),phase, & na, va, hy ) naermod(m)=na(i) vaerosol(m)=va(i) hygro(m)=hy(i) end do ! m=1 ! naermod(m)=1000.e6 ! vaerosol(m)=naermod(m)/(density*num_to_mass_aer(m)) ! call t_startf ('activate') call activate_modal(wbar,wmix,wdiab,wmin,wmax,temp(i,k),cs(i,k), & naermod, ntot_amode,ntot_amode, vaerosol, sigmag,hygro, & fn,fm,fluxn,fluxm,flux_fullact(k)) ! call t_stopf ('activate') ! write(iulog,'(a,15f8.2)')'wtke,naer,fn=',wtke(i,k),naer_tot*1.e-6,(fn(m),m=1,ntot_amode) dumc=(cldn_tmp-cldo_tmp) do m=1,ntot_amode lnum=numptr_amode(m) lnumcw=numptrcw_amode(m) dact=dumc*fn(m)*raer(i,k,lnum) ! interstitial only qcld(k)=qcld(k)+dact nsource(i,k)=nsource(i,k)+dact*dtinv if(lnum.gt.0)then raercol_cw(k,lnumcw,nsav)=raercol_cw(k,lnumcw,nsav)+dact ! cloud-borne aerosol raercol(k,lnum,nsav)=raercol(k,lnum,nsav)-dact endif dum=dumc*fm(m) do l=1,nspec_amode(m) lmass=lmassptr_amode(l,m) lmasscw=lmassptrcw_amode(l,m) dact=dum*(raer(i,k,lmass)) ! interstitial only raercol_cw(k,lmasscw,nsav)=raercol_cw(k,lmasscw,nsav)+dact ! cloud-borne aerosol raercol(k,lmass,nsav)=raercol(k,lmass,nsav)-dact enddo enddo endif enddo grow_shrink_main_k_loop ! end of k-loop for growing/shrinking cloud calcs ...................... ! ...................................................................... ! start of k-loop for calc of old cloud activation tendencies .......... ! ! rce-comment ! changed this part of code to use current cloud fraction (cldn) exclusively ! consider case of cldo(:)=0, cldn(k)=1, cldn(k+1)=0 ! previous code (which used cldo below here) would have no cloud-base activation ! into layer k. however, activated particles in k mix out to k+1, ! so they are incorrectly depleted with no replacement ! old_cloud_main_k_loop: & do k=1,pver km1=max0(k-1,1) kp1=min0(k+1,pver) taumix_internal_pver_inv = 0.0 if(cldn(i,k).gt.0.01)then ! rce-comment ! if(cldo(i,k).gt.0.01)then ! with cldo changed to cldn this is redundant wdiab=0 ! wmix=wtke(i,k) ! spectrum of updrafts ! wbar=w(i,k) ! spectrum of updrafts wmix=0._r8 ! single updraft wbar=wtke(i,k) ! single updraft ! rce-comment - different treatment for k=pver if (k == pver) wbar=wtke_cen(i,k) ! single updraft wmax=10._r8 wmin=0._r8 ! old cloud if(cldn(i,k)-cldn(i,kp1).gt.0.01.or.k.eq.pver)then ! cloud base ekd(k)=wtke(i,k)*dz(i,k)/sq2pi ! rce-comments ! first, should probably have 1/zs(k) here rather than dz(i,k) because ! the turbulent flux is proportional to ekd(k)*zs(k), ! while the dz(i,k) is used to get flux divergences ! and mixing ratio tendency/change ! second and more importantly, using a single updraft velocity here ! means having monodisperse turbulent updraft and downdrafts. ! The sq2pi factor assumes a normal draft spectrum. ! The fluxn/fluxm from activate must be consistent with the ! fluxes calculated in explmix. ekd(k)=wbar/zs(k) alogarg=max(1.e-20_r8,1/cldn(i,k)-1._r8) wmin=wbar+wmix*0.25*sq2pi*log(alogarg) phase=1 ! interstitial do m=1,ntot_amode ! rce-comment - use kp1 here as old-cloud activation involves ! aerosol from layer below ! call loadaer(raer,qqcw,i,i,k,m,cs, npv(m),phase, & call loadaer(raer,qqcw,i,i,kp1,m,cs, npv(m),phase, & na, va, hy ) naermod(m)=na(i) vaerosol(m)=va(i) hygro(m)=hy(i) end do ! m=1 ! naermod(m)=1000.e6 ! vaerosol(1,m)=naermod(m)/(density*num_to_mass_aer(m)) ! call t_startf ('activate') call activate_modal(wbar,wmix,wdiab,wmin,wmax,temp(i,k),cs(i,k), & naermod, ntot_amode,ntot_amode, vaerosol, sigmag, hygro, & fn,fm,fluxn,fluxm,flux_fullact(k)) ! call t_stopf ('activate') ! write(iulog,'(a,15f8.2)')'wtke,naer,fn=',wtke(i,k),naer_tot*1.e-6,(fn(m),m=1,ntot_amode) if(k.lt.pver)then dumc = cldn(i,k)-cldn(i,kp1) else dumc=cldn(i,k) endif fluxntot=0 ! rce-comment 1 ! flux of activated mass into layer k (in kg/m2/s) ! = "actmassflux" = dumc*fluxm*raercol(kp1,lmass)*csbot(k) ! source of activated mass (in kg/kg/s) = flux divergence ! = actmassflux/(cs(i,k)*dz(i,k)) ! so need factor of csbot_cscen = csbot(k)/cs(i,k) ! dum=1./(dz(i,k)) dum=csbot_cscen(k)/(dz(i,k)) ! rce-comment 2 ! code for k=pver was changed to use the following conceptual model ! in k=pver, there can be no cloud-base activation unless one considers ! a scenario such as the layer being partially cloudy, ! with clear air at bottom and cloudy air at top ! assume this scenario, and that the clear/cloudy portions mix with ! a timescale taumix_internal = dz(i,pver)/wtke_cen(i,pver) ! in the absence of other sources/sinks, qact (the activated particle ! mixratio) attains a steady state value given by ! qact_ss = fcloud*fact*qtot ! where fcloud is cloud fraction, fact is activation fraction, ! qtot=qact+qint, qint is interstitial particle mixratio ! the activation rate (from mixing within the layer) can now be ! written as ! d(qact)/dt = (qact_ss - qact)/taumix_internal ! = qtot*(fcloud*fact*wtke/dz) - qact*(wtke/dz) ! note that (fcloud*fact*wtke/dz) is equal to the nact/mact ! also, d(qact)/dt can be negative. in the code below ! it is forced to be >= 0 ! ! steve -- ! you will likely want to change this. i did not really understand ! what was previously being done in k=pver ! in the cam3_5_3 code, wtke(i,pver) appears to be equal to the ! droplet deposition velocity which is quite small ! in the cam3_5_37 version, wtke is done differently and is much ! larger in k=pver, so the activation is stronger there ! if (k == pver) then taumix_internal_pver_inv = flux_fullact(k)/dz(i,k) end if do m=1,ntot_amode fluxn(m)=fluxn(m)*dumc fluxm(m)=fluxm(m)*dumc lnum=numptr_amode(m) nact(k,m)=nact(k,m)+fluxn(m)*dum mact(k,m)=mact(k,m)+fluxm(m)*dum if (k > pver) then ! note that kp1 is used here fluxntot = fluxntot & +fluxn(m)*raercol(kp1,lnum,nsav)*cs(i,k) else lnumcw=numptrcw_amode(m) tmpa = raercol(kp1,lnum,nsav)*fluxn(m) & + raercol(kp1,lnumcw,nsav)*(fluxn(m) & -taumix_internal_pver_inv) fluxntot = fluxntot + max(0.0_r8,tmpa)*cs(i,k) end if enddo srcn(k)=srcn(k)+fluxntot/(cs(i,k)*dz(i,k)) nsource(i,k)=nsource(i,k)+fluxntot/(cs(i,k)*dz(i,k)) endif ! (cldn(i,k)-cldn(i,kp1).gt.0.01 .or. k.eq.pver) ! rce-comment ! endif ! (cldo(i,k).gt.0.01) ! with cldo changed to cldn this is redundant else ! no cloud nsource(i,k)=nsource(i,k)-qcld(k)*dtinv qcld(k)=0 ! convert activated aerosol to interstitial in decaying cloud do m=1,ntot_amode lnum=numptr_amode(m) lnumcw=numptrcw_amode(m) if(lnum.gt.0)then raercol(k,lnum,nsav)=raercol(k,lnum,nsav)+raercol_cw(k,lnumcw,nsav) ! cloud-borne aerosol raercol_cw(k,lnumcw,nsav)=0. endif do l=1,nspec_amode(m) lmass=lmassptr_amode(l,m) lmasscw=lmassptrcw_amode(l,m) raercol(k,lmass,nsav)=raercol(k,lmass,nsav)+raercol_cw(k,lmasscw,nsav) ! cloud-borne aerosol raercol_cw(k,lmasscw,nsav)=0. enddo enddo endif enddo old_cloud_main_k_loop ! switch nsav, nnew so that nnew is the updated aerosol ntemp=nsav nsav=nnew nnew=ntemp ! call t_startf ('nsubmix') ! load new droplets in layers above, below clouds dtmin=dtmicro ekk(0)=0.0 ekk(pver)=0.0 do k=1,pver-1 ! rce-comment -- ekd(k) is eddy-diffusivity at k/k+1 interface ! want ekk(k) = ekd(k) * (density at k/k+1 interface) ! so use pint(i,k+1) as pint is 1:pverp ! ekk(k)=ekd(k)*2.*pint(i,k)/(rair*(temp(i,k)+temp(i,k+1))) ! ekk(k)=ekd(k)*2.*pint(i,k+1)/(rair*(temp(i,k)+temp(i,k+1))) ekk(k)=ekd(k)*csbot(k) enddo do k=1,pver km1=max0(k-1,1) ekkp(k)=zn(k)*ekk(k)*zs(k) ekkm(k)=zn(k)*ekk(k-1)*zs(km1) tinv=ekkp(k)+ekkm(k) if(k.eq.pver)tinv=tinv+surfratemax ! rce-comment -- tinv is the sum of all first-order-loss-rates ! for the layer. for most layers, the activation loss rate ! (for interstitial particles) is accounted for by the loss by ! turb-transfer to the layer above. ! k=pver is special, and the loss rate for activation within ! the layer must be added to tinv. if not, the time step ! can be too big, and explmix can produce negative values. ! the negative values are reset to zero, resulting in an ! artificial source. if(k.eq.pver)tinv=tinv+taumix_internal_pver_inv if(tinv.gt.1.e-6)then dtt=1./tinv dtmin=min(dtmin,dtt) endif enddo dtmix=0.9*dtmin nsubmix=dtmicro/dtmix+1 if(nsubmix>100)then nsubmix_bnd=100 else nsubmix_bnd=nsubmix endif count_submix(nsubmix_bnd)=count_submix(nsubmix_bnd)+1 dtmix=dtmicro/nsubmix fac_srflx = -1.0/(zn(pver)*nsubmix) do k=1,pver kp1=min(k+1,pver) km1=max(k-1,1) ! maximum overlap assumption if(cldn(i,kp1).gt.1.e-10_r8)then overlapp(k)=min(cldn(i,k)/cldn(i,kp1),1._r8) else overlapp(k)=1. endif if(cldn(i,km1).gt.1.e-10_r8)then overlapm(k)=min(cldn(i,k)/cldn(i,km1),1._r8) else overlapm(k)=1. endif enddo ! rce-comment ! the activation source(k) = mact(k,m)*raercol(kp1,lmass) ! should not exceed the rate of transfer of unactivated particles ! from kp1 to k which = ekkp(k)*raercol(kp1,lmass) ! however it might if things are not "just right" in subr activate ! the following is a safety measure to avoid negatives in explmix do k = 1, pver-1 do m = 1, ntot_amode nact(k,m) = min( nact(k,m), ekkp(k) ) mact(k,m) = min( mact(k,m), ekkp(k) ) end do end do old_cloud_nsubmix_loop: & do n=1,nsubmix qncld(:)=qcld(:) ! switch nsav, nnew so that nsav is the updated aerosol ntemp=nsav nsav=nnew nnew=ntemp srcn(:)=0.0 do m=1,ntot_amode lnum=numptr_amode(m) lnumcw=numptrcw_amode(m) ! update droplet source ! rce-comment- activation source in layer k involves particles from k+1 ! srcn(:)=srcn(:)+nact(:,m)*(raercol(:,lnum,nsav)) srcn(1:pver-1)=srcn(1:pver-1)+nact(1:pver-1,m)*(raercol(2:pver,lnum,nsav)) ! rce-comment- new formulation for k=pver ! srcn( pver )=srcn( pver )+nact( pver ,m)*(raercol( pver,lnum,nsav)) tmpa = raercol(pver,lnum,nsav)*nact(pver,m) & + raercol(pver,lnumcw,nsav)*(nact(pver,m) - taumix_internal_pver_inv) srcn(pver)=srcn(pver) + max(0.0_r8,tmpa) enddo call explmix(qcld,srcn,ekkp,ekkm,overlapp,overlapm, & qncld,surfrate(ixndrop),zero,pver,dtmix,.false.) ! rce-comment ! the interstitial particle mixratio is different in clear/cloudy portions ! of a layer, and generally higher in the clear portion. (we have/had ! a method for diagnosing the the clear/cloudy mixratios.) the activation ! source terms involve clear air (from below) moving into cloudy air (above). ! in theory, the clear-portion mixratio should be used when calculating ! source terms do m=1,ntot_amode lnum=numptr_amode(m) lnumcw=numptrcw_amode(m) if(lnum>0)then ! rce-comment - activation source in layer k involves particles from k+1 ! source(:)= nact(:,m)*(raercol(:,lnum,nsav)) source(1:pver-1)= nact(1:pver-1,m)*(raercol(2:pver,lnum,nsav)) ! rce-comment- new formulation for k=pver ! source( pver )= nact( pver, m)*(raercol( pver,lnum,nsav)) tmpa = raercol(pver,lnum,nsav)*nact(pver,m) & + raercol(pver,lnumcw,nsav)*(nact(pver,m) - taumix_internal_pver_inv) source(pver) = max(0.0_r8,tmpa) ! flxconv=cflx(i,lnum)*zn(pver) flxconv=0. call explmix(raercol_cw(1,lnumcw,nnew),source,ekkp,ekkm,overlapp,overlapm, & raercol_cw(1,lnumcw,nsav),surfrate_cw(lnumcw),zero,pver,dtmix,& .false.) call explmix(raercol(1,lnum,nnew),source,ekkp,ekkm,overlapp,overlapm, & raercol(1,lnum,nsav),surfrate(lnum),flxconv,pver,dtmix, & .true.,raercol_cw(1,lnumcw,nsav)) endif do l=1,nspec_amode(m) lmass=lmassptr_amode(l,m) lmasscw=lmassptrcw_amode(l,m) ! rce-comment - activation source in layer k involves particles from k+1 ! source(:)= mact(:,m)*(raercol(:,lmass,nsav)) source(1:pver-1)= mact(1:pver-1,m)*(raercol(2:pver,lmass,nsav)) ! rce-comment- new formulation for k=pver ! source( pver )= mact( pver ,m)*(raercol( pver,lmass,nsav)) tmpa = raercol(pver,lmass,nsav)*mact(pver,m) & + raercol(pver,lmasscw,nsav)*(mact(pver,m) - taumix_internal_pver_inv) source(pver) = max(0.0_r8,tmpa) ! flxconv=cflx(i,lmass)*zn(pver) flxconv=0. call explmix(raercol_cw(1,lmasscw,nnew),source,ekkp,ekkm,overlapp,overlapm, & raercol_cw(1,lmasscw,nsav),surfrate_cw(lmasscw),zero,pver,dtmix,& .false.) call explmix(raercol(1,lmass,nnew),source,ekkp,ekkm,overlapp,overlapm, & raercol(1,lmass,nsav),surfrate(lmass),flxconv, pver,dtmix,& .true.,raercol_cw(1,lmasscw,nsav)) enddo ! l ! lwater=lwaterptr_amode(m) ! aerosol water ! source(:)=0. ! call explmix( raercol(1,lwater,nnew),source,ekkp,ekkm,overlapp,overlapm, & ! raercol(1,lwater,nsav),surfrate(lwater),zero,pver,dtmix,& ! .true.,source) enddo ! m enddo old_cloud_nsubmix_loop ! call t_stopf ('nsubmix') ! evaporate particles again if no cloud do k=1,pver if(cldn(i,k).eq.0.)then ! no cloud qcld(k)=0. ! convert activated aerosol to interstitial in decaying cloud do m=1,ntot_amode lnum=numptr_amode(m) lnumcw=numptrcw_amode(m) if(lnum.gt.0)then raercol(k,lnum,nnew)=raercol(k,lnum,nnew)+raercol_cw(k,lnumcw,nnew) raercol_cw(k,lnumcw,nnew)=0. endif ! do l=1,nspec_amode(m) lmass=lmassptr_amode(l,m) lmasscw=lmassptrcw_amode(l,m) ! raercol(k,lmass,nnew)=raercol(k,lmass,nnew)+raercol(k,lmasscw,nnew) ! raercol(k,lmasscw,nnew)=0. raercol(k,lmass,nnew)=raercol(k,lmass,nnew)+raercol_cw(k,lmasscw,nnew) raercol_cw(k,lmasscw,nnew)=0. enddo enddo endif enddo ! droplet number ndropcol(i)=0. do k=1,pver ndropmix(i,k)=(qcld(k)-ncldwtr(i,k))*dtinv - nsource(i,k) tendnd(i,k) = (max(qcld(k),1.e-6_r8)-ncldwtr(i,k))*dtinv ndropcol(i) = ndropcol(i) + ncldwtr(i,k)*pdel(i,k) enddo ndropcol(i) = ndropcol(i)/gravit ! aerosol tendency do m=1,ntot_amode lnum=numptr_amode(m) lnumcw=numptrcw_amode(m) if(lnum.gt.0)then qqcwtend(i,:,lnumcw)=(raercol_cw(:,lnumcw,nnew)-qqcw(lnumcw)%fldcw(i,:))*dtinv qqcw(lnumcw)%fldcw(i,:)=raercol_cw(:,lnumcw,nnew) ! update cloud-borne aerosol raertend(i,:,lnum)= (raercol(:,lnum,nnew)-raer(i,:,lnum))*dtinv endif do l=1,nspec_amode(m) lmass=lmassptr_amode(l,m) lmasscw=lmassptrcw_amode(l,m) qqcwtend(i,:,lmasscw)=(raercol_cw(:,lmasscw,nnew)-qqcw(lmasscw)%fldcw(i,:))*dtinv qqcw(lmasscw)%fldcw(i,:)=raercol_cw(:,lmasscw,nnew) ! update cloud-borne aerosol raertend(i,:,lmass)=(raercol(:,lmass,nnew)-raer(i,:,lmass))*dtinv enddo ! lwater=lwaterptr_amode(m) ! raertend(i,:,lwater)=(raercol(:,lwater,nnew)-raer(i,:,lwater))*dtinv enddo enddo overall_main_i_loop ! end of main loop over i/longitude .................................... call outfld('NDROPCOL', ndropcol , pcols, lchnk ) call outfld('NDROPSRC', nsource , pcols, lchnk ) call outfld('NDROPMIX', ndropmix , pcols, lchnk ) call outfld('LCLOUD ', cldn , pcols, lchnk ) call outfld('WTKE ', wtke , pcols, lchnk ) call ccncalc(lchnk,ncol,temp,cs,raer,qqcw,ccn,psat,supersat,alogsig,npv) do l=1,psat call outfld(ccn_name(l), ccn(1,1,l) , pcols, lchnk ) enddo ! do column tendencies do m = 1, ntot_amode do lphase = 1, 2 do lspec = 0, nspec_amode(m)+1 ! loop over number + chem constituents + water if (lspec == 0) then ! number if (lphase == 1) then l = numptr_amode(m) else l = numptrcw_amode(m) endif else if (lspec <= nspec_amode(m)) then ! non-water mass if (lphase == 1) then l = lmassptr_amode(lspec,m) else l = lmassptrcw_amode(lspec,m) endif else ! water mass ! if (lphase == 1) then ! l = lwaterptr_amode(m) ! else cycle ! end if end if if (l <= 0) cycle ! mixnuc1 tendency for interstitial = (total tendency) - cflx ! mixnuc1 tendency for activated = (total tendency) coltend(:) = 0.0 if (lphase == 1) then tmpname = cnst_name(l) do i = 1, ncol coltend(i) = sum( pdel(i,:)*raertend(i,:,l) )/gravit ! coltend(i) = coltend(i) - cflx(i,l) end do else tmpname = cnst_name_cw(l) do i = 1, ncol coltend(i) = sum( pdel(i,:)*qqcwtend(i,:,l) )/gravit end do end if fieldname = trim(tmpname) // '_mixnuc1' call outfld( fieldname, coltend, pcols, lchnk) end do ! lspec end do ! lphase end do ! m return end subroutine dropmixnuc subroutine explmix( q, src, ekkp, ekkm, overlapp, overlapm, & 11 qold, surfrate, flxconv, pver, dt, is_unact, qactold ) ! explicit integration of droplet/aerosol mixing ! with source due to activation/nucleation implicit none integer, intent(in) :: pver ! number of levels real(r8), intent(out) :: q(pver) ! mixing ratio to be updated real(r8), intent(in) :: qold(pver) ! mixing ratio from previous time step real(r8), intent(in) :: src(pver) ! source due to activation/nucleation (/s) real(r8), intent(in) :: ekkp(pver) ! zn*zs*density*diffusivity (kg/m3 m2/s) at interface ! below layer k (k,k+1 interface) real(r8), intent(in) :: ekkm(pver) ! zn*zs*density*diffusivity (kg/m3 m2/s) at interface ! above layer k (k,k+1 interface) real(r8), intent(in) :: overlapp(pver) ! cloud overlap below real(r8), intent(in) :: overlapm(pver) ! cloud overlap above real(r8), intent(in) :: surfrate ! surface exchange rate (/s) real(r8), intent(in) :: flxconv ! convergence of flux from surface real(r8), intent(in) :: dt ! time step (s) logical, intent(in) :: is_unact ! true if this is an unactivated species real(r8), intent(in),optional :: qactold(pver) ! mixing ratio of ACTIVATED species from previous step ! *** this should only be present ! if the current species is unactivated number/sfc/mass integer k,kp1,km1 if ( is_unact ) then ! the qactold*(1-overlap) terms are resuspension of activated material do k=1,pver kp1=min(k+1,pver) km1=max(k-1,1) q(k) = qold(k) + dt*( - src(k) + ekkp(k)*(qold(kp1) - qold(k) + & qactold(kp1)*(1.0-overlapp(k))) & + ekkm(k)*(qold(km1) - qold(k) + & qactold(km1)*(1.0-overlapm(k))) ) ! force to non-negative ! if(q(k)<-1.e-30)then ! write(iulog,*)'q=',q(k),' in explmix' q(k)=max(q(k),0._r8) ! endif end do ! diffusion loss at base of lowest layer q(pver)=q(pver)-surfrate*qold(pver)*dt+flxconv*dt ! force to non-negative ! if(q(pver)<-1.e-30)then ! write(iulog,*)'q=',q(pver),' in explmix' q(pver)=max(q(pver),0._r8) ! endif else do k=1,pver kp1=min(k+1,pver) km1=max(k-1,1) q(k) = qold(k) + dt*(src(k) + ekkp(k)*(overlapp(k)*qold(kp1)-qold(k)) + & ekkm(k)*(overlapm(k)*qold(km1)-qold(k)) ) ! force to non-negative ! if(q(k)<-1.e-30)then ! write(iulog,*)'q=',q(k),' in explmix' q(k)=max(q(k),0._r8) ! endif end do ! diffusion loss at base of lowest layer q(pver)=q(pver)-surfrate*qold(pver)*dt+flxconv*dt ! force to non-negative ! if(q(pver)<-1.e-30)then ! write(iulog,*)'q=',q(pver),' in explmix' q(pver)=max(q(pver),0._r8) ! endif end if return end subroutine explmix subroutine activate_init 3,16 #ifndef WRF_PORT use ppgrid, only: pver #else use module_cam_support, only: pver #endif #ifndef WRF_PORT use modal_aero_data use cam_history, only: addfld, add_default, phys_decomp #else use modal_aero_data, only:sigmag_amode => sigmag_amode_mp, & numptr_amode => numptr_amode_mp, nspec_amode => nspec_amode_mp, & lmassptr_amode => lmassptr_amode_mp, dgnum_amode => dgnum_amode_mp use module_cam_support, only: addfld, add_default, phys_decomp #endif use physconst, only: rhoh2o, mwh2o, r_universal use error_function, only: erf implicit none integer l,m real(r8) arg integer lnum, lmass character*16 ccn_longname ! mathematical constants zero=0._r8 third=1./3._r8 twothird=2.*third sixth=1./6._r8 sq2=sqrt(2._r8) pi=4._r8*atan(1.0_r8) sqpi=sqrt(pi) t0=273. surften=0.076_r8 aten=2.*mwh2o*surften/(r_universal*t0*rhoh2o) alogaten=log(aten) alog2=log(2._r8) alog3=log(3._r8) do m=1,ntot_amode ! use only if width of size distribution is prescribed alogsig(m)=log(sigmag_amode(m)) exp45logsig(m)=exp(4.5*alogsig(m)*alogsig(m)) argfactor(m)=2./(3.*sqrt(2.)*alogsig(m)) f1(m)=0.5*exp(2.5*alogsig(m)*alogsig(m)) f2(m)=1.+0.25*alogsig(m) lnum=numptr_amode(m) do l=1,nspec_amode(m) lmass=lmassptr_amode(l,m) if(lmass.le.0)then write(iulog,*)'lmassptr_amode(',l,m,')=',lmass #ifdef WRF_PORT call wrf_message(iulog) #endif call endrun endif enddo npv(m)=6./(pi*dgnum_amode(m)**3*exp45logsig(m)) enddo call addfld ('WTKE ', 'm/s ', pver, 'A', 'Standard deviation of updraft velocity' ,phys_decomp) call addfld ('LCLOUD ', ' ', pver, 'A', 'Liquid cloud fraction' ,phys_decomp) call addfld ('NDROPMIX ', '#/kg/s ', pver, 'A', 'Droplet number mixing' ,phys_decomp) call addfld ('NDROPSRC ', '#/kg/s ', pver, 'A', 'Droplet number source' ,phys_decomp) call addfld ('NDROPSNK ', '#/kg/s ', pver, 'A', 'Droplet number loss by microphysics' ,phys_decomp) call addfld ('NDROPCOL ', '#/m2 ', 1, 'A', 'Column droplet number' ,phys_decomp) call add_default ('WTKE ', 1, ' ') call add_default ('LCLOUD ', 1, ' ') return end subroutine activate_init subroutine activate_modal(wbar, sigw, wdiab, wminf, wmaxf, tair, rhoair, & 2,27 na, pmode, nmode, volume, sigman, hygro, & fn, fm, fluxn, fluxm, flux_fullact ) ! calculates number, surface, and mass fraction of aerosols activated as CCN ! calculates flux of cloud droplets, surface area, and aerosol mass into cloud ! assumes an internal mixture within each of up to pmode multiple aerosol modes ! a gaussiam spectrum of updrafts can be treated. ! mks units ! Abdul-Razzak and Ghan, A parameterization of aerosol activation. ! 2. Multiple aerosol types. J. Geophys. Res., 105, 6837-6844. use physconst, only: rair, epsilo, cpair, rh2o, latvap, gravit, & rhoh2o, mwh2o, r_universal use wv_saturation, only: estblf, epsqs use error_function, only: erf implicit none ! input integer pmode ! dimension of modes real(r8) wbar ! grid cell mean vertical velocity (m/s) real(r8) sigw ! subgrid standard deviation of vertical vel (m/s) real(r8) wdiab ! diabatic vertical velocity (0 if adiabatic) real(r8) wminf ! minimum updraft velocity for integration (m/s) real(r8) wmaxf ! maximum updraft velocity for integration (m/s) real(r8) tair ! air temperature (K) real(r8) rhoair ! air density (kg/m3) real(r8) na(pmode) ! aerosol number concentration (/m3) integer nmode ! number of aerosol modes real(r8) volume(pmode) ! aerosol volume concentration (m3/m3) real(r8) sigman(pmode) ! geometric standard deviation of aerosol size distribution real(r8) hygro(pmode) ! hygroscopicity of aerosol mode ! output real(r8) fn(pmode) ! number fraction of aerosols activated real(r8) fm(pmode) ! mass fraction of aerosols activated real(r8) fluxn(pmode) ! flux of activated aerosol number fraction into cloud (cm/s) real(r8) fluxm(pmode) ! flux of activated aerosol mass fraction into cloud (cm/s) real(r8) flux_fullact ! flux of activated aerosol fraction assuming 100% activation (cm/s) ! rce-comment ! used for consistency check -- this should match (ekd(k)*zs(k)) ! also, fluxm/flux_fullact gives fraction of aerosol mass flux ! that is activated ! local integer, parameter:: nx=200 integer iquasisect_option, isectional real(r8) integ,integf real(r8), parameter :: p0 = 1013.25e2_r8 ! reference pressure (Pa) real(r8) xmin(ntot_amode),xmax(ntot_amode) ! ln(r) at section interfaces real(r8) volmin(ntot_amode),volmax(ntot_amode) ! volume at interfaces real(r8) tmass ! total aerosol mass concentration (g/cm3) real(r8) sign(ntot_amode) ! geometric standard deviation of size distribution real(r8) rm ! number mode radius of aerosol at max supersat (cm) real(r8) pres ! pressure (Pa) real(r8) path ! mean free path (m) real(r8) diff ! diffusivity (m2/s) real(r8) conduct ! thermal conductivity (Joule/m/sec/deg) real(r8) diff0,conduct0 real(r8) es ! saturation vapor pressure real(r8) qs ! water vapor saturation mixing ratio real(r8) dqsdt ! change in qs with temperature real(r8) dqsdp ! change in qs with pressure real(r8) g ! thermodynamic function (m2/s) real(r8) zeta(ntot_amode), eta(ntot_amode) real(r8) lnsmax ! ln(smax) real(r8) alpha real(r8) gamma real(r8) beta real(r8) sqrtg(ntot_amode) real(r8) :: amcube(ntot_amode) ! cube of dry mode radius (m) real(r8) :: smcrit(ntot_amode) ! critical supersatuation for activation real(r8) :: lnsm(ntot_amode) ! ln(smcrit) real(r8) smc(ntot_amode) ! critical supersaturation for number mode radius real(r8) sumflx_fullact real(r8) sumflxn(ntot_amode) real(r8) sumflxm(ntot_amode) real(r8) sumfn(ntot_amode) real(r8) sumfm(ntot_amode) real(r8) fnold(ntot_amode) ! number fraction activated real(r8) fmold(ntot_amode) ! mass fraction activated real(r8) wold,gold real(r8) alogam real(r8) rlo,rhi,xint1,xint2,xint3,xint4 real(r8) wmin,wmax,w,dw,dwmax,dwmin,wnuc,dwnew,wb real(r8) dfmin,dfmax,fnew,fold,fnmin,fnbar,fsbar,fmbar real(r8) alw,sqrtalw real(r8) smax real(r8) x,arg real(r8) xmincoeff,xcut,volcut,surfcut real(r8) z,z1,z2,wf1,wf2,zf1,zf2,gf1,gf2,gf real(r8) etafactor1,etafactor2(ntot_amode),etafactor2max integer m,n ! numerical integration parameters real(r8), parameter :: eps=0.3_r8,fmax=0.99_r8,sds=3._r8 real(r8), parameter :: namin=1.e6_r8 ! minimum aerosol number concentration (/m3) integer ndist(nx) ! accumulates frequency distribution of integration bins required data ndist/nx*0/ save ndist if(ntot_amode<pmode)then write(iulog,*)'ntot_amode,pmode in activate =',ntot_amode,pmode #ifdef WRF_PORT call wrf_message(iulog) #endif call endrun('activate') endif fn(:)=0._r8 fm(:)=0._r8 fluxn(:)=0._r8 fluxm(:)=0._r8 flux_fullact=0._r8 if(nmode.eq.1.and.na(1).lt.1.e-20_r8)return if(sigw.le.1.e-5_r8.and.wbar.le.0.)return pres=rair*rhoair*tair diff0=0.211e-4_r8*(p0/pres)*(tair/t0)**1.94 conduct0=(5.69_r8+0.017_r8*(tair-t0))*4.186e2_r8*1.e-5_r8 ! convert to J/m/s/deg es = estblf(tair) qs = epsilo*es/(pres-(1.0_r8 - epsqs)*es) dqsdt=latvap/(rh2o*tair*tair)*qs alpha=gravit*(latvap/(cpair*rh2o*tair*tair)-1./(rair*tair)) gamma=(1+latvap/cpair*dqsdt)/(rhoair*qs) etafactor2max=1.e10/(alpha*wmaxf)**1.5 ! this should make eta big if na is very small. do m=1,nmode if(volume(m).gt.1.e-39_r8.and.na(m).gt.1.e-39_r8)then ! number mode radius (m) ! write(iulog,*)'alogsig,volc,na=',alogsig(m),volc(m),na(m) amcube(m)=(3.*volume(m)/(4.*pi*exp45logsig(m)*na(m))) ! only if variable size dist ! growth coefficent Abdul-Razzak & Ghan 1998 eqn 16 ! should depend on mean radius of mode to account for gas kinetic effects ! see Fountoukis and Nenes, JGR2005 and Meskhidze et al., JGR2006 ! for approriate size to use for effective diffusivity. g=1._r8/(rhoh2o/(diff0*rhoair*qs) & +latvap*rhoh2o/(conduct0*tair)*(latvap/(rh2o*tair)-1._r8)) sqrtg(m)=sqrt(g) beta=2._r8*pi*rhoh2o*g*gamma etafactor2(m)=1._r8/(na(m)*beta*sqrtg(m)) if(hygro(m).gt.1.e-10)then smc(m)=2.*aten*sqrt(aten/(27.*hygro(m)*amcube(m))) ! only if variable size dist else smc(m)=100. endif ! write(iulog,*)'sm,hygro,amcube=',smcrit(m),hygro(m),amcube(m) else g=1._r8/(rhoh2o/(diff0*rhoair*qs) & +latvap*rhoh2o/(conduct0*tair)*(latvap/(rh2o*tair)-1._r8)) sqrtg(m)=sqrt(g) smc(m)=1._r8 etafactor2(m)=etafactor2max ! this should make eta big if na is very small. endif lnsm(m)=log(smc(m)) ! only if variable size dist ! write(iulog,'(a,i4,4g12.2)')'m,na,amcube,hygro,sm,lnsm=', & ! m,na(m),amcube(m),hygro(m),sm(m),lnsm(m) enddo if(sigw.gt.1.e-5_r8)then ! spectrum of updrafts wmax=min(wmaxf,wbar+sds*sigw) wmin=max(wminf,-wdiab) wmin=max(wmin,wbar-sds*sigw) w=wmin dwmax=eps*sigw dw=dwmax dfmax=0.2_r8 dfmin=0.1_r8 if(wmax.le.w)then do m=1,nmode fluxn(m)=0. fn(m)=0. fluxm(m)=0. fm(m)=0. enddo flux_fullact=0._r8 return endif do m=1,nmode sumflxn(m)=0._r8 sumfn(m)=0._r8 fnold(m)=0._r8 sumflxm(m)=0._r8 sumfm(m)=0._r8 fmold(m)=0._r8 enddo sumflx_fullact=0._r8 fold=0._r8 wold=0._r8 gold=0._r8 dwmin = min( dwmax, 0.01_r8 ) do n=1,200 100 wnuc=w+wdiab ! write(iulog,*)'wnuc=',wnuc alw=alpha*wnuc sqrtalw=sqrt(alw) etafactor1=alw*sqrtalw do m=1,nmode eta(m)=etafactor1*etafactor2(m) zeta(m)=twothird*sqrtalw*aten/sqrtg(m) enddo call maxsat(zeta,eta,nmode,smc,smax) ! write(iulog,*)'w,smax=',w,smax lnsmax=log(smax) x=twothird*(lnsm(nmode)-lnsmax)/(sq2*alogsig(nmode)) fnew=0.5_r8*(1._r8-erf(x)) dwnew = dw if(fnew-fold.gt.dfmax.and.n.gt.1)then ! reduce updraft increment for greater accuracy in integration if (dw .gt. 1.01*dwmin) then dw=0.7_r8*dw dw=max(dw,dwmin) w=wold+dw go to 100 else dwnew = dwmin endif endif if(fnew-fold.lt.dfmin)then ! increase updraft increment to accelerate integration dwnew=min(1.5_r8*dw,dwmax) endif fold=fnew z=(w-wbar)/(sigw*sq2) g=exp(-z*z) fnmin=1._r8 xmincoeff=alogaten-twothird*(lnsmax-alog2)-alog3 do m=1,nmode ! modal x=twothird*(lnsm(m)-lnsmax)/(sq2*alogsig(m)) fn(m)=0.5_r8*(1.-erf(x)) fnmin=min(fn(m),fnmin) ! integration is second order accurate ! assumes linear variation of f*g with w fnbar=(fn(m)*g+fnold(m)*gold) arg=x-1.5_r8*sq2*alogsig(m) fm(m)=0.5_r8*(1._r8-erf(arg)) fmbar=(fm(m)*g+fmold(m)*gold) wb=(w+wold) if(w.gt.0.)then sumflxn(m)=sumflxn(m)+sixth*(wb*fnbar & +(fn(m)*g*w+fnold(m)*gold*wold))*dw sumflxm(m)=sumflxm(m)+sixth*(wb*fmbar & +(fm(m)*g*w+fmold(m)*gold*wold))*dw endif sumfn(m)=sumfn(m)+0.5_r8*fnbar*dw ! write(iulog,'(a,9g10.2)')'lnsmax,lnsm(m),x,fn(m),fnold(m),g,gold,fnbar,dw=',lnsmax,lnsm(m),x,fn(m),fnold(m),g,gold,fnbar,dw fnold(m)=fn(m) sumfm(m)=sumfm(m)+0.5_r8*fmbar*dw fmold(m)=fm(m) enddo ! same form as sumflxm but replace the fm with 1.0 sumflx_fullact = sumflx_fullact & + sixth*(wb*(g+gold) + (g*w+gold*wold))*dw ! sumg=sumg+0.5_r8*(g+gold)*dw gold=g wold=w dw=dwnew if(n.gt.1.and.(w.gt.wmax.or.fnmin.gt.fmax))go to 20 w=w+dw enddo write(iulog,*)'do loop is too short in activate' #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'wmin=',wmin,' w=',w,' wmax=',wmax,' dw=',dw #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'wbar=',wbar,' sigw=',sigw,' wdiab=',wdiab #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'wnuc=',wnuc #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'na=',(na(m),m=1,nmode) #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'fn=',(fn(m),m=1,nmode) #ifdef WRF_PORT call wrf_message(iulog) #endif ! dump all subr parameters to allow testing with standalone code ! (build a driver that will read input and call activate) write(iulog,*)'wbar,sigw,wdiab,tair,rhoair,nmode=' #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*) wbar,sigw,wdiab,tair,rhoair,nmode #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'na=',na #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'volume=', (volume(m),m=1,nmode) #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'sigman=',sigman #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'hydro=' #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*) hygro #ifdef WRF_PORT call wrf_message(iulog) #endif call endrun 20 continue ndist(n)=ndist(n)+1 if(w.lt.wmaxf)then ! contribution from all updrafts stronger than wmax ! assuming constant f (close to fmax) wnuc=w+wdiab z1=(w-wbar)/(sigw*sq2) z2=(wmaxf-wbar)/(sigw*sq2) g=exp(-z1*z1) integ=sigw*0.5*sq2*sqpi*(erf(z2)-erf(z1)) ! consider only upward flow into cloud base when estimating flux wf1=max(w,zero) zf1=(wf1-wbar)/(sigw*sq2) gf1=exp(-zf1*zf1) wf2=max(wmaxf,zero) zf2=(wf2-wbar)/(sigw*sq2) gf2=exp(-zf2*zf2) gf=(gf1-gf2) integf=wbar*sigw*0.5*sq2*sqpi*(erf(zf2)-erf(zf1))+sigw*sigw*gf do m=1,nmode sumflxn(m)=sumflxn(m)+integf*fn(m) sumfn(m)=sumfn(m)+fn(m)*integ sumflxm(m)=sumflxm(m)+integf*fm(m) sumfm(m)=sumfm(m)+fm(m)*integ enddo ! same form as sumflxm but replace the fm with 1.0 sumflx_fullact = sumflx_fullact + integf ! sumg=sumg+integ endif do m=1,nmode fn(m)=sumfn(m)/(sq2*sqpi*sigw) ! fn(m)=sumfn(m)/(sumg) if(fn(m).gt.1.01)then write(iulog,*)'fn=',fn(m),' > 1 in activate' #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'w,m,na,amcube=',w,m,na(m),amcube(m) #ifdef WRF_PORT call wrf_message(iulog) #endif write(iulog,*)'integ,sumfn,sigw=',integ,sumfn(m),sigw #ifdef WRF_PORT call wrf_message(iulog) #endif call endrun('activate') endif fluxn(m)=sumflxn(m)/(sq2*sqpi*sigw) fm(m)=sumfm(m)/(sq2*sqpi*sigw) ! fm(m)=sumfm(m)/(sumg) if(fm(m).gt.1.01)then write(iulog,*)'fm=',fm(m),' > 1 in activate' #ifdef WRF_PORT call wrf_message(iulog) #endif endif fluxm(m)=sumflxm(m)/(sq2*sqpi*sigw) enddo ! same form as fluxm flux_fullact = sumflx_fullact/(sq2*sqpi*sigw) else ! single updraft wnuc=wbar+wdiab if(wnuc.gt.0._r8)then w=wbar alw=alpha*wnuc sqrtalw=sqrt(alw) etafactor1=alw*sqrtalw do m=1,nmode eta(m)=etafactor1*etafactor2(m) zeta(m)=twothird*sqrtalw*aten/sqrtg(m) enddo call maxsat(zeta,eta,nmode,smc,smax) lnsmax=log(smax) xmincoeff=alogaten-twothird*(lnsmax-alog2)-alog3 do m=1,nmode ! modal x=twothird*(lnsm(m)-lnsmax)/(sq2*alogsig(m)) fn(m)=0.5_r8*(1._r8-erf(x)) arg=x-1.5_r8*sq2*alogsig(m) fm(m)=0.5_r8*(1._r8-erf(arg)) if(wbar.gt.0._r8)then fluxn(m)=fn(m)*w fluxm(m)=fm(m)*w endif enddo flux_fullact = w endif endif return end subroutine activate_modal subroutine maxsat(zeta,eta,nmode,smc,smax) 7 ! calculates maximum supersaturation for multiple ! competing aerosol modes. ! Abdul-Razzak and Ghan, A parameterization of aerosol activation. ! 2. Multiple aerosol types. J. Geophys. Res., 105, 6837-6844. implicit none integer nmode ! number of modes real(r8) smc(ntot_amode) ! critical supersaturation for number mode radius real(r8) zeta(ntot_amode), eta(ntot_amode) real(r8) smax ! maximum supersaturation integer m ! mode index real(r8) sum, g1, g2, g1sqrt, g2sqrt do m=1,nmode if(zeta(m).gt.1.e5_r8*eta(m).or.smc(m)*smc(m).gt.1.e5_r8*eta(m))then ! weak forcing. essentially none activated smax=1.e-20_r8 else ! significant activation of this mode. calc activation all modes. go to 1 endif enddo return 1 continue sum=0 do m=1,nmode if(eta(m).gt.1.e-20_r8)then g1=zeta(m)/eta(m) g1sqrt=sqrt(g1) g1=g1sqrt*g1 g1=g1sqrt*g1 g2=smc(m)/sqrt(eta(m)+3._r8*zeta(m)) g2sqrt=sqrt(g2) g2=g2sqrt*g2 sum=sum+(f1(m)*g1+f2(m)*g2)/(smc(m)*smc(m)) else sum=1.e20_r8 endif enddo smax=1._r8/sqrt(sum) return end subroutine maxsat subroutine ccncalc(lchnk,ncol,tair,cs,raer,qqcw,ccn,psat,supersat,alogsig,npv) 1,8 ! calculates number concentration of aerosols activated as CCN at ! supersaturation supersat. ! assumes an internal mixture of a multiple externally-mixed aerosol modes ! cgs units ! Ghan et al., Atmos. Res., 1993, 198-221. use shr_kind_mod, only: r8 => shr_kind_r8 #ifndef WRF_PORT use ppgrid, only: pcols, pver, pverp use constituents, only: pcnst use modal_aero_data #else use module_cam_support, only: pcols, pver, pverp, pcnst =>pcnst_mp #endif use physconst, only: rhoh2o, mwh2o, r_universal use modal_aero_data, only: use error_function, only: erf implicit none ! input integer lchnk ! chunk index integer, intent(in) :: ncol ! number of columns integer, intent(in) :: psat ! number of supesaturations real(r8), intent(in) :: raer(pcols,pver,pcnst) ! aerosol mass, number mixing ratios type(qqcw_type), intent(in) :: QQCW(:) real(r8), intent(in) :: tair(pcols,pver) ! air temperature (K) real(r8), intent(in) :: cs(pcols,pver) ! air density (kg/m3) real(r8), intent(in) :: supersat(psat) real(r8), intent(in) :: npv(ntot_amode) ! number per volume concentration real(r8), intent(in) :: alogsig(ntot_amode) ! natl log of geometric standard dev of aerosol real(r8), intent(out) :: ccn(pcols,pver,psat) ! number conc of aerosols activated at supersat (#/m3) ! local real(r8) naerosol(pcols) ! interstit+activated aerosol number conc (/m3) real(r8) vaerosol(pcols) ! interstit+activated aerosol volume conc (m3/m3) real(r8) exp45logsig(ntot_amode) ! exp(4.5*alogsig**2) real(r8) amcube(pcols) real(r8) super(psat) ! supersaturation real(r8) amcubecoef(ntot_amode) real(r8) :: surften ! surface tension of water w/respect to air (N/m) real(r8) surften_coef real(r8) a(pcols) ! surface tension parameter real(r8) hygro(pcols) ! aerosol hygroscopicity real(r8) sm(pcols) ! critical supersaturation at mode radius real(r8) argfactor(ntot_amode),arg(pcols) ! mathematical constants real(r8) pi real(r8) twothird,sq2 integer l,m,n,i,k real(r8) log,cc real(r8) smcoefcoef,smcoef(pcols) integer phase ! phase of aerosol super(:)=supersat(:)*0.01 pi = 4.*atan(1.0) sq2=sqrt(2._r8) twothird=2._r8/3._r8 surften=0.076_r8 surften_coef=2._r8*mwh2o*surften/(r_universal*rhoh2o) smcoefcoef=2._r8/sqrt(27._r8) do m=1,ntot_amode exp45logsig(m)=exp(4.5*alogsig(m)*alogsig(m)) amcubecoef(m)=3./(4.*pi*exp45logsig(m)) argfactor(m)=twothird/(sq2*alogsig(m)) end do do k=1,pver do i=1,ncol ccn(i,k,:)=0. a(i)=surften_coef/tair(i,k) smcoef(i)=smcoefcoef*a(i)*sqrt(a(i)) end do do m=1,ntot_amode phase=3 ! interstitial+cloudborne call loadaer(raer,qqcw,1,ncol,k,m,cs,npv(m), phase, & naerosol, vaerosol, hygro ) where(naerosol(:ncol)>1.e-3) amcube(:ncol)=amcubecoef(m)*vaerosol(:ncol)/naerosol(:ncol) sm(:ncol)=smcoef(:ncol)/sqrt(hygro(:ncol)*amcube(:ncol)) ! critical supersaturation elsewhere sm(:ncol)=1. ! value shouldn't matter much since naerosol is small endwhere do l=1,psat do i=1,ncol arg(i)=argfactor(m)*log(sm(i)/super(l)) ccn(i,k,l)=ccn(i,k,l)+naerosol(i)*0.5*(1._r8-erf(arg(i))) enddo enddo enddo enddo ccn(:ncol,:,:)=ccn(:ncol,:,:)*1.e-6 ! convert from #/m3 to #/cm3 return end subroutine ccncalc subroutine loadaer(raer,qqcw,istart,istop,k,m,cs,npv1, phase, & 5,6 naerosol, vaerosol, hygro ) use shr_kind_mod, only: r8 => shr_kind_r8 #ifndef WRF_PORT use abortutils, only: endrun use ppgrid, only: pcols, pver use modal_aero_data #else use module_cam_support, only: pcols, pver, endrun, pcnst =>pcnst_mp use modal_aero_data, only: nspec_amode => nspec_amode_mp, & lmassptr_amode => lmassptr_amode_mp , lmassptrcw_amode => lmassptrcw_amode_mp, & lspectype_amode => lspectype_amode_mp, specdens_amode => specdens_amode_mp, & spechygro => spechygro_mp , numptr_amode => numptr_amode_mp, & numptrcw_amode => numptrcw_amode_mp , voltonumbhi_amode => voltonumbhi_amode_mp,& voltonumblo_amode => voltonumblo_amode_mp #endif implicit none ! load aerosol number, volume concentrations, and bulk hygroscopicity type(qqcw_type), intent(in) :: QQCW(:) real(r8), intent(in) :: raer(pcols,pver,pcnst) ! aerosol mass, number mixing ratios integer, intent(in) :: istart, istop ! start, stop column index integer, intent(in) :: m ! m=mode index integer, intent(in) :: k ! level index real(r8), intent(in) :: cs(pcols,pver) ! air density (kg/m3) integer, intent(in) :: phase ! phase of aerosol: 1 for interstitial, 2 for cloud-borne, 3 for sum real(r8), intent(in) :: npv1 ! number per volume real(r8), intent(out) :: naerosol(pcols) ! interstitial number conc (/m3) real(r8), intent(out) :: vaerosol(pcols) ! interstitial+activated volume conc (m3/m3) real(r8), intent(out) :: hygro(pcols) ! bulk hygroscopicity of mode ! internal real(r8) vol(pcols) ! aerosol volume mixing ratio integer i,lnum,lnumcw,l,ltype,lmass,lmasscw do i=istart,istop vaerosol(i)=0._r8 hygro(i)=0._r8 end do do l=1,nspec_amode(m) lmass=lmassptr_amode(l,m) ! interstitial lmasscw=lmassptrcw_amode(l,m) ! cloud-borne ltype=lspectype_amode(l,m) if(phase.eq.3)then do i=istart,istop ! vol(i)=max(raer(i,k,lmass)+raer(i,k,lmasscw),0._r8)/specdens_amode(ltype) vol(i)=max(raer(i,k,lmass)+qqcw(lmasscw)%fldcw(i,k),0._r8)/specdens_amode(ltype) end do elseif(phase.eq.2)then do i=istart,istop vol(i)=max(qqcw(lmasscw)%fldcw(i,k),0._r8)/specdens_amode(ltype) end do elseif(phase.eq.1)then do i=istart,istop vol(i)=max(raer(i,k,lmass),0._r8)/specdens_amode(ltype) end do else write(iulog,*)'phase=',phase,' in loadaer' #ifdef WRF_PORT call wrf_message(iulog) #endif call endrun('phase error in loadaer') endif do i=istart,istop vaerosol(i)=vaerosol(i)+vol(i) hygro(i)=hygro(i)+vol(i)*spechygro(ltype) end do enddo do i=istart,istop if (vaerosol(i) > 1.0e-30_r8) then ! +++xl add 8/2/2007 hygro(i)=hygro(i)/(vaerosol(i)) vaerosol(i)=vaerosol(i)*cs(i,k) else hygro(i)=0.0_r8 vaerosol(i)=0.0_r8 endif end do lnum=numptr_amode(m) lnumcw=numptrcw_amode(m) if(lnum.gt.0)then ! aerosol number predicted if(phase.eq.3)then do i=istart,istop ! naerosol(i)=(raer(i,k,lnum)+raer(i,k,lnumcw))*cs(i,k) naerosol(i)=(raer(i,k,lnum)+qqcw(lnumcw)%fldcw(i,k))*cs(i,k) end do elseif(phase.eq.2)then do i=istart,istop naerosol(i)=qqcw(lnumcw)%fldcw(i,k)*cs(i,k) end do else do i=istart,istop naerosol(i)=raer(i,k,lnum)*cs(i,k) end do endif ! adjust number so that dgnumlo < dgnum < dgnumhi do i=istart,istop naerosol(i) = max( naerosol(i), vaerosol(i)*voltonumbhi_amode(m) ) naerosol(i) = min( naerosol(i), vaerosol(i)*voltonumblo_amode(m) ) end do else ! aerosol number diagnosed from mass and prescribed size do i=istart,istop naerosol(i)=vaerosol(i)*npv1 naerosol(i)=max(naerosol(i),0._r8) end do endif return end subroutine loadaer #endif end module ndrop