module vertical_diffusion 4,9 !----------------------------------------------------------------------------------------------------- ! ! Module to compute vertical diffusion of momentum, moisture, trace constituents ! ! and static energy. Separate modules compute ! ! 1. stresses associated with turbulent flow over orography ! ! ( turbulent mountain stress ) ! ! 2. eddy diffusivities, including nonlocal tranport terms ! ! 3. molecular diffusivities ! ! 4. coming soon... gravity wave drag ! ! Lastly, a implicit diffusion solver is called, and tendencies retrieved by ! ! differencing the diffused and initial states. ! ! ! ! Calling sequence: ! ! ! ! vertical_diffusion_init Initializes vertical diffustion constants and modules ! ! init_molec_diff Initializes molecular diffusivity module ! ! init_eddy_diff Initializes eddy diffusivity module (includes PBL) ! ! init_tms Initializes turbulent mountain stress module ! ! init_vdiff Initializes diffusion solver module ! ! vertical_diffusion_ts_init Time step initialization (only used for upper boundary condition) ! ! vertical_diffusion_tend Computes vertical diffusion tendencies ! ! compute_tms Computes turbulent mountain stresses ! ! compute_eddy_diff Computes eddy diffusivities and countergradient terms ! ! compute_vdiff Solves vertical diffusion equations, including molecular diffusivities ! ! ! !---------------------------Code history-------------------------------------------------------------- ! ! J. Rosinski : Jun. 1992 ! ! J. McCaa : Sep. 2004 ! ! S. Park : Aug. 2006, Dec. 2008. Jan. 2010 ! !----------------------------------------------------------------------------------------------------- ! use shr_kind_mod, only : r8 => shr_kind_r8 use ppgrid, only : pcols, pver, pverp use constituents, only : pcnst, qmin use diffusion_solver, only : vdiff_selector use abortutils, only : endrun use physconst, only : & cpair , & ! Specific heat of dry air gravit , & ! Acceleration due to gravity rair , & ! Gas constant for dry air zvir , & ! rh2o/rair - 1 latvap , & ! Latent heat of vaporization latice , & ! Latent heat of fusion karman , & ! von Karman constant mwdry , & ! Molecular weight of dry air avogad , & ! Avogadro's number boltz ! Boltzman's constant use cam_history, only : fieldname_len use perf_mod use cam_logfile, only : iulog use phys_control, only : phys_getopts implicit none private save ! ----------------- ! ! Public interfaces ! ! ----------------- ! public vd_register ! Register multi-time-level variables with physics buffer public vertical_diffusion_init ! Initialization public vertical_diffusion_ts_init ! Time step initialization (only used for upper boundary condition) public vertical_diffusion_tend ! Full vertical diffusion routine ! ------------ ! ! Private data ! ! ------------ ! character(len=16) :: eddy_scheme ! Default set in phys_control.F90, use namelist to change ! 'HB' = Holtslag and Boville (default) ! 'HBR' = Holtslag and Boville and Rash ! 'diag_TKE' = Bretherton and Park ( UW Moist Turbulence Scheme ) integer, parameter :: nturb = 5 ! Number of iterations for solution ( when 'diag_TKE' scheme is selected ) logical, parameter :: wstarent = .true. ! Use wstar (.true.) or TKE (.false.) entrainment closure ( when 'diag_TKE' scheme is selected ) logical :: do_pseudocon_diff = .false. ! If .true., do pseudo-conservative variables diffusion character(len=16) :: shallow_scheme ! For checking compatibility between eddy diffusion and shallow convection schemes ! 'Hack' = Hack Shallow Convection Scheme ! 'UW' = Park and Bretherton ( UW Shallow Convection Scheme ) character(len=16) :: microp_scheme ! Microphysics scheme logical :: do_molec_diff = .false. ! Switch for molecular diffusion logical :: do_tms ! Switch for turbulent mountain stress real(r8) :: tms_orocnst ! Converts from standard deviation to height type(vdiff_selector) :: fieldlist_wet ! Logical switches for moist mixing ratio diffusion type(vdiff_selector) :: fieldlist_dry ! Logical switches for dry mixing ratio diffusion integer :: ntop ! Top interface level to which vertical diffusion is applied ( = 1 ). integer :: nbot ! Bottom interface level to which vertical diffusion is applied ( = pver ). integer :: tke_idx, kvh_idx, kvm_idx ! TKE and eddy diffusivity indices for fields in the physics buffer integer :: turbtype_idx, smaw_idx ! Turbulence type and instability functions integer :: tauresx_idx, tauresy_idx ! Redisual stress for implicit surface stress character(len=fieldname_len) :: vdiffnam(pcnst) ! Names of vertical diffusion tendencies integer :: ixcldice, ixcldliq ! Constituent indices for cloud liquid and ice water integer :: ixnumice, ixnumliq #ifdef MODAL_AERO integer :: ixndrop #endif integer :: wgustd_index logical :: history_budget ! Output tendencies and state variables for CAM4 T, qv, ql, qi contains ! =============================================================================== ! ! ! ! =============================================================================== ! subroutine vd_register() 1,10 !------------------------------------------------ ! ! Register physics buffer fields and constituents ! !------------------------------------------------ ! use phys_buffer, only : pbuf_times, pbuf_add ! Get eddy_scheme setting from phys_control.F90 call phys_getopts( eddy_scheme_out = eddy_scheme, & shallow_scheme_out = shallow_scheme, & microp_scheme_out = microp_scheme, & do_tms_out = do_tms, & tms_orocnst_out = tms_orocnst ) ! Request physics buffer space for fields that persist across timesteps. call pbuf_add( 'tke', 'global', 1, pverp, pbuf_times, tke_idx ) call pbuf_add( 'kvh', 'global', 1, pverp, pbuf_times, kvh_idx ) call pbuf_add( 'kvm', 'global', 1, pverp, pbuf_times, kvm_idx ) call pbuf_add( 'turbtype', 'global', 1, pverp, pbuf_times, turbtype_idx ) call pbuf_add( 'smaw', 'global', 1, pverp, pbuf_times, smaw_idx ) call pbuf_add( 'tauresx', 'global', 1, 1, pbuf_times, tauresx_idx ) call pbuf_add( 'tauresy', 'global', 1, 1, pbuf_times, tauresy_idx ) call pbuf_add( 'wgustd', 'global', 1, 1, 1, wgustd_index ) end subroutine vd_register ! =============================================================================== ! ! ! ! =============================================================================== ! subroutine vertical_diffusion_init() 1,126 !------------------------------------------------------------------! ! Initialization of time independent fields for vertical diffusion ! ! Calls initialization routines for subsidiary modules ! !----------------------------------------------------------------- ! use cam_history, only : addfld, add_default, phys_decomp use eddy_diff, only : init_eddy_diff use hb_diff, only : init_hb_diff use molec_diff, only : init_molec_diff use trb_mtn_stress, only : init_tms use diffusion_solver, only : init_vdiff, vdiff_select use constituents, only : cnst_get_ind, cnst_get_type_byind, cnst_name use spmd_utils, only : masterproc use hycoef, only : hypm #ifdef MODAL_AERO use modal_aero_data #endif character(128) :: errstring ! Error status for init_vdiff integer :: ntop_eddy ! Top interface level to which eddy vertical diffusion is applied ( = 1 ) integer :: nbot_eddy ! Bottom interface level to which eddy vertical diffusion is applied ( = pver ) integer :: ntop_molec ! Top interface level to which molecular vertical diffusion is applied ( = 1 ) integer :: nbot_molec ! Bottom interface level to which molecular vertical diffusion is applied integer :: k ! Vertical loop index #ifdef MODAL_AERO integer :: m, l #endif ! ----------------------------------------------------------------- ! ! Get indices of cloud liquid and ice within the constituents array ! ! ----------------------------------------------------------------- ! call cnst_get_ind( 'CLDLIQ', ixcldliq ) call cnst_get_ind( 'CLDICE', ixcldice ) if( microp_scheme .eq. 'MG' ) then call cnst_get_ind( 'NUMLIQ', ixnumliq ) call cnst_get_ind( 'NUMICE', ixnumice ) endif if (masterproc) then write(iulog,*)'Initializing vertical diffusion (vertical_diffusion_init)' end if ! ---------------------------------------------------------------------------------------- ! ! Initialize molecular diffusivity module ! ! Molecular diffusion turned on above ~60 km (50 Pa) if model top is above ~90 km (.1 Pa). ! ! Note that computing molecular diffusivities is a trivial expense, but constituent ! ! diffusivities depend on their molecular weights. Decomposing the diffusion matric ! ! for each constituent is a needless expense unless the diffusivity is significant. ! ! ---------------------------------------------------------------------------------------- ! ntop_molec = 1 ! Should always be 1 nbot_molec = 0 ! Should be set below about 70 km if( hypm(1) .lt. 0.1_r8 ) then do_molec_diff = .true. do k = 1, pver if( hypm(k) .lt. 50._r8 ) nbot_molec = k end do call init_molec_diff( r8, pcnst, rair, ntop_molec, nbot_molec, mwdry, & avogad, gravit, cpair, boltz ) call addfld( 'TTPXMLC', 'K/S', 1, 'A', 'Top interf. temp. flux: molec. viscosity', phys_decomp ) call add_default ( 'TTPXMLC', 1, ' ' ) if( masterproc ) write(iulog,fmt='(a,i3,5x,a,i3)') 'NTOP_MOLEC =', ntop_molec, 'NBOT_MOLEC =', nbot_molec end if ! ---------------------------------- ! ! Initialize eddy diffusivity module ! ! ---------------------------------- ! ntop_eddy = 1 ! No reason not to make this 1, if > 1, must be <= nbot_molec nbot_eddy = pver ! Should always be pver if( masterproc ) write(iulog,fmt='(a,i3,5x,a,i3)') 'NTOP_EDDY =', ntop_eddy, 'NBOT_EDDY =', nbot_eddy select case ( eddy_scheme ) case ( 'diag_TKE' ) if( masterproc ) write(iulog,*) 'vertical_diffusion_init: eddy_diffusivity scheme: UW Moist Turbulence Scheme by Bretherton and Park' ! Check compatibility of eddy and shallow scheme if( shallow_scheme .ne. 'UW' ) then write(iulog,*) 'ERROR: shallow convection scheme ', shallow_scheme,' is incompatible with eddy scheme ', eddy_scheme call endrun( 'convect_shallow_init: shallow_scheme and eddy_scheme are incompatible' ) endif call init_eddy_diff( r8, pver, gravit, cpair, rair, zvir, latvap, latice, & ntop_eddy, nbot_eddy, hypm, karman ) if( masterproc ) write(iulog,*) 'vertical_diffusion: nturb, ntop_eddy, nbot_eddy ', nturb, ntop_eddy, nbot_eddy case ( 'HB', 'HBR' ) if( masterproc ) write(iulog,*) 'vertical_diffusion_init: eddy_diffusivity scheme: Holtslag and Boville' call init_hb_diff( gravit, cpair, rair, zvir, ntop_eddy, nbot_eddy, hypm, karman, eddy_scheme ) end select ! The vertical diffusion solver must operate ! over the full range of molecular and eddy diffusion ntop = min(ntop_molec,ntop_eddy) nbot = max(nbot_molec,nbot_eddy) ! ------------------------------------------- ! ! Initialize turbulent mountain stress module ! ! ------------------------------------------- ! if( do_tms ) then call init_tms( r8, tms_orocnst, karman, gravit, rair ) call addfld( 'TAUTMSX' ,'N/m2 ', 1, 'A', 'Zonal turbulent mountain surface stress', phys_decomp ) call addfld( 'TAUTMSY' ,'N/m2 ', 1, 'A', 'Meridional turbulent mountain surface stress', phys_decomp ) call add_default( 'TAUTMSX ', 1, ' ' ) call add_default( 'TAUTMSY ', 1, ' ' ) if (masterproc) then write(iulog,*)'Using turbulent mountain stress module' write(iulog,*)' tms_orocnst = ',tms_orocnst end if endif ! ---------------------------------- ! ! Initialize diffusion solver module ! ! ---------------------------------- ! call init_vdiff( r8, pcnst, rair, gravit, fieldlist_wet, fieldlist_dry, errstring ) if( errstring .ne. '' ) call endrun( errstring ) ! Use fieldlist_wet to select the fields which will be diffused using moist mixing ratios ( all by default ) ! Use fieldlist_dry to select the fields which will be diffused using dry mixing ratios. if( vdiff_select( fieldlist_wet, 'u' ) .ne. '' ) call endrun( vdiff_select( fieldlist_wet, 'u' ) ) if( vdiff_select( fieldlist_wet, 'v' ) .ne. '' ) call endrun( vdiff_select( fieldlist_wet, 'v' ) ) if( vdiff_select( fieldlist_wet, 's' ) .ne. '' ) call endrun( vdiff_select( fieldlist_wet, 's' ) ) #ifdef MODAL_AERO call cnst_get_ind( 'NUMLIQ', ixndrop ) #endif do 20 k = 1, pcnst #ifdef MODAL_AERO ! Do not diffuse droplet number - treated in dropmixnuc if( k == ixndrop ) go to 20 ! Don't diffuse aerosol - treated in dropmixnuc do m = 1, ntot_amode if( k == numptr_amode(m) ) go to 20 ! if( k == numptrcw_amode(m) ) go to 20 do l = 1, nspec_amode(m) if( k == lmassptr_amode(l,m) ) go to 20 ! if( k == lmassptrcw_amode(l,m) ) go to 20 enddo ! if( k == lwaterptr_amode(m) ) go to 20 enddo #endif if( cnst_get_type_byind(k) .eq. 'wet' ) then if( vdiff_select( fieldlist_wet, 'q', k ) .ne. '' ) call endrun( vdiff_select( fieldlist_wet, 'q', k ) ) else if( vdiff_select( fieldlist_dry, 'q', k ) .ne. '' ) call endrun( vdiff_select( fieldlist_dry, 'q', k ) ) endif 20 continue ! ------------------------ ! ! Diagnostic output fields ! ! ------------------------ ! do k = 1, pcnst vdiffnam(k) = 'VD'//cnst_name(k) if( k == 1 ) vdiffnam(k) = 'VD01' !**** compatibility with old code **** call addfld( vdiffnam(k), 'kg/kg/s ', pver, 'A', 'Vertical diffusion of '//cnst_name(k), phys_decomp ) end do call phys_getopts( history_budget_out = history_budget ) if( history_budget ) then call add_default( vdiffnam(ixcldliq), 1, ' ' ) call add_default( vdiffnam(ixcldice), 1, ' ' ) end if call addfld( 'TKE' , 'm2/s2' , pverp , 'A', 'Turbulent Kinetic Energy' , phys_decomp ) call addfld( 'PBLH' , 'm' , 1 , 'A', 'PBL height' , phys_decomp ) call addfld( 'TPERT' , 'K' , 1 , 'A', 'Perturbation temperature (eddies in PBL)' , phys_decomp ) call addfld( 'QPERT' , 'kg/kg' , 1 , 'A', 'Perturbation specific humidity (eddies in PBL)' , phys_decomp ) call addfld( 'USTAR' , 'm/s' , 1 , 'A', 'Surface friction velocity' , phys_decomp ) call addfld( 'KVH' , 'm2/s' , pverp , 'A', 'Vertical diffusion diffusivities (heat/moisture)' , phys_decomp ) call addfld( 'KVM' , 'm2/s' , pverp , 'A', 'Vertical diffusion diffusivities (momentum)' , phys_decomp ) call addfld( 'CGS' , 's/m2' , pverp , 'A', 'Counter-gradient coeff on surface kinematic fluxes', phys_decomp ) call addfld( 'DTVKE' , 'K/s' , pver , 'A', 'dT/dt vertical diffusion KE dissipation' , phys_decomp ) call addfld( 'DTV' , 'K/s' , pver , 'A', 'T vertical diffusion' , phys_decomp ) call addfld( 'DUV' , 'm/s2' , pver , 'A', 'U vertical diffusion' , phys_decomp ) call addfld( 'DVV' , 'm/s2' , pver , 'A', 'V vertical diffusion' , phys_decomp ) call addfld( 'QT' , 'kg/kg' , pver , 'A', 'Total water mixing ratio' , phys_decomp ) call addfld( 'SL' , 'J/kg' , pver , 'A', 'Liquid water static energy' , phys_decomp ) call addfld( 'SLV' , 'J/kg' , pver , 'A', 'Liq wat virtual static energy' , phys_decomp ) call addfld( 'SLFLX' , 'W/m2' , pverp , 'A', 'Liquid static energy flux' , phys_decomp ) call addfld( 'QTFLX' , 'W/m2' , pverp , 'A', 'Total water flux' , phys_decomp ) call addfld( 'UFLX' , 'W/m2' , pverp , 'A', 'Zonal momentum flux' , phys_decomp ) call addfld( 'VFLX' , 'W/m2' , pverp , 'A', 'Meridional momentm flux' , phys_decomp ) call addfld( 'WGUSTD' , 'm/s' , 1 , 'A', 'wind gusts from turbulence' , phys_decomp ) ! ---------------------------------------------------------------------------- ! ! Below ( with '_PBL') are for detailed analysis of UW Moist Turbulence Scheme ! ! ---------------------------------------------------------------------------- ! call addfld( 'qt_pre_PBL ', 'kg/kg' , pver , 'A', 'qt_prePBL' , phys_decomp ) call addfld( 'sl_pre_PBL ', 'J/kg' , pver , 'A', 'sl_prePBL' , phys_decomp ) call addfld( 'slv_pre_PBL ', 'J/kg' , pver , 'A', 'slv_prePBL' , phys_decomp ) call addfld( 'u_pre_PBL ', 'm/s' , pver , 'A', 'u_prePBL' , phys_decomp ) call addfld( 'v_pre_PBL ', 'm/s' , pver , 'A', 'v_prePBL' , phys_decomp ) call addfld( 'qv_pre_PBL ', 'kg/kg' , pver , 'A', 'qv_prePBL' , phys_decomp ) call addfld( 'ql_pre_PBL ', 'kg/kg' , pver , 'A', 'ql_prePBL' , phys_decomp ) call addfld( 'qi_pre_PBL ', 'kg/kg' , pver , 'A', 'qi_prePBL' , phys_decomp ) call addfld( 't_pre_PBL ', 'K' , pver , 'A', 't_prePBL' , phys_decomp ) call addfld( 'rh_pre_PBL ', '%' , pver , 'A', 'rh_prePBL' , phys_decomp ) call addfld( 'qt_aft_PBL ', 'kg/kg' , pver , 'A', 'qt_afterPBL' , phys_decomp ) call addfld( 'sl_aft_PBL ', 'J/kg' , pver , 'A', 'sl_afterPBL' , phys_decomp ) call addfld( 'slv_aft_PBL ', 'J/kg' , pver , 'A', 'slv_afterPBL' , phys_decomp ) call addfld( 'u_aft_PBL ', 'm/s' , pver , 'A', 'u_afterPBL' , phys_decomp ) call addfld( 'v_aft_PBL ', 'm/s' , pver , 'A', 'v_afterPBL' , phys_decomp ) call addfld( 'qv_aft_PBL ', 'kg/kg' , pver , 'A', 'qv_afterPBL' , phys_decomp ) call addfld( 'ql_aft_PBL ', 'kg/kg' , pver , 'A', 'ql_afterPBL' , phys_decomp ) call addfld( 'qi_aft_PBL ', 'kg/kg' , pver , 'A', 'qi_afterPBL' , phys_decomp ) call addfld( 't_aft_PBL ', 'K' , pver , 'A', 't_afterPBL' , phys_decomp ) call addfld( 'rh_aft_PBL ', '%' , pver , 'A', 'rh_afterPBL' , phys_decomp ) call addfld( 'slflx_PBL ', 'J/m2/s' , pverp , 'A', 'sl flux by PBL' , phys_decomp ) call addfld( 'qtflx_PBL ', 'kg/m2/s', pverp , 'A', 'qt flux by PBL' , phys_decomp ) call addfld( 'uflx_PBL ', 'kg/m/s2', pverp , 'A', 'u flux by PBL' , phys_decomp ) call addfld( 'vflx_PBL ', 'kg/m/s2', pverp , 'A', 'v flux by PBL' , phys_decomp ) call addfld( 'slflx_cg_PBL', 'J/m2/s' , pverp , 'A', 'sl_cg flux by PBL' , phys_decomp ) call addfld( 'qtflx_cg_PBL', 'kg/m2/s', pverp , 'A', 'qt_cg flux by PBL' , phys_decomp ) call addfld( 'uflx_cg_PBL ', 'kg/m/s2', pverp , 'A', 'u_cg flux by PBL' , phys_decomp ) call addfld( 'vflx_cg_PBL ', 'kg/m/s2', pverp , 'A', 'v_cg flux by PBL' , phys_decomp ) call addfld( 'qtten_PBL ', 'kg/kg/s', pver , 'A', 'qt tendency by PBL' , phys_decomp ) call addfld( 'slten_PBL ', 'J/kg/s' , pver , 'A', 'sl tendency by PBL' , phys_decomp ) call addfld( 'uten_PBL ', 'm/s2' , pver , 'A', 'u tendency by PBL' , phys_decomp ) call addfld( 'vten_PBL ', 'm/s2' , pver , 'A', 'v tendency by PBL' , phys_decomp ) call addfld( 'qvten_PBL ', 'kg/kg/s', pver , 'A', 'qv tendency by PBL' , phys_decomp ) call addfld( 'qlten_PBL ', 'kg/kg/s', pver , 'A', 'ql tendency by PBL' , phys_decomp ) call addfld( 'qiten_PBL ', 'kg/kg/s', pver , 'A', 'qi tendency by PBL' , phys_decomp ) call addfld( 'tten_PBL ', 'K/s' , pver , 'A', 'T tendency by PBL' , phys_decomp ) call addfld( 'rhten_PBL ', '%/s' , pver , 'A', 'RH tendency by PBL' , phys_decomp ) call add_default( vdiffnam(1), 1, ' ' ) call add_default( 'DTV' , 1, ' ' ) call add_default( 'PBLH' , 1, ' ' ) if( eddy_scheme .eq. 'diag_TKE' ) then call addfld( 'BPROD ','M2/S3 ',pverp, 'A','Buoyancy Production',phys_decomp) call addfld( 'SPROD ','M2/S3 ',pverp, 'A','Shear Production',phys_decomp) call addfld( 'SFI ','FRACTION',pverp, 'A','Interface-layer sat frac',phys_decomp) call add_default( 'BPROD ', 1, ' ' ) call add_default( 'SPROD ', 1, ' ' ) call add_default( 'SFI ', 1, ' ' ) call add_default( 'TKE ', 1, ' ' ) call add_default( 'KVH ', 1, ' ' ) call add_default( 'KVM ', 1, ' ' ) call add_default( 'WGUSTD ', 1, ' ' ) call add_default( 'QT ', 1, ' ' ) call add_default( 'SL ', 1, ' ' ) call add_default( 'SLV ', 1, ' ' ) call add_default( 'SLFLX ', 1, ' ' ) call add_default( 'QTFLX ', 1, ' ' ) call add_default( 'UFLX ', 1, ' ' ) call add_default( 'VFLX ', 1, ' ' ) endif #if( defined WACCM_GHG || defined WACCM_MOZART ) call add_default( 'DUV' , 1, ' ' ) call add_default( 'DVV' , 1, ' ' ) #endif end subroutine vertical_diffusion_init ! =============================================================================== ! ! ! ! =============================================================================== ! subroutine vertical_diffusion_ts_init( state ) 1,4 !-------------------------------------------------------------- ! ! Timestep dependent setting, ! ! At present only invokes upper bc code for molecular diffusion ! !-------------------------------------------------------------- ! use molec_diff , only : init_timestep_molec_diff use physics_types , only : physics_state use ppgrid , only : begchunk, endchunk type(physics_state), intent(in) :: state(begchunk:endchunk) if (do_molec_diff) call init_timestep_molec_diff( state ) end subroutine vertical_diffusion_ts_init ! =============================================================================== ! ! ! ! =============================================================================== ! subroutine vertical_diffusion_tend( & 1,97 ztodt , state , & taux , tauy , shflx , cflx , pblh , & tpert , qpert , ustar , obklen , ptend , & cldn , ocnfrac , landfrac , sgh , pbuf ) !---------------------------------------------------- ! ! This is an interface routine for vertical diffusion ! !---------------------------------------------------- ! use physics_types, only : physics_state, physics_ptend use cam_history, only : outfld use phys_buffer, only : pbuf_size_max, pbuf_fld, pbuf_old_tim_idx, pbuf_times, pbuf_get_fld_idx use time_manager, only : is_first_step use geopotential, only : geopotential_dse use trb_mtn_stress, only : compute_tms use eddy_diff, only : compute_eddy_diff use hb_diff, only : compute_hb_diff use wv_saturation, only : fqsatd, aqsat use molec_diff, only : compute_molec_diff, vd_lu_qdecomp use constituents, only : qmincg, qmin use infnan #ifdef MODAL_AERO use modal_aero_data #endif ! The commented 'only' limiter from the following line acommodates broken pgf90 v.5.1.6 use diffusion_solver !, only : compute_vdiff, any, operator(.not.) ! --------------- ! ! Input Auguments ! ! --------------- ! type(physics_state), intent(in) :: state ! Physics state variables real(r8), intent(in) :: taux(pcols) ! x surface stress [ N/m2 ] real(r8), intent(in) :: tauy(pcols) ! y surface stress [ N/m2 ] real(r8), intent(in) :: shflx(pcols) ! Surface sensible heat flux [ w/m2 ] real(r8), intent(in) :: cflx(pcols,pcnst) ! Surface constituent flux [ kg/m2/s ] real(r8), intent(in) :: ztodt ! 2 delta-t [ s ] real(r8), intent(in) :: cldn(pcols,pver) ! New stratus fraction [ fraction ] real(r8), intent(in) :: ocnfrac(pcols) ! Ocean fraction real(r8), intent(in) :: landfrac(pcols) ! Land fraction real(r8), intent(in) :: sgh(pcols) ! Standard deviation of orography [ unit ? ] ! ---------------------- ! ! Input-Output Auguments ! ! ---------------------- ! type(physics_ptend), intent(inout) :: ptend ! Individual parameterization tendencies type(pbuf_fld), intent(inout), dimension(pbuf_size_max) :: pbuf ! Physics buffer ! ---------------- ! ! Output Auguments ! ! ---------------- ! real(r8), intent(out) :: pblh(pcols) ! Planetary boundary layer height [ m ] real(r8), intent(out) :: tpert(pcols) ! Convective temperature excess [ K ] real(r8), intent(out) :: qpert(pcols) ! Convective humidity excess [ kg/kg ] real(r8), intent(out) :: ustar(pcols) ! Surface friction velocity [ m/s ] real(r8), intent(out) :: obklen(pcols) ! Obukhov length [ m ] ! --------------- ! ! Local Variables ! ! --------------- ! logical :: kvinit ! Tell compute_eddy_diff/ caleddy to initialize kvh, kvm (uses kvf) character(128) :: errstring ! Error status for compute_vdiff real(r8), pointer, dimension(:,:) :: qrl ! LW radiative cooling rate real(r8), pointer, dimension(:,:) :: wsedl ! Sedimentation velocity of stratiform liquid cloud droplet [ m/s ] integer :: lchnk ! Chunk identifier integer :: ncol ! Number of atmospheric columns integer :: i, k, m ! Longitude, level, constituent indices integer :: time_index ! Time level index for physics buffer access real(r8) :: dtk(pcols,pver) ! T tendency from KE dissipation real(r8) :: tke(pcols,pverp) ! Turbulent kinetic energy [ m2/s2 ] real(r8) :: turbtype(pcols,pverp) ! Turbulent interface types [ no unit ] real(r8) :: smaw(pcols,pverp) ! Normalized Galperin instability function ( 0<= <=4.964 and 1 at neutral ) real(r8) :: cgs(pcols,pverp) ! Counter-gradient star [ cg/flux ] real(r8) :: cgh(pcols,pverp) ! Counter-gradient term for heat real(r8) :: rztodt ! 1./ztodt [ 1/s ] real(r8) :: ksrftms(pcols) ! Turbulent mountain stress surface drag coefficient [ kg/s/m2 ] real(r8) :: tautmsx(pcols) ! U component of turbulent mountain stress [ N/m2 ] real(r8) :: tautmsy(pcols) ! V component of turbulent mountain stress [ N/m2 ] real(r8) :: tautotx(pcols) ! U component of total surface stress [ N/m2 ] real(r8) :: tautoty(pcols) ! V component of total surface stress [ N/m2 ] real(r8) :: kvh(pcols,pverp) ! Eddy diffusivity for heat [ m2/s ] real(r8) :: kvm(pcols,pverp) ! Eddy diffusivity for momentum [ m2/s ] real(r8) :: kvq(pcols,pverp) ! Eddy diffusivity for constituents [ m2/s ] real(r8) :: kvh_in(pcols,pverp) ! kvh from previous timestep [ m2/s ] real(r8) :: kvm_in(pcols,pverp) ! kvm from previous timestep [ m2/s ] real(r8) :: bprod(pcols,pverp) ! Buoyancy production of tke [ m2/s3 ] real(r8) :: sprod(pcols,pverp) ! Shear production of tke [ m2/s3 ] real(r8) :: sfi(pcols,pverp) ! Saturation fraction at interfaces [ fraction ] real(r8) :: sl(pcols,pver) real(r8) :: qt(pcols,pver) real(r8) :: slv(pcols,pver) real(r8) :: sl_prePBL(pcols,pver) real(r8) :: qt_prePBL(pcols,pver) real(r8) :: slv_prePBL(pcols,pver) real(r8) :: slten(pcols,pver) real(r8) :: qtten(pcols,pver) real(r8) :: slvten(pcols,pver) real(r8) :: slflx(pcols,pverp) real(r8) :: qtflx(pcols,pverp) real(r8) :: uflx(pcols,pverp) real(r8) :: vflx(pcols,pverp) real(r8) :: slflx_cg(pcols,pverp) real(r8) :: qtflx_cg(pcols,pverp) real(r8) :: uflx_cg(pcols,pverp) real(r8) :: vflx_cg(pcols,pverp) real(r8) :: th(pcols,pver) ! Potential temperature real(r8) :: topflx(pcols) ! Molecular heat flux at top interface real(r8) :: wpert(pcols) ! Turbulent wind gusts real(r8) :: rhoair real(r8) :: ftem(pcols,pver) ! Saturation vapor pressure before PBL real(r8) :: ftem_prePBL(pcols,pver) ! Saturation vapor pressure before PBL real(r8) :: ftem_aftPBL(pcols,pver) ! Saturation vapor pressure after PBL real(r8) :: tem2(pcols,pver) ! Saturation specific humidity and RH real(r8) :: t_aftPBL(pcols,pver) ! Temperature after PBL diffusion real(r8) :: tten(pcols,pver) ! Temperature tendency by PBL diffusion real(r8) :: rhten(pcols,pver) ! RH tendency by PBL diffusion real(r8) :: qv_aft_PBL(pcols,pver) ! qv after PBL diffusion real(r8) :: ql_aft_PBL(pcols,pver) ! ql after PBL diffusion real(r8) :: qi_aft_PBL(pcols,pver) ! qi after PBL diffusion real(r8) :: s_aft_PBL(pcols,pver) ! s after PBL diffusion real(r8) :: u_aft_PBL(pcols,pver) ! u after PBL diffusion real(r8) :: v_aft_PBL(pcols,pver) ! v after PBL diffusion real(r8) :: qv_pro(pcols,pver) real(r8) :: ql_pro(pcols,pver) real(r8) :: qi_pro(pcols,pver) real(r8) :: s_pro(pcols,pver) real(r8) :: t_pro(pcols,pver) real(r8) :: tauresx(pcols) ! Residual stress to be added in vdiff to correct real(r8) :: tauresy(pcols) ! for turb stress mismatch between sfc and atm accumulated. real(r8) :: ipbl(pcols) real(r8) :: kpblh(pcols) real(r8) :: wstarPBL(pcols) real(r8) :: tpertPBL(pcols) real(r8) :: qpertPBL(pcols) #ifdef MODAL_AERO real(r8) :: tmp1(pcols) ! Temporary storage integer :: l, lspec #endif ! ----------------------- ! ! Main Computation Begins ! ! ----------------------- ! rztodt = 1._r8 / ztodt lchnk = state%lchnk ncol = state%ncol ! Retrieve 'tauresx, tauresy' from physics buffer from the last timestep time_index = pbuf_old_tim_idx() if( is_first_step() ) then tauresx(:ncol) = 0._r8 tauresy(:ncol) = 0._r8 else tauresx(:ncol) = pbuf(tauresx_idx)%fld_ptr(1,1:ncol,1,lchnk,time_index) tauresy(:ncol) = pbuf(tauresy_idx)%fld_ptr(1,1:ncol,1,lchnk,time_index) endif ! All variables are modified by vertical diffusion ptend%name = "vertical diffusion" ptend%lq(:) = .TRUE. ptend%ls = .TRUE. ptend%lu = .TRUE. ptend%lv = .TRUE. ! ---------------------------------------- ! ! Computation of turbulent mountain stress ! ! ---------------------------------------- ! ! Consistent with the computation of 'normal' drag coefficient, we are using ! the raw input (u,v) to compute 'ksrftms', not the provisionally-marched 'u,v' ! within the iteration loop of the PBL scheme. if( do_tms ) then call compute_tms( pcols , pver , ncol , & state%u , state%v , state%t , state%pmid , & state%exner, state%zm , sgh , ksrftms , & tautmsx , tautmsy , landfrac ) ! Here, both 'taux, tautmsx' are explicit surface stresses. ! Note that this 'tautotx, tautoty' are different from the total stress ! that has been actually added into the atmosphere. This is because both ! taux and tautmsx are fully implicitly treated within compute_vdiff. ! However, 'tautotx, tautoty' are not used in the actual numerical ! computation in this module. tautotx(:ncol) = taux(:ncol) + tautmsx(:ncol) tautoty(:ncol) = tauy(:ncol) + tautmsy(:ncol) else ksrftms(:ncol) = 0._r8 tautotx(:ncol) = taux(:ncol) tautoty(:ncol) = tauy(:ncol) endif !----------------------------------------------------------------------- ! ! Computation of eddy diffusivities - Select appropriate PBL scheme ! !----------------------------------------------------------------------- ! select case (eddy_scheme) case ( 'diag_TKE' ) ! ---------------------------------------------------------------- ! ! At first time step, have eddy_diff.F90:caleddy() use kvh=kvm=kvf ! ! This has to be done in compute_eddy_diff after kvf is calculated ! ! ---------------------------------------------------------------- ! if( is_first_step() ) then kvinit = .true. else kvinit = .false. endif ! ---------------------------------------------- ! ! Get LW radiative heating out of physics buffer ! ! ---------------------------------------------- ! qrl => pbuf(pbuf_get_fld_idx('QRL'))%fld_ptr(1,1:pcols,1:pver,lchnk,1) wsedl => pbuf(pbuf_get_fld_idx('WSEDL'))%fld_ptr(1,1:pcols,1:pver,lchnk,1) ! Retrieve eddy diffusivities for heat and momentum from physics buffer ! from last timestep ( if first timestep, has been initialized by inidat.F90 ) time_index = pbuf_old_tim_idx() kvm_in(:ncol,:) = pbuf(kvm_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,time_index) kvh_in(:ncol,:) = pbuf(kvh_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,time_index) call compute_eddy_diff( lchnk , & pcols , pver , ncol , state%t , state%q(:,:,1) , ztodt , & state%q(:,:,ixcldliq) , state%q(:,:,ixcldice) , & state%s , state%rpdel , cldn , qrl , wsedl , & state%zm , state%zi , state%pmid , state%pint , state%u , state%v , & taux , tauy , shflx , cflx(:,1) , wstarent , nturb , & ustar , pblh , kvm_in , kvh_in , kvm , kvh , & kvq , cgh , & cgs , tpert , qpert , wpert , tke , bprod , & sprod , sfi , fqsatd , kvinit , & tauresx , tauresy , ksrftms , & ipbl(:) , kpblh(:) , wstarPBL(:), turbtype , smaw ) obklen(:ncol) = 0._r8 ! ----------------------------------------------- ! ! Store TKE in pbuf for use by shallow convection ! ! ----------------------------------------------- ! tpertPBL(:ncol) = tpert(:ncol) qpertPBL(:ncol) = qpert(:ncol) pbuf(tke_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,time_index) = tke(:ncol,:) pbuf(turbtype_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,time_index) = turbtype(:ncol,:) pbuf(smaw_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,time_index) = smaw(:ncol,:) ! Store updated kvh, kvm in pbuf to use here on the next timestep pbuf(kvh_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,time_index) = kvh(:ncol,:) pbuf(kvm_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,time_index) = kvm(:ncol,:) if( is_first_step() ) then do i = 1, pbuf_times pbuf(kvh_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,i) = kvh(:ncol,:) pbuf(kvm_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,i) = kvm(:ncol,:) enddo endif ! Write out fields that are only used by this scheme call outfld( 'BPROD ', bprod(1,1), pcols, lchnk ) call outfld( 'SPROD ', sprod(1,1), pcols, lchnk ) call outfld( 'SFI ', sfi, pcols, lchnk ) case ( 'HB', 'HBR' ) ! Modification : We may need to use 'taux' instead of 'tautotx' here, for ! consistency with the previous HB scheme. th(:ncol,:pver) = state%t(:ncol,:pver) * state%exner(:ncol,:pver) call compute_hb_diff( lchnk , ncol , & th , state%t , state%q , state%zm , state%zi, & state%pmid, state%u , state%v , tautotx , tautoty , & shflx , cflx , obklen , ustar , pblh , & kvm , kvh , kvq , cgh , cgs , & tpert , qpert , cldn , ocnfrac , tke , & eddy_scheme ) wpert = 0._r8 ! Save kvh in physics buffer, used by gw_intr from tphysac time_index = pbuf_old_tim_idx() pbuf(kvm_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,time_index) = kvm(:ncol,:) pbuf(kvh_idx)%fld_ptr(1,1:ncol,1:pverp,lchnk,time_index) = kvh(:ncol,:) turbtype(:,:) = 0._r8 smaw(:,:) = 0._r8 end select pbuf(wgustd_index)%fld_ptr(1,1:ncol,1,lchnk,1) = wpert(:ncol) call outfld( 'WGUSTD' , wpert, pcols, lchnk ) !------------------------------------ ! ! Application of diffusivities ! !------------------------------------ ! ptend%q(:ncol,:,:) = state%q(:ncol,:,:) ptend%s(:ncol,:) = state%s(:ncol,:) ptend%u(:ncol,:) = state%u(:ncol,:) ptend%v(:ncol,:) = state%v(:ncol,:) !------------------------------------------------------ ! ! Write profile output before applying diffusion scheme ! !------------------------------------------------------ ! sl_prePBL(:ncol,:pver) = ptend%s(:ncol,:pver) - latvap * ptend%q(:ncol,:pver,ixcldliq) & - ( latvap + latice) * ptend%q(:ncol,:pver,ixcldice) qt_prePBL(:ncol,:pver) = ptend%q(:ncol,:pver,1) + ptend%q(:ncol,:pver,ixcldliq) & + ptend%q(:ncol,:pver,ixcldice) slv_prePBL(:ncol,:pver) = sl_prePBL(:ncol,:pver) * ( 1._r8 + zvir*qt_prePBL(:ncol,:pver) ) call aqsat( state%t, state%pmid, tem2, ftem, pcols, ncol, pver, 1, pver ) ftem_prePBL(:ncol,:) = state%q(:ncol,:,1)/ftem(:ncol,:)*100._r8 call outfld( 'qt_pre_PBL ', qt_prePBL, pcols, lchnk ) call outfld( 'sl_pre_PBL ', sl_prePBL, pcols, lchnk ) call outfld( 'slv_pre_PBL ', slv_prePBL, pcols, lchnk ) call outfld( 'u_pre_PBL ', state%u, pcols, lchnk ) call outfld( 'v_pre_PBL ', state%v, pcols, lchnk ) call outfld( 'qv_pre_PBL ', state%q(:ncol,:,1), pcols, lchnk ) call outfld( 'ql_pre_PBL ', state%q(:ncol,:,ixcldliq), pcols, lchnk ) call outfld( 'qi_pre_PBL ', state%q(:ncol,:,ixcldice), pcols, lchnk ) call outfld( 't_pre_PBL ', state%t, pcols, lchnk ) call outfld( 'rh_pre_PBL ', ftem_prePBL, pcols, lchnk ) ! --------------------------------------------------------------------------------- ! ! Call the diffusivity solver and solve diffusion equation ! ! The final two arguments are optional function references to ! ! constituent-independent and constituent-dependent moleculuar diffusivity routines ! ! --------------------------------------------------------------------------------- ! ! Modification : We may need to output 'tautotx_im,tautoty_im' from below 'compute_vdiff' and ! separately print out as diagnostic output, because these are different from ! the explicit 'tautotx, tautoty' computed above. ! Note that the output 'tauresx,tauresy' from below subroutines are fully implicit ones. if( any(fieldlist_wet) ) then call compute_vdiff( state%lchnk , & pcols , pver , pcnst , ncol , state%pmid , & state%pint , state%rpdel , state%t , ztodt , taux , & tauy , shflx , cflx , ntop , nbot , & kvh , kvm , kvq , cgs , cgh , & state%zi , ksrftms , qmincg , fieldlist_wet , & ptend%u , ptend%v , ptend%q , ptend%s , & tautmsx , tautmsy , dtk , topflx , errstring , & tauresx , tauresy , 1 , & do_molec_diff , compute_molec_diff , vd_lu_qdecomp ) end if if( errstring .ne. '' ) call endrun(errstring) if( any( fieldlist_dry ) ) then if( do_molec_diff ) then errstring = "Design flaw: dry vdiff not currently supported with molecular diffusion" call endrun(errstring) end if call compute_vdiff( state%lchnk , & pcols , pver , pcnst , ncol , state%pmiddry , & state%pintdry , state%rpdeldry , state%t , ztodt , taux , & tauy , shflx , cflx , ntop , nbot , & kvh , kvm , kvq , cgs , cgh , & state%zi , ksrftms , qmincg , fieldlist_dry , & ptend%u , ptend%v , ptend%q , ptend%s , & tautmsx , tautmsy , dtk , topflx , errstring , & tauresx , tauresy , 1 , & do_molec_diff , compute_molec_diff , vd_lu_qdecomp ) if( errstring .ne. '' ) call endrun(errstring) end if ! Store updated tauresx, tauresy in pbuf to use here on the next timestep pbuf(tauresx_idx)%fld_ptr(1,1:ncol,1,lchnk,time_index) = tauresx(:ncol) pbuf(tauresy_idx)%fld_ptr(1,1:ncol,1,lchnk,time_index) = tauresy(:ncol) if( is_first_step() ) then do i = 1, pbuf_times pbuf(tauresx_idx)%fld_ptr(1,1:ncol,1,lchnk,i) = tauresx(:ncol) pbuf(tauresy_idx)%fld_ptr(1,1:ncol,1,lchnk,i) = tauresy(:ncol) end do end if #ifdef MODAL_AERO ! Add the explicit surface fluxes to the lowest layer ! Modification : I should check whether this explicit adding is consistent with ! the treatment of other tracers. tmp1(:ncol) = ztodt * gravit * state%rpdel(:ncol,pver) do m = 1, ntot_amode l = numptr_amode(m) ptend%q(:ncol,pver,l) = ptend%q(:ncol,pver,l) + tmp1(:ncol) * cflx(:ncol,l) do lspec = 1, nspec_amode(m) l = lmassptr_amode(lspec,m) ptend%q(:ncol,pver,l) = ptend%q(:ncol,pver,l) + tmp1(:ncol) * cflx(:ncol,l) enddo enddo #endif ! -------------------------------------------------------- ! ! Diagnostics and output writing after applying PBL scheme ! ! -------------------------------------------------------- ! sl(:ncol,:pver) = ptend%s(:ncol,:pver) - latvap * ptend%q(:ncol,:pver,ixcldliq) & - ( latvap + latice) * ptend%q(:ncol,:pver,ixcldice) qt(:ncol,:pver) = ptend%q(:ncol,:pver,1) + ptend%q(:ncol,:pver,ixcldliq) & + ptend%q(:ncol,:pver,ixcldice) slv(:ncol,:pver) = sl(:ncol,:pver) * ( 1._r8 + zvir*qt(:ncol,:pver) ) slflx(:ncol,1) = 0._r8 qtflx(:ncol,1) = 0._r8 uflx(:ncol,1) = 0._r8 vflx(:ncol,1) = 0._r8 slflx_cg(:ncol,1) = 0._r8 qtflx_cg(:ncol,1) = 0._r8 uflx_cg(:ncol,1) = 0._r8 vflx_cg(:ncol,1) = 0._r8 do k = 2, pver do i = 1, ncol rhoair = state%pint(i,k) / ( rair * ( ( 0.5*(slv(i,k)+slv(i,k-1)) - gravit*state%zi(i,k))/cpair ) ) slflx(i,k) = kvh(i,k) * & ( - rhoair*(sl(i,k-1)-sl(i,k))/(state%zm(i,k-1)-state%zm(i,k)) & + cgh(i,k) ) qtflx(i,k) = kvh(i,k) * & ( - rhoair*(qt(i,k-1)-qt(i,k))/(state%zm(i,k-1)-state%zm(i,k)) & + rhoair*(cflx(i,1)+cflx(i,2)+cflx(i,3))*cgs(i,k) ) uflx(i,k) = kvm(i,k) * & ( - rhoair*(ptend%u(i,k-1)-ptend%u(i,k))/(state%zm(i,k-1)-state%zm(i,k))) vflx(i,k) = kvm(i,k) * & ( - rhoair*(ptend%v(i,k-1)-ptend%v(i,k))/(state%zm(i,k-1)-state%zm(i,k))) slflx_cg(i,k) = kvh(i,k) * cgh(i,k) qtflx_cg(i,k) = kvh(i,k) * rhoair * ( cflx(i,1) + cflx(i,2) + cflx(i,3) ) * cgs(i,k) uflx_cg(i,k) = 0._r8 vflx_cg(i,k) = 0._r8 end do end do ! Modification : I should check whether slflx(:ncol,pverp) is correctly computed. ! Note also that 'tautotx' is explicit total stress, different from ! the ones that have been actually added into the atmosphere. slflx(:ncol,pverp) = shflx(:ncol) qtflx(:ncol,pverp) = cflx(:ncol,1) uflx(:ncol,pverp) = tautotx(:ncol) vflx(:ncol,pverp) = tautoty(:ncol) slflx_cg(:ncol,pverp) = 0._r8 qtflx_cg(:ncol,pverp) = 0._r8 uflx_cg(:ncol,pverp) = 0._r8 vflx_cg(:ncol,pverp) = 0._r8 ! --------------------------------------------------------------- ! ! Convert the new profiles into vertical diffusion tendencies. ! ! Convert KE dissipative heat change into "temperature" tendency. ! ! --------------------------------------------------------------- ! ptend%s(:ncol,:) = ( ptend%s(:ncol,:) - state%s(:ncol,:) ) * rztodt ptend%u(:ncol,:) = ( ptend%u(:ncol,:) - state%u(:ncol,:) ) * rztodt ptend%v(:ncol,:) = ( ptend%v(:ncol,:) - state%v(:ncol,:) ) * rztodt ptend%q(:ncol,:pver,:) = ( ptend%q(:ncol,:pver,:) - state%q(:ncol,:pver,:) ) * rztodt slten(:ncol,:) = ( sl(:ncol,:) - sl_prePBL(:ncol,:) ) * rztodt qtten(:ncol,:) = ( qt(:ncol,:) - qt_prePBL(:ncol,:) ) * rztodt ! ----------------------------------------------------------- ! ! In order to perform 'pseudo-conservative varible diffusion' ! ! perform the following two stages: ! ! ! ! I. Re-set (1) 'qvten' by 'qtten', and 'qlten = qiten = 0' ! ! (2) 'sten' by 'slten', and ! ! (3) 'qlten = qiten = 0' ! ! ! ! II. Apply 'positive_moisture' ! ! ! ! ----------------------------------------------------------- ! if( eddy_scheme .eq. 'diag_TKE' .and. do_pseudocon_diff ) then ptend%q(:ncol,:pver,1) = qtten(:ncol,:pver) ptend%s(:ncol,:pver) = slten(:ncol,:pver) ptend%q(:ncol,:pver,ixcldliq) = 0._r8 ptend%q(:ncol,:pver,ixcldice) = 0._r8 ptend%q(:ncol,:pver,ixnumliq) = 0._r8 ptend%q(:ncol,:pver,ixnumice) = 0._r8 do i = 1, ncol do k = 1, pver qv_pro(i,k) = state%q(i,k,1) + ptend%q(i,k,1) * ztodt ql_pro(i,k) = state%q(i,k,ixcldliq) + ptend%q(i,k,ixcldliq) * ztodt qi_pro(i,k) = state%q(i,k,ixcldice) + ptend%q(i,k,ixcldice) * ztodt s_pro(i,k) = state%s(i,k) + ptend%s(i,k) * ztodt t_pro(i,k) = state%t(i,k) + (1._r8/cpair)*ptend%s(i,k) * ztodt end do end do call positive_moisture( cpair, latvap, latvap+latice, ncol, pver, ztodt, qmin(1), qmin(2), qmin(3), & state%pdel(:ncol,pver:1:-1), qv_pro(:ncol,pver:1:-1), ql_pro(:ncol,pver:1:-1), & qi_pro(:ncol,pver:1:-1), t_pro(:ncol,pver:1:-1), s_pro(:ncol,pver:1:-1), & ptend%q(:ncol,pver:1:-1,1), ptend%q(:ncol,pver:1:-1,ixcldliq), & ptend%q(:ncol,pver:1:-1,ixcldice), ptend%s(:ncol,pver:1:-1) ) end if ! ----------------------------------------------------------------- ! ! Re-calculate diagnostic output variables after vertical diffusion ! ! ----------------------------------------------------------------- ! qv_aft_PBL(:ncol,:pver) = state%q(:ncol,:pver,1) + ptend%q(:ncol,:pver,1) * ztodt ql_aft_PBL(:ncol,:pver) = state%q(:ncol,:pver,ixcldliq) + ptend%q(:ncol,:pver,ixcldliq) * ztodt qi_aft_PBL(:ncol,:pver) = state%q(:ncol,:pver,ixcldice) + ptend%q(:ncol,:pver,ixcldice) * ztodt s_aft_PBL(:ncol,:pver) = state%s(:ncol,:pver) + ptend%s(:ncol,:pver) * ztodt t_aftPBL(:ncol,:pver) = ( s_aft_PBL(:ncol,:pver) - gravit*state%zm(:ncol,:pver) ) / cpair u_aft_PBL(:ncol,:pver) = state%u(:ncol,:pver) + ptend%u(:ncol,:pver) * ztodt v_aft_PBL(:ncol,:pver) = state%v(:ncol,:pver) + ptend%v(:ncol,:pver) * ztodt call aqsat( t_aftPBL, state%pmid, tem2, ftem, pcols, ncol, pver, 1, pver ) ftem_aftPBL(:ncol,:pver) = qv_aft_PBL(:ncol,:pver) / ftem(:ncol,:pver) * 100._r8 tten(:ncol,:pver) = ( t_aftPBL(:ncol,:pver) - state%t(:ncol,:pver) ) * rztodt rhten(:ncol,:pver) = ( ftem_aftPBL(:ncol,:pver) - ftem_prePBL(:ncol,:pver) ) * rztodt ! -------------------------------------------------------------- ! ! Writing state variables after PBL scheme for detailed analysis ! ! -------------------------------------------------------------- ! call outfld( 'sl_aft_PBL' , sl, pcols, lchnk ) call outfld( 'qt_aft_PBL' , qt, pcols, lchnk ) call outfld( 'slv_aft_PBL' , slv, pcols, lchnk ) call outfld( 'u_aft_PBL' , u_aft_PBL, pcols, lchnk ) call outfld( 'v_aft_PBL' , v_aft_PBL, pcols, lchnk ) call outfld( 'qv_aft_PBL' , qv_aft_PBL, pcols, lchnk ) call outfld( 'ql_aft_PBL' , ql_aft_PBL, pcols, lchnk ) call outfld( 'qi_aft_PBL' , qi_aft_PBL, pcols, lchnk ) call outfld( 't_aft_PBL ' , t_aftPBL, pcols, lchnk ) call outfld( 'rh_aft_PBL' , ftem_aftPBL, pcols, lchnk ) call outfld( 'slflx_PBL' , slflx, pcols, lchnk ) call outfld( 'qtflx_PBL' , qtflx, pcols, lchnk ) call outfld( 'uflx_PBL' , uflx, pcols, lchnk ) call outfld( 'vflx_PBL' , vflx, pcols, lchnk ) call outfld( 'slflx_cg_PBL' , slflx_cg, pcols, lchnk ) call outfld( 'qtflx_cg_PBL' , qtflx_cg, pcols, lchnk ) call outfld( 'uflx_cg_PBL' , uflx_cg, pcols, lchnk ) call outfld( 'vflx_cg_PBL' , vflx_cg, pcols, lchnk ) call outfld( 'slten_PBL' , slten, pcols, lchnk ) call outfld( 'qtten_PBL' , qtten, pcols, lchnk ) call outfld( 'uten_PBL' , ptend%u(:ncol,:), pcols, lchnk ) call outfld( 'vten_PBL' , ptend%v(:ncol,:), pcols, lchnk ) call outfld( 'qvten_PBL' , ptend%q(:ncol,:,1), pcols, lchnk ) call outfld( 'qlten_PBL' , ptend%q(:ncol,:,ixcldliq), pcols, lchnk ) call outfld( 'qiten_PBL' , ptend%q(:ncol,:,ixcldice), pcols, lchnk ) call outfld( 'tten_PBL' , tten, pcols, lchnk ) call outfld( 'rhten_PBL' , rhten, pcols, lchnk ) ! ------------------------------------------- ! ! Writing the other standard output variables ! ! ------------------------------------------- ! call outfld( 'QT' , qt, pcols, lchnk ) call outfld( 'SL' , sl, pcols, lchnk ) call outfld( 'SLV' , slv, pcols, lchnk ) call outfld( 'SLFLX' , slflx, pcols, lchnk ) call outfld( 'QTFLX' , qtflx, pcols, lchnk ) call outfld( 'UFLX' , uflx, pcols, lchnk ) call outfld( 'VFLX' , vflx, pcols, lchnk ) call outfld( 'TKE' , tke, pcols, lchnk ) call outfld( 'PBLH' , pblh, pcols, lchnk ) call outfld( 'TPERT' , tpert, pcols, lchnk ) call outfld( 'QPERT' , qpert, pcols, lchnk ) call outfld( 'USTAR' , ustar, pcols, lchnk ) call outfld( 'KVH' , kvh, pcols, lchnk ) call outfld( 'KVM' , kvm, pcols, lchnk ) call outfld( 'CGS' , cgs, pcols, lchnk ) dtk(:ncol,:) = dtk(:ncol,:) / cpair ! Normalize heating for history call outfld( 'DTVKE' , dtk, pcols, lchnk ) dtk(:ncol,:) = ptend%s(:ncol,:) / cpair ! Normalize heating for history using dtk call outfld( 'DTV' , dtk, pcols, lchnk ) call outfld( 'DUV' , ptend%u, pcols, lchnk ) call outfld( 'DVV' , ptend%v, pcols, lchnk ) do m = 1, pcnst call outfld( vdiffnam(m) , ptend%q(1,1,m), pcols, lchnk ) end do if( do_tms ) then ! Here, 'tautmsx,tautmsy' are implicit 'tms' that have been actually ! added into the atmosphere. call outfld( 'TAUTMSX' , tautmsx, pcols, lchnk ) call outfld( 'TAUTMSY' , tautmsy, pcols, lchnk ) end if if( do_molec_diff ) then call outfld( 'TTPXMLC' , topflx, pcols, lchnk ) end if return end subroutine vertical_diffusion_tend ! =============================================================================== ! ! ! ! =============================================================================== ! subroutine positive_moisture( cp, xlv, xls, ncol, mkx, dt, qvmin, qlmin, qimin, & 4 dp, qv, ql, qi, t, s, qvten, qlten, qiten, sten ) ! ------------------------------------------------------------------------------- ! ! If any 'ql < qlmin, qi < qimin, qv < qvmin' are developed in any layer, ! ! force them to be larger than minimum value by (1) condensating water vapor ! ! into liquid or ice, and (2) by transporting water vapor from the very lower ! ! layer. '2._r8' is multiplied to the minimum values for safety. ! ! Update final state variables and tendencies associated with this correction. ! ! If any condensation happens, update (s,t) too. ! ! Note that (qv,ql,qi,t,s) are final state variables after applying corresponding ! ! input tendencies. ! ! Be careful the order of k : '1': near-surface layer, 'mkx' : top layer ! ! ------------------------------------------------------------------------------- ! implicit none integer, intent(in) :: ncol, mkx real(r8), intent(in) :: cp, xlv, xls real(r8), intent(in) :: dt, qvmin, qlmin, qimin real(r8), intent(in) :: dp(ncol,mkx) real(r8), intent(inout) :: qv(ncol,mkx), ql(ncol,mkx), qi(ncol,mkx), t(ncol,mkx), s(ncol,mkx) real(r8), intent(inout) :: qvten(ncol,mkx), qlten(ncol,mkx), qiten(ncol,mkx), sten(ncol,mkx) integer i, k real(r8) dql, dqi, dqv, sum, aa, dum ! Modification : I should check whether this is exactly same as the one used in ! shallow convection and cloud macrophysics. do i = 1, ncol do k = mkx, 1, -1 ! From the top to the 1st (lowest) layer from the surface dql = max(0._r8,1._r8*qlmin-ql(i,k)) dqi = max(0._r8,1._r8*qimin-qi(i,k)) qlten(i,k) = qlten(i,k) + dql/dt qiten(i,k) = qiten(i,k) + dqi/dt qvten(i,k) = qvten(i,k) - (dql+dqi)/dt sten(i,k) = sten(i,k) + xlv * (dql/dt) + xls * (dqi/dt) ql(i,k) = ql(i,k) + dql qi(i,k) = qi(i,k) + dqi qv(i,k) = qv(i,k) - dql - dqi s(i,k) = s(i,k) + xlv * dql + xls * dqi t(i,k) = t(i,k) + (xlv * dql + xls * dqi)/cp dqv = max(0._r8,1._r8*qvmin-qv(i,k)) qvten(i,k) = qvten(i,k) + dqv/dt qv(i,k) = qv(i,k) + dqv if( k .ne. 1 ) then qv(i,k-1) = qv(i,k-1) - dqv*dp(i,k)/dp(i,k-1) qvten(i,k-1) = qvten(i,k-1) - dqv*dp(i,k)/dp(i,k-1)/dt endif qv(i,k) = max(qv(i,k),qvmin) ql(i,k) = max(ql(i,k),qlmin) qi(i,k) = max(qi(i,k),qimin) end do ! Extra moisture used to satisfy 'qv(i,1)=qvmin' is proportionally ! extracted from all the layers that has 'qv > 2*qvmin'. This fully ! preserves column moisture. if( dqv .gt. 1.e-20_r8 ) then sum = 0._r8 do k = 1, mkx if( qv(i,k) .gt. 2._r8*qvmin ) sum = sum + qv(i,k)*dp(i,k) enddo aa = dqv*dp(i,1)/max(1.e-20_r8,sum) if( aa .lt. 0.5_r8 ) then do k = 1, mkx if( qv(i,k) .gt. 2._r8*qvmin ) then dum = aa*qv(i,k) qv(i,k) = qv(i,k) - dum qvten(i,k) = qvten(i,k) - dum/dt endif enddo else write(iulog,*) 'Full positive_moisture is impossible in vertical_diffusion' endif endif end do return end subroutine positive_moisture end module vertical_diffusion