!*********************************************************************** !* GNU Lesser General Public License !* !* This file is part of the FV3 dynamical core. !* !* The FV3 dynamical core is free software: you can redistribute it !* and/or modify it under the terms of the !* GNU Lesser General Public License as published by the !* Free Software Foundation, either version 3 of the License, or !* (at your option) any later version. !* !* The FV3 dynamical core is distributed in the hope that it will be !* useful, but WITHOUT ANYWARRANTY; without even the implied warranty !* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. !* See the GNU General Public License for more details. !* !* You should have received a copy of the GNU Lesser General Public !* License along with the FV3 dynamical core. !* If not, see . !*********************************************************************** !>@brief The module 'fv_dynamics' is the top-level routine for the dynamical core. !>@details It executes on the dt_atmos (or p_split) loop. module fv_dynamics_mod ! Modules Included: ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
Module NameFunctions Included
boundary_modnested_grid_BC_apply_intT
constants_modgrav, pi=>pi_8, radius, hlv, rdgas, omega, rvgas, cp_vapor
diag_manager_modsend_data
dyn_core_moddyn_core, del2_cubed, init_ijk_mem
field_manager_modMODEL_ATMOS
fv_arrays_modfv_grid_type, fv_flags_type, fv_atmos_type, fv_nest_type, fv_diag_type, fv_grid_bounds_type
fv_diagnostics_modfv_time, prt_mxm, range_check, prt_minmax
fv_fill_modfill2D
fv_grid_utils_modcubed_to_latlon, c2l_ord2, g_sum
fv_mapz_modcompute_total_energy, Lagrangian_to_Eulerian, moist_cv, moist_cp
fv_mp_modis_master,group_halo_update_type, start_group_halo_update, complete_group_halo_update
fv_nesting_modsetup_nested_grid_BCs
fv_nwp_moddo_adiabatic_init
fv_restart_modd2a_setup, d2c_setup
fv_sg_modneg_adj3
fv_sg_modneg_adj2
fv_sg_modneg_adj4
fv_timing_modtiming_on, timing_off
fv_tracer2d_modtracer_2d, tracer_2d_1L, tracer_2d_nested
mpp_mod/td> ! mpp_pe
mpp_domains_mod/td> ! DGRID_NE, CGRID_NE, mpp_update_domains, domain2D
fv_sg_modneg_adj3
fv_sg_modneg_adj4
tracer_manager_modget_tracer_index
#ifdef OVERLOAD_R4 use constantsR4_mod, only: grav, pi=>pi_8, radius, hlv, rdgas, omega, rvgas, cp_vapor #else use constants_mod, only: grav, pi=>pi_8, radius, hlv, rdgas, omega, rvgas, cp_vapor #endif use dyn_core_mod, only: dyn_core, del2_cubed, init_ijk_mem use fv_mapz_mod, only: compute_total_energy, Lagrangian_to_Eulerian, moist_cv, moist_cp use fv_tracer2d_mod, only: tracer_2d, tracer_2d_1L, tracer_2d_nested use fv_grid_utils_mod, only: cubed_to_latlon, c2l_ord2, g_sum use fv_fill_mod, only: fill2D use fv_mp_mod, only: is_master use fv_mp_mod, only: group_halo_update_type use fv_mp_mod, only: start_group_halo_update, complete_group_halo_update use fv_timing_mod, only: timing_on, timing_off use diag_manager_mod, only: send_data use fv_diagnostics_mod, only: fv_time, prt_mxm, range_check, prt_minmax use mpp_domains_mod, only: DGRID_NE, CGRID_NE, mpp_update_domains, domain2D use mpp_mod, only: mpp_pe use field_manager_mod, only: MODEL_ATMOS use tracer_manager_mod, only: get_tracer_index use fv_sg_mod, only: neg_adj3, neg_adj2, neg_adj4 use fv_nesting_mod, only: setup_nested_grid_BCs use fv_regional_mod, only: regional_boundary_update, set_regional_BCs use fv_regional_mod, only: dump_field, H_STAGGER, U_STAGGER, V_STAGGER use fv_regional_mod, only: a_step, p_step, k_step use fv_regional_mod, only: current_time_in_seconds use boundary_mod, only: nested_grid_BC_apply_intT use fv_arrays_mod, only: fv_grid_type, fv_flags_type, fv_atmos_type, fv_nest_type, fv_diag_type, fv_grid_bounds_type, inline_mp_type use fv_nwp_nudge_mod, only: do_adiabatic_init use time_manager_mod, only: get_time #ifdef MULTI_GASES use multi_gases_mod, only: virq, vicpq, virqd, vicpqd #endif implicit none logical :: RF_initialized = .false. logical :: pt_initialized = .false. logical :: bad_range = .false. real, allocatable :: rf(:) real, allocatable :: rw(:) !< related to tau_w integer :: kmax=1 integer :: k_rf = 0 real :: agrav #ifdef HIWPP real, allocatable:: u00(:,:,:), v00(:,:,:) #endif private public :: fv_dynamics contains !----------------------------------------------------------------------- ! fv_dynamics :: FV dynamical core driver !----------------------------------------------------------------------- subroutine fv_dynamics(npx, npy, npz, nq_tot, ng, bdt, consv_te, fill, & reproduce_sum, kappa, cp_air, zvir, ptop, ks, ncnst, n_split, & q_split, u, v, w, delz, hydrostatic, pt, delp, q, & ps, pe, pk, peln, pkz, phis, q_con, omga, ua, va, uc, vc, & ak, bk, mfx, mfy, cx, cy, ze0, hybrid_z, & gridstruct, flagstruct, neststruct, idiag, bd, & parent_grid, domain, diss_est, inline_mp) use mpp_mod, only: FATAL, mpp_error use ccpp_static_api, only: ccpp_physics_timestep_init, & ccpp_physics_timestep_finalize use CCPP_data, only: ccpp_suite use CCPP_data, only: cdata => cdata_tile use CCPP_data, only: GFDL_interstitial use molecular_diffusion_mod, only: md_time, md_wait_sec, md_tadj_layers, & thermosphere_adjustment real, intent(IN) :: bdt !< Large time-step real, intent(IN) :: consv_te real, intent(IN) :: kappa, cp_air real, intent(IN) :: zvir, ptop integer, intent(IN) :: npx integer, intent(IN) :: npy integer, intent(IN) :: npz integer, intent(IN) :: nq_tot !< transported tracers integer, intent(IN) :: ng integer, intent(IN) :: ks integer, intent(IN) :: ncnst integer, intent(IN) :: n_split !< small-step horizontal dynamics integer, intent(IN) :: q_split !< tracer logical, intent(IN) :: fill logical, intent(IN) :: reproduce_sum logical, intent(IN) :: hydrostatic logical, intent(IN) :: hybrid_z !< Using hybrid_z for remapping type(fv_grid_bounds_type), intent(IN) :: bd real, intent(inout), dimension(bd%isd:bd%ied ,bd%jsd:bd%jed+1,npz) :: u !< D grid zonal wind (m/s) real, intent(inout), dimension(bd%isd:bd%ied+1,bd%jsd:bd%jed ,npz) :: v !< D grid meridional wind (m/s) real, intent(inout) :: w( bd%isd: ,bd%jsd: ,1:) !< W (m/s) real, intent(inout) :: pt( bd%isd:bd%ied ,bd%jsd:bd%jed ,npz) !< temperature (K) real, intent(inout) :: delp(bd%isd:bd%ied ,bd%jsd:bd%jed ,npz) !< pressure thickness (pascal) real, intent(inout) :: q( bd%isd:bd%ied ,bd%jsd:bd%jed ,npz, ncnst) !< specific humidity and constituents real, intent(inout) :: delz(bd%is:,bd%js:,1:) !< delta-height (m); non-hydrostatic only real, intent(inout) :: ze0(bd%is:, bd%js: ,1:) !< height at edges (m); non-hydrostatic real, intent(inout), dimension(bd%isd:bd%ied ,bd%jsd:bd%jed,npz) :: diss_est! diffusion estimate for SKEB ! ze0 no longer used !----------------------------------------------------------------------- ! Auxilliary pressure arrays: ! The 5 vars below can be re-computed from delp and ptop. !----------------------------------------------------------------------- ! dyn_aux: real, intent(inout) :: ps (bd%isd:bd%ied ,bd%jsd:bd%jed) !< Surface pressure (pascal) real, intent(inout) :: pe (bd%is-1:bd%ie+1, npz+1,bd%js-1:bd%je+1) !< edge pressure (pascal) real, intent(inout) :: pk (bd%is:bd%ie,bd%js:bd%je, npz+1) !< pe**kappa real, intent(inout) :: peln(bd%is:bd%ie,npz+1,bd%js:bd%je) !< ln(pe) real, intent(inout) :: pkz (bd%is:bd%ie,bd%js:bd%je,npz) !< finite-volume mean pk real, intent(inout):: q_con(bd%isd:, bd%jsd:, 1:) !----------------------------------------------------------------------- ! Others: !----------------------------------------------------------------------- real, intent(inout) :: phis(bd%isd:bd%ied,bd%jsd:bd%jed) !< Surface geopotential (g*Z_surf) real, intent(inout) :: omga(bd%isd:bd%ied,bd%jsd:bd%jed,npz) !< Vertical pressure velocity (pa/s) real, intent(inout) :: uc(bd%isd:bd%ied+1,bd%jsd:bd%jed ,npz) !< (uc,vc) mostly used as the C grid winds real, intent(inout) :: vc(bd%isd:bd%ied ,bd%jsd:bd%jed+1,npz) real, intent(inout), dimension(bd%isd:bd%ied ,bd%jsd:bd%jed ,npz):: ua, va real, intent(in), dimension(npz+1):: ak, bk type(inline_mp_type), intent(inout) :: inline_mp ! Accumulated Mass flux arrays: the "Flux Capacitor" real, intent(inout) :: mfx(bd%is:bd%ie+1, bd%js:bd%je, npz) real, intent(inout) :: mfy(bd%is:bd%ie , bd%js:bd%je+1, npz) ! Accumulated Courant number arrays real, intent(inout) :: cx(bd%is:bd%ie+1, bd%jsd:bd%jed, npz) real, intent(inout) :: cy(bd%isd:bd%ied ,bd%js:bd%je+1, npz) type(fv_grid_type), intent(inout), target :: gridstruct type(fv_flags_type), intent(INOUT) :: flagstruct type(fv_nest_type), intent(INOUT) :: neststruct type(domain2d), intent(INOUT) :: domain type(fv_atmos_type), pointer, intent(IN) :: parent_grid type(fv_diag_type), intent(IN) :: idiag ! Local Arrays real:: ws(bd%is:bd%ie,bd%js:bd%je) real:: teq(bd%is:bd%ie,bd%js:bd%je) real:: ps2(bd%isd:bd%ied,bd%jsd:bd%jed) real:: m_fac(bd%is:bd%ie,bd%js:bd%je) real:: pfull(npz) real, dimension(bd%is:bd%ie):: cvm #ifdef MULTI_GASES real, allocatable :: kapad(:,:,:) #endif real, save :: time_offset = -1.00 real t(bd%isd:bd%ied,bd%jsd:bd%jed) real :: correct, aave_t1, ttsave, tdsave real:: akap, rdg, ph1, ph2, mdt, gam, amdt, u0 real:: recip_k_split,reg_bc_update_time integer:: kord_tracer(ncnst) integer :: i,j,k, n, iq, n_map, nq, nr, nwat, k_split integer :: sphum, liq_wat = -999, ice_wat = -999 ! GFDL physics integer :: rainwat = -999, snowwat = -999, graupel = -999, hailwat = -999, cld_amt = -999 integer :: theta_d = -999 logical used, do_omega integer, parameter :: max_packs=13 type(group_halo_update_type), save :: i_pack(max_packs) integer :: is, ie, js, je integer :: isd, ied, jsd, jed real :: dt2 integer :: ierr real :: time_total integer :: seconds, days ccpp_associate: associate( cappa => GFDL_interstitial%cappa, & dp1 => GFDL_interstitial%te0, & dtdt_m => GFDL_interstitial%dtdt, & last_step => GFDL_interstitial%last_step, & te_2d => GFDL_interstitial%te0_2d ) is = bd%is ie = bd%ie js = bd%js je = bd%je isd = bd%isd ied = bd%ied jsd = bd%jsd jed = bd%jed ! cv_air = cp_air - rdgas agrav = 1. / grav dt2 = 0.5*bdt k_split = flagstruct%k_split recip_k_split=1./real(k_split) nwat = flagstruct%nwat nq = nq_tot - flagstruct%dnats nr = nq_tot - flagstruct%dnrts rdg = -rdgas * agrav ! Call CCPP timestep init call ccpp_physics_timestep_init(cdata, suite_name=trim(ccpp_suite), group_name="fast_physics", ierr=ierr) ! Reset all interstitial variables for CCPP version ! of fast physics, and manually set runtime parameters call GFDL_interstitial%reset() if (flagstruct%do_sat_adj) then GFDL_interstitial%out_dt = (idiag%id_mdt > 0) end if #ifdef MULTI_GASES allocate ( kapad(isd:ied, jsd:jed, npz) ) call init_ijk_mem(isd,ied, jsd,jed, npz, kapad, kappa) #endif if (flagstruct%molecular_diffusion ) then call get_time(fv_time, seconds, days) time_total = seconds + 86400. * days if( time_offset .lt. 0.0) time_offset = time_total endif !We call this BEFORE converting pt to virtual potential temperature, !since we interpolate on (regular) temperature rather than theta. if (gridstruct%nested .or. ANY(neststruct%child_grids)) then call timing_on('NEST_BCs') call setup_nested_grid_BCs(npx, npy, npz, zvir, ncnst, & u, v, w, pt, delp, delz, q, uc, vc, & #ifdef USE_COND q_con, & #ifdef MOIST_CAPPA cappa, & #endif #endif neststruct%nested, flagstruct%inline_q, flagstruct%make_nh, ng, & gridstruct, flagstruct, neststruct, & neststruct%nest_timestep, neststruct%tracer_nest_timestep, & domain, parent_grid, bd, nwat, ak, bk) call timing_off('NEST_BCs') endif ! For the regional domain set values valid the beginning of the ! current large timestep at the boundary points of the pertinent ! prognostic arrays. if (flagstruct%regional) then call timing_on('Regional_BCs') reg_bc_update_time=current_time_in_seconds call set_regional_BCs & !<-- Insert values into the boundary region valid for the start of this large timestep. (delp,delz,w,pt & #ifdef USE_COND ,q_con & #endif #ifdef MOIST_CAPPA ,cappa & #endif ,q,u,v,uc,vc, bd, npz, reg_bc_update_time ) call timing_off('Regional_BCs') endif if ( flagstruct%no_dycore ) then if ( nwat.eq.2 .and. (.not.hydrostatic) ) then sphum = get_tracer_index (MODEL_ATMOS, 'sphum') endif goto 911 endif if ( nwat==0 ) then sphum = 1 cld_amt = -1 ! to cause trouble if (mis)used else sphum = get_tracer_index (MODEL_ATMOS, 'sphum') liq_wat = get_tracer_index (MODEL_ATMOS, 'liq_wat') ice_wat = get_tracer_index (MODEL_ATMOS, 'ice_wat') rainwat = get_tracer_index (MODEL_ATMOS, 'rainwat') snowwat = get_tracer_index (MODEL_ATMOS, 'snowwat') graupel = get_tracer_index (MODEL_ATMOS, 'graupel') hailwat = get_tracer_index (MODEL_ATMOS, 'hailwat') cld_amt = get_tracer_index (MODEL_ATMOS, 'cld_amt') endif theta_d = get_tracer_index (MODEL_ATMOS, 'theta_d') #ifdef SW_DYNAMICS akap = 1. pfull(1) = 0.5*flagstruct%p_ref #else akap = kappa !$OMP parallel do default(none) shared(npz,ak,bk,flagstruct,pfull) & !$OMP private(ph1, ph2,k) do k=1,npz ph1 = ak(k ) + bk(k )*flagstruct%p_ref ph2 = ak(k+1) + bk(k+1)*flagstruct%p_ref pfull(k) = (ph2 - ph1) / log(ph2/ph1) enddo if ( hydrostatic ) then #ifdef __GFORTRAN__ !$OMP parallel do default(none) shared(is,ie,js,je,isd,ied,jsd,jed,npz,zvir,nwat,q,q_con,sphum,liq_wat, & #else !$OMP parallel do default(none) shared(is,ie,js,je,isd,ied,jsd,jed,npz,dp1,zvir,nwat,q,q_con,sphum,liq_wat, & #endif !$OMP rainwat,ice_wat,snowwat,graupel,hailwat) private(cvm,i,j,k) do k=1,npz do j=js,je #ifdef USE_COND call moist_cp(is,ie,isd,ied,jsd,jed, npz, j, k, nwat, sphum, liq_wat, rainwat, & ice_wat, snowwat, graupel, hailwat, q, q_con(is:ie,j,k), cvm) #endif do i=is,ie dp1(i,j,k) = zvir*q(i,j,k,sphum) enddo enddo enddo else #ifdef __GFORTRAN__ !$OMP parallel do default(none) shared(is,ie,js,je,isd,ied,jsd,jed,npz,zvir,q,q_con,sphum,liq_wat, & #else !$OMP parallel do default(none) shared(is,ie,js,je,isd,ied,jsd,jed,npz,dp1,zvir,q,q_con,sphum,liq_wat, & #endif !$OMP rainwat,ice_wat,snowwat,graupel,hailwat,pkz,flagstruct, & #ifdef MULTI_GASES !$OMP kapad, & #endif #ifdef __GFORTRAN__ !$OMP kappa,rdg,delp,pt,delz,nwat) & #else !$OMP cappa,kappa,rdg,delp,pt,delz,nwat) & #endif !$OMP private(cvm,i,j,k) do k=1,npz if ( flagstruct%moist_phys ) then do j=js,je #ifdef MOIST_CAPPA call moist_cv(is,ie,isd,ied,jsd,jed, npz, j, k, nwat, sphum, liq_wat, rainwat, & ice_wat, snowwat, graupel, hailwat, q, q_con(is:ie,j,k), cvm) #endif do i=is,ie #ifdef MULTI_GASES dp1(i,j,k) = virq(q(i,j,k,:))-1. kapad(i,j,k)= kappa * (virqd(q(i,j,k,:))/vicpqd(q(i,j,k,:))) #else dp1(i,j,k) = zvir*q(i,j,k,sphum) #endif #ifdef MOIST_CAPPA cappa(i,j,k) = rdgas/(rdgas + cvm(i)/(1.+dp1(i,j,k))) pkz(i,j,k) = exp(cappa(i,j,k)*log(rdg*delp(i,j,k)*pt(i,j,k)* & #ifdef MULTI_GASES (1.+dp1(i,j,k)) /delz(i,j,k)) ) #else (1.+dp1(i,j,k))*(1.-q_con(i,j,k))/delz(i,j,k)) ) #endif #else pkz(i,j,k) = exp( kappa*log(rdg*delp(i,j,k)*pt(i,j,k)* & (1.+dp1(i,j,k))/delz(i,j,k)) ) ! Using dry pressure for the definition of the virtual potential temperature ! pkz(i,j,k) = exp( kappa*log(rdg*delp(i,j,k)*pt(i,j,k)* & ! (1.-q(i,j,k,sphum))/delz(i,j,k)) ) #endif enddo enddo else do j=js,je do i=is,ie dp1(i,j,k) = 0. #ifdef MULTI_GASES kapad(i,j,k)= kappa * (virqd(q(i,j,k,:))/vicpqd(q(i,j,k,:))) pkz(i,j,k) = exp(kapad(i,j,k)*log(rdg*virqd(q(i,j,k,:))*delp(i,j,k)*pt(i,j,k)/delz(i,j,k))) #else pkz(i,j,k) = exp(kappa*log(rdg*delp(i,j,k)*pt(i,j,k)/delz(i,j,k))) #endif enddo enddo endif enddo endif if ( flagstruct%fv_debug ) then #ifdef MOIST_CAPPA call prt_mxm('cappa', cappa, is, ie, js, je, ng, npz, 1., gridstruct%area_64, domain) #endif call prt_mxm('PS', ps, is, ie, js, je, ng, 1, 0.01, gridstruct%area_64, domain) call prt_mxm('T_dyn_b', pt, is, ie, js, je, ng, npz, 1., gridstruct%area_64, domain) if ( .not. hydrostatic) call prt_mxm('delz', delz, is, ie, js, je, 0, npz, 1., gridstruct%area_64, domain) call prt_mxm('delp_b ', delp, is, ie, js, je, ng, npz, 0.01, gridstruct%area_64, domain) call prt_mxm('pk_b', pk, is, ie, js, je, 0, npz+1, 1.,gridstruct%area_64, domain) call prt_mxm('pkz_b', pkz,is, ie, js, je, 0, npz, 1.,gridstruct%area_64, domain) endif !--------------------- ! Compute Total Energy !--------------------- if ( (consv_te > 0. .or. idiag%id_te>0) .and. (.not.do_adiabatic_init) ) then call compute_total_energy(is, ie, js, je, isd, ied, jsd, jed, npz, & u, v, w, delz, pt, delp, q, dp1, pe, peln, phis, & gridstruct%rsin2, gridstruct%cosa_s, & zvir, cp_air, rdgas, hlv, te_2d, ua, va, teq, & flagstruct%moist_phys, nwat, sphum, liq_wat, rainwat, & ice_wat, snowwat, graupel, hailwat, hydrostatic, idiag%id_te) if( idiag%id_te>0 ) then used = send_data(idiag%id_te, teq, fv_time) ! te_den=1.E-9*g_sum(teq, is, ie, js, je, ng, area, 0)/(grav*4.*pi*radius**2) ! if(is_master()) write(*,*) 'Total Energy Density (Giga J/m**2)=',te_den endif endif if( (flagstruct%consv_am .or. idiag%id_amdt>0) .and. (.not.do_adiabatic_init) ) then call compute_aam(npz, is, ie, js, je, isd, ied, jsd, jed, gridstruct, bd, & ptop, ua, va, u, v, delp, teq, ps2, m_fac) endif if( .not.flagstruct%RF_fast .and. flagstruct%tau .ne. 0. ) then if ( gridstruct%grid_type<4 .or. gridstruct%bounded_domain ) then ! if ( flagstruct%RF_fast ) then ! call Ray_fast(abs(dt), npx, npy, npz, pfull, flagstruct%tau, u, v, w, & ! dp_ref, ptop, hydrostatic, flagstruct%rf_cutoff, bd) ! else call Rayleigh_Super(abs(bdt), npx, npy, npz, ks, pfull, phis, flagstruct%tau, flagstruct%tau_w, u, v, w, pt, & #ifdef MULTI_GASES q, ncnst, & #endif ua, va, delz, gridstruct%agrid, cp_air, rdgas, ptop, hydrostatic, & .not. gridstruct%bounded_domain, flagstruct%molecular_diffusion, consv_te, flagstruct%rf_cutoff, gridstruct, domain, bd) ! endif else call Rayleigh_Friction(abs(bdt), npx, npy, npz, ks, pfull, flagstruct%tau, u, v, w, pt, & ua, va, delz, cp_air, rdgas, ptop, hydrostatic, .true., flagstruct%rf_cutoff, gridstruct, domain, bd) endif endif #endif #ifndef SW_DYNAMICS ! Convert pt to virtual potential temperature on the first timestep if ( flagstruct%adiabatic .and. flagstruct%kord_tm>0 ) then if ( .not.pt_initialized )then !$OMP parallel do default(none) shared(theta_d,is,ie,js,je,npz,pt,pkz,q) do k=1,npz do j=js,je do i=is,ie pt(i,j,k) = pt(i,j,k)/pkz(i,j,k) enddo enddo if ( theta_d>0 ) then do j=js,je do i=is,ie q(i,j,k,theta_d) = pt(i,j,k) enddo enddo endif enddo pt_initialized = .true. endif else #ifdef __GFORTRAN__ !$OMP parallel do default(none) shared(is,ie,js,je,npz,pt,pkz,q_con) #else !$OMP parallel do default(none) shared(is,ie,js,je,npz,pt,dp1,pkz,q_con) #endif do k=1,npz do j=js,je do i=is,ie #ifdef MULTI_GASES pt(i,j,k) = pt(i,j,k)*(1.+dp1(i,j,k))/pkz(i,j,k) #else #ifdef USE_COND pt(i,j,k) = pt(i,j,k)*(1.+dp1(i,j,k))*(1.-q_con(i,j,k))/pkz(i,j,k) #else pt(i,j,k) = pt(i,j,k)*(1.+dp1(i,j,k))/pkz(i,j,k) #endif #endif enddo enddo enddo endif #endif last_step = .false. mdt = bdt / real(k_split) if ( idiag%id_mdt > 0 .and. (.not. do_adiabatic_init) ) then #ifdef __GFORTRAN__ !$OMP parallel do default(none) shared(is,ie,js,je,npz) #else !$OMP parallel do default(none) shared(is,ie,js,je,npz,dtdt_m) #endif do k=1,npz do j=js,je do i=is,ie dtdt_m(i,j,k) = 0. enddo enddo enddo endif call timing_on('FV_DYN_LOOP') do n_map=1, k_split ! first level of time-split k_step = n_map call timing_on('COMM_TOTAL') #ifdef USE_COND call start_group_halo_update(i_pack(11), q_con, domain) #ifdef MOIST_CAPPA call start_group_halo_update(i_pack(12), cappa, domain) #endif #endif #ifdef MULTI_GASES call start_group_halo_update(i_pack(13), kapad, domain) #endif call start_group_halo_update(i_pack(1), delp, domain, complete=.false.) call start_group_halo_update(i_pack(1), pt, domain, complete=.true.) #ifndef ROT3 call start_group_halo_update(i_pack(8), u, v, domain, gridtype=DGRID_NE) #endif call timing_off('COMM_TOTAL') #ifdef __GFORTRAN__ !$OMP parallel do default(none) shared(isd,ied,jsd,jed,npz,delp) #else !$OMP parallel do default(none) shared(isd,ied,jsd,jed,npz,dp1,delp) #endif do k=1,npz do j=jsd,jed do i=isd,ied dp1(i,j,k) = delp(i,j,k) enddo enddo enddo if ( flagstruct%trdm2 > 1.e-4 ) then call start_group_halo_update(i_pack(13), dp1, domain) endif if ( n_map==k_split ) last_step = .true. #ifdef USE_COND call timing_on('COMM_TOTAL') call complete_group_halo_update(i_pack(11), domain) #ifdef MOIST_CAPPA call complete_group_halo_update(i_pack(12), domain) #endif call timing_off('COMM_TOTAL') #endif #ifdef MULTI_GASES call complete_group_halo_update(i_pack(13), domain) #endif call timing_on('DYN_CORE') call dyn_core(npx, npy, npz, ng, sphum, nq, mdt, n_map, n_split, zvir, cp_air, akap, cappa, & #ifdef MULTI_GASES kapad, & #endif grav, hydrostatic, & u, v, w, delz, pt, q, delp, pe, pk, phis, ws, omga, ptop, pfull, ua, va, & uc, vc, mfx, mfy, cx, cy, pkz, peln, q_con, ak, bk, ks, & gridstruct, flagstruct, neststruct, idiag, bd, & domain, n_map==1, i_pack, last_step, diss_est,time_total) call timing_off('DYN_CORE') #ifdef SW_DYNAMICS !!$OMP parallel do default(none) shared(is,ie,js,je,ps,delp,agrav) do j=js,je do i=is,ie ps(i,j) = delp(i,j,1) * agrav enddo enddo #else if( .not. flagstruct%inline_q .and. nq /= 0 ) then !-------------------------------------------------------- ! Perform large-time-step scalar transport using the accumulated CFL and ! mass fluxes call timing_on('tracer_2d') !!! CLEANUP: merge these two calls? if (gridstruct%bounded_domain) then call tracer_2d_nested(q, dp1, mfx, mfy, cx, cy, gridstruct, bd, domain, npx, npy, npz, nq, & flagstruct%hord_tr, q_split, mdt, idiag%id_divg, i_pack(10), i_pack(13), & flagstruct%nord_tr, flagstruct%trdm2, & k_split, neststruct, parent_grid, n_map, flagstruct%lim_fac) else if ( flagstruct%z_tracer ) then call tracer_2d_1L(q, dp1, mfx, mfy, cx, cy, gridstruct, bd, domain, npx, npy, npz, nq, & flagstruct%hord_tr, q_split, mdt, idiag%id_divg, i_pack(10), i_pack(13), & flagstruct%nord_tr, flagstruct%trdm2, flagstruct%lim_fac) else call tracer_2d(q, dp1, mfx, mfy, cx, cy, gridstruct, bd, domain, npx, npy, npz, nq, & flagstruct%hord_tr, q_split, mdt, idiag%id_divg, i_pack(10), i_pack(13), & flagstruct%nord_tr, flagstruct%trdm2, flagstruct%lim_fac) endif endif call timing_off('tracer_2d') #ifdef FILL2D if ( flagstruct%hord_tr<8 .and. flagstruct%moist_phys ) then call timing_on('Fill2D') if ( liq_wat > 0 ) & call fill2D(is, ie, js, je, ng, npz, q(isd,jsd,1,liq_wat), delp, gridstruct%area, domain, gridstruct%bounded_domain, npx, npy) if ( rainwat > 0 ) & call fill2D(is, ie, js, je, ng, npz, q(isd,jsd,1,rainwat), delp, gridstruct%area, domain, gridstruct%bounded_domain, npx, npy) if ( ice_wat > 0 ) & call fill2D(is, ie, js, je, ng, npz, q(isd,jsd,1,ice_wat), delp, gridstruct%area, domain, gridstruct%bounded_domain, npx, npy) if ( snowwat > 0 ) & call fill2D(is, ie, js, je, ng, npz, q(isd,jsd,1,snowwat), delp, gridstruct%area, domain, gridstruct%bounded_domain, npx, npy) if ( graupel > 0 ) & call fill2D(is, ie, js, je, ng, npz, q(isd,jsd,1,graupel), delp, gridstruct%area, domain, gridstruct%bounded_domain, npx, npy) if ( hailwat > 0 ) & call fill2D(is, ie, js, je, ng, npz, q(isd,jsd,1,hailwat), delp, gridstruct%area, domain, gridstruct%bounded_domain, npx, npy) call timing_off('Fill2D') endif #endif if( last_step .and. idiag%id_divg>0 ) then used = send_data(idiag%id_divg, dp1, fv_time) if(flagstruct%fv_debug) call prt_mxm('divg', dp1, is, ie, js, je, 0, npz, 1.,gridstruct%area_64, domain) endif endif if ( npz > 4 ) then !------------------------------------------------------------------------ ! Perform vertical remapping from Lagrangian control-volume to ! the Eulerian coordinate as specified by the routine set_eta. ! Note that this finite-volume dycore is otherwise independent of the vertical ! Eulerian coordinate. !------------------------------------------------------------------------ do iq=1,nr kord_tracer(iq) = flagstruct%kord_tr if ( iq==cld_amt ) kord_tracer(iq) = 9 ! monotonic enddo do_omega = hydrostatic .and. last_step call timing_on('Remapping') #ifdef AVEC_TIMERS call avec_timer_start(6) #endif call Lagrangian_to_Eulerian(last_step, consv_te, ps, pe, delp, & pkz, pk, mdt, bdt, npx, npy, npz, is,ie,js,je, isd,ied,jsd,jed, & nr, nwat, sphum, q_con, u, v, w, delz, pt, q, phis, & zvir, cp_air, akap, cappa, flagstruct%kord_mt, flagstruct%kord_wz, & kord_tracer, flagstruct%kord_tm, peln, te_2d, & ng, ua, va, omga, dp1, ws, fill, reproduce_sum, & idiag%id_mdt>0, dtdt_m, ptop, ak, bk, pfull, gridstruct, domain, & flagstruct%do_sat_adj, hydrostatic, flagstruct%phys_hydrostatic, & hybrid_z, do_omega, & flagstruct%adiabatic, do_adiabatic_init, flagstruct%do_inline_mp, & inline_mp, flagstruct%c2l_ord, bd, flagstruct%fv_debug, & flagstruct%moist_phys) if ( flagstruct%molecular_diffusion ) then ! do thermosphere adjustment if it is turned on and at last_step. if( md_tadj_layers .gt.0 .and. md_time .and. last_step ) then call thermosphere_adjustment(domain,gridstruct,npz,bd,ng,pt) endif ! md_tadj_layers>0 and md_time and last_step endif if ( flagstruct%fv_debug ) then if (is_master()) write(*,'(A, I3, A1, I3)') 'finished k_split ', n_map, '/', k_split call prt_mxm('T_dyn_a4', pt, is, ie, js, je, ng, npz, 1., gridstruct%area_64, domain) if (sphum > 0) call prt_mxm('SPHUM_dyn', q(isd,jsd,1,sphum ), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) if (liq_wat > 0) call prt_mxm('liq_wat_dyn', q(isd,jsd,1,liq_wat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) if (rainwat > 0) call prt_mxm('rainwat_dyn', q(isd,jsd,1,rainwat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) if (ice_wat > 0) call prt_mxm('ice_wat_dyn', q(isd,jsd,1,ice_wat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) if (snowwat > 0) call prt_mxm('snowwat_dyn', q(isd,jsd,1,snowwat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) if (graupel > 0) call prt_mxm('graupel_dyn', q(isd,jsd,1,graupel), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) if (hailwat > 0) call prt_mxm('hailwat_dyn', q(isd,jsd,1,hailwat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) endif #ifdef AVEC_TIMERS call avec_timer_stop(6) #endif call timing_off('Remapping') #ifdef MOIST_CAPPA if ( neststruct%nested .and. .not. last_step) then call nested_grid_BC_apply_intT(cappa, & 0, 0, npx, npy, npz, bd, real(n_map+1), real(k_split), & neststruct%cappa_BC, bctype=neststruct%nestbctype ) endif if ( flagstruct%regional .and. .not. last_step) then reg_bc_update_time=current_time_in_seconds+(n_map+1)*mdt call regional_boundary_update(cappa, 'cappa', & isd, ied, jsd, jed, npz, & is, ie, js, je, & isd, ied, jsd, jed, & reg_bc_update_time,1 ) endif #endif if( last_step ) then if( .not. hydrostatic ) then !$OMP parallel do default(none) shared(is,ie,js,je,npz,omga,delp,delz,w) do k=1,npz do j=js,je do i=is,ie omga(i,j,k) = delp(i,j,k)/delz(i,j,k)*w(i,j,k) enddo enddo enddo endif !-------------------------- ! Filter omega for physics: !-------------------------- if(flagstruct%nf_omega>0) & call del2_cubed(omga, 0.18*gridstruct%da_min, gridstruct, domain, npx, npy, npz, flagstruct%nf_omega, bd) endif end if #endif enddo ! n_map loop call timing_off('FV_DYN_LOOP') if ( flagstruct%molecular_diffusion ) then if( .not. md_time .and. time_total - time_offset .gt. md_wait_sec ) then md_time= .true. if( is_master() ) write(*,*) 'Molecular diffusion is on with explicit scheme ' endif endif if ( idiag%id_mdt > 0 .and. (.not.do_adiabatic_init) ) then ! Output temperature tendency due to inline moist physics: #ifdef __GFORTRAN__ !$OMP parallel do default(none) shared(is,ie,js,je,npz,bdt) #else !$OMP parallel do default(none) shared(is,ie,js,je,npz,dtdt_m,bdt) #endif do k=1,npz do j=js,je do i=is,ie dtdt_m(i,j,k) = dtdt_m(i,j,k) / bdt * 86400. enddo enddo enddo ! call prt_mxm('Fast DTDT (deg/Day)', dtdt_m, is, ie, js, je, 0, npz, 1., gridstruct%area_64, domain) used = send_data(idiag%id_mdt, dtdt_m, fv_time) endif if( nwat==7 ) then if (cld_amt > 0) then call neg_adj4(is, ie, js, je, ng, npz, & flagstruct%hydrostatic, & peln, delz, & pt, delp, q(isd,jsd,1,sphum), & q(isd,jsd,1,liq_wat), & q(isd,jsd,1,rainwat), & q(isd,jsd,1,ice_wat), & q(isd,jsd,1,snowwat), & q(isd,jsd,1,graupel), & q(isd,jsd,1,hailwat), & q(isd,jsd,1,cld_amt), flagstruct%check_negative) else call neg_adj4(is, ie, js, je, ng, npz, & flagstruct%hydrostatic, & peln, delz, & pt, delp, q(isd,jsd,1,sphum), & q(isd,jsd,1,liq_wat), & q(isd,jsd,1,rainwat), & q(isd,jsd,1,ice_wat), & q(isd,jsd,1,snowwat), & q(isd,jsd,1,graupel), & q(isd,jsd,1,hailwat), check_negative=flagstruct%check_negative) endif if ( flagstruct%fv_debug ) then call prt_mxm('T_dyn_a3', pt, is, ie, js, je, ng, npz, 1., gridstruct%area_64, domain) call prt_mxm('SPHUM_dyn', q(isd,jsd,1,sphum ), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('liq_wat_dyn', q(isd,jsd,1,liq_wat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('rainwat_dyn', q(isd,jsd,1,rainwat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('ice_wat_dyn', q(isd,jsd,1,ice_wat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('snowwat_dyn', q(isd,jsd,1,snowwat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('graupel_dyn', q(isd,jsd,1,graupel), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) IF ( hailwat > 0 ) call prt_mxm('hailwat_dyn', q(isd,jsd,1,hailwat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) endif endif if( nwat == 6 ) then if (cld_amt > 0) then call neg_adj3(is, ie, js, je, ng, npz, & flagstruct%hydrostatic, & peln, delz, & pt, delp, q(isd,jsd,1,sphum), & q(isd,jsd,1,liq_wat), & q(isd,jsd,1,rainwat), & q(isd,jsd,1,ice_wat), & q(isd,jsd,1,snowwat), & q(isd,jsd,1,graupel), & q(isd,jsd,1,cld_amt), flagstruct%check_negative) else call neg_adj3(is, ie, js, je, ng, npz, & flagstruct%hydrostatic, & peln, delz, & pt, delp, q(isd,jsd,1,sphum), & q(isd,jsd,1,liq_wat), & q(isd,jsd,1,rainwat), & q(isd,jsd,1,ice_wat), & q(isd,jsd,1,snowwat), & q(isd,jsd,1,graupel), check_negative=flagstruct%check_negative) endif if ( flagstruct%fv_debug ) then call prt_mxm('T_dyn_a3', pt, is, ie, js, je, ng, npz, 1., gridstruct%area_64, domain) call prt_mxm('SPHUM_dyn', q(isd,jsd,1,sphum ), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('liq_wat_dyn', q(isd,jsd,1,liq_wat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('rainwat_dyn', q(isd,jsd,1,rainwat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('ice_wat_dyn', q(isd,jsd,1,ice_wat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('snowwat_dyn', q(isd,jsd,1,snowwat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('graupel_dyn', q(isd,jsd,1,graupel), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) endif endif if( nwat == 5 ) then if (cld_amt > 0) then call neg_adj2(is, ie, js, je, ng, npz, & flagstruct%hydrostatic, & peln, delz, & pt, delp, q(isd,jsd,1,sphum), & q(isd,jsd,1,liq_wat), & q(isd,jsd,1,rainwat), & q(isd,jsd,1,ice_wat), & q(isd,jsd,1,snowwat), & q(isd,jsd,1,cld_amt), flagstruct%check_negative) else call neg_adj2(is, ie, js, je, ng, npz, & flagstruct%hydrostatic, & peln, delz, & pt, delp, q(isd,jsd,1,sphum), & q(isd,jsd,1,liq_wat), & q(isd,jsd,1,rainwat), & q(isd,jsd,1,ice_wat), & q(isd,jsd,1,snowwat), & check_negative=flagstruct%check_negative) endif if ( flagstruct%fv_debug ) then call prt_mxm('T_dyn_a3', pt, is, ie, js, je, ng, npz, 1., gridstruct%area_64, domain) call prt_mxm('SPHUM_dyn', q(isd,jsd,1,sphum ), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('liq_wat_dyn', q(isd,jsd,1,liq_wat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('rainwat_dyn', q(isd,jsd,1,rainwat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('ice_wat_dyn', q(isd,jsd,1,ice_wat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) call prt_mxm('snowwat_dyn', q(isd,jsd,1,snowwat), is, ie, js, je, ng, npz, 1.,gridstruct%area_64, domain) endif endif if( (flagstruct%consv_am.or.idiag%id_amdt>0.or.idiag%id_aam>0) .and. (.not.do_adiabatic_init) ) then call compute_aam(npz, is, ie, js, je, isd, ied, jsd, jed, gridstruct, bd, & ptop, ua, va, u, v, delp, te_2d, ps, m_fac) if( idiag%id_aam>0 ) then used = send_data(idiag%id_aam, te_2d, fv_time) endif if ( idiag%id_aam>0 .or. flagstruct%consv_am ) then if ( prt_minmax ) then gam = g_sum( domain, te_2d, is, ie, js, je, ng, gridstruct%area_64, 0) if( is_master() ) write(6,*) 'Total AAM =', gam endif endif endif if( (flagstruct%consv_am.or.idiag%id_amdt>0) .and. (.not.do_adiabatic_init) ) then #ifdef __GFORTRAN__ !$OMP parallel do default(none) shared(is,ie,js,je,teq,dt2,ps2,ps,idiag) #else !$OMP parallel do default(none) shared(is,ie,js,je,te_2d,teq,dt2,ps2,ps,idiag) #endif do j=js,je do i=is,ie ! Note: the mountain torque computation contains also numerical error ! The numerical error is mostly from the zonal gradient of the terrain (zxg) te_2d(i,j) = te_2d(i,j)-teq(i,j) + dt2*(ps2(i,j)+ps(i,j))*idiag%zxg(i,j) enddo enddo if( idiag%id_amdt>0 ) used = send_data(idiag%id_amdt, te_2d/bdt, fv_time) if ( flagstruct%consv_am .or. prt_minmax ) then amdt = g_sum( domain, te_2d, is, ie, js, je, ng, gridstruct%area_64, 0, reproduce=.true.) u0 = -radius*amdt/g_sum( domain, m_fac, is, ie, js, je, ng, gridstruct%area_64, 0,reproduce=.true.) if(is_master() .and. prt_minmax) & write(6,*) 'Dynamic AM tendency (Hadleys)=', amdt/(bdt*1.e18), 'del-u (per day)=', u0*86400./bdt endif if( flagstruct%consv_am ) then !$OMP parallel do default(none) shared(is,ie,js,je,m_fac,u0,gridstruct) do j=js,je do i=is,ie m_fac(i,j) = u0*cos(gridstruct%agrid(i,j,2)) enddo enddo !$OMP parallel do default(none) shared(is,ie,js,je,npz,hydrostatic,pt,m_fac,ua,cp_air, & !$OMP u,u0,gridstruct,v ) do k=1,npz do j=js,je+1 do i=is,ie u(i,j,k) = u(i,j,k) + u0*gridstruct%l2c_u(i,j) enddo enddo do j=js,je do i=is,ie+1 v(i,j,k) = v(i,j,k) + u0*gridstruct%l2c_v(i,j) enddo enddo enddo endif ! consv_am endif 911 call cubed_to_latlon(u, v, ua, va, gridstruct, & npx, npy, npz, 1, gridstruct%grid_type, domain, gridstruct%bounded_domain, flagstruct%c2l_ord, bd) #ifdef MULTI_GASES deallocate(kapad) #endif if ( flagstruct%fv_debug ) then call prt_mxm('UA', ua, is, ie, js, je, ng, npz, 1., gridstruct%area_64, domain) call prt_mxm('VA', va, is, ie, js, je, ng, npz, 1., gridstruct%area_64, domain) call prt_mxm('TA', pt, is, ie, js, je, ng, npz, 1., gridstruct%area_64, domain) if (.not. hydrostatic) call prt_mxm('W ', w, is, ie, js, je, ng, npz, 1., gridstruct%area_64, domain) endif if ( flagstruct%range_warn ) then if ( flagstruct%molecular_diffusion ) then call range_check('UA_dyn', ua, is, ie, js, je, ng, npz,gridstruct%agrid,& -880., 880., bad_range) call range_check('VA_dyn', ua, is, ie, js, je, ng, npz,gridstruct%agrid,& -880., 880., bad_range) call range_check('TA_dyn', pt, is, ie, js, je, ng, npz,gridstruct%agrid,& 150.,3350., bad_range) call range_check('W_dyn', w, is, ie, js, je, ng, npz,gridstruct%agrid, & -250., 250., bad_range) else call range_check('UA_dyn', ua, is, ie, js, je, ng, npz, gridstruct%agrid, & -280., 280., bad_range, fv_time) call range_check('VA_dyn', ua, is, ie, js, je, ng, npz, gridstruct%agrid, & -280., 280., bad_range, fv_time) call range_check('TA_dyn', pt, is, ie, js, je, ng, npz, gridstruct%agrid, & 150., 335., bad_range, fv_time) if ( .not. hydrostatic ) & call range_check('W_dyn', w, is, ie, js, je, ng, npz, gridstruct%agrid, & -50., 100., bad_range, fv_time) endif endif ! Call CCPP timestep finalize call ccpp_physics_timestep_finalize(cdata, suite_name=trim(ccpp_suite), group_name="fast_physics", ierr=ierr) end associate ccpp_associate end subroutine fv_dynamics #ifdef USE_RF_FAST subroutine Rayleigh_fast(dt, npx, npy, npz, pfull, tau, u, v, w, & ks, dp, ptop, hydrostatic, rf_cutoff, bd) ! Simple "inline" version of the Rayleigh friction real, intent(in):: dt real, intent(in):: tau !< time scale (days) real, intent(in):: ptop, rf_cutoff real, intent(in), dimension(npz):: pfull integer, intent(in):: npx, npy, npz, ks logical, intent(in):: hydrostatic type(fv_grid_bounds_type), intent(IN) :: bd real, intent(inout):: u(bd%isd:bd%ied ,bd%jsd:bd%jed+1,npz) !< D grid zonal wind (m/s) real, intent(inout):: v(bd%isd:bd%ied+1,bd%jsd:bd%jed,npz) !< D grid meridional wind (m/s) real, intent(inout):: w(bd%isd: ,bd%jsd: ,1: ) !< cell center vertical wind (m/s) real, intent(in):: dp(npz) ! real(kind=R_GRID):: rff(npz) real, parameter:: sday = 86400. !< seconds per day real, dimension(bd%is:bd%ie+1):: dmv real, dimension(bd%is:bd%ie):: dmu real:: tau0, dm integer i, j, k integer :: is, ie, js, je integer :: isd, ied, jsd, jed is = bd%is ie = bd%ie js = bd%js je = bd%je isd = bd%isd ied = bd%ied jsd = bd%jsd jed = bd%jed if ( .not. RF_initialized ) then tau0 = tau * sday allocate( rf(npz) ) rf(:) = 1. if( is_master() ) write(6,*) 'Fast Rayleigh friction E-folding time (days):' do k=1, npz if ( pfull(k) < rf_cutoff ) then rff(k) = dt/tau0*sin(0.5*pi*log(rf_cutoff/pfull(k))/log(rf_cutoff/ptop))**2 ! Re-FACTOR rf if( is_master() ) write(6,*) k, 0.01*pfull(k), dt/(rff(k)*sday) kmax = k rff(k) = 1.d0 / (1.0d0+rff(k)) rf(k) = rff(k) else exit endif enddo dm = 0. do k=1,ks if ( pfull(k) < 100.E2 ) then dm = dm + dp(k) k_rf = k else exit endif enddo if( is_master() ) write(6,*) 'k_rf=', k_rf, 0.01*pfull(k_rf), 'dm=', dm RF_initialized = .true. endif !$OMP parallel do default(none) shared(k_rf,is,ie,js,je,kmax,pfull,rf_cutoff,w,rf,dp,u,v,hydrostatic) & !$OMP private(dm, dmu, dmv) do j=js,je+1 dm = 0. do k=1, k_rf dm = dm + dp(k) enddo dmu(:) = 0. dmv(:) = 0. do k=1,kmax do i=is,ie dmu(i) = dmu(i) + (1.-rf(k))*dp(k)*u(i,j,k) u(i,j,k) = rf(k)*u(i,j,k) enddo if ( j/=je+1 ) then do i=is,ie+1 dmv(i) = dmv(i) + (1.-rf(k))*dp(k)*v(i,j,k) v(i,j,k) = rf(k)*v(i,j,k) enddo if ( .not. hydrostatic ) then do i=is,ie w(i,j,k) = rf(k)*w(i,j,k) enddo endif endif enddo do i=is,ie dmu(i) = dmu(i) / dm enddo if ( j/=je+1 ) then do i=is,ie+1 dmv(i) = dmv(i) / dm enddo endif do k=1, k_rf do i=is,ie u(i,j,k) = u(i,j,k) + dmu(i) enddo if ( j/=je+1 ) then do i=is,ie+1 v(i,j,k) = v(i,j,k) + dmv(i) enddo endif enddo enddo end subroutine Rayleigh_fast #endif subroutine Rayleigh_Super(dt, npx, npy, npz, ks, pm, phis, tau, tau_w, u, v, w, pt, & #ifdef MULTI_GASES q, ncnst, & #endif ua, va, delz, agrid, cp, rg, ptop, hydrostatic, & conserve, molecular_diffusion, consv_te, rf_cutoff, gridstruct, domain, bd) real, intent(in):: dt real, intent(in):: tau !< time scale (days) real, intent(in):: tau_w !< time scale (days) for w real, intent(in):: cp, rg, ptop, rf_cutoff real, intent(in), dimension(npz):: pm integer, intent(in):: npx, npy, npz, ks logical, intent(in):: hydrostatic logical, intent(in):: conserve, molecular_diffusion real, intent(in):: consv_te type(fv_grid_bounds_type), intent(IN) :: bd real, intent(inout):: u(bd%isd:bd%ied ,bd%jsd:bd%jed+1,npz) !< D grid zonal wind (m/s) real, intent(inout):: v(bd%isd:bd%ied+1,bd%jsd:bd%jed,npz) !< D grid meridional wind (m/s) real, intent(inout):: w(bd%isd: ,bd%jsd: ,1: ) !< cell center vertical wind (m/s) real, intent(inout):: pt(bd%isd:bd%ied,bd%jsd:bd%jed,npz) !< temp real, intent(inout):: ua(bd%isd:bd%ied,bd%jsd:bd%jed,npz) ! real, intent(inout):: va(bd%isd:bd%ied,bd%jsd:bd%jed,npz) ! real, intent(inout):: delz(bd%is: ,bd%js: ,1: ) !< delta-height (m); non-hydrostatic only #ifdef MULTI_GASES integer, intent(in):: ncnst real, intent(in) :: q(bd%isd:bd%ied,bd%jsd:bd%jed,npz,ncnst) ! #endif real, intent(in) :: agrid(bd%isd:bd%ied, bd%jsd:bd%jed,2) real, intent(in) :: phis(bd%isd:bd%ied,bd%jsd:bd%jed) !< Surface geopotential (g*Z_surf) type(fv_grid_type), intent(IN) :: gridstruct type(domain2d), intent(INOUT) :: domain ! real, allocatable :: u2f(:,:,:) real, allocatable :: w2f(:,:,:) !< related to tau_w real, parameter:: u0 = 60. !< scaling velocity real, parameter:: sday = 86400. real rcp, rcv, tau0, tau1 integer i, j, k logical convert integer :: is, ie, js, je integer :: isd, ied, jsd, jed is = bd%is ie = bd%ie js = bd%js je = bd%je isd = bd%isd ied = bd%ied jsd = bd%jsd jed = bd%jed rcp = 1. / cp rcv = 1. / (cp - rg) convert=conserve if ( molecular_diffusion )then if ( consv_te>0 ) then convert=.true. else convert=.false. endif endif if ( .not. RF_initialized ) then #ifdef HIWPP allocate ( u00(is:ie, js:je+1,npz) ) allocate ( v00(is:ie+1,js:je ,npz) ) !$OMP parallel do default(none) shared(is,ie,js,je,npz,u00,u,v00,v) do k=1,npz do j=js,je+1 do i=is,ie u00(i,j,k) = u(i,j,k) enddo enddo do j=js,je do i=is,ie+1 v00(i,j,k) = v(i,j,k) enddo enddo enddo #endif #ifdef SMALL_EARTH_TEST ! changed!!! tau0 = abs( tau ) tau1 = abs( tau_w ) #else tau0 = abs( tau * sday ) tau1 = abs( tau_w * sday ) #endif if( tau_w .eq. 0.0 ) tau1 = tau0 if( is_master() ) write(6,*) 'Rayleigh_Super in sec tau=',tau0,' tau_w=',tau1 allocate( rf(npz) ) allocate( rw(npz) ) rf(:) = 0. rw(:) = 0. do k=1, ks+1 if( is_master() ) write(6,*) k, 0.01*pm(k) enddo if( is_master() ) write(6,*) 'Rayleigh_Super E-folding time (mb days):' do k=1, npz if ( pm(k) < rf_cutoff ) then if ( tau < 0 ) then !< GSM formula rf(k) = dt/tau0*( log(rf_cutoff/pm(k)) )**2 rw(k) = dt/tau1*( log(rf_cutoff/pm(k)) )**2 else rf(k) = dt/tau0*sin(0.5*pi*log(rf_cutoff/pm(k))/log(rf_cutoff/ptop))**2 rw(k) = dt/tau1*sin(0.5*pi*log(rf_cutoff/pm(k))/log(rf_cutoff/ptop))**2 endif if( is_master() ) write(6,*) k, 0.01*pm(k), dt/(rf(k)*sday) kmax = k else exit endif enddo RF_initialized = .true. endif call c2l_ord2(u, v, ua, va, gridstruct, npz, gridstruct%grid_type, bd, gridstruct%bounded_domain) allocate( u2f(isd:ied,jsd:jed,kmax) ) allocate( w2f(isd:ied,jsd:jed,kmax) ) !$OMP parallel do default(none) shared(is,ie,js,je,kmax,pm,rf_cutoff,hydrostatic,ua,va,agrid, & !$OMP u2f,w2f,rf,rw,w) do k=1,kmax if ( pm(k) < rf_cutoff ) then u2f(:,:,k) = 1. / (1.+rf(k)) w2f(:,:,k) = 1. / (1.+rw(k)) else u2f(:,:,k) = 1. w2f(:,:,k) = 1. endif enddo call timing_on('COMM_TOTAL') call mpp_update_domains(u2f, domain) call mpp_update_domains(w2f, domain) call timing_off('COMM_TOTAL') !$OMP parallel do default(none) shared(is,ie,js,je,kmax,pm,rf_cutoff,w,rf,u,v, & #ifdef HIWPP !$OMP u00,v00, & #endif #ifdef MULTI_GASES !$OMP q, & #endif !$OMP convert,molecular_diffusion, consv_te, & !$OMP hydrostatic,pt,ua,va,u2f,w2f,cp,rg,ptop,rcp,rcv) do k=1,kmax if ( pm(k) < rf_cutoff ) then #ifdef HIWPP if (.not. hydrostatic) then do j=js,je do i=is,ie w(i,j,k) = w(i,j,k)/(1.+rw(k)) enddo enddo endif do j=js,je+1 do i=is,ie u(i,j,k) = (u(i,j,k)+rf(k)*u00(i,j,k))/(1.+rf(k)) enddo enddo do j=js,je do i=is,ie+1 v(i,j,k) = (v(i,j,k)+rf(k)*v00(i,j,k))/(1.+rf(k)) enddo enddo #else ! Add heat so as to conserve TE if ( convert ) then if ( hydrostatic ) then do j=js,je do i=is,ie pt(i,j,k) = pt(i,j,k) + 0.5*(ua(i,j,k)**2+va(i,j,k)**2)*(1.-u2f(i,j,k)**2)/(cp-rg*ptop/pm(k)) enddo enddo else do j=js,je do i=is,ie #ifdef MULTI_GASES pt(i,j,k) = pt(i,j,k) + 0.5*((ua(i,j,k)**2+va(i,j,k)**2)*(1.-u2f(i,j,k)**2)+w(i,j,k)**2*(1.-w2f(i,j,k)**2)) & /(cp*vicpq(q(i,j,k,:)) - rg*virq(q(i,j,k,:))) #else pt(i,j,k) = pt(i,j,k) + 0.5*((ua(i,j,k)**2+va(i,j,k)**2)*(1.-u2f(i,j,k)**2)+w(i,j,k)**2*(1.-w2f(i,j,k)**2))*rcv #endif enddo enddo endif endif do j=js,je+1 do i=is,ie u(i,j,k) = 0.5*(u2f(i,j-1,k)+u2f(i,j,k))*u(i,j,k) enddo enddo do j=js,je do i=is,ie+1 v(i,j,k) = 0.5*(u2f(i-1,j,k)+u2f(i,j,k))*v(i,j,k) enddo enddo if ( .not. hydrostatic ) then do j=js,je do i=is,ie w(i,j,k) = w2f(i,j,k)*w(i,j,k) enddo enddo endif #endif endif enddo deallocate ( u2f ) deallocate ( w2f ) end subroutine Rayleigh_Super subroutine Rayleigh_Friction(dt, npx, npy, npz, ks, pm, tau, u, v, w, pt, & ua, va, delz, cp, rg, ptop, hydrostatic, conserve, & rf_cutoff, gridstruct, domain, bd) real, intent(in):: dt real, intent(in):: tau !< time scale (days) real, intent(in):: cp, rg, ptop, rf_cutoff real, intent(in), dimension(npz):: pm integer, intent(in):: npx, npy, npz, ks logical, intent(in):: hydrostatic logical, intent(in):: conserve type(fv_grid_bounds_type), intent(IN) :: bd real, intent(inout):: u(bd%isd:bd%ied ,bd%jsd:bd%jed+1,npz) !< D grid zonal wind (m/s) real, intent(inout):: v(bd%isd:bd%ied+1,bd%jsd:bd%jed,npz) !< D grid meridional wind (m/s) real, intent(inout):: w(bd%isd: ,bd%jsd: ,1: ) !< cell center vertical wind (m/s) real, intent(inout):: pt(bd%isd:bd%ied,bd%jsd:bd%jed,npz) !< temp real, intent(inout):: ua(bd%isd:bd%ied,bd%jsd:bd%jed,npz) ! real, intent(inout):: va(bd%isd:bd%ied,bd%jsd:bd%jed,npz) ! real, intent(inout):: delz(bd%is: ,bd%js: ,1: ) !< delta-height (m); non-hydrostatic only type(fv_grid_type), intent(IN) :: gridstruct type(domain2d), intent(INOUT) :: domain ! local: real, allocatable :: u2f(:,:,:) real, parameter:: sday = 86400. !< seconds per day real, parameter:: u000 = 4900. !< scaling velocity **2 real rcv integer i, j, k integer :: is, ie, js, je integer :: isd, ied, jsd, jed is = bd%is ie = bd%ie js = bd%js je = bd%je isd = bd%isd ied = bd%ied jsd = bd%jsd jed = bd%jed rcv = 1. / (cp - rg) if ( .not. RF_initialized ) then allocate( rf(npz) ) if( is_master() ) write(6,*) 'Rayleigh friction E-folding time (days):' do k=1, npz if ( pm(k) < rf_cutoff ) then rf(k) = dt/(tau*sday)*sin(0.5*pi*log(rf_cutoff/pm(k))/log(rf_cutoff/ptop))**2 if( is_master() ) write(6,*) k, 0.01*pm(k), dt/(rf(k)*sday) kmax = k else exit endif enddo RF_initialized = .true. endif allocate( u2f(isd:ied,jsd:jed,kmax) ) call c2l_ord2(u, v, ua, va, gridstruct, npz, gridstruct%grid_type, bd, gridstruct%bounded_domain) !$OMP parallel do default(none) shared(is,ie,js,je,kmax,u2f,hydrostatic,ua,va,w) do k=1,kmax if ( hydrostatic ) then do j=js,je do i=is,ie u2f(i,j,k) = ua(i,j,k)**2 + va(i,j,k)**2 enddo enddo else do j=js,je do i=is,ie u2f(i,j,k) = ua(i,j,k)**2 + va(i,j,k)**2 + w(i,j,k)**2 enddo enddo endif enddo call timing_on('COMM_TOTAL') call mpp_update_domains(u2f, domain) call timing_off('COMM_TOTAL') !$OMP parallel do default(none) shared(is,ie,js,je,kmax,conserve,hydrostatic,pt,u2f,cp,rg, & !$OMP ptop,pm,rf,delz,rcv,u,v,w) do k=1,kmax if ( conserve ) then if ( hydrostatic ) then do j=js,je do i=is,ie pt(i,j,k) = pt(i,j,k) + 0.5*u2f(i,j,k)/(cp-rg*ptop/pm(k)) & * ( 1. - 1./(1.+rf(k)*sqrt(u2f(i,j,k)/u000))**2 ) enddo enddo else do j=js,je do i=is,ie delz(i,j,k) = delz(i,j,k) / pt(i,j,k) pt(i,j,k) = pt(i,j,k) + 0.5*u2f(i,j,k) * rcv & * ( 1. - 1./(1.+rf(k)*sqrt(u2f(i,j,k)/u000))**2 ) delz(i,j,k) = delz(i,j,k) * pt(i,j,k) enddo enddo endif endif do j=js-1,je+1 do i=is-1,ie+1 u2f(i,j,k) = rf(k)*sqrt(u2f(i,j,k)/u000) enddo enddo do j=js,je+1 do i=is,ie u(i,j,k) = u(i,j,k) / (1.+0.5*(u2f(i,j-1,k)+u2f(i,j,k))) enddo enddo do j=js,je do i=is,ie+1 v(i,j,k) = v(i,j,k) / (1.+0.5*(u2f(i-1,j,k)+u2f(i,j,k))) enddo enddo if ( .not. hydrostatic ) then do j=js,je do i=is,ie w(i,j,k) = w(i,j,k) / (1.+u2f(i,j,k)) enddo enddo endif enddo deallocate ( u2f ) end subroutine Rayleigh_Friction !>@brief The subroutine 'compute_aam' computes vertically (mass) integrated Atmospheric Angular Momentum. subroutine compute_aam(npz, is, ie, js, je, isd, ied, jsd, jed, gridstruct, bd, & ptop, ua, va, u, v, delp, aam, ps, m_fac) ! Compute vertically (mass) integrated Atmospheric Angular Momentum integer, intent(in):: npz integer, intent(in):: is, ie, js, je integer, intent(in):: isd, ied, jsd, jed real, intent(in):: ptop real, intent(inout):: u(isd:ied ,jsd:jed+1,npz) !< D grid zonal wind (m/s) real, intent(inout):: v(isd:ied+1,jsd:jed,npz) !< D grid meridional wind (m/s) real, intent(inout):: delp(isd:ied,jsd:jed,npz) real, intent(inout), dimension(isd:ied,jsd:jed, npz):: ua, va real, intent(out):: aam(is:ie,js:je) real, intent(out):: m_fac(is:ie,js:je) real, intent(out):: ps(isd:ied,jsd:jed) type(fv_grid_bounds_type), intent(IN) :: bd type(fv_grid_type), intent(IN) :: gridstruct ! local: real, dimension(is:ie):: r1, r2, dm integer i, j, k call c2l_ord2(u, v, ua, va, gridstruct, npz, gridstruct%grid_type, bd, gridstruct%bounded_domain) !$OMP parallel do default(none) shared(is,ie,js,je,npz,gridstruct,aam,m_fac,ps,ptop,delp,agrav,ua) & !$OMP private(r1, r2, dm) do j=js,je do i=is,ie r1(i) = radius*cos(gridstruct%agrid(i,j,2)) r2(i) = r1(i)*r1(i) aam(i,j) = 0. m_fac(i,j) = 0. ps(i,j) = ptop enddo do k=1,npz do i=is,ie dm(i) = delp(i,j,k) ps(i,j) = ps(i,j) + dm(i) dm(i) = dm(i)*agrav aam(i,j) = aam(i,j) + (r2(i)*omega + r1(i)*ua(i,j,k)) * dm(i) m_fac(i,j) = m_fac(i,j) + dm(i)*r2(i) enddo enddo enddo end subroutine compute_aam end module fv_dynamics_mod