!***********************************************************************
!* 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_mapz' contains the vertical mapping routines \cite lin2004vertically
!>@note April 12, 2012 -SJL: This revision may actually produce rounding level differences
!! due to the elimination of KS to compute pressure level for remapping.
module fv_mapz_mod
! Modules Included:
!
!
! Module Name |
! Functions Included |
!
!
!
! constants_mod |
! radius, pi=>pi_8, rvgas, rdgas, grav, hlv, hlf, cp_air, cp_vapor |
!
! field_manager_mod |
! MODEL_ATMOS |
!
!
! fv_arrays_mod |
! fv_grid_type |
!
!
! fv_cmp_mod |
! qs_init, fv_sat_adj |
!
!
! fv_fill_mod |
! fillz |
!
!
! fv_grid_utils_mod |
! g_sum, ptop_min |
!
!
! fv_mp_mod |
! is_master |
!
!
! fv_timing_mod |
! timing_on, timing_off |
!
!
! fv_tracer2d_mod |
! tracer_2d, tracer_2d_1L, tracer_2d_nested |
!
!
! mpp_mod/td>
! | NOTE, mpp_error, get_unit, mpp_root_pe, mpp_pe |
!
!
! mpp_domains_mod/td>
! | mpp_update_domains, domain2d |
!
!
! tracer_manager_mod |
! get_tracer_index |
!
!
use constants_mod, only: radius, pi=>pi_8, rvgas, rdgas, grav, hlv, hlf, cp_air, cp_vapor
use tracer_manager_mod,only: get_tracer_index
use field_manager_mod, only: MODEL_ATMOS
use fv_grid_utils_mod, only: g_sum, ptop_min
use fv_fill_mod, only: fillz
use mpp_domains_mod, only: mpp_update_domains, domain2d
use mpp_mod, only: NOTE, FATAL, mpp_error, get_unit, mpp_root_pe, mpp_pe
use fv_arrays_mod, only: fv_grid_type
use fv_timing_mod, only: timing_on, timing_off
use fv_mp_mod, only: is_master
#ifndef CCPP
use fv_cmp_mod, only: qs_init, fv_sat_adj
#else
#ifdef STATIC
! For static builds, the ccpp_physics_{init,run,finalize} calls
! are not pointing to code in the CCPP framework, but to auto-generated
! ccpp_suite_cap and ccpp_group_*_cap modules behind a ccpp_static_api
use ccpp_api, only: ccpp_initialized
use ccpp_static_api, only: ccpp_physics_run
use CCPP_data, only: ccpp_suite
#else
use ccpp_api, only: ccpp_initialized, ccpp_physics_run
#endif
use CCPP_data, only: cdata => cdata_tile, CCPP_interstitial
#endif
#ifdef MULTI_GASES
use multi_gases_mod, only: virq, virqd, vicpqd, vicvqd, num_gas
#endif
implicit none
real, parameter:: consv_min= 0.001 !< below which no correction applies
real, parameter:: t_min= 184. !< below which applies stricter constraint
real, parameter:: r3 = 1./3., r23 = 2./3., r12 = 1./12.
real, parameter:: cv_vap = 3.*rvgas !< 1384.5
real, parameter:: cv_air = cp_air - rdgas !< = rdgas * (7/2-1) = 2.5*rdgas=717.68
! real, parameter:: c_ice = 2106. !< heat capacity of ice at 0.C
real, parameter:: c_ice = 1972. !< heat capacity of ice at -15.C
real, parameter:: c_liq = 4.1855e+3 !< GFS: heat capacity of water at 0C
! real, parameter:: c_liq = 4218. !< ECMWF-IFS
real, parameter:: cp_vap = cp_vapor !< 1846.
real, parameter:: tice = 273.16
real(kind=4) :: E_Flux = 0.
private
public compute_total_energy, Lagrangian_to_Eulerian, moist_cv, moist_cp, &
rst_remap, mappm, E_Flux
contains
!>@brief The subroutine 'Lagrangian_to_Eulerian' remaps deformed Lagrangian layers back to the reference Eulerian coordinate.
!>@details It also includes the entry point for calling fast microphysical processes. This is typically calle on the k_split loop.
subroutine Lagrangian_to_Eulerian(last_step, consv, ps, pe, delp, pkz, pk, &
mdt, pdt, km, is,ie,js,je, isd,ied,jsd,jed, &
nq, nwat, sphum, q_con, u, v, w, delz, pt, q, hs, r_vir, cp, &
akap, cappa, kord_mt, kord_wz, kord_tr, kord_tm, peln, te0_2d, &
ng, ua, va, omga, te, ws, fill, reproduce_sum, out_dt, dtdt, &
ptop, ak, bk, pfull, gridstruct, domain, do_sat_adj, &
hydrostatic, hybrid_z, do_omega, adiabatic, do_adiabatic_init)
logical, intent(in):: last_step
real, intent(in):: mdt !< remap time step
real, intent(in):: pdt !< phys time step
integer, intent(in):: km
integer, intent(in):: nq !< number of tracers (including h2o)
integer, intent(in):: nwat
integer, intent(in):: sphum !< index for water vapor (specific humidity)
integer, intent(in):: ng
integer, intent(in):: is,ie,isd,ied !< starting & ending X-Dir index
integer, intent(in):: js,je,jsd,jed !< starting & ending Y-Dir index
integer, intent(in):: kord_mt !< Mapping order for the vector winds
integer, intent(in):: kord_wz !< Mapping order/option for w
integer, intent(in):: kord_tr(nq) !< Mapping order for tracers
integer, intent(in):: kord_tm !< Mapping order for thermodynamics
real, intent(in):: consv !< factor for TE conservation
real, intent(in):: r_vir
real, intent(in):: cp
real, intent(in):: akap
real, intent(in):: hs(isd:ied,jsd:jed) !< surface geopotential
real, intent(inout):: te0_2d(is:ie,js:je)
real, intent(in):: ws(is:ie,js:je)
logical, intent(in):: do_sat_adj
logical, intent(in):: fill !< fill negative tracers
logical, intent(in):: reproduce_sum
logical, intent(in):: do_omega, adiabatic, do_adiabatic_init
real, intent(in) :: ptop
real, intent(in) :: ak(km+1)
real, intent(in) :: bk(km+1)
real, intent(in):: pfull(km)
type(fv_grid_type), intent(IN), target :: gridstruct
type(domain2d), intent(INOUT) :: domain
! INPUT/OUTPUT
real, intent(inout):: pk(is:ie,js:je,km+1) !< pe to the kappa
real, intent(inout):: q(isd:ied,jsd:jed,km,*)
real, intent(inout):: delp(isd:ied,jsd:jed,km) !< pressure thickness
real, intent(inout):: pe(is-1:ie+1,km+1,js-1:je+1) !< pressure at layer edges
real, intent(inout):: ps(isd:ied,jsd:jed) !< surface pressure
! u-wind will be ghosted one latitude to the north upon exit
real, intent(inout):: u(isd:ied ,jsd:jed+1,km) !< u-wind (m/s)
real, intent(inout):: v(isd:ied+1,jsd:jed ,km) !< v-wind (m/s)
real, intent(inout):: w(isd: ,jsd: ,1:) !< vertical velocity (m/s)
real, intent(inout):: pt(isd:ied ,jsd:jed ,km) !< cp*virtual potential temperature
!< as input; output: temperature
real, intent(inout), dimension(isd:,jsd:,1:)::delz, q_con, cappa
logical, intent(in):: hydrostatic
logical, intent(in):: hybrid_z
logical, intent(in):: out_dt
real, intent(inout):: ua(isd:ied,jsd:jed,km) !< u-wind (m/s) on physics grid
real, intent(inout):: va(isd:ied,jsd:jed,km) !< v-wind (m/s) on physics grid
real, intent(inout):: omga(isd:ied,jsd:jed,km) !< vertical press. velocity (pascal/sec)
real, intent(inout):: peln(is:ie,km+1,js:je) !< log(pe)
real, intent(inout):: dtdt(is:ie,js:je,km)
real, intent(out):: pkz(is:ie,js:je,km) !< layer-mean pk for converting t to pt
real, intent(out):: te(isd:ied,jsd:jed,km)
! !DESCRIPTION:
!
! !REVISION HISTORY:
! SJL 03.11.04: Initial version for partial remapping
!
!-----------------------------------------------------------------------
#ifdef CCPP
real, dimension(is:ie,js:je):: te_2d, zsum0, zsum1
#else
real, dimension(is:ie,js:je):: te_2d, zsum0, zsum1, dpln
#endif
real, dimension(is:ie,km) :: q2, dp2
real, dimension(is:ie,km+1):: pe1, pe2, pk1, pk2, pn2, phis
real, dimension(is:ie+1,km+1):: pe0, pe3
real, dimension(is:ie):: gz, cvm, qv
real rcp, rg, rrg, bkh, dtmp, k1k
#ifndef CCPP
logical:: fast_mp_consv
#endif
integer:: i,j,k
integer:: kdelz
#ifdef CCPP
integer:: nt, liq_wat, ice_wat, rainwat, snowwat, cld_amt, graupel, iq, n, kp, k_next
integer :: ierr
#else
integer:: nt, liq_wat, ice_wat, rainwat, snowwat, cld_amt, graupel, iq, n, kmp, kp, k_next
#endif
#ifdef CCPP
ccpp_associate: associate( fast_mp_consv => CCPP_interstitial%fast_mp_consv, &
kmp => CCPP_interstitial%kmp )
#endif
k1k = rdgas/cv_air ! akap / (1.-akap) = rg/Cv=0.4
rg = rdgas
rcp = 1./ cp
rrg = -rdgas/grav
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')
cld_amt = get_tracer_index (MODEL_ATMOS, 'cld_amt')
if ( do_sat_adj ) then
fast_mp_consv = (.not.do_adiabatic_init) .and. consv>consv_min
#ifndef CCPP
do k=1,km
kmp = k
if ( pfull(k) > 10.E2 ) exit
enddo
call qs_init(kmp)
#endif
endif
!$OMP parallel do default(none) shared(is,ie,js,je,km,pe,ptop,kord_tm,hydrostatic, &
!$OMP pt,pk,rg,peln,q,nwat,liq_wat,rainwat,ice_wat,snowwat, &
!$OMP graupel,q_con,sphum,cappa,r_vir,rcp,k1k,delp, &
!$OMP delz,akap,pkz,te,u,v,ps, gridstruct, last_step, &
!$OMP ak,bk,nq,isd,ied,jsd,jed,kord_tr,fill, adiabatic, &
#ifdef MULTI_GASES
!$OMP num_gas, &
#endif
!$OMP hs,w,ws,kord_wz,do_omega,omga,rrg,kord_mt,ua) &
!$OMP private(qv,gz,cvm,kp,k_next,bkh,dp2, &
!$OMP pe0,pe1,pe2,pe3,pk1,pk2,pn2,phis,q2)
do 1000 j=js,je+1
do k=1,km+1
do i=is,ie
pe1(i,k) = pe(i,k,j)
enddo
enddo
do i=is,ie
pe2(i, 1) = ptop
pe2(i,km+1) = pe(i,km+1,j)
enddo
if ( j /= (je+1) ) then
if ( kord_tm < 0 ) then
! Note: pt at this stage is Theta_v
if ( hydrostatic ) then
! Transform virtual pt to virtual Temp
do k=1,km
do i=is,ie
#ifdef MULTI_GASES
pkz(i,j,k) = (pk(i,j,k+1)-pk(i,j,k))/(akap*(peln(i,k+1,j)-peln(i,k,j)))
pkz(i,j,k) = exp(virqd(q(i,j,k,1:num_gas))/vicpqd(q(i,j,k,1:num_gas))*log(pkz(i,j,k)))
pt(i,j,k) = pt(i,j,k)*pkz(i,j,k)
#else
pt(i,j,k) = pt(i,j,k)*(pk(i,j,k+1)-pk(i,j,k))/(akap*(peln(i,k+1,j)-peln(i,k,j)))
#endif
enddo
enddo
else
! Transform "density pt" to "density temp"
do k=1,km
#ifdef MOIST_CAPPA
call moist_cv(is,ie,isd,ied,jsd,jed, km, j, k, nwat, sphum, liq_wat, rainwat, &
ice_wat, snowwat, graupel, q, gz, cvm)
do i=is,ie
q_con(i,j,k) = gz(i)
#ifdef MULTI_GASES
cappa(i,j,k) = rdgas / ( rdgas + cvm(i)/virq(q(i,j,k,1:num_gas)) )
#else
cappa(i,j,k) = rdgas / ( rdgas + cvm(i)/(1.+r_vir*q(i,j,k,sphum)) )
#endif
pt(i,j,k) = pt(i,j,k)*exp(cappa(i,j,k)/(1.-cappa(i,j,k))*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
enddo
#else
do i=is,ie
#ifdef MULTI_GASES
pt(i,j,k) = pt(i,j,k)*exp(k1k*(virqd(q(i,j,k,1:num_gas))/vicvqd(q(i,j,k,1:num_gas))*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
#else
pt(i,j,k) = pt(i,j,k)*exp(k1k*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
#endif
! Using dry pressure for the definition of the virtual potential temperature
! pt(i,j,k) = pt(i,j,k)*exp(k1k*log(rrg*(1.-q(i,j,k,sphum))*delp(i,j,k)/delz(i,j,k)* &
! pt(i,j,k)/(1.+r_vir*q(i,j,k,sphum))))
enddo
#endif
enddo
endif ! hydro test
elseif ( hydrostatic ) then
call pkez(km, is, ie, js, je, j, pe, pk, akap, peln, pkz, ptop)
! Compute cp_air*Tm + KE
do k=1,km
do i=is,ie
#ifdef MULTI_GASES
pkz(i,j,k) = exp(virqd(q(i,j,k,1:num_gas))/vicpqd(q(i,j,k,1:num_gas))*log(pkz(i,j,k)))
#endif
te(i,j,k) = 0.25*gridstruct%rsin2(i,j)*(u(i,j,k)**2+u(i,j+1,k)**2 + &
v(i,j,k)**2+v(i+1,j,k)**2 - &
(u(i,j,k)+u(i,j+1,k))*(v(i,j,k)+v(i+1,j,k))*gridstruct%cosa_s(i,j)) &
+ cp_air*pt(i,j,k)*pkz(i,j,k)
enddo
enddo
endif
if ( .not. hydrostatic ) then
do k=1,km
do i=is,ie
delz(i,j,k) = -delz(i,j,k) / delp(i,j,k) ! ="specific volume"/grav
enddo
enddo
endif
! update ps
do i=is,ie
ps(i,j) = pe1(i,km+1)
enddo
!
! Hybrid sigma-P coordinate:
!
do k=2,km
do i=is,ie
pe2(i,k) = ak(k) + bk(k)*pe(i,km+1,j)
enddo
enddo
do k=1,km
do i=is,ie
dp2(i,k) = pe2(i,k+1) - pe2(i,k)
enddo
enddo
!------------
! update delp
!------------
do k=1,km
do i=is,ie
delp(i,j,k) = dp2(i,k)
enddo
enddo
!------------------
! Compute p**Kappa
!------------------
do k=1,km+1
do i=is,ie
pk1(i,k) = pk(i,j,k)
enddo
enddo
do i=is,ie
pn2(i, 1) = peln(i, 1,j)
pn2(i,km+1) = peln(i,km+1,j)
pk2(i, 1) = pk1(i, 1)
pk2(i,km+1) = pk1(i,km+1)
enddo
do k=2,km
do i=is,ie
pn2(i,k) = log(pe2(i,k))
pk2(i,k) = exp(akap*pn2(i,k))
enddo
enddo
if ( kord_tm<0 ) then
!----------------------------------
! Map t using logp
!----------------------------------
call map_scalar(km, peln(is,1,j), pt, gz, &
km, pn2, pt, &
is, ie, j, isd, ied, jsd, jed, 1, abs(kord_tm), t_min)
else
! Map pt using pe
call map1_ppm (km, pe1, pt, gz, &
km, pe2, pt, &
is, ie, j, isd, ied, jsd, jed, 1, abs(kord_tm))
endif
!----------------
! Map constituents
!----------------
if( nq > 5 ) then
call mapn_tracer(nq, km, pe1, pe2, q, dp2, kord_tr, j, &
is, ie, isd, ied, jsd, jed, 0., fill)
elseif ( nq > 0 ) then
! Remap one tracer at a time
do iq=1,nq
call map1_q2(km, pe1, q(isd,jsd,1,iq), &
km, pe2, q2, dp2, &
is, ie, 0, kord_tr(iq), j, isd, ied, jsd, jed, 0.)
if (fill) call fillz(ie-is+1, km, 1, q2, dp2)
do k=1,km
do i=is,ie
q(i,j,k,iq) = q2(i,k)
enddo
enddo
enddo
endif
if ( .not. hydrostatic ) then
! Remap vertical wind:
call map1_ppm (km, pe1, w, ws(is,j), &
km, pe2, w, &
is, ie, j, isd, ied, jsd, jed, -2, kord_wz)
! Remap delz for hybrid sigma-p coordinate
call map1_ppm (km, pe1, delz, gz, &
km, pe2, delz, &
is, ie, j, isd, ied, jsd, jed, 1, abs(kord_tm))
do k=1,km
do i=is,ie
delz(i,j,k) = -delz(i,j,k)*dp2(i,k)
enddo
enddo
endif
!----------
! Update pk
!----------
do k=1,km+1
do i=is,ie
pk(i,j,k) = pk2(i,k)
enddo
enddo
!----------------
if ( do_omega ) then
! Start do_omega
! Copy omega field to pe3
do i=is,ie
pe3(i,1) = 0.
enddo
do k=2,km+1
do i=is,ie
pe3(i,k) = omga(i,j,k-1)
enddo
enddo
endif
do k=1,km+1
do i=is,ie
pe0(i,k) = peln(i,k,j)
peln(i,k,j) = pn2(i,k)
enddo
enddo
!------------
! Compute pkz
!hmhj pk is pe**kappa(=rgas/cp_air), but pkz=plyr**kappa(=r/cp)
!------------
if ( hydrostatic ) then
do k=1,km
do i=is,ie
pkz(i,j,k) = (pk2(i,k+1)-pk2(i,k))/(akap*(peln(i,k+1,j)-peln(i,k,j)))
#ifdef MULTI_GASES
pkz(i,j,k) = exp(virqd(q(i,j,k,1:num_gas))/vicpqd(q(i,j,k,1:num_gas))*log(pkz(i,j,k)))
#endif
enddo
enddo
else
! Note: pt at this stage is T_v or T_m
do k=1,km
#ifdef MOIST_CAPPA
call moist_cv(is,ie,isd,ied,jsd,jed, km, j, k, nwat, sphum, liq_wat, rainwat, &
ice_wat, snowwat, graupel, q, gz, cvm)
do i=is,ie
q_con(i,j,k) = gz(i)
#ifdef MULTI_GASES
cappa(i,j,k) = rdgas / ( rdgas + cvm(i)/virq(q(i,j,k,1:num_gas)) )
#else
cappa(i,j,k) = rdgas / ( rdgas + cvm(i)/(1.+r_vir*q(i,j,k,sphum)) )
#endif
pkz(i,j,k) = exp(cappa(i,j,k)*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
enddo
#else
if ( kord_tm < 0 ) then
do i=is,ie
#ifdef MULTI_GASES
pkz(i,j,k) = exp(akap*virqd(q(i,j,k,1:num_gas))/vicpqd(q(i,j,k,1:num_gas))*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
#else
pkz(i,j,k) = exp(akap*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
#endif
! Using dry pressure for the definition of the virtual potential temperature
! pkz(i,j,k) = exp(akap*log(rrg*(1.-q(i,j,k,sphum))*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)/(1.+r_vir*q(i,j,k,sphum))))
enddo
else
do i=is,ie
#ifdef MULTI_GASES
pkz(i,j,k) = exp(k1k*virqd(q(i,j,k,1:num_gas))/vicvqd(q(i,j,k,1:num_gas))*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
#else
pkz(i,j,k) = exp(k1k*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
#endif
! Using dry pressure for the definition of the virtual potential temperature
! pkz(i,j,k) = exp(k1k*log(rrg*(1.-q(i,j,k,sphum))*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)/(1.+r_vir*q(i,j,k,sphum))))
enddo
if ( last_step .and. (.not.adiabatic) ) then
do i=is,ie
pt(i,j,k) = pt(i,j,k)*pkz(i,j,k)
enddo
endif
endif
#endif
enddo
endif
! Interpolate omega/pe3 (defined at pe0) to remapped cell center (dp2)
if ( do_omega ) then
do k=1,km
do i=is,ie
dp2(i,k) = 0.5*(peln(i,k,j) + peln(i,k+1,j))
enddo
enddo
do i=is,ie
k_next = 1
do n=1,km
kp = k_next
do k=kp,km
if( dp2(i,n) <= pe0(i,k+1) .and. dp2(i,n) >= pe0(i,k) ) then
omga(i,j,n) = pe3(i,k) + (pe3(i,k+1) - pe3(i,k)) * &
(dp2(i,n)-pe0(i,k)) / (pe0(i,k+1)-pe0(i,k) )
k_next = k
exit
endif
enddo
enddo
enddo
endif ! end do_omega
endif !(j < je+1)
do i=is,ie+1
pe0(i,1) = pe(i,1,j)
enddo
!------
! map u
!------
do k=2,km+1
do i=is,ie
pe0(i,k) = 0.5*(pe(i,k,j-1)+pe1(i,k))
enddo
enddo
do k=1,km+1
bkh = 0.5*bk(k)
do i=is,ie
pe3(i,k) = ak(k) + bkh*(pe(i,km+1,j-1)+pe1(i,km+1))
enddo
enddo
call map1_ppm( km, pe0(is:ie,:), u, gz, &
km, pe3(is:ie,:), u, &
is, ie, j, isd, ied, jsd, jed+1, -1, kord_mt)
if (j < je+1) then
!------
! map v
!------
do i=is,ie+1
pe3(i,1) = ak(1)
enddo
do k=2,km+1
bkh = 0.5*bk(k)
do i=is,ie+1
pe0(i,k) = 0.5*(pe(i-1,k, j)+pe(i,k, j))
pe3(i,k) = ak(k) + bkh*(pe(i-1,km+1,j)+pe(i,km+1,j))
enddo
enddo
call map1_ppm (km, pe0, v, gz, &
km, pe3, v, is, ie+1, &
j, isd, ied+1, jsd, jed, -1, kord_mt)
endif ! (j < je+1)
do k=1,km
do i=is,ie
ua(i,j,k) = pe2(i,k+1)
enddo
enddo
1000 continue
#if defined(CCPP) && defined(__GFORTRAN__)
!$OMP parallel default(none) shared(is,ie,js,je,km,ptop,u,v,pe,ua,isd,ied,jsd,jed,kord_mt, &
!$OMP te_2d,te,delp,hydrostatic,hs,rg,pt,peln, adiabatic, &
!$OMP cp,delz,nwat,rainwat,liq_wat,ice_wat,snowwat, &
!$OMP graupel,q_con,r_vir,sphum,w,pk,pkz,last_step,consv, &
!$OMP do_adiabatic_init,zsum1,zsum0,te0_2d,domain, &
!$OMP ng,gridstruct,E_Flux,pdt,dtmp,reproduce_sum,q, &
!$OMP mdt,cld_amt,cappa,dtdt,out_dt,rrg,akap,do_sat_adj, &
!$OMP kord_tm,cdata,CCPP_interstitial) &
#ifdef STATIC
!$OMP shared(ccpp_suite) &
#endif
#ifdef MULTI_GASES
!$OMP shared(num_gas) &
#endif
!$OMP private(pe0,pe1,pe2,pe3,qv,cvm,gz,phis,kdelz,ierr)
#elif defined(CCPP)
!$OMP parallel default(none) shared(is,ie,js,je,km,kmp,ptop,u,v,pe,ua,isd,ied,jsd,jed,kord_mt, &
!$OMP te_2d,te,delp,hydrostatic,hs,rg,pt,peln, adiabatic, &
!$OMP cp,delz,nwat,rainwat,liq_wat,ice_wat,snowwat, &
!$OMP graupel,q_con,r_vir,sphum,w,pk,pkz,last_step,consv, &
!$OMP do_adiabatic_init,zsum1,zsum0,te0_2d,domain, &
!$OMP ng,gridstruct,E_Flux,pdt,dtmp,reproduce_sum,q, &
!$OMP mdt,cld_amt,cappa,dtdt,out_dt,rrg,akap,do_sat_adj, &
!$OMP fast_mp_consv,kord_tm,cdata, CCPP_interstitial) &
#ifdef STATIC
!$OMP shared(ccpp_suite) &
#endif
#ifdef MULTI_GASES
!$OMP shared(num_gas) &
#endif
!$OMP private(pe0,pe1,pe2,pe3,qv,cvm,gz,phis,kdelz,ierr)
#else
!$OMP parallel default(none) shared(is,ie,js,je,km,kmp,ptop,u,v,pe,ua,isd,ied,jsd,jed,kord_mt, &
!$OMP te_2d,te,delp,hydrostatic,hs,rg,pt,peln, adiabatic, &
!$OMP cp,delz,nwat,rainwat,liq_wat,ice_wat,snowwat, &
!$OMP graupel,q_con,r_vir,sphum,w,pk,pkz,last_step,consv, &
!$OMP do_adiabatic_init,zsum1,zsum0,te0_2d,domain, &
!$OMP ng,gridstruct,E_Flux,pdt,dtmp,reproduce_sum,q, &
!$OMP mdt,cld_amt,cappa,dtdt,out_dt,rrg,akap,do_sat_adj, &
!$OMP fast_mp_consv,kord_tm) &
#ifdef MULTI_GASES
!$OMP shared(num_gas) &
#endif
!$OMP private(pe0,pe1,pe2,pe3,qv,cvm,gz,phis,kdelz,dpln)
#endif
!$OMP do
do k=2,km
do j=js,je
do i=is,ie
pe(i,k,j) = ua(i,j,k-1)
enddo
enddo
enddo
dtmp = 0.
if( last_step .and. (.not.do_adiabatic_init) ) then
if ( consv > consv_min ) then
!$OMP do
do j=js,je
if ( hydrostatic ) then
do i=is,ie
gz(i) = hs(i,j)
do k=1,km
gz(i) = gz(i) + rg*pt(i,j,k)*(peln(i,k+1,j)-peln(i,k,j))
enddo
enddo
do i=is,ie
te_2d(i,j) = pe(i,km+1,j)*hs(i,j) - pe(i,1,j)*gz(i)
enddo
do k=1,km
do i=is,ie
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*(cp*pt(i,j,k) + &
0.25*gridstruct%rsin2(i,j)*(u(i,j,k)**2+u(i,j+1,k)**2 + &
v(i,j,k)**2+v(i+1,j,k)**2 - &
(u(i,j,k)+u(i,j+1,k))*(v(i,j,k)+v(i+1,j,k))*gridstruct%cosa_s(i,j)))
enddo
enddo
else
do i=is,ie
te_2d(i,j) = 0.
phis(i,km+1) = hs(i,j)
enddo
do k=km,1,-1
do i=is,ie
phis(i,k) = phis(i,k+1) - grav*delz(i,j,k)
enddo
enddo
do k=1,km
#ifdef MOIST_CAPPA
call moist_cv(is,ie,isd,ied,jsd,jed, km, j, k, nwat, sphum, liq_wat, rainwat, &
ice_wat, snowwat, graupel, q, gz, cvm)
do i=is,ie
! KE using 3D winds:
q_con(i,j,k) = gz(i)
#ifdef MULTI_GASES
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*(cvm(i)*pt(i,j,k)/virq(q(i,j,k,1:num_gas)) + &
#else
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*(cvm(i)*pt(i,j,k)/((1.+r_vir*q(i,j,k,sphum))*(1.-gz(i))) + &
#endif
0.5*(phis(i,k)+phis(i,k+1) + w(i,j,k)**2 + 0.5*gridstruct%rsin2(i,j)*( &
u(i,j,k)**2+u(i,j+1,k)**2 + v(i,j,k)**2+v(i+1,j,k)**2 - &
(u(i,j,k)+u(i,j+1,k))*(v(i,j,k)+v(i+1,j,k))*gridstruct%cosa_s(i,j))))
enddo
#else
do i=is,ie
#ifdef MULTI_GASES
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*(cv_air*pt(i,j,k)/virq(q(i,j,k,1:num_gas)) + &
#else
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*(cv_air*pt(i,j,k)/(1.+r_vir*q(i,j,k,sphum)) + &
#endif
0.5*(phis(i,k)+phis(i,k+1) + w(i,j,k)**2 + 0.5*gridstruct%rsin2(i,j)*( &
u(i,j,k)**2+u(i,j+1,k)**2 + v(i,j,k)**2+v(i+1,j,k)**2 - &
(u(i,j,k)+u(i,j+1,k))*(v(i,j,k)+v(i+1,j,k))*gridstruct%cosa_s(i,j))))
enddo
#endif
enddo ! k-loop
endif ! end non-hydro
do i=is,ie
te_2d(i,j) = te0_2d(i,j) - te_2d(i,j)
zsum1(i,j) = pkz(i,j,1)*delp(i,j,1)
enddo
do k=2,km
do i=is,ie
zsum1(i,j) = zsum1(i,j) + pkz(i,j,k)*delp(i,j,k)
enddo
enddo
if ( hydrostatic ) then
do i=is,ie
zsum0(i,j) = ptop*(pk(i,j,1)-pk(i,j,km+1)) + zsum1(i,j)
enddo
endif
enddo ! j-loop
!$OMP single
dtmp = consv*g_sum(domain, te_2d, is, ie, js, je, ng, gridstruct%area_64, 0, reproduce=.true.)
E_Flux = dtmp / (grav*pdt*4.*pi*radius**2) ! unit: W/m**2
! Note pdt is "phys" time step
if ( hydrostatic ) then
dtmp = dtmp / (cp* g_sum(domain, zsum0, is, ie, js, je, ng, gridstruct%area_64, 0, reproduce=.true.))
else
dtmp = dtmp / (cv_air*g_sum(domain, zsum1, is, ie, js, je, ng, gridstruct%area_64, 0, reproduce=.true.))
endif
!$OMP end single
elseif ( consv < -consv_min ) then
!$OMP do
do j=js,je
do i=is,ie
zsum1(i,j) = pkz(i,j,1)*delp(i,j,1)
enddo
do k=2,km
do i=is,ie
zsum1(i,j) = zsum1(i,j) + pkz(i,j,k)*delp(i,j,k)
enddo
enddo
if ( hydrostatic ) then
do i=is,ie
zsum0(i,j) = ptop*(pk(i,j,1)-pk(i,j,km+1)) + zsum1(i,j)
enddo
endif
enddo
E_Flux = consv
!$OMP single
if ( hydrostatic ) then
dtmp = E_flux*(grav*pdt*4.*pi*radius**2) / &
(cp*g_sum(domain, zsum0, is, ie, js, je, ng, gridstruct%area_64, 0, reproduce=.true.))
else
dtmp = E_flux*(grav*pdt*4.*pi*radius**2) / &
(cv_air*g_sum(domain, zsum1, is, ie, js, je, ng, gridstruct%area_64, 0, reproduce=.true.))
endif
!$OMP end single
endif ! end consv check
endif ! end last_step check
! Note: pt at this stage is T_v
! if ( (.not.do_adiabatic_init) .and. do_sat_adj ) then
if ( do_sat_adj ) then
call timing_on('sat_adj2')
#ifdef CCPP
if (ccpp_initialized(cdata)) then
#ifdef STATIC
call ccpp_physics_run(cdata, suite_name=trim(ccpp_suite), group_name='fast_physics', ierr=ierr)
#else
call ccpp_physics_run(cdata, group_name='fast_physics', ierr=ierr)
#endif
if (ierr/=0) call mpp_error(FATAL, "Call to ccpp_physics_run for group 'fast_physics' failed")
else
call mpp_error (FATAL, 'Lagrangian_to_Eulerian: can not call CCPP fast physics because cdata not initialized')
endif
#else
!$OMP do
do k=kmp,km
do j=js,je
do i=is,ie
dpln(i,j) = peln(i,k+1,j) - peln(i,k,j)
enddo
enddo
if (hydrostatic) then
kdelz = 1
else
kdelz = k
end if
call fv_sat_adj(abs(mdt), r_vir, is, ie, js, je, ng, hydrostatic, fast_mp_consv, &
te(isd,jsd,k), &
#ifdef MULTI_GASES
km, &
#endif
q(isd,jsd,k,sphum), q(isd,jsd,k,liq_wat), &
q(isd,jsd,k,ice_wat), q(isd,jsd,k,rainwat), &
q(isd,jsd,k,snowwat), q(isd,jsd,k,graupel), &
hs ,dpln, delz(isd:,jsd:,kdelz), pt(isd,jsd,k), delp(isd,jsd,k), q_con(isd:,jsd:,k), &
cappa(isd:,jsd:,k), gridstruct%area_64, dtdt(is,js,k), out_dt, last_step, cld_amt>0, q(isd,jsd,k,cld_amt))
if ( .not. hydrostatic ) then
do j=js,je
do i=is,ie
#ifdef MOIST_CAPPA
pkz(i,j,k) = exp(cappa(i,j,k)*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
#else
#ifdef MULTI_GASES
pkz(i,j,k) = exp(akap*(virqd(q(i,j,k,1:num_gas))/vicpqd(q(i,j,k,1:num_gas))*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
#else
pkz(i,j,k) = exp(akap*log(rrg*delp(i,j,k)/delz(i,j,k)*pt(i,j,k)))
#endif
#endif
enddo
enddo
endif
enddo ! OpenMP k-loop
if ( fast_mp_consv ) then
!$OMP do
do j=js,je
do i=is,ie
do k=kmp,km
te0_2d(i,j) = te0_2d(i,j) + te(i,j,k)
enddo
enddo
enddo
endif
#endif
call timing_off('sat_adj2')
endif ! do_sat_adj
if ( last_step ) then
! Output temperature if last_step
!!! if ( is_master() ) write(*,*) 'dtmp=', dtmp, nwat
!$OMP do
do k=1,km
do j=js,je
#ifdef USE_COND
if ( nwat==2 ) then
do i=is,ie
gz(i) = max(0., q(i,j,k,liq_wat))
qv(i) = max(0., q(i,j,k,sphum))
#ifdef MULTI_GASES
pt(i,j,k) = (pt(i,j,k)+dtmp*pkz(i,j,k)) / virq(q(i,j,k,1:num_gas))
#else
pt(i,j,k) = (pt(i,j,k)+dtmp*pkz(i,j,k)) / ((1.+r_vir*qv(i))*(1.-gz(i)))
#endif
enddo
elseif ( nwat==6 ) then
do i=is,ie
gz(i) = q(i,j,k,liq_wat)+q(i,j,k,rainwat)+q(i,j,k,ice_wat)+q(i,j,k,snowwat)+q(i,j,k,graupel)
#ifdef MULTI_GASES
pt(i,j,k) = (pt(i,j,k)+dtmp*pkz(i,j,k))/ virq(q(i,j,k,1:num_gas))
#else
pt(i,j,k) = (pt(i,j,k)+dtmp*pkz(i,j,k))/((1.+r_vir*q(i,j,k,sphum))*(1.-gz(i)))
#endif
enddo
else
call moist_cv(is,ie,isd,ied,jsd,jed, km, j, k, nwat, sphum, liq_wat, rainwat, &
ice_wat, snowwat, graupel, q, gz, cvm)
do i=is,ie
#ifdef MULTI_GASES
pt(i,j,k) = (pt(i,j,k)+dtmp*pkz(i,j,k)) / virq(q(i,j,k,1:num_gas))
#else
pt(i,j,k) = (pt(i,j,k)+dtmp*pkz(i,j,k)) / ((1.+r_vir*q(i,j,k,sphum))*(1.-gz(i)))
#endif
enddo
endif
#else
if ( .not. adiabatic ) then
do i=is,ie
#ifdef MULTI_GASES
pt(i,j,k) = (pt(i,j,k)+dtmp*pkz(i,j,k)) / virq(q(i,j,k,1:num_gas))
#else
pt(i,j,k) = (pt(i,j,k)+dtmp*pkz(i,j,k)) / (1.+r_vir*q(i,j,k,sphum))
#endif
enddo
endif
#endif
enddo ! j-loop
enddo ! k-loop
else ! not last_step
if ( kord_tm < 0 ) then
!$OMP do
do k=1,km
do j=js,je
do i=is,ie
pt(i,j,k) = pt(i,j,k)/pkz(i,j,k)
enddo
enddo
enddo
endif
endif
!$OMP end parallel
#ifdef CCPP
end associate ccpp_associate
#endif
end subroutine Lagrangian_to_Eulerian
!>@brief The subroutine 'compute_total_energy' performs the FV3-consistent computation of the global total energy.
!>@details It includes the potential, internal (latent and sensible heat), kinetic terms.
subroutine compute_total_energy(is, ie, js, je, isd, ied, jsd, jed, km, &
u, v, w, delz, pt, delp, q, qc, pe, peln, hs, &
rsin2_l, cosa_s_l, &
r_vir, cp, rg, hlv, te_2d, ua, va, teq, &
moist_phys, nwat, sphum, liq_wat, rainwat, ice_wat, snowwat, graupel, hydrostatic, id_te)
!------------------------------------------------------
! Compute vertically integrated total energy per column
!------------------------------------------------------
! !INPUT PARAMETERS:
integer, intent(in):: km, is, ie, js, je, isd, ied, jsd, jed, id_te
integer, intent(in):: sphum, liq_wat, ice_wat, rainwat, snowwat, graupel, nwat
real, intent(inout), dimension(isd:ied,jsd:jed,km):: ua, va
real, intent(in), dimension(isd:ied,jsd:jed,km):: pt, delp
real, intent(in), dimension(isd:ied,jsd:jed,km,*):: q
real, intent(in), dimension(isd:ied,jsd:jed,km):: qc
real, intent(inout):: u(isd:ied, jsd:jed+1,km)
real, intent(inout):: v(isd:ied+1,jsd:jed, km)
real, intent(in):: w(isd:,jsd:,1:) !< vertical velocity (m/s)
real, intent(in):: delz(isd:,jsd:,1:)
real, intent(in):: hs(isd:ied,jsd:jed) !< surface geopotential
real, intent(in):: pe(is-1:ie+1,km+1,js-1:je+1) !< pressure at layer edges
real, intent(in):: peln(is:ie,km+1,js:je) !< log(pe)
real, intent(in):: cp, rg, r_vir, hlv
real, intent(in) :: rsin2_l(isd:ied, jsd:jed)
real, intent(in) :: cosa_s_l(isd:ied, jsd:jed)
logical, intent(in):: moist_phys, hydrostatic
!! Output:
real, intent(out):: te_2d(is:ie,js:je) !< vertically integrated TE
real, intent(out):: teq(is:ie,js:je) !< Moist TE
!! Local
real, dimension(is:ie,km):: tv
real phiz(is:ie,km+1)
real cvm(is:ie), qd(is:ie)
integer i, j, k
!----------------------
! Output lat-lon winds:
!----------------------
! call cubed_to_latlon(u, v, ua, va, dx, dy, rdxa, rdya, km, flagstruct%c2l_ord)
!$OMP parallel do default(none) shared(is,ie,js,je,isd,ied,jsd,jed,km,hydrostatic,hs,pt,qc,rg,peln,te_2d, &
!$OMP pe,delp,cp,rsin2_l,u,v,cosa_s_l,delz,moist_phys,w, &
#ifdef MULTI_GASES
!$OMP num_gas, &
#endif
!$OMP q,nwat,liq_wat,rainwat,ice_wat,snowwat,graupel,sphum) &
!$OMP private(phiz, tv, cvm, qd)
do j=js,je
if ( hydrostatic ) then
do i=is,ie
phiz(i,km+1) = hs(i,j)
enddo
do k=km,1,-1
do i=is,ie
tv(i,k) = pt(i,j,k)*(1.+qc(i,j,k))
phiz(i,k) = phiz(i,k+1) + rg*tv(i,k)*(peln(i,k+1,j)-peln(i,k,j))
enddo
enddo
do i=is,ie
te_2d(i,j) = pe(i,km+1,j)*phiz(i,km+1) - pe(i,1,j)*phiz(i,1)
enddo
do k=1,km
do i=is,ie
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*(cp*tv(i,k) + &
0.25*rsin2_l(i,j)*(u(i,j,k)**2+u(i,j+1,k)**2 + &
v(i,j,k)**2+v(i+1,j,k)**2 - &
(u(i,j,k)+u(i,j+1,k))*(v(i,j,k)+v(i+1,j,k))*cosa_s_l(i,j)))
enddo
enddo
else
!-----------------
! Non-hydrostatic:
!-----------------
do i=is,ie
phiz(i,km+1) = hs(i,j)
do k=km,1,-1
phiz(i,k) = phiz(i,k+1) - grav*delz(i,j,k)
enddo
enddo
do i=is,ie
te_2d(i,j) = 0.
enddo
if ( moist_phys ) then
do k=1,km
#ifdef MOIST_CAPPA
call moist_cv(is,ie,isd,ied,jsd,jed, km, j, k, nwat, sphum, liq_wat, rainwat, &
ice_wat, snowwat, graupel, q, qd, cvm)
#endif
do i=is,ie
#ifdef MOIST_CAPPA
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*( cvm(i)*pt(i,j,k) + &
#else
#ifdef MULTI_GASES
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*( cv_air*vicvqd(q(i,j,k,1:num_gas) )*pt(i,j,k) + &
#else
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*( cv_air*pt(i,j,k) + &
#endif
#endif
0.5*(phiz(i,k)+phiz(i,k+1)+w(i,j,k)**2+0.5*rsin2_l(i,j)*(u(i,j,k)**2+u(i,j+1,k)**2 + &
v(i,j,k)**2+v(i+1,j,k)**2-(u(i,j,k)+u(i,j+1,k))*(v(i,j,k)+v(i+1,j,k))*cosa_s_l(i,j))))
enddo
enddo
else
do k=1,km
do i=is,ie
#ifdef MULTI_GASES
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*( cv_air*vicvqd(q(i,j,k,1:num_gas))*pt(i,j,k) + &
#else
te_2d(i,j) = te_2d(i,j) + delp(i,j,k)*( cv_air*pt(i,j,k) + &
#endif
0.5*(phiz(i,k)+phiz(i,k+1)+w(i,j,k)**2+0.5*rsin2_l(i,j)*(u(i,j,k)**2+u(i,j+1,k)**2 + &
v(i,j,k)**2+v(i+1,j,k)**2-(u(i,j,k)+u(i,j+1,k))*(v(i,j,k)+v(i+1,j,k))*cosa_s_l(i,j))))
enddo
enddo
endif
endif
enddo
!-------------------------------------
! Diganostics computation for moist TE
!-------------------------------------
if( id_te>0 ) then
!$OMP parallel do default(none) shared(is,ie,js,je,teq,te_2d,moist_phys,km,hlv,sphum,q,delp)
do j=js,je
do i=is,ie
teq(i,j) = te_2d(i,j)
enddo
if ( moist_phys ) then
do k=1,km
do i=is,ie
teq(i,j) = teq(i,j) + hlv*q(i,j,k,sphum)*delp(i,j,k)
enddo
enddo
endif
enddo
endif
end subroutine compute_total_energy
subroutine pkez(km, ifirst, ilast, jfirst, jlast, j, &
pe, pk, akap, peln, pkz, ptop)
! INPUT PARAMETERS:
integer, intent(in):: km, j
integer, intent(in):: ifirst, ilast !< Latitude strip
integer, intent(in):: jfirst, jlast !< Latitude strip
real, intent(in):: akap
real, intent(in):: pe(ifirst-1:ilast+1,km+1,jfirst-1:jlast+1)
real, intent(in):: pk(ifirst:ilast,jfirst:jlast,km+1)
real, intent(IN):: ptop
! OUTPUT
real, intent(out):: pkz(ifirst:ilast,jfirst:jlast,km)
real, intent(inout):: peln(ifirst:ilast, km+1, jfirst:jlast) !< log (pe)
! Local
real pk2(ifirst:ilast, km+1)
real pek
real lnp
real ak1
integer i, k
ak1 = (akap + 1.) / akap
pek = pk(ifirst,j,1)
do i=ifirst, ilast
pk2(i,1) = pek
enddo
do k=2,km+1
do i=ifirst, ilast
! peln(i,k,j) = log(pe(i,k,j))
pk2(i,k) = pk(i,j,k)
enddo
enddo
!---- GFDL modification
if( ptop < ptop_min ) then
do i=ifirst, ilast
peln(i,1,j) = peln(i,2,j) - ak1
enddo
else
lnp = log( ptop )
do i=ifirst, ilast
peln(i,1,j) = lnp
enddo
endif
!---- GFDL modification
do k=1,km
do i=ifirst, ilast
pkz(i,j,k) = (pk2(i,k+1) - pk2(i,k) ) / &
(akap*(peln(i,k+1,j) - peln(i,k,j)) )
enddo
enddo
end subroutine pkez
subroutine remap_z(km, pe1, q1, kn, pe2, q2, i1, i2, iv, kord)
! INPUT PARAMETERS:
integer, intent(in) :: i1 !< Starting longitude
integer, intent(in) :: i2 !< Finishing longitude
integer, intent(in) :: kord !< Method order
integer, intent(in) :: km !< Original vertical dimension
integer, intent(in) :: kn !< Target vertical dimension
integer, intent(in) :: iv
real, intent(in) :: pe1(i1:i2,km+1) !< height at layer edges from model top to bottom surface
real, intent(in) :: pe2(i1:i2,kn+1) !< height at layer edges from model top to bottom surface
real, intent(in) :: q1(i1:i2,km) !< Field input
! INPUT/OUTPUT PARAMETERS:
real, intent(inout):: q2(i1:i2,kn) !< Field output
! LOCAL VARIABLES:
real qs(i1:i2)
real dp1( i1:i2,km)
real q4(4,i1:i2,km)
real pl, pr, qsum, delp, esl
integer i, k, l, m, k0
do k=1,km
do i=i1,i2
dp1(i,k) = pe1(i,k+1) - pe1(i,k) ! negative
q4(1,i,k) = q1(i,k)
enddo
enddo
! Compute vertical subgrid distribution
if ( kord >7 ) then
call cs_profile( qs, q4, dp1, km, i1, i2, iv, kord )
else
call ppm_profile( q4, dp1, km, i1, i2, iv, kord )
endif
! Mapping
do 1000 i=i1,i2
k0 = 1
do 555 k=1,kn
do 100 l=k0,km
! locate the top edge: pe2(i,k)
if(pe2(i,k) <= pe1(i,l) .and. pe2(i,k) >= pe1(i,l+1)) then
pl = (pe2(i,k)-pe1(i,l)) / dp1(i,l)
if(pe2(i,k+1) >= pe1(i,l+1)) then
! entire new grid is within the original grid
pr = (pe2(i,k+1)-pe1(i,l)) / dp1(i,l)
q2(i,k) = q4(2,i,l) + 0.5*(q4(4,i,l)+q4(3,i,l)-q4(2,i,l)) &
*(pr+pl)-q4(4,i,l)*r3*(pr*(pr+pl)+pl**2)
k0 = l
goto 555
else
! Fractional area...
qsum = (pe1(i,l+1)-pe2(i,k))*(q4(2,i,l)+0.5*(q4(4,i,l)+ &
q4(3,i,l)-q4(2,i,l))*(1.+pl)-q4(4,i,l)* &
(r3*(1.+pl*(1.+pl))))
do m=l+1,km
! locate the bottom edge: pe2(i,k+1)
if(pe2(i,k+1) < pe1(i,m+1) ) then
! Whole layer..
qsum = qsum + dp1(i,m)*q4(1,i,m)
else
delp = pe2(i,k+1)-pe1(i,m)
esl = delp / dp1(i,m)
qsum = qsum + delp*(q4(2,i,m)+0.5*esl* &
(q4(3,i,m)-q4(2,i,m)+q4(4,i,m)*(1.-r23*esl)))
k0 = m
goto 123
endif
enddo
goto 123
endif
endif
100 continue
123 q2(i,k) = qsum / ( pe2(i,k+1) - pe2(i,k) )
555 continue
1000 continue
end subroutine remap_z
subroutine map_scalar( km, pe1, q1, qs, &
kn, pe2, q2, i1, i2, &
j, ibeg, iend, jbeg, jend, iv, kord, q_min)
! iv=1
integer, intent(in) :: i1 !< Starting longitude
integer, intent(in) :: i2 !< Finishing longitude
integer, intent(in) :: iv !< Mode: 0 == constituents 1 == temp 2 == remap temp with cs scheme
integer, intent(in) :: kord !< Method order
integer, intent(in) :: j !< Current latitude
integer, intent(in) :: ibeg, iend, jbeg, jend
integer, intent(in) :: km !< Original vertical dimension
integer, intent(in) :: kn !< Target vertical dimension
real, intent(in) :: qs(i1:i2) !< bottom BC
real, intent(in) :: pe1(i1:i2,km+1) !< pressure at layer edges from model top to bottom surface in the original vertical coordinate
real, intent(in) :: pe2(i1:i2,kn+1) !< pressure at layer edges from model top to bottom surface in the new vertical coordinate
real, intent(in) :: q1(ibeg:iend,jbeg:jend,km) !< Field input
! INPUT/OUTPUT PARAMETERS:
real, intent(inout):: q2(ibeg:iend,jbeg:jend,kn) !< Field output
real, intent(in):: q_min
! DESCRIPTION:
! IV = 0: constituents
! pe1: pressure at layer edges (from model top to bottom surface)
! in the original vertical coordinate
! pe2: pressure at layer edges (from model top to bottom surface)
! in the new vertical coordinate
! LOCAL VARIABLES:
real dp1(i1:i2,km)
real q4(4,i1:i2,km)
real pl, pr, qsum, dp, esl
integer i, k, l, m, k0
do k=1,km
do i=i1,i2
dp1(i,k) = pe1(i,k+1) - pe1(i,k)
q4(1,i,k) = q1(i,j,k)
enddo
enddo
! Compute vertical subgrid distribution
if ( kord >7 ) then
call scalar_profile( qs, q4, dp1, km, i1, i2, iv, kord, q_min )
else
call ppm_profile( q4, dp1, km, i1, i2, iv, kord )
endif
do i=i1,i2
k0 = 1
do 555 k=1,kn
do l=k0,km
! locate the top edge: pe2(i,k)
if( pe2(i,k) >= pe1(i,l) .and. pe2(i,k) <= pe1(i,l+1) ) then
pl = (pe2(i,k)-pe1(i,l)) / dp1(i,l)
if( pe2(i,k+1) <= pe1(i,l+1) ) then
! entire new grid is within the original grid
pr = (pe2(i,k+1)-pe1(i,l)) / dp1(i,l)
q2(i,j,k) = q4(2,i,l) + 0.5*(q4(4,i,l)+q4(3,i,l)-q4(2,i,l)) &
*(pr+pl)-q4(4,i,l)*r3*(pr*(pr+pl)+pl**2)
k0 = l
goto 555
else
! Fractional area...
qsum = (pe1(i,l+1)-pe2(i,k))*(q4(2,i,l)+0.5*(q4(4,i,l)+ &
q4(3,i,l)-q4(2,i,l))*(1.+pl)-q4(4,i,l)* &
(r3*(1.+pl*(1.+pl))))
do m=l+1,km
! locate the bottom edge: pe2(i,k+1)
if( pe2(i,k+1) > pe1(i,m+1) ) then
! Whole layer
qsum = qsum + dp1(i,m)*q4(1,i,m)
else
dp = pe2(i,k+1)-pe1(i,m)
esl = dp / dp1(i,m)
qsum = qsum + dp*(q4(2,i,m)+0.5*esl* &
(q4(3,i,m)-q4(2,i,m)+q4(4,i,m)*(1.-r23*esl)))
k0 = m
goto 123
endif
enddo
goto 123
endif
endif
enddo
123 q2(i,j,k) = qsum / ( pe2(i,k+1) - pe2(i,k) )
555 continue
enddo
end subroutine map_scalar
subroutine map1_ppm( km, pe1, q1, qs, &
kn, pe2, q2, i1, i2, &
j, ibeg, iend, jbeg, jend, iv, kord)
integer, intent(in) :: i1 !< Starting longitude
integer, intent(in) :: i2 !< Finishing longitude
integer, intent(in) :: iv !< Mode: 0 == constituents 1 == ??? 2 == remap temp with cs scheme
integer, intent(in) :: kord !< Method order
integer, intent(in) :: j !< Current latitude
integer, intent(in) :: ibeg, iend, jbeg, jend
integer, intent(in) :: km !< Original vertical dimension
integer, intent(in) :: kn !< Target vertical dimension
real, intent(in) :: qs(i1:i2) !< bottom BC
real, intent(in) :: pe1(i1:i2,km+1) !< pressure at layer edges from model top to bottom surface in the original vertical coordinate
real, intent(in) :: pe2(i1:i2,kn+1) !< pressure at layer edges from model top to bottom surface in the new vertical coordinate
real, intent(in) :: q1(ibeg:iend,jbeg:jend,km) !< Field input
! INPUT/OUTPUT PARAMETERS:
real, intent(inout):: q2(ibeg:iend,jbeg:jend,kn) !< Field output
! DESCRIPTION:
! IV = 0: constituents
! pe1: pressure at layer edges (from model top to bottom surface)
! in the original vertical coordinate
! pe2: pressure at layer edges (from model top to bottom surface)
! in the new vertical coordinate
! LOCAL VARIABLES:
real dp1(i1:i2,km)
real q4(4,i1:i2,km)
real pl, pr, qsum, dp, esl
integer i, k, l, m, k0
do k=1,km
do i=i1,i2
dp1(i,k) = pe1(i,k+1) - pe1(i,k)
q4(1,i,k) = q1(i,j,k)
enddo
enddo
! Compute vertical subgrid distribution
if ( kord >7 ) then
call cs_profile( qs, q4, dp1, km, i1, i2, iv, kord )
else
call ppm_profile( q4, dp1, km, i1, i2, iv, kord )
endif
do i=i1,i2
k0 = 1
do 555 k=1,kn
do l=k0,km
! locate the top edge: pe2(i,k)
if( pe2(i,k) >= pe1(i,l) .and. pe2(i,k) <= pe1(i,l+1) ) then
pl = (pe2(i,k)-pe1(i,l)) / dp1(i,l)
if( pe2(i,k+1) <= pe1(i,l+1) ) then
! entire new grid is within the original grid
pr = (pe2(i,k+1)-pe1(i,l)) / dp1(i,l)
q2(i,j,k) = q4(2,i,l) + 0.5*(q4(4,i,l)+q4(3,i,l)-q4(2,i,l)) &
*(pr+pl)-q4(4,i,l)*r3*(pr*(pr+pl)+pl**2)
k0 = l
goto 555
else
! Fractional area...
qsum = (pe1(i,l+1)-pe2(i,k))*(q4(2,i,l)+0.5*(q4(4,i,l)+ &
q4(3,i,l)-q4(2,i,l))*(1.+pl)-q4(4,i,l)* &
(r3*(1.+pl*(1.+pl))))
do m=l+1,km
! locate the bottom edge: pe2(i,k+1)
if( pe2(i,k+1) > pe1(i,m+1) ) then
! Whole layer
qsum = qsum + dp1(i,m)*q4(1,i,m)
else
dp = pe2(i,k+1)-pe1(i,m)
esl = dp / dp1(i,m)
qsum = qsum + dp*(q4(2,i,m)+0.5*esl* &
(q4(3,i,m)-q4(2,i,m)+q4(4,i,m)*(1.-r23*esl)))
k0 = m
goto 123
endif
enddo
goto 123
endif
endif
enddo
123 q2(i,j,k) = qsum / ( pe2(i,k+1) - pe2(i,k) )
555 continue
enddo
end subroutine map1_ppm
subroutine mapn_tracer(nq, km, pe1, pe2, q1, dp2, kord, j, &
i1, i2, isd, ied, jsd, jed, q_min, fill)
! INPUT PARAMETERS:
integer, intent(in):: km !< vertical dimension
integer, intent(in):: j, nq, i1, i2
integer, intent(in):: isd, ied, jsd, jed
integer, intent(in):: kord(nq)
real, intent(in):: pe1(i1:i2,km+1) !< pressure at layer edges from model top to bottom surface in the original vertical coordinate
real, intent(in):: pe2(i1:i2,km+1) !< pressure at layer edges from model top to bottom surface in the new vertical coordinate
real, intent(in):: dp2(i1:i2,km)
real, intent(in):: q_min
logical, intent(in):: fill
real, intent(inout):: q1(isd:ied,jsd:jed,km,nq) ! Field input
! LOCAL VARIABLES:
real:: q4(4,i1:i2,km,nq)
real:: q2(i1:i2,km,nq) !< Field output
real:: qsum(nq)
real:: dp1(i1:i2,km)
real:: qs(i1:i2)
real:: pl, pr, dp, esl, fac1, fac2
integer:: i, k, l, m, k0, iq
do k=1,km
do i=i1,i2
dp1(i,k) = pe1(i,k+1) - pe1(i,k)
enddo
enddo
do iq=1,nq
do k=1,km
do i=i1,i2
q4(1,i,k,iq) = q1(i,j,k,iq)
enddo
enddo
call scalar_profile( qs, q4(1,i1,1,iq), dp1, km, i1, i2, 0, kord(iq), q_min )
enddo
! Mapping
do 1000 i=i1,i2
k0 = 1
do 555 k=1,km
do 100 l=k0,km
! locate the top edge: pe2(i,k)
if(pe2(i,k) >= pe1(i,l) .and. pe2(i,k) <= pe1(i,l+1)) then
pl = (pe2(i,k)-pe1(i,l)) / dp1(i,l)
if(pe2(i,k+1) <= pe1(i,l+1)) then
! entire new grid is within the original grid
pr = (pe2(i,k+1)-pe1(i,l)) / dp1(i,l)
fac1 = pr + pl
fac2 = r3*(pr*fac1 + pl*pl)
fac1 = 0.5*fac1
do iq=1,nq
q2(i,k,iq) = q4(2,i,l,iq) + (q4(4,i,l,iq)+q4(3,i,l,iq)-q4(2,i,l,iq))*fac1 &
- q4(4,i,l,iq)*fac2
enddo
k0 = l
goto 555
else
! Fractional area...
dp = pe1(i,l+1) - pe2(i,k)
fac1 = 1. + pl
fac2 = r3*(1.+pl*fac1)
fac1 = 0.5*fac1
do iq=1,nq
qsum(iq) = dp*(q4(2,i,l,iq) + (q4(4,i,l,iq)+ &
q4(3,i,l,iq) - q4(2,i,l,iq))*fac1 - q4(4,i,l,iq)*fac2)
enddo
do m=l+1,km
! locate the bottom edge: pe2(i,k+1)
if(pe2(i,k+1) > pe1(i,m+1) ) then
! Whole layer..
do iq=1,nq
qsum(iq) = qsum(iq) + dp1(i,m)*q4(1,i,m,iq)
enddo
else
dp = pe2(i,k+1)-pe1(i,m)
esl = dp / dp1(i,m)
fac1 = 0.5*esl
fac2 = 1.-r23*esl
do iq=1,nq
qsum(iq) = qsum(iq) + dp*( q4(2,i,m,iq) + fac1*( &
q4(3,i,m,iq)-q4(2,i,m,iq)+q4(4,i,m,iq)*fac2 ) )
enddo
k0 = m
goto 123
endif
enddo
goto 123
endif
endif
100 continue
123 continue
do iq=1,nq
q2(i,k,iq) = qsum(iq) / dp2(i,k)
enddo
555 continue
1000 continue
if (fill) call fillz(i2-i1+1, km, nq, q2, dp2)
do iq=1,nq
! if (fill) call fillz(i2-i1+1, km, 1, q2(i1,1,iq), dp2)
do k=1,km
do i=i1,i2
q1(i,j,k,iq) = q2(i,k,iq)
enddo
enddo
enddo
end subroutine mapn_tracer
subroutine map1_q2(km, pe1, q1, &
kn, pe2, q2, dp2, &
i1, i2, iv, kord, j, &
ibeg, iend, jbeg, jend, q_min )
! INPUT PARAMETERS:
integer, intent(in) :: j
integer, intent(in) :: i1, i2
integer, intent(in) :: ibeg, iend, jbeg, jend
integer, intent(in) :: iv !< Mode: 0 == constituents 1 == ???
integer, intent(in) :: kord
integer, intent(in) :: km !< Original vertical dimension
integer, intent(in) :: kn !< Target vertical dimension
real, intent(in) :: pe1(i1:i2,km+1) !< pressure at layer edges from model top to bottom surface in the original vertical coordinate
real, intent(in) :: pe2(i1:i2,kn+1) !< pressure at layer edges from model top to bottom surface in the new vertical coordinate
real, intent(in) :: q1(ibeg:iend,jbeg:jend,km) !< Field input
real, intent(in) :: dp2(i1:i2,kn)
real, intent(in) :: q_min
! INPUT/OUTPUT PARAMETERS:
real, intent(inout):: q2(i1:i2,kn) !< Field output
! LOCAL VARIABLES:
real qs(i1:i2)
real dp1(i1:i2,km)
real q4(4,i1:i2,km)
real pl, pr, qsum, dp, esl
integer i, k, l, m, k0
do k=1,km
do i=i1,i2
dp1(i,k) = pe1(i,k+1) - pe1(i,k)
q4(1,i,k) = q1(i,j,k)
enddo
enddo
! Compute vertical subgrid distribution
if ( kord >7 ) then
call scalar_profile( qs, q4, dp1, km, i1, i2, iv, kord, q_min )
else
call ppm_profile( q4, dp1, km, i1, i2, iv, kord )
endif
! Mapping
do 1000 i=i1,i2
k0 = 1
do 555 k=1,kn
do 100 l=k0,km
! locate the top edge: pe2(i,k)
if(pe2(i,k) >= pe1(i,l) .and. pe2(i,k) <= pe1(i,l+1)) then
pl = (pe2(i,k)-pe1(i,l)) / dp1(i,l)
if(pe2(i,k+1) <= pe1(i,l+1)) then
! entire new grid is within the original grid
pr = (pe2(i,k+1)-pe1(i,l)) / dp1(i,l)
q2(i,k) = q4(2,i,l) + 0.5*(q4(4,i,l)+q4(3,i,l)-q4(2,i,l)) &
*(pr+pl)-q4(4,i,l)*r3*(pr*(pr+pl)+pl**2)
k0 = l
goto 555
else
! Fractional area...
qsum = (pe1(i,l+1)-pe2(i,k))*(q4(2,i,l)+0.5*(q4(4,i,l)+ &
q4(3,i,l)-q4(2,i,l))*(1.+pl)-q4(4,i,l)* &
(r3*(1.+pl*(1.+pl))))
do m=l+1,km
! locate the bottom edge: pe2(i,k+1)
if(pe2(i,k+1) > pe1(i,m+1) ) then
! Whole layer..
qsum = qsum + dp1(i,m)*q4(1,i,m)
else
dp = pe2(i,k+1)-pe1(i,m)
esl = dp / dp1(i,m)
qsum = qsum + dp*(q4(2,i,m)+0.5*esl* &
(q4(3,i,m)-q4(2,i,m)+q4(4,i,m)*(1.-r23*esl)))
k0 = m
goto 123
endif
enddo
goto 123
endif
endif
100 continue
123 q2(i,k) = qsum / dp2(i,k)
555 continue
1000 continue
end subroutine map1_q2
subroutine remap_2d(km, pe1, q1, &
kn, pe2, q2, &
i1, i2, iv, kord)
integer, intent(in):: i1, i2
integer, intent(in):: iv !< Mode: 0 == constituents 1 ==others
integer, intent(in):: kord
integer, intent(in):: km !< Original vertical dimension
integer, intent(in):: kn !< Target vertical dimension
real, intent(in):: pe1(i1:i2,km+1) !< Pressure at layer edges from model top to bottom surface in the original vertical coordinate
real, intent(in):: pe2(i1:i2,kn+1) !< Pressure at layer edges from model top to bottom surface in the new vertical coordinate
real, intent(in) :: q1(i1:i2,km) !< Field input
real, intent(out):: q2(i1:i2,kn) !< Field output
! LOCAL VARIABLES:
real qs(i1:i2)
real dp1(i1:i2,km)
real q4(4,i1:i2,km)
real pl, pr, qsum, dp, esl
integer i, k, l, m, k0
do k=1,km
do i=i1,i2
dp1(i,k) = pe1(i,k+1) - pe1(i,k)
q4(1,i,k) = q1(i,k)
enddo
enddo
! Compute vertical subgrid distribution
if ( kord >7 ) then
call cs_profile( qs, q4, dp1, km, i1, i2, iv, kord )
else
call ppm_profile( q4, dp1, km, i1, i2, iv, kord )
endif
do i=i1,i2
k0 = 1
do 555 k=1,kn
#ifdef OLD_TOP_EDGE
if( pe2(i,k+1) <= pe1(i,1) ) then
! Entire grid above old ptop
q2(i,k) = q4(2,i,1)
elseif( pe2(i,k) < pe1(i,1) .and. pe2(i,k+1)>pe1(i,1) ) then
! Partially above old ptop:
q2(i,k) = q1(i,1)
#else
if( pe2(i,k) <= pe1(i,1) ) then
! above old ptop:
q2(i,k) = q1(i,1)
#endif
else
do l=k0,km
! locate the top edge: pe2(i,k)
if( pe2(i,k) >= pe1(i,l) .and. pe2(i,k) <= pe1(i,l+1) ) then
pl = (pe2(i,k)-pe1(i,l)) / dp1(i,l)
if(pe2(i,k+1) <= pe1(i,l+1)) then
! entire new grid is within the original grid
pr = (pe2(i,k+1)-pe1(i,l)) / dp1(i,l)
q2(i,k) = q4(2,i,l) + 0.5*(q4(4,i,l)+q4(3,i,l)-q4(2,i,l)) &
*(pr+pl)-q4(4,i,l)*r3*(pr*(pr+pl)+pl**2)
k0 = l
goto 555
else
! Fractional area...
qsum = (pe1(i,l+1)-pe2(i,k))*(q4(2,i,l)+0.5*(q4(4,i,l)+ &
q4(3,i,l)-q4(2,i,l))*(1.+pl)-q4(4,i,l)* &
(r3*(1.+pl*(1.+pl))))
do m=l+1,km
! locate the bottom edge: pe2(i,k+1)
if(pe2(i,k+1) > pe1(i,m+1) ) then
! Whole layer..
qsum = qsum + dp1(i,m)*q4(1,i,m)
else
dp = pe2(i,k+1)-pe1(i,m)
esl = dp / dp1(i,m)
qsum = qsum + dp*(q4(2,i,m)+0.5*esl* &
(q4(3,i,m)-q4(2,i,m)+q4(4,i,m)*(1.-r23*esl)))
k0 = m
goto 123
endif
enddo
goto 123
endif
endif
enddo
123 q2(i,k) = qsum / ( pe2(i,k+1) - pe2(i,k) )
endif
555 continue
enddo
end subroutine remap_2d
subroutine scalar_profile(qs, a4, delp, km, i1, i2, iv, kord, qmin)
! Optimized vertical profile reconstruction:
! Latest: Apr 2008 S.-J. Lin, NOAA/GFDL
integer, intent(in):: i1, i2
integer, intent(in):: km !< vertical dimension
integer, intent(in):: iv !< iv =-1: winds iv = 0: positive definite scalars iv = 1: others
integer, intent(in):: kord
real, intent(in) :: qs(i1:i2)
real, intent(in) :: delp(i1:i2,km) !< Layer pressure thickness
real, intent(inout):: a4(4,i1:i2,km) !< Interpolated values
real, intent(in):: qmin
!-----------------------------------------------------------------------
logical, dimension(i1:i2,km):: extm, ext5, ext6
real gam(i1:i2,km)
real q(i1:i2,km+1)
real d4(i1:i2)
real bet, a_bot, grat
real pmp_1, lac_1, pmp_2, lac_2, x0, x1
integer i, k, im
if ( iv .eq. -2 ) then
do i=i1,i2
gam(i,2) = 0.5
q(i,1) = 1.5*a4(1,i,1)
enddo
do k=2,km-1
do i=i1, i2
grat = delp(i,k-1) / delp(i,k)
bet = 2. + grat + grat - gam(i,k)
q(i,k) = (3.*(a4(1,i,k-1)+a4(1,i,k)) - q(i,k-1))/bet
gam(i,k+1) = grat / bet
enddo
enddo
do i=i1,i2
grat = delp(i,km-1) / delp(i,km)
q(i,km) = (3.*(a4(1,i,km-1)+a4(1,i,km)) - grat*qs(i) - q(i,km-1)) / &
(2. + grat + grat - gam(i,km))
q(i,km+1) = qs(i)
enddo
do k=km-1,1,-1
do i=i1,i2
q(i,k) = q(i,k) - gam(i,k+1)*q(i,k+1)
enddo
enddo
else
do i=i1,i2
grat = delp(i,2) / delp(i,1) ! grid ratio
bet = grat*(grat+0.5)
q(i,1) = ( (grat+grat)*(grat+1.)*a4(1,i,1) + a4(1,i,2) ) / bet
gam(i,1) = ( 1. + grat*(grat+1.5) ) / bet
enddo
do k=2,km
do i=i1,i2
d4(i) = delp(i,k-1) / delp(i,k)
bet = 2. + d4(i) + d4(i) - gam(i,k-1)
q(i,k) = ( 3.*(a4(1,i,k-1)+d4(i)*a4(1,i,k)) - q(i,k-1) )/bet
gam(i,k) = d4(i) / bet
enddo
enddo
do i=i1,i2
a_bot = 1. + d4(i)*(d4(i)+1.5)
q(i,km+1) = (2.*d4(i)*(d4(i)+1.)*a4(1,i,km)+a4(1,i,km-1)-a_bot*q(i,km)) &
/ ( d4(i)*(d4(i)+0.5) - a_bot*gam(i,km) )
enddo
do k=km,1,-1
do i=i1,i2
q(i,k) = q(i,k) - gam(i,k)*q(i,k+1)
enddo
enddo
endif
!----- Perfectly linear scheme --------------------------------
if ( abs(kord) > 16 ) then
do k=1,km
do i=i1,i2
a4(2,i,k) = q(i,k )
a4(3,i,k) = q(i,k+1)
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
enddo
return
endif
!----- Perfectly linear scheme --------------------------------
!------------------
! Apply constraints
!------------------
im = i2 - i1 + 1
! Apply *large-scale* constraints
do i=i1,i2
q(i,2) = min( q(i,2), max(a4(1,i,1), a4(1,i,2)) )
q(i,2) = max( q(i,2), min(a4(1,i,1), a4(1,i,2)) )
enddo
do k=2,km
do i=i1,i2
gam(i,k) = a4(1,i,k) - a4(1,i,k-1)
enddo
enddo
! Interior:
do k=3,km-1
do i=i1,i2
if ( gam(i,k-1)*gam(i,k+1)>0. ) then
! Apply large-scale constraint to ALL fields if not local max/min
q(i,k) = min( q(i,k), max(a4(1,i,k-1),a4(1,i,k)) )
q(i,k) = max( q(i,k), min(a4(1,i,k-1),a4(1,i,k)) )
else
if ( gam(i,k-1) > 0. ) then
! There exists a local max
q(i,k) = max(q(i,k), min(a4(1,i,k-1),a4(1,i,k)))
else
! There exists a local min
q(i,k) = min(q(i,k), max(a4(1,i,k-1),a4(1,i,k)))
if ( iv==0 ) q(i,k) = max(0., q(i,k))
endif
endif
enddo
enddo
! Bottom:
do i=i1,i2
q(i,km) = min( q(i,km), max(a4(1,i,km-1), a4(1,i,km)) )
q(i,km) = max( q(i,km), min(a4(1,i,km-1), a4(1,i,km)) )
enddo
do k=1,km
do i=i1,i2
a4(2,i,k) = q(i,k )
a4(3,i,k) = q(i,k+1)
enddo
enddo
do k=1,km
if ( k==1 .or. k==km ) then
do i=i1,i2
extm(i,k) = (a4(2,i,k)-a4(1,i,k)) * (a4(3,i,k)-a4(1,i,k)) > 0.
enddo
else
do i=i1,i2
extm(i,k) = gam(i,k)*gam(i,k+1) < 0.
enddo
endif
if ( abs(kord) > 9 ) then
do i=i1,i2
x0 = 2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k))
x1 = abs(a4(2,i,k)-a4(3,i,k))
a4(4,i,k) = 3.*x0
ext5(i,k) = abs(x0) > x1
ext6(i,k) = abs(a4(4,i,k)) > x1
enddo
endif
enddo
!---------------------------
! Apply subgrid constraints:
!---------------------------
! f(s) = AL + s*[(AR-AL) + A6*(1-s)] ( 0 <= s <= 1 )
! Top 2 and bottom 2 layers always use monotonic mapping
if ( iv==0 ) then
do i=i1,i2
a4(2,i,1) = max(0., a4(2,i,1))
enddo
elseif ( iv==-1 ) then
do i=i1,i2
if ( a4(2,i,1)*a4(1,i,1) <= 0. ) a4(2,i,1) = 0.
enddo
elseif ( iv==2 ) then
do i=i1,i2
a4(2,i,1) = a4(1,i,1)
a4(3,i,1) = a4(1,i,1)
a4(4,i,1) = 0.
enddo
endif
if ( iv/=2 ) then
do i=i1,i2
a4(4,i,1) = 3.*(2.*a4(1,i,1) - (a4(2,i,1)+a4(3,i,1)))
enddo
call cs_limiters(im, extm(i1,1), a4(1,i1,1), 1)
endif
! k=2
do i=i1,i2
a4(4,i,2) = 3.*(2.*a4(1,i,2) - (a4(2,i,2)+a4(3,i,2)))
enddo
call cs_limiters(im, extm(i1,2), a4(1,i1,2), 2)
!-------------------------------------
! Huynh's 2nd constraint for interior:
!-------------------------------------
do k=3,km-2
if ( abs(kord)<9 ) then
do i=i1,i2
! Left edges
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
! Right edges
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
elseif ( abs(kord)==9 ) then
do i=i1,i2
if ( extm(i,k) .and. extm(i,k-1) ) then
! grid-scale 2-delta-z wave detected
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
a4(4,i,k) = 0.
else if ( extm(i,k) .and. extm(i,k+1) ) then
! grid-scale 2-delta-z wave detected
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
a4(4,i,k) = 0.
else if ( extm(i,k) .and. a4(1,i,k) abs(a4(2,i,k)-a4(3,i,k)) ) then
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
endif
endif
enddo
elseif ( abs(kord)==10 ) then
do i=i1,i2
if( ext5(i,k) ) then
if( ext5(i,k-1) .or. ext5(i,k+1) ) then
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
elseif ( ext6(i,k-1) .or. ext6(i,k+1) ) then
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
endif
elseif( ext6(i,k) ) then
if( ext5(i,k-1) .or. ext5(i,k+1) ) then
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
endif
endif
enddo
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
elseif ( abs(kord)==12 ) then
do i=i1,i2
if( extm(i,k) ) then
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
a4(4,i,k) = 0.
else ! not a local extremum
a4(4,i,k) = 6.*a4(1,i,k) - 3.*(a4(2,i,k)+a4(3,i,k))
! Check within the smooth region if subgrid profile is non-monotonic
if( abs(a4(4,i,k)) > abs(a4(2,i,k)-a4(3,i,k)) ) then
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
a4(4,i,k) = 6.*a4(1,i,k) - 3.*(a4(2,i,k)+a4(3,i,k))
endif
endif
enddo
elseif ( abs(kord)==13 ) then
do i=i1,i2
if( ext6(i,k) ) then
if ( ext6(i,k-1) .and. ext6(i,k+1) ) then
! grid-scale 2-delta-z wave detected
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
endif
endif
enddo
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
elseif ( abs(kord)==14 ) then
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
elseif ( abs(kord)==15 ) then ! Revised abs(kord)=9 scheme
do i=i1,i2
if ( ext5(i,k) .and. ext5(i,k-1) ) then
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
else if ( ext5(i,k) .and. ext5(i,k+1) ) then
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
else if ( ext5(i,k) .and. a4(1,i,k) 16 ) then
do k=1,km
do i=i1,i2
a4(2,i,k) = q(i,k )
a4(3,i,k) = q(i,k+1)
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
enddo
return
endif
!----- Perfectly linear scheme --------------------------------
!------------------
! Apply constraints
!------------------
im = i2 - i1 + 1
! Apply *large-scale* constraints
do i=i1,i2
q(i,2) = min( q(i,2), max(a4(1,i,1), a4(1,i,2)) )
q(i,2) = max( q(i,2), min(a4(1,i,1), a4(1,i,2)) )
enddo
do k=2,km
do i=i1,i2
gam(i,k) = a4(1,i,k) - a4(1,i,k-1)
enddo
enddo
! Interior:
do k=3,km-1
do i=i1,i2
if ( gam(i,k-1)*gam(i,k+1)>0. ) then
! Apply large-scale constraint to ALL fields if not local max/min
q(i,k) = min( q(i,k), max(a4(1,i,k-1),a4(1,i,k)) )
q(i,k) = max( q(i,k), min(a4(1,i,k-1),a4(1,i,k)) )
else
if ( gam(i,k-1) > 0. ) then
! There exists a local max
q(i,k) = max(q(i,k), min(a4(1,i,k-1),a4(1,i,k)))
else
! There exists a local min
q(i,k) = min(q(i,k), max(a4(1,i,k-1),a4(1,i,k)))
if ( iv==0 ) q(i,k) = max(0., q(i,k))
endif
endif
enddo
enddo
! Bottom:
do i=i1,i2
q(i,km) = min( q(i,km), max(a4(1,i,km-1), a4(1,i,km)) )
q(i,km) = max( q(i,km), min(a4(1,i,km-1), a4(1,i,km)) )
enddo
do k=1,km
do i=i1,i2
a4(2,i,k) = q(i,k )
a4(3,i,k) = q(i,k+1)
enddo
enddo
do k=1,km
if ( k==1 .or. k==km ) then
do i=i1,i2
extm(i,k) = (a4(2,i,k)-a4(1,i,k)) * (a4(3,i,k)-a4(1,i,k)) > 0.
enddo
else
do i=i1,i2
extm(i,k) = gam(i,k)*gam(i,k+1) < 0.
enddo
endif
if ( abs(kord) > 9 ) then
do i=i1,i2
x0 = 2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k))
x1 = abs(a4(2,i,k)-a4(3,i,k))
a4(4,i,k) = 3.*x0
ext5(i,k) = abs(x0) > x1
ext6(i,k) = abs(a4(4,i,k)) > x1
enddo
endif
enddo
!---------------------------
! Apply subgrid constraints:
!---------------------------
! f(s) = AL + s*[(AR-AL) + A6*(1-s)] ( 0 <= s <= 1 )
! Top 2 and bottom 2 layers always use monotonic mapping
if ( iv==0 ) then
do i=i1,i2
a4(2,i,1) = max(0., a4(2,i,1))
enddo
elseif ( iv==-1 ) then
do i=i1,i2
if ( a4(2,i,1)*a4(1,i,1) <= 0. ) a4(2,i,1) = 0.
enddo
elseif ( iv==2 ) then
do i=i1,i2
a4(2,i,1) = a4(1,i,1)
a4(3,i,1) = a4(1,i,1)
a4(4,i,1) = 0.
enddo
endif
if ( iv/=2 ) then
do i=i1,i2
a4(4,i,1) = 3.*(2.*a4(1,i,1) - (a4(2,i,1)+a4(3,i,1)))
enddo
call cs_limiters(im, extm(i1,1), a4(1,i1,1), 1)
endif
! k=2
do i=i1,i2
a4(4,i,2) = 3.*(2.*a4(1,i,2) - (a4(2,i,2)+a4(3,i,2)))
enddo
call cs_limiters(im, extm(i1,2), a4(1,i1,2), 2)
!-------------------------------------
! Huynh's 2nd constraint for interior:
!-------------------------------------
do k=3,km-2
if ( abs(kord)<9 ) then
do i=i1,i2
! Left edges
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
! Right edges
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
elseif ( abs(kord)==9 ) then
do i=i1,i2
if ( extm(i,k) .and. extm(i,k-1) ) then ! c90_mp122
! grid-scale 2-delta-z wave detected
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
a4(4,i,k) = 0.
else if ( extm(i,k) .and. extm(i,k+1) ) then ! c90_mp122
! grid-scale 2-delta-z wave detected
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
a4(4,i,k) = 0.
else
a4(4,i,k) = 6.*a4(1,i,k) - 3.*(a4(2,i,k)+a4(3,i,k))
! Check within the smooth region if subgrid profile is non-monotonic
if( abs(a4(4,i,k)) > abs(a4(2,i,k)-a4(3,i,k)) ) then
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
a4(4,i,k) = 6.*a4(1,i,k) - 3.*(a4(2,i,k)+a4(3,i,k))
endif
endif
enddo
elseif ( abs(kord)==10 ) then
do i=i1,i2
if( ext5(i,k) ) then
if( ext5(i,k-1) .or. ext5(i,k+1) ) then
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
elseif ( ext6(i,k-1) .or. ext6(i,k+1) ) then
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
endif
elseif( ext6(i,k) ) then
if( ext5(i,k-1) .or. ext5(i,k+1) ) then
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
endif
endif
enddo
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
elseif ( abs(kord)==12 ) then
do i=i1,i2
if( extm(i,k) ) then
! grid-scale 2-delta-z wave detected
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
a4(4,i,k) = 0.
else ! not a local extremum
a4(4,i,k) = 6.*a4(1,i,k) - 3.*(a4(2,i,k)+a4(3,i,k))
! Check within the smooth region if subgrid profile is non-monotonic
if( abs(a4(4,i,k)) > abs(a4(2,i,k)-a4(3,i,k)) ) then
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
a4(4,i,k) = 6.*a4(1,i,k) - 3.*(a4(2,i,k)+a4(3,i,k))
endif
endif
enddo
elseif ( abs(kord)==13 ) then
do i=i1,i2
if( ext6(i,k) ) then
if ( ext6(i,k-1) .and. ext6(i,k+1) ) then
! grid-scale 2-delta-z wave detected
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
endif
endif
enddo
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
elseif ( abs(kord)==14 ) then
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
elseif ( abs(kord)==15 ) then ! revised kord=9 scehem
do i=i1,i2
if ( ext5(i,k) ) then ! c90_mp122
if ( ext5(i,k-1) .or. ext5(i,k+1) ) then ! c90_mp122
! grid-scale 2-delta-z wave detected
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
endif
elseif( ext6(i,k) ) then
! Check within the smooth region if subgrid profile is non-monotonic
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
endif
enddo
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
elseif ( abs(kord)==16 ) then
do i=i1,i2
if( ext5(i,k) ) then
if ( ext5(i,k-1) .or. ext5(i,k+1) ) then
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
elseif ( ext6(i,k-1) .or. ext6(i,k+1) ) then
! Left edges
pmp_1 = a4(1,i,k) - 2.*gam(i,k+1)
lac_1 = pmp_1 + 1.5*gam(i,k+2)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), pmp_1, lac_1)), &
max(a4(1,i,k), pmp_1, lac_1) )
! Right edges
pmp_2 = a4(1,i,k) + 2.*gam(i,k)
lac_2 = pmp_2 - 1.5*gam(i,k-1)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), pmp_2, lac_2)), &
max(a4(1,i,k), pmp_2, lac_2) )
endif
endif
enddo
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
else ! kord = 11
do i=i1,i2
if ( ext5(i,k) .and. (ext5(i,k-1) .or. ext5(i,k+1)) ) then
! Noisy region:
a4(2,i,k) = a4(1,i,k)
a4(3,i,k) = a4(1,i,k)
a4(4,i,k) = 0.
else
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
endif
enddo
endif
! Additional constraint to ensure positivity
if ( iv==0 ) call cs_limiters(im, extm(i1,k), a4(1,i1,k), 0)
enddo ! k-loop
!----------------------------------
! Bottom layer subgrid constraints:
!----------------------------------
if ( iv==0 ) then
do i=i1,i2
a4(3,i,km) = max(0., a4(3,i,km))
enddo
elseif ( iv .eq. -1 ) then
do i=i1,i2
if ( a4(3,i,km)*a4(1,i,km) <= 0. ) a4(3,i,km) = 0.
enddo
endif
do k=km-1,km
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
if(k==(km-1)) call cs_limiters(im, extm(i1,k), a4(1,i1,k), 2)
if(k== km ) call cs_limiters(im, extm(i1,k), a4(1,i1,k), 1)
enddo
end subroutine cs_profile
subroutine cs_limiters(im, extm, a4, iv)
integer, intent(in) :: im
integer, intent(in) :: iv
logical, intent(in) :: extm(im)
real , intent(inout) :: a4(4,im) !< PPM array
! LOCAL VARIABLES:
real da1, da2, a6da
integer i
if ( iv==0 ) then
! Positive definite constraint
do i=1,im
if( a4(1,i)<=0.) then
a4(2,i) = a4(1,i)
a4(3,i) = a4(1,i)
a4(4,i) = 0.
else
if( abs(a4(3,i)-a4(2,i)) < -a4(4,i) ) then
if( (a4(1,i)+0.25*(a4(3,i)-a4(2,i))**2/a4(4,i)+a4(4,i)*r12) < 0. ) then
! local minimum is negative
if( a4(1,i) a4(2,i) ) then
a4(4,i) = 3.*(a4(2,i)-a4(1,i))
a4(3,i) = a4(2,i) - a4(4,i)
else
a4(4,i) = 3.*(a4(3,i)-a4(1,i))
a4(2,i) = a4(3,i) - a4(4,i)
endif
endif
endif
endif
enddo
elseif ( iv==1 ) then
do i=1,im
if( (a4(1,i)-a4(2,i))*(a4(1,i)-a4(3,i))>=0. ) then
a4(2,i) = a4(1,i)
a4(3,i) = a4(1,i)
a4(4,i) = 0.
else
da1 = a4(3,i) - a4(2,i)
da2 = da1**2
a6da = a4(4,i)*da1
if(a6da < -da2) then
a4(4,i) = 3.*(a4(2,i)-a4(1,i))
a4(3,i) = a4(2,i) - a4(4,i)
elseif(a6da > da2) then
a4(4,i) = 3.*(a4(3,i)-a4(1,i))
a4(2,i) = a4(3,i) - a4(4,i)
endif
endif
enddo
else
! Standard PPM constraint
do i=1,im
if( extm(i) ) then
a4(2,i) = a4(1,i)
a4(3,i) = a4(1,i)
a4(4,i) = 0.
else
da1 = a4(3,i) - a4(2,i)
da2 = da1**2
a6da = a4(4,i)*da1
if(a6da < -da2) then
a4(4,i) = 3.*(a4(2,i)-a4(1,i))
a4(3,i) = a4(2,i) - a4(4,i)
elseif(a6da > da2) then
a4(4,i) = 3.*(a4(3,i)-a4(1,i))
a4(2,i) = a4(3,i) - a4(4,i)
endif
endif
enddo
endif
end subroutine cs_limiters
subroutine ppm_profile(a4, delp, km, i1, i2, iv, kord)
! INPUT PARAMETERS:
integer, intent(in):: iv !< iv =-1: winds iv = 0: positive definite scalars iv = 1: others iv = 2: temp (if remap_t) and w (iv=-2)
integer, intent(in):: i1 !< Starting longitude
integer, intent(in):: i2 !< Finishing longitude
integer, intent(in):: km !< Vertical dimension
integer, intent(in):: kord !< Order (or more accurately method no.):
!
real , intent(in):: delp(i1:i2,km) !< Layer pressure thickness
! !INPUT/OUTPUT PARAMETERS:
real , intent(inout):: a4(4,i1:i2,km) !< Interpolated values
! DESCRIPTION:
!
! Perform the piecewise parabolic reconstruction
!
! !REVISION HISTORY:
! S.-J. Lin revised at GFDL 2007
!-----------------------------------------------------------------------
! local arrays:
real dc(i1:i2,km)
real h2(i1:i2,km)
real delq(i1:i2,km)
real df2(i1:i2,km)
real d4(i1:i2,km)
! local scalars:
integer i, k, km1, lmt, it
real fac
real a1, a2, c1, c2, c3, d1, d2
real qm, dq, lac, qmp, pmp
km1 = km - 1
it = i2 - i1 + 1
do k=2,km
do i=i1,i2
delq(i,k-1) = a4(1,i,k) - a4(1,i,k-1)
d4(i,k ) = delp(i,k-1) + delp(i,k)
enddo
enddo
do k=2,km1
do i=i1,i2
c1 = (delp(i,k-1)+0.5*delp(i,k))/d4(i,k+1)
c2 = (delp(i,k+1)+0.5*delp(i,k))/d4(i,k)
df2(i,k) = delp(i,k)*(c1*delq(i,k) + c2*delq(i,k-1)) / &
(d4(i,k)+delp(i,k+1))
dc(i,k) = sign( min(abs(df2(i,k)), &
max(a4(1,i,k-1),a4(1,i,k),a4(1,i,k+1))-a4(1,i,k), &
a4(1,i,k)-min(a4(1,i,k-1),a4(1,i,k),a4(1,i,k+1))), df2(i,k) )
enddo
enddo
!-----------------------------------------------------------
! 4th order interpolation of the provisional cell edge value
!-----------------------------------------------------------
do k=3,km1
do i=i1,i2
c1 = delq(i,k-1)*delp(i,k-1) / d4(i,k)
a1 = d4(i,k-1) / (d4(i,k) + delp(i,k-1))
a2 = d4(i,k+1) / (d4(i,k) + delp(i,k))
a4(2,i,k) = a4(1,i,k-1) + c1 + 2./(d4(i,k-1)+d4(i,k+1)) * &
( delp(i,k)*(c1*(a1 - a2)+a2*dc(i,k-1)) - &
delp(i,k-1)*a1*dc(i,k ) )
enddo
enddo
! if(km>8 .and. kord>4) call steepz(i1, i2, km, a4, df2, dc, delq, delp, d4)
! Area preserving cubic with 2nd deriv. = 0 at the boundaries
! Top
do i=i1,i2
d1 = delp(i,1)
d2 = delp(i,2)
qm = (d2*a4(1,i,1)+d1*a4(1,i,2)) / (d1+d2)
dq = 2.*(a4(1,i,2)-a4(1,i,1)) / (d1+d2)
c1 = 4.*(a4(2,i,3)-qm-d2*dq) / ( d2*(2.*d2*d2+d1*(d2+3.*d1)) )
c3 = dq - 0.5*c1*(d2*(5.*d1+d2)-3.*d1*d1)
a4(2,i,2) = qm - 0.25*c1*d1*d2*(d2+3.*d1)
! Top edge:
!-------------------------------------------------------
a4(2,i,1) = d1*(2.*c1*d1**2-c3) + a4(2,i,2)
!-------------------------------------------------------
! a4(2,i,1) = (12./7.)*a4(1,i,1)-(13./14.)*a4(1,i,2)+(3./14.)*a4(1,i,3)
!-------------------------------------------------------
! No over- and undershoot condition
a4(2,i,2) = max( a4(2,i,2), min(a4(1,i,1), a4(1,i,2)) )
a4(2,i,2) = min( a4(2,i,2), max(a4(1,i,1), a4(1,i,2)) )
dc(i,1) = 0.5*(a4(2,i,2) - a4(1,i,1))
enddo
! Enforce monotonicity within the top layer
if( iv==0 ) then
do i=i1,i2
a4(2,i,1) = max(0., a4(2,i,1))
a4(2,i,2) = max(0., a4(2,i,2))
enddo
elseif( iv==-1 ) then
do i=i1,i2
if ( a4(2,i,1)*a4(1,i,1) <= 0. ) a4(2,i,1) = 0.
enddo
elseif( abs(iv)==2 ) then
do i=i1,i2
a4(2,i,1) = a4(1,i,1)
a4(3,i,1) = a4(1,i,1)
enddo
endif
! Bottom
! Area preserving cubic with 2nd deriv. = 0 at the surface
do i=i1,i2
d1 = delp(i,km)
d2 = delp(i,km1)
qm = (d2*a4(1,i,km)+d1*a4(1,i,km1)) / (d1+d2)
dq = 2.*(a4(1,i,km1)-a4(1,i,km)) / (d1+d2)
c1 = (a4(2,i,km1)-qm-d2*dq) / (d2*(2.*d2*d2+d1*(d2+3.*d1)))
c3 = dq - 2.0*c1*(d2*(5.*d1+d2)-3.*d1*d1)
a4(2,i,km) = qm - c1*d1*d2*(d2+3.*d1)
! Bottom edge:
!-----------------------------------------------------
a4(3,i,km) = d1*(8.*c1*d1**2-c3) + a4(2,i,km)
! dc(i,km) = 0.5*(a4(3,i,km) - a4(1,i,km))
!-----------------------------------------------------
! a4(3,i,km) = (12./7.)*a4(1,i,km)-(13./14.)*a4(1,i,km-1)+(3./14.)*a4(1,i,km-2)
! No over- and under-shoot condition
a4(2,i,km) = max( a4(2,i,km), min(a4(1,i,km), a4(1,i,km1)) )
a4(2,i,km) = min( a4(2,i,km), max(a4(1,i,km), a4(1,i,km1)) )
dc(i,km) = 0.5*(a4(1,i,km) - a4(2,i,km))
enddo
! Enforce constraint on the "slope" at the surface
#ifdef BOT_MONO
do i=i1,i2
a4(4,i,km) = 0
if( a4(3,i,km) * a4(1,i,km) <= 0. ) a4(3,i,km) = 0.
d1 = a4(1,i,km) - a4(2,i,km)
d2 = a4(3,i,km) - a4(1,i,km)
if ( d1*d2 < 0. ) then
a4(2,i,km) = a4(1,i,km)
a4(3,i,km) = a4(1,i,km)
else
dq = sign(min(abs(d1),abs(d2),0.5*abs(delq(i,km-1))), d1)
a4(2,i,km) = a4(1,i,km) - dq
a4(3,i,km) = a4(1,i,km) + dq
endif
enddo
#else
if( iv==0 ) then
do i=i1,i2
a4(2,i,km) = max(0.,a4(2,i,km))
a4(3,i,km) = max(0.,a4(3,i,km))
enddo
elseif( iv<0 ) then
do i=i1,i2
if( a4(1,i,km)*a4(3,i,km) <= 0. ) a4(3,i,km) = 0.
enddo
endif
#endif
do k=1,km1
do i=i1,i2
a4(3,i,k) = a4(2,i,k+1)
enddo
enddo
!-----------------------------------------------------------
! f(s) = AL + s*[(AR-AL) + A6*(1-s)] ( 0 <= s <= 1 )
!-----------------------------------------------------------
! Top 2 and bottom 2 layers always use monotonic mapping
do k=1,2
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
call ppm_limiters(dc(i1,k), a4(1,i1,k), it, 0)
enddo
if(kord >= 7) then
!-----------------------
! Huynh's 2nd constraint
!-----------------------
do k=2,km1
do i=i1,i2
! Method#1
! h2(i,k) = delq(i,k) - delq(i,k-1)
! Method#2 - better
h2(i,k) = 2.*(dc(i,k+1)/delp(i,k+1) - dc(i,k-1)/delp(i,k-1)) &
/ ( delp(i,k)+0.5*(delp(i,k-1)+delp(i,k+1)) ) &
* delp(i,k)**2
! Method#3
!!! h2(i,k) = dc(i,k+1) - dc(i,k-1)
enddo
enddo
fac = 1.5 ! original quasi-monotone
do k=3,km-2
do i=i1,i2
! Right edges
! qmp = a4(1,i,k) + 2.0*delq(i,k-1)
! lac = a4(1,i,k) + fac*h2(i,k-1) + 0.5*delq(i,k-1)
!
pmp = 2.*dc(i,k)
qmp = a4(1,i,k) + pmp
lac = a4(1,i,k) + fac*h2(i,k-1) + dc(i,k)
a4(3,i,k) = min(max(a4(3,i,k), min(a4(1,i,k), qmp, lac)), &
max(a4(1,i,k), qmp, lac) )
! Left edges
! qmp = a4(1,i,k) - 2.0*delq(i,k)
! lac = a4(1,i,k) + fac*h2(i,k+1) - 0.5*delq(i,k)
!
qmp = a4(1,i,k) - pmp
lac = a4(1,i,k) + fac*h2(i,k+1) - dc(i,k)
a4(2,i,k) = min(max(a4(2,i,k), min(a4(1,i,k), qmp, lac)), &
max(a4(1,i,k), qmp, lac))
!-------------
! Recompute A6
!-------------
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
! Additional constraint to ensure positivity when kord=7
if (iv == 0 .and. kord >= 6 ) &
call ppm_limiters(dc(i1,k), a4(1,i1,k), it, 2)
enddo
else
lmt = kord - 3
lmt = max(0, lmt)
if (iv == 0) lmt = min(2, lmt)
do k=3,km-2
if( kord /= 4) then
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
endif
if(kord/=6) call ppm_limiters(dc(i1,k), a4(1,i1,k), it, lmt)
enddo
endif
do k=km1,km
do i=i1,i2
a4(4,i,k) = 3.*(2.*a4(1,i,k) - (a4(2,i,k)+a4(3,i,k)))
enddo
call ppm_limiters(dc(i1,k), a4(1,i1,k), it, 0)
enddo
end subroutine ppm_profile
subroutine ppm_limiters(dm, a4, itot, lmt)
! INPUT PARAMETERS:
real , intent(in):: dm(*) !< Linear slope
integer, intent(in) :: itot !< Total Longitudes
integer, intent(in) :: lmt !< 0: Standard PPM constraint 1: Improved full monotonicity constraint
!< (Lin) 2: Positive definite constraint
!< 3: do nothing (return immediately)
! INPUT/OUTPUT PARAMETERS:
real , intent(inout) :: a4(4,*) !< PPM array AA <-- a4(1,i) AL <-- a4(2,i) AR <-- a4(3,i) A6 <-- a4(4,i)
! LOCAL VARIABLES:
real qmp
real da1, da2, a6da
real fmin
integer i
! Developer: S.-J. Lin
if ( lmt == 3 ) return
if(lmt == 0) then
! Standard PPM constraint
do i=1,itot
if(dm(i) == 0.) then
a4(2,i) = a4(1,i)
a4(3,i) = a4(1,i)
a4(4,i) = 0.
else
da1 = a4(3,i) - a4(2,i)
da2 = da1**2
a6da = a4(4,i)*da1
if(a6da < -da2) then
a4(4,i) = 3.*(a4(2,i)-a4(1,i))
a4(3,i) = a4(2,i) - a4(4,i)
elseif(a6da > da2) then
a4(4,i) = 3.*(a4(3,i)-a4(1,i))
a4(2,i) = a4(3,i) - a4(4,i)
endif
endif
enddo
elseif (lmt == 1) then
! Improved full monotonicity constraint (Lin 2004)
! Note: no need to provide first guess of A6 <-- a4(4,i)
do i=1, itot
qmp = 2.*dm(i)
a4(2,i) = a4(1,i)-sign(min(abs(qmp),abs(a4(2,i)-a4(1,i))), qmp)
a4(3,i) = a4(1,i)+sign(min(abs(qmp),abs(a4(3,i)-a4(1,i))), qmp)
a4(4,i) = 3.*( 2.*a4(1,i) - (a4(2,i)+a4(3,i)) )
enddo
elseif (lmt == 2) then
! Positive definite constraint
do i=1,itot
if( abs(a4(3,i)-a4(2,i)) < -a4(4,i) ) then
fmin = a4(1,i)+0.25*(a4(3,i)-a4(2,i))**2/a4(4,i)+a4(4,i)*r12
if( fmin < 0. ) then
if(a4(1,i) a4(2,i)) then
a4(4,i) = 3.*(a4(2,i)-a4(1,i))
a4(3,i) = a4(2,i) - a4(4,i)
else
a4(4,i) = 3.*(a4(3,i)-a4(1,i))
a4(2,i) = a4(3,i) - a4(4,i)
endif
endif
endif
enddo
endif
end subroutine ppm_limiters
subroutine steepz(i1, i2, km, a4, df2, dm, dq, dp, d4)
integer, intent(in) :: km, i1, i2
real , intent(in) :: dp(i1:i2,km) !< Grid size
real , intent(in) :: dq(i1:i2,km) !< Backward diff of q
real , intent(in) :: d4(i1:i2,km) !< Backward sum: dp(k)+ dp(k-1)
real , intent(in) :: df2(i1:i2,km) !< First guess mismatch
real , intent(in) :: dm(i1:i2,km) !< Monotonic mismatch
! INPUT/OUTPUT PARAMETERS:
real , intent(inout) :: a4(4,i1:i2,km) !@brief The subroutine 'rst_remap' remaps all variables required for a restart.
!>@details npz_restart /= npz (i.e., when the number of vertical levels is
!! changed at restart)
subroutine rst_remap(km, kn, is,ie,js,je, isd,ied,jsd,jed, nq, ntp, &
delp_r, u_r, v_r, w_r, delz_r, pt_r, q_r, qdiag_r, &
delp, u, v, w, delz, pt, q, qdiag, &
ak_r, bk_r, ptop, ak, bk, hydrostatic, make_nh, &
domain, square_domain)
!------------------------------------
! Assuming hybrid sigma-P coordinate:
!------------------------------------
! INPUT PARAMETERS:
integer, intent(in):: km !< Restart z-dimension
integer, intent(in):: kn !< Run time dimension
integer, intent(in):: nq, ntp !< Number of tracers (including H2O)
integer, intent(in):: is,ie,isd,ied !< Starting & ending X-Dir index
integer, intent(in):: js,je,jsd,jed !< Starting & ending Y-Dir index
logical, intent(in):: hydrostatic, make_nh, square_domain
real, intent(IN) :: ptop
real, intent(in) :: ak_r(km+1)
real, intent(in) :: bk_r(km+1)
real, intent(in) :: ak(kn+1)
real, intent(in) :: bk(kn+1)
real, intent(in):: delp_r(is:ie,js:je,km) !< Pressure thickness
real, intent(in):: u_r(is:ie, js:je+1,km) !< u-wind (m/s)
real, intent(in):: v_r(is:ie+1,js:je ,km) !< v-wind (m/s)
real, intent(inout):: pt_r(is:ie,js:je,km)
real, intent(in):: w_r(is:ie,js:je,km)
real, intent(in):: q_r(is:ie,js:je,km,1:ntp)
real, intent(in):: qdiag_r(is:ie,js:je,km,ntp+1:nq)
real, intent(inout)::delz_r(is:ie,js:je,km)
type(domain2d), intent(INOUT) :: domain
! Output:
real, intent(out):: delp(isd:ied,jsd:jed,kn) !< Pressure thickness
real, intent(out):: u(isd:ied ,jsd:jed+1,kn) !< u-wind (m/s)
real, intent(out):: v(isd:ied+1,jsd:jed ,kn) !< v-wind (m/s)
real, intent(out):: w(isd: ,jsd: ,1:) !< Vertical velocity (m/s)
real, intent(out):: pt(isd:ied ,jsd:jed ,kn) !< Temperature
real, intent(out):: q(isd:ied,jsd:jed,kn,1:ntp)
real, intent(out):: qdiag(isd:ied,jsd:jed,kn,ntp+1:nq)
real, intent(out):: delz(isd:,jsd:,1:) !< Delta-height (m)
!-----------------------------------------------------------------------
real r_vir, rgrav
real ps(isd:ied,jsd:jed) !< Surface pressure
real pe1(is:ie,km+1)
real pe2(is:ie,kn+1)
real pv1(is:ie+1,km+1)
real pv2(is:ie+1,kn+1)
integer i,j,k , iq
integer, parameter:: kord=4
#ifdef HYDRO_DELZ_REMAP
if (is_master() .and. .not. hydrostatic) then
print*, ''
print*, ' REMAPPING IC: INITIALIZING DELZ WITH HYDROSTATIC STATE '
print*, ''
endif
#endif
#ifdef HYDRO_DELZ_EXTRAP
if (is_master() .and. .not. hydrostatic) then
print*, ''
print*, ' REMAPPING IC: INITIALIZING DELZ WITH HYDROSTATIC STATE ABOVE INPUT MODEL TOP '
print*, ''
endif
#endif
#ifdef ZERO_W_EXTRAP
if (is_master() .and. .not. hydrostatic) then
print*, ''
print*, ' REMAPPING IC: INITIALIZING W TO ZERO ABOVE INPUT MODEL TOP '
print*, ''
endif
#endif
r_vir = rvgas/rdgas - 1.
rgrav = 1./grav
!$OMP parallel do default(none) shared(is,ie,js,je,ps,ak_r)
do j=js,je
do i=is,ie
ps(i,j) = ak_r(1)
enddo
enddo
! this OpenMP do-loop setup cannot work in it's current form....
!$OMP parallel do default(none) shared(is,ie,js,je,km,ps,delp_r)
do j=js,je
do k=1,km
do i=is,ie
ps(i,j) = ps(i,j) + delp_r(i,j,k)
enddo
enddo
enddo
! only one cell is needed
if ( square_domain ) then
call mpp_update_domains(ps, domain, whalo=1, ehalo=1, shalo=1, nhalo=1, complete=.true.)
else
call mpp_update_domains(ps, domain, complete=.true.)
endif
! Compute virtual Temp
!$OMP parallel do default(none) shared(is,ie,js,je,km,pt_r,r_vir,q_r)
do k=1,km
do j=js,je
do i=is,ie
#ifdef MULTI_GASES
pt_r(i,j,k) = pt_r(i,j,k) * virq(q_r(i,j,k,:))
#else
pt_r(i,j,k) = pt_r(i,j,k) * (1.+r_vir*q_r(i,j,k,1))
#endif
enddo
enddo
enddo
!$OMP parallel do default(none) shared(is,ie,js,je,km,ak_r,bk_r,ps,kn,ak,bk,u_r,u,delp, &
!$OMP ntp,nq,hydrostatic,make_nh,w_r,w,delz_r,delp_r,delz, &
!$OMP pt_r,pt,v_r,v,q,q_r,qdiag,qdiag_r) &
!$OMP private(pe1, pe2, pv1, pv2)
do 1000 j=js,je+1
!------
! map u
!------
do k=1,km+1
do i=is,ie
pe1(i,k) = ak_r(k) + 0.5*bk_r(k)*(ps(i,j-1)+ps(i,j))
enddo
enddo
do k=1,kn+1
do i=is,ie
pe2(i,k) = ak(k) + 0.5*bk(k)*(ps(i,j-1)+ps(i,j))
enddo
enddo
call remap_2d(km, pe1, u_r(is:ie,j:j,1:km), &
kn, pe2, u(is:ie,j:j,1:kn), &
is, ie, -1, kord)
if ( j /= (je+1) ) then
!---------------
! Hybrid sigma-p
!---------------
do k=1,km+1
do i=is,ie
pe1(i,k) = ak_r(k) + bk_r(k)*ps(i,j)
enddo
enddo
do k=1,kn+1
do i=is,ie
pe2(i,k) = ak(k) + bk(k)*ps(i,j)
enddo
enddo
!-------------
! Compute delp
!-------------
do k=1,kn
do i=is,ie
delp(i,j,k) = pe2(i,k+1) - pe2(i,k)
enddo
enddo
!----------------
! Map constituents
!----------------
if( nq /= 0 ) then
do iq=1,ntp
call remap_2d(km, pe1, q_r(is:ie,j:j,1:km,iq:iq), &
kn, pe2, q(is:ie,j:j,1:kn,iq:iq), &
is, ie, 0, kord)
enddo
do iq=ntp+1,nq
call remap_2d(km, pe1, qdiag_r(is:ie,j:j,1:km,iq:iq), &
kn, pe2, qdiag(is:ie,j:j,1:kn,iq:iq), &
is, ie, 0, kord)
enddo
endif
if ( .not. hydrostatic .and. .not. make_nh) then
! Remap vertical wind:
call remap_2d(km, pe1, w_r(is:ie,j:j,1:km), &
kn, pe2, w(is:ie,j:j,1:kn), &
is, ie, -1, kord)
#ifdef ZERO_W_EXTRAP
do k=1,kn
do i=is,ie
if (pe2(i,k) < pe1(i,1)) then
w(i,j,k) = 0.
endif
enddo
enddo
#endif
#ifndef HYDRO_DELZ_REMAP
! Remap delz for hybrid sigma-p coordinate
do k=1,km
do i=is,ie
delz_r(i,j,k) = -delz_r(i,j,k)/delp_r(i,j,k) ! ="specific volume"/grav
enddo
enddo
call remap_2d(km, pe1, delz_r(is:ie,j:j,1:km), &
kn, pe2, delz(is:ie,j:j,1:kn), &
is, ie, 1, kord)
do k=1,kn
do i=is,ie
delz(i,j,k) = -delz(i,j,k)*delp(i,j,k)
enddo
enddo
#endif
endif
! Geopotential conserving remap of virtual temperature:
do k=1,km+1
do i=is,ie
pe1(i,k) = log(pe1(i,k))
enddo
enddo
do k=1,kn+1
do i=is,ie
pe2(i,k) = log(pe2(i,k))
enddo
enddo
call remap_2d(km, pe1, pt_r(is:ie,j:j,1:km), &
kn, pe2, pt(is:ie,j:j,1:kn), &
is, ie, 1, kord)
#ifdef HYDRO_DELZ_REMAP
!initialize delz from the hydrostatic state
do k=1,kn
do i=is,ie
delz(i,j,k) = (rdgas*rgrav)*pt(i,j,k)*(pe2(i,k)-pe2(i,k+1))
enddo
enddo
#endif
#ifdef HYDRO_DELZ_EXTRAP
!initialize delz from the hydrostatic state
do k=1,kn
do i=is,ie
if (pe2(i,k) < pe1(i,1)) then
delz(i,j,k) = (rdgas*rgrav)*pt(i,j,k)*(pe2(i,k)-pe2(i,k+1))
endif
enddo
enddo
#endif
!------
! map v
!------
do k=1,km+1
do i=is,ie+1
pv1(i,k) = ak_r(k) + 0.5*bk_r(k)*(ps(i-1,j)+ps(i,j))
enddo
enddo
do k=1,kn+1
do i=is,ie+1
pv2(i,k) = ak(k) + 0.5*bk(k)*(ps(i-1,j)+ps(i,j))
enddo
enddo
call remap_2d(km, pv1, v_r(is:ie+1,j:j,1:km), &
kn, pv2, v(is:ie+1,j:j,1:kn), &
is, ie+1, -1, kord)
endif !(j < je+1)
1000 continue
!$OMP parallel do default(none) shared(is,ie,js,je,kn,pt,r_vir,q)
do k=1,kn
do j=js,je
do i=is,ie
#ifdef MULTI_GASES
pt(i,j,k) = pt(i,j,k) / virq(q(i,j,k,:))
#else
pt(i,j,k) = pt(i,j,k) / (1.+r_vir*q(i,j,k,1))
#endif
enddo
enddo
enddo
end subroutine rst_remap
!>@brief The subroutine 'mappm' is a general-purpose routine for remapping
!! one set of vertical levels to another.
subroutine mappm(km, pe1, q1, kn, pe2, q2, i1, i2, iv, kord, ptop)
! IV = 0: constituents
! IV = 1: potential temp
! IV =-1: winds
! Mass flux preserving mapping: q1(im,km) -> q2(im,kn)
! pe1: pressure at layer edges (from model top to bottom surface)
! in the original vertical coordinate
! pe2: pressure at layer edges (from model top to bottom surface)
! in the new vertical coordinate
integer, intent(in):: i1, i2, km, kn, kord, iv
real, intent(in ):: pe1(i1:i2,km+1), pe2(i1:i2,kn+1) !< pe1: pressure at layer edges from model top to bottom
!! surface in the ORIGINAL vertical coordinate
!< pe2: pressure at layer edges from model top to bottom
!! surface in the NEW vertical coordinate
! Mass flux preserving mapping: q1(im,km) -> q2(im,kn)
real, intent(in ):: q1(i1:i2,km)
real, intent(out):: q2(i1:i2,kn)
real, intent(IN) :: ptop
! local
real qs(i1:i2)
real dp1(i1:i2,km)
real a4(4,i1:i2,km)
integer i, k, l
integer k0, k1
real pl, pr, tt, delp, qsum, dpsum, esl
do k=1,km
do i=i1,i2
dp1(i,k) = pe1(i,k+1) - pe1(i,k)
a4(1,i,k) = q1(i,k)
enddo
enddo
if ( kord >7 ) then
call cs_profile( qs, a4, dp1, km, i1, i2, iv, kord )
else
call ppm_profile( a4, dp1, km, i1, i2, iv, kord )
endif
!------------------------------------
! Lowest layer: constant distribution
!------------------------------------
#ifdef NGGPS_SUBMITTED
do i=i1,i2
a4(2,i,km) = q1(i,km)
a4(3,i,km) = q1(i,km)
a4(4,i,km) = 0.
enddo
#endif
do 5555 i=i1,i2
k0 = 1
do 555 k=1,kn
if(pe2(i,k) .le. pe1(i,1)) then
! above old ptop
q2(i,k) = q1(i,1)
elseif(pe2(i,k) .ge. pe1(i,km+1)) then
! Entire grid below old ps
#ifdef NGGPS_SUBMITTED
q2(i,k) = a4(3,i,km) ! this is not good.
#else
q2(i,k) = q1(i,km)
#endif
else
do 45 L=k0,km
! locate the top edge at pe2(i,k)
if( pe2(i,k) .ge. pe1(i,L) .and. &
pe2(i,k) .le. pe1(i,L+1) ) then
k0 = L
PL = (pe2(i,k)-pe1(i,L)) / dp1(i,L)
if(pe2(i,k+1) .le. pe1(i,L+1)) then
! entire new grid is within the original grid
PR = (pe2(i,k+1)-pe1(i,L)) / dp1(i,L)
TT = r3*(PR*(PR+PL)+PL**2)
q2(i,k) = a4(2,i,L) + 0.5*(a4(4,i,L)+a4(3,i,L) &
- a4(2,i,L))*(PR+PL) - a4(4,i,L)*TT
goto 555
else
! Fractional area...
delp = pe1(i,L+1) - pe2(i,k)
TT = r3*(1.+PL*(1.+PL))
qsum = delp*(a4(2,i,L)+0.5*(a4(4,i,L)+ &
a4(3,i,L)-a4(2,i,L))*(1.+PL)-a4(4,i,L)*TT)
dpsum = delp
k1 = L + 1
goto 111
endif
endif
45 continue
111 continue
do 55 L=k1,km
if( pe2(i,k+1) .gt. pe1(i,L+1) ) then
! Whole layer..
qsum = qsum + dp1(i,L)*q1(i,L)
dpsum = dpsum + dp1(i,L)
else
delp = pe2(i,k+1)-pe1(i,L)
esl = delp / dp1(i,L)
qsum = qsum + delp * (a4(2,i,L)+0.5*esl* &
(a4(3,i,L)-a4(2,i,L)+a4(4,i,L)*(1.-r23*esl)) )
dpsum = dpsum + delp
k0 = L
goto 123
endif
55 continue
delp = pe2(i,k+1) - pe1(i,km+1)
if(delp > 0.) then
! Extended below old ps
#ifdef NGGPS_SUBMITTED
qsum = qsum + delp * a4(3,i,km) ! not good.
#else
qsum = qsum + delp * q1(i,km)
#endif
dpsum = dpsum + delp
endif
123 q2(i,k) = qsum / dpsum
endif
555 continue
5555 continue
end subroutine mappm
!>@brief The subroutine 'moist_cv' computes the FV3-consistent moist heat capacity under constant volume,
!! including the heating capacity of water vapor and condensates.
!>@details See \cite emanuel1994atmospheric for information on variable heat capacities.
subroutine moist_cv(is,ie, isd,ied, jsd,jed, km, j, k, nwat, sphum, liq_wat, rainwat, &
ice_wat, snowwat, graupel, q, qd, cvm, t1)
integer, intent(in):: is, ie, isd,ied, jsd,jed, km, nwat, j, k
integer, intent(in):: sphum, liq_wat, rainwat, ice_wat, snowwat, graupel
real, intent(in), dimension(isd:ied,jsd:jed,km,nwat):: q
real, intent(out), dimension(is:ie):: cvm, qd
real, intent(in), optional:: t1(is:ie)
!
real, parameter:: t_i0 = 15.
real, dimension(is:ie):: qv, ql, qs
integer:: i
select case (nwat)
case(2)
if ( present(t1) ) then ! Special case for GFS physics
do i=is,ie
qd(i) = max(0., q(i,j,k,liq_wat))
if ( t1(i) > tice ) then
qs(i) = 0.
elseif ( t1(i) < tice-t_i0 ) then
qs(i) = qd(i)
else
qs(i) = qd(i)*(tice-t1(i))/t_i0
endif
ql(i) = qd(i) - qs(i)
qv(i) = max(0.,q(i,j,k,sphum))
#ifdef MULTI_GASES
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air*vicvqd(q(i,j,k,1:num_gas)) + qv(i)*cv_vap + ql(i)*c_liq + qs(i)*c_ice
#else
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air + qv(i)*cv_vap + ql(i)*c_liq + qs(i)*c_ice
#endif
enddo
else
do i=is,ie
qv(i) = max(0.,q(i,j,k,sphum))
qs(i) = max(0.,q(i,j,k,liq_wat))
qd(i) = qs(i)
#ifdef MULTI_GASES
cvm(i) = (1.-qv(i))*cv_air*vicvqd(q(i,j,k,1:num_gas)) + qv(i)*cv_vap
#else
cvm(i) = (1.-qv(i))*cv_air + qv(i)*cv_vap
#endif
enddo
endif
case (3)
do i=is,ie
qv(i) = q(i,j,k,sphum)
ql(i) = q(i,j,k,liq_wat)
qs(i) = q(i,j,k,ice_wat)
qd(i) = ql(i) + qs(i)
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air + qv(i)*cv_vap + ql(i)*c_liq + qs(i)*c_ice
enddo
case(4) ! K_warm_rain with fake ice
do i=is,ie
#ifndef CCPP
qv(i) = q(i,j,k,sphum)
qd(i) = q(i,j,k,liq_wat) + q(i,j,k,rainwat)
#ifdef MULTI_GASES
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air*vicvqd(q(i,j,k,1:num_gas)) + qv(i)*cv_vap + qd(i)*c_liq
#else
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air + qv(i)*cv_vap + qd(i)*c_liq
#endif
#else
qv(i) = q(i,j,k,sphum)
ql(i) = q(i,j,k,liq_wat) + q(i,j,k,rainwat)
qs(i) = q(i,j,k,ice_wat)
qd(i) = ql(i) + qs(i)
#ifdef MULTI_GASES
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air*vicvqd(q(i,j,k,1:num_gas)) + qv(i)*cv_vap + ql(i)*c_liq + qs(i)*c_ice
#else
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air + qv(i)*cv_vap + ql(i)*c_liq + qs(i)*c_ice
#endif
#endif
enddo
case(5)
do i=is,ie
qv(i) = q(i,j,k,sphum)
ql(i) = q(i,j,k,liq_wat) + q(i,j,k,rainwat)
qs(i) = q(i,j,k,ice_wat) + q(i,j,k,snowwat)
qd(i) = ql(i) + qs(i)
#ifdef MULTI_GASES
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air*vicvqd(q(i,j,k,1:num_gas)) + qv(i)*cv_vap + ql(i)*c_liq + qs(i)*c_ice
#else
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air + qv(i)*cv_vap + ql(i)*c_liq + qs(i)*c_ice
#endif
enddo
case(6)
do i=is,ie
qv(i) = q(i,j,k,sphum)
ql(i) = q(i,j,k,liq_wat) + q(i,j,k,rainwat)
qs(i) = q(i,j,k,ice_wat) + q(i,j,k,snowwat) + q(i,j,k,graupel)
qd(i) = ql(i) + qs(i)
#ifdef MULTI_GASES
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air*vicvqd(q(i,j,k,1:num_gas)) + qv(i)*cv_vap + ql(i)*c_liq + qs(i)*c_ice
#else
cvm(i) = (1.-(qv(i)+qd(i)))*cv_air + qv(i)*cv_vap + ql(i)*c_liq + qs(i)*c_ice
#endif
enddo
case default
call mpp_error (NOTE, 'fv_mapz::moist_cv - using default cv_air')
do i=is,ie
qd(i) = 0.
#ifdef MULTI_GASES
cvm(i) = cv_air*vicvqd(q(i,j,k,1:num_gas))
#else
cvm(i) = cv_air
#endif
enddo
end select
end subroutine moist_cv
!>@brief The subroutine 'moist_cp' computes the FV3-consistent moist heat capacity under constant pressure,
!! including the heating capacity of water vapor and condensates.
subroutine moist_cp(is,ie, isd,ied, jsd,jed, km, j, k, nwat, sphum, liq_wat, rainwat, &
ice_wat, snowwat, graupel, q, qd, cpm, t1)
integer, intent(in):: is, ie, isd,ied, jsd,jed, km, nwat, j, k
integer, intent(in):: sphum, liq_wat, rainwat, ice_wat, snowwat, graupel
real, intent(in), dimension(isd:ied,jsd:jed,km,nwat):: q
real, intent(out), dimension(is:ie):: cpm, qd
real, intent(in), optional:: t1(is:ie)
!
real, parameter:: t_i0 = 15.
real, dimension(is:ie):: qv, ql, qs
integer:: i
select case (nwat)
case(2)
if ( present(t1) ) then ! Special case for GFS physics
do i=is,ie
qd(i) = max(0., q(i,j,k,liq_wat))
if ( t1(i) > tice ) then
qs(i) = 0.
elseif ( t1(i) < tice-t_i0 ) then
qs(i) = qd(i)
else
qs(i) = qd(i)*(tice-t1(i))/t_i0
endif
ql(i) = qd(i) - qs(i)
qv(i) = max(0.,q(i,j,k,sphum))
#ifdef MULTI_GASES
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air * vicpqd(q(i,j,k,:)) + qv(i)*cp_vapor + ql(i)*c_liq + qs(i)*c_ice
#else
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air + qv(i)*cp_vapor + ql(i)*c_liq + qs(i)*c_ice
#endif
enddo
else
do i=is,ie
qv(i) = max(0.,q(i,j,k,sphum))
qs(i) = max(0.,q(i,j,k,liq_wat))
qd(i) = qs(i)
#ifdef MULTI_GASES
cpm(i) = (1.-qv(i))*cp_air*vicpqd(q(i,j,k,:)) + qv(i)*cp_vapor
#else
cpm(i) = (1.-qv(i))*cp_air + qv(i)*cp_vapor
#endif
enddo
endif
case(3)
do i=is,ie
qv(i) = q(i,j,k,sphum)
ql(i) = q(i,j,k,liq_wat)
qs(i) = q(i,j,k,ice_wat)
qd(i) = ql(i) + qs(i)
#ifdef MULTI_GASES
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air*vicpqd(q(i,j,k,:)) + qv(i)*cp_vapor + ql(i)*c_liq + qs(i)*c_ice
#else
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air + qv(i)*cp_vapor + ql(i)*c_liq + qs(i)*c_ice
#endif
enddo
case(4) ! K_warm_rain scheme with fake ice
do i=is,ie
#ifndef CCPP
qv(i) = q(i,j,k,sphum)
qd(i) = q(i,j,k,liq_wat) + q(i,j,k,rainwat)
#ifdef MULTI_GASES
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air*vicpqd(q(i,j,k,:)) + qv(i)*cp_vapor + qd(i)*c_liq
#else
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air + qv(i)*cp_vapor + qd(i)*c_liq
#endif
#else
qv(i) = q(i,j,k,sphum)
ql(i) = q(i,j,k,liq_wat) + q(i,j,k,rainwat)
qs(i) = q(i,j,k,ice_wat)
qd(i) = ql(i) + qs(i)
#ifdef MULTI_GASES
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air*vicpqd(q(i,j,k,:)) + qv(i)*cp_vapor + ql(i)*c_liq + qs(i)*c_ice
#else
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air + qv(i)*cp_vapor + ql(i)*c_liq + qs(i)*c_ice
#endif
#endif
enddo
case(5)
do i=is,ie
qv(i) = q(i,j,k,sphum)
ql(i) = q(i,j,k,liq_wat) + q(i,j,k,rainwat)
qs(i) = q(i,j,k,ice_wat) + q(i,j,k,snowwat)
qd(i) = ql(i) + qs(i)
#ifdef MULTI_GASES
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air*vicpqd(q(i,j,k,:)) + qv(i)*cp_vapor + ql(i)*c_liq + qs(i)*c_ice
#else
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air + qv(i)*cp_vapor + ql(i)*c_liq + qs(i)*c_ice
#endif
enddo
case(6)
do i=is,ie
qv(i) = q(i,j,k,sphum)
ql(i) = q(i,j,k,liq_wat) + q(i,j,k,rainwat)
qs(i) = q(i,j,k,ice_wat) + q(i,j,k,snowwat) + q(i,j,k,graupel)
qd(i) = ql(i) + qs(i)
#ifdef MULTI_GASES
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air*vicpqd(q(i,j,k,:)) + qv(i)*cp_vapor + ql(i)*c_liq + qs(i)*c_ice
#else
cpm(i) = (1.-(qv(i)+qd(i)))*cp_air + qv(i)*cp_vapor + ql(i)*c_liq + qs(i)*c_ice
#endif
enddo
case default
call mpp_error (NOTE, 'fv_mapz::moist_cp - using default cp_air')
do i=is,ie
qd(i) = 0.
#ifdef MULTI_GASES
cpm(i) = cp_air*vicpqd(q(i,j,k,:))
#else
cpm(i) = cp_air
#endif
enddo
end select
end subroutine moist_cp
end module fv_mapz_mod