C**** QUSDEF.f
MODULE QUSDEF 29
!@sum QUSDEF contains definitions for manipulating moments
!@auth Maxwell Kelley
integer, parameter ::
& nmom=9,
& mx =1, my =2, mz =3,
& mxx=4, myy=5, mzz=6,
& mxy=7, mzx=8, myz=9
C**** some useful vector subscripts:
C**** moments with a vertical component
integer, dimension(4), parameter ::
& zmoms=(/MZ,MZZ,MYZ,MZX/)
C**** moments with no vertical component
integer, dimension(5), parameter ::
& xymoms=(/MX,MY,MXX,MXY,MYY/)
C**** moments with a horizontal component
integer, dimension(7), parameter ::
& ihmoms=(/MX,MY,MXX,MYY,MXY,MYZ,MZX/)
C**** moments with no horizontal component
integer, dimension(2), parameter ::
& zomoms=(/MZ,MZZ/)
C**** x-x, x-y, x-z switches
integer, dimension(nmom), parameter ::
& xdir=(/mx,my,mz,mxx,myy,mzz,mxy,myz,mzx/)
integer, dimension(nmom), parameter ::
& ydir=(/my,mx,mz,myy,mxx,mzz,mxy,mzx,myz/)
integer, dimension(nmom), parameter ::
& zdir=(/mz,my,mx,mzz,myy,mxx,myz,mxy,mzx/)
!@dbparam prather_limits forces +ve sub-grid scale profiles (default=0)
integer :: prather_limits = 0
Integer, Parameter :: FLUX_NEGATIVE=-1
Integer, Parameter :: FLUX_NONNEGATIVE=+1
END MODULE QUSDEF
subroutine adv1d(s,smom, f,fmom, mass,dm, nx,qlimit,stride,dir 13,2
* ,ierr,nerr)
!@sum adv1d implements the quadratic upstream scheme in one dimension
!@auth G. Russell, modified by Maxwell Kelley
c--------------------------------------------------------------
c adv1d advects tracers in x-direction using the qus
c the order of the moments in dir is: x,y,z,xx,yy,zz,xy,yz,zx
c--------------------------------------------------------------
use QUSDEF
, only : nmom,prather_limits
implicit none
!
!@var s mean tracer amount (kg or J)
!@var smom qus tracer moments (kg or J)
!@var f tracer flux (diagnostic output) (kg or J)
!@var fmom tracer moment flux (diagnostic output) (kg or J)
!@var mass mass field (kg)
!@var dm mass flux (kg)
!@var nx length of 1D vector
!@var qlimit true if negative tracer is to be avoided
!@var stride spacing in s array between elements of relevant 1D array
!@var dir direction switch (equals one of xdir ydir or zdir)
!@var ierr, nerr error codes
integer, intent(in) :: nx,stride
logical, intent(in) :: qlimit
REAL*8, dimension(nx) :: dm, f
REAL*8, dimension(nx*stride) :: s,mass
REAL*8, dimension(nmom,nx*stride) :: smom
REAL*8, dimension(nmom,nx) :: fmom
integer, dimension(nmom) :: dir
integer :: mx,my,mz,mxx,myy,mzz,mxy,myz,mzx
integer :: n,np1,nm1,nn,ns
integer,intent(out) :: ierr,nerr
REAL*8 :: fracm,frac1,bymnew,mnew,dm2,tmp
! qlimit variables
REAL*8 :: an, anm1, fn, fnm1, sn, sxn, sxxn
!
ierr=0 ; nerr=0
mx = dir(1)
my = dir(2)
mz = dir(3)
mxx = dir(4)
myy = dir(5)
mzz = dir(6)
mxy = dir(7)
myz = dir(8)
mzx = dir(9)
c-----------------------------------------------------------
! calculate tracer mass flux f
c-----------------------------------------------------------
n = nx
do np1=1,nx
if(dm(n).lt.0.) then ! air mass flux is negative
nn=np1
frac1=+1.
else ! air mass flux is positive
nn=n
frac1=-1.
endif
ns=1+(nn-1)*stride
fracm=dm(n)/mass(ns)
if(mass(ns).le.0.d0) fracm=0.d0
frac1=fracm+frac1
f(n)=fracm*(s(ns)-frac1*(smom(mx,ns)-
& (frac1+fracm)*smom(mxx,ns)))
! temporary storage of fracm in fx, to be used below
fmom(mx,n)=fracm
! temporary storage of frac1 in fxx, to be used below
fmom(mxx,n)=frac1
!
n = np1
enddo
if(qlimit) then
nm1 = nx
do n=1,nx
ns=1+(n-1)*stride
an = fmom(mx,n) ! reading fracm which was stored in fx
anm1 = fmom(mx,nm1)
fn = f(n)
fnm1 = f(nm1)
sn = s(ns)
sxn = smom(mx,ns)
sxxn = smom(mxx,ns)
call limitq
(anm1,an,fnm1,fn,sn,sxn,sxxn,ierr)
if (ierr.gt.0) then
nerr=n
if (ierr.eq.2) return
end if
f(n) = fn
f(nm1) = fnm1
smom(mx,ns) = sxn
smom(mxx,ns) = sxxn
nm1 = n
enddo
endif
c--------------------------------------------------------------------
! calculate tracer fluxes of slopes and curvatures
c--------------------------------------------------------------------
n = nx
do np1=1,nx
if(dm(n).lt.0.) then ! air mass flux is negative
nn=np1
else ! air mass flux is positive
nn=n
endif
ns=1+(nn-1)*stride
! retrieving fracm, which was stored in fx
fracm=fmom(mx,n)
! retrieving frac1, which was stored in fxx
frac1=fmom(mxx,n)
!
fmom(mx,n)=dm(n)*(fracm*fracm*(smom(mx,ns)
& -3.*frac1*smom(mxx,ns))-3.*f(n))
fmom(mxx,n)=dm(n)*(dm(n)*fracm**3 *smom(mxx,ns)
& -5.*(dm(n)*f(n)+fmom(mx,n)))
! cross moments
fmom(my,n) = fracm*(smom(my,ns)-frac1*smom(mxy,ns))
fmom(mxy,n) = dm(n)*(fracm*fracm*smom(mxy,ns)-3.*fmom(my,n))
fmom(mz,n) = fracm*(smom(mz,ns)-frac1*smom(mzx,ns))
fmom(mzx,n) = dm(n)*(fracm*fracm*smom(mzx,ns)-3.*fmom(mz,n))
fmom(myy,n) = fracm*smom(myy,ns)
fmom(mzz,n) = fracm*smom(mzz,ns)
fmom(myz,n) = fracm*smom(myz,ns)
n = np1
enddo
c-------------------------------------------------------------------
c update tracer mass, moments of tracer mass, air mass distribution
c-------------------------------------------------------------------
nm1 = nx
do n=1,nx
ns=1+(n-1)*stride
tmp=mass(ns)+dm(nm1)
mnew=tmp-dm(n)
! mnew=mass(ns)+dm(nm1)-dm(n)
bymnew = 1./mnew
dm2=dm(nm1)+dm(n)
tmp=s(ns)+f(nm1)
s(ns)=tmp-f(n)
! s(ns)=s(ns)+f(nm1)-f(n)
!
smom(mx,ns)=(smom(mx,ns)*mass(ns)-3.*(-dm2*s(ns)
& +mass(ns)*(f(nm1)+f(n)))+(fmom(mx,nm1)-fmom(mx,n)))*bymnew
smom(mxx,ns) = (smom(mxx,ns)*mass(ns)*mass(ns)
& +2.5*s(ns)*(mass(ns)*mass(ns)-mnew*mnew-3.*dm2*dm2)
& +5.*(mass(ns)*(mass(ns)*(f(nm1)-f(n))-fmom(mx,nm1)
& -fmom(mx,n))+dm2*smom(mx,ns)*mnew)
& +(fmom(mxx,nm1)-fmom(mxx,n))) * (bymnew*bymnew)
! cross moments
smom(my,ns)=smom(my,ns)+fmom(my,nm1)-fmom(my,n)
smom(mxy,ns)=(smom(mxy,ns)*mass(ns)-3.*(-dm2*smom(my,ns) +
& mass(ns)*(fmom(my,nm1)+fmom(my,n))) +
& (fmom(mxy,nm1)-fmom(mxy,n)))*bymnew
smom(mz,ns)=smom(mz,ns)+fmom(mz,nm1)-fmom(mz,n)
smom(mzx,ns)=(smom(mzx,ns)*mass(ns)-3.*(-dm2*smom(mz,ns) +
& mass(ns)*(fmom(mz,nm1)+fmom(mz,n))) +
& (fmom(mzx,nm1)-fmom(mzx,n)))*bymnew
!
smom(myy,ns)=smom(myy,ns)+fmom(myy,nm1)-fmom(myy,n)
smom(mzz,ns)=smom(mzz,ns)+fmom(mzz,nm1)-fmom(mzz,n)
smom(myz,ns)=smom(myz,ns)+fmom(myz,nm1)-fmom(myz,n)
!
c------------------------------------------------------------------
mass(ns) = mnew
if(mass(ns).le.0.) then
s(ns)=0.
smom(:,ns)=0.
endif
if (qlimit .and. prather_limits.eq.1) then ! force Prather limits
smom(mx,ns)=min(1.5*s(ns),max(-1.5*s(ns),smom(mx,ns)))
smom(mxx,ns)=min(2.*s(ns)-abs(smom(mx,ns))/3.,max(abs(smom(mx
* ,ns))-s(ns),smom(mxx,ns)))
smom(mxy,ns)=min(s(ns),max(-s(ns),smom(mxy,ns)))
smom(mzx,ns)=min(s(ns),max(-s(ns),smom(mzx,ns)))
end if
c-----------------------------------------------------------------
nm1 = n
enddo
return
end subroutine adv1d
c************************************************************************
subroutine advection_1D_custom(s,smom, f,fmom, mass,dm, nx, 2,19
* nlev,idx, qlimit,stride,dir,ierr, err_loc)
!@sum advection_1d_custom is a parallel variant of adv1d which does not
!@sum include qlimits. This increases locality such that global
!@sum communication can be significantly reduced for advection along
!@sum longitudes.
!@auth T. Clune
c--------------------------------------------------------------
c adv1d advects tracers in x-direction using the qus
c the order of the moments in dir is: x,y,z,xx,yy,zz,xy,yz,zx
c--------------------------------------------------------------
use QUSDEF
, only : nmom,prather_limits
USE DOMAIN_DECOMP
, only: grid, GET
USE DOMAIN_DECOMP
, only: NORTH, SOUTH
USE DOMAIN_DECOMP
, only: HALO_UPDATE, HALO_UPDATE_COLUMN
USE DOMAIN_DECOMP
, only: CHECKSUM
implicit none
!
!@var s mean tracer amount (kg or J)
!@var smom qus tracer moments (kg or J)
!@var f tracer flux (diagnostic output) (kg or J)
!@var fmom tracer moment flux (diagnostic output) (kg or J)
!@var mass mass field (kg)
!@var dm mass flux (kg)
!@var nx length of 1D vector
!@var qlimit true if negative tracer is to be avoided
!@var stride spacing in s array between elements of relevant 1D array
!@var dir direction switch (equals one of xdir ydir or zdir)
!@var ierr, nerr error codes
integer, intent(in) :: nx,nlev,stride
logical, intent(in) :: idx(nlev)
logical, intent(in) :: qlimit
REAL*8, dimension(stride, grid%j_strt_halo:grid%j_stop_halo,
* nlev) :: dm, f, s,mass
REAL*8, dimension(nmom,stride, grid%j_strt_halo:grid%j_stop_halo,
* nlev) :: fmom, smom
integer, dimension(nmom) :: dir
integer :: mx,my,mz,mxx,myy,mzz,mxy,myz,mzx
integer :: n,np1,nm1,nn,ns
integer,intent(out) :: ierr,err_loc(3) ! 3 dimensions
REAL*8 :: fracm,frac1,bymnew,mnew,dm2,tmp
INTEGER :: i
INTEGER :: l
INTEGER :: J_0, J_1, J_0S, J_1S
LOGICAL :: HAVE_SOUTH_POLE
! qlimit variables
REAL*8 :: an, anm1, fn, fnm1, sn, sxn, sxxn
!
! cpp used to avoid numerous detailed changes in source for
! new arrangement.
#define S(i,j) s(i,j,l)
#define DM(i,j) dm(i,j,l)
#define F(i,j) f(i,j,l)
#define MASS(i,j) mass(i,j,l)
#define SMOM(i,j,k) smom(i,j,k,l)
#define FMOM(i,j,k) fmom(i,j,k,l)
ierr=0
mx = dir(1)
my = dir(2)
mz = dir(3)
mxx = dir(4)
myy = dir(5)
mzz = dir(6)
mxy = dir(7)
myz = dir(8)
mzx = dir(9)
CALL GET
(grid, J_STRT = J_0, J_STOP=J_1,
& J_STRT_SKP=J_0S, J_STOP_SKP=J_1S,
& HAVE_SOUTH_POLE=HAVE_SOUTH_POLE)
CALL HALO_UPDATE
(grid, mass, FROM=NORTH)
CALL HALO_UPDATE
(grid, s, FROM=NORTH)
CALL HALO_UPDATE_COLUMN
(grid, smom, FROM=NORTH)
DO l=1,nlev
if (.not. idx(l)) cycle
Do i=1, stride
Call calc_tracer_mass_flux
()
End Do
end do
! Limit fluxes to maintain positive mean values?
CALL HALO_UPDATE_COLUMN
(grid, fmom, FROM=NORTH+SOUTH)
If (qlimit) Then
Call HALO_UPDATE
(grid, f, FROM=NORTH+SOUTH)
DO l=1,nlev
if (.not. idx(l)) cycle
Do i=1, stride
Call apply_limiter
()
End Do
end do
Call HALO_UPDATE
(grid, f, FROM=NORTH+SOUTH)
End If
c--------------------------------------------------------------------
! calculate tracer fluxes of slopes and curvatures
c--------------------------------------------------------------------
DO l=1,nlev
if (.not. idx(l)) cycle
Do i=1, stride
Call tracer_slopes_and_curvatures
()
end do
end do
c-------------------------------------------------------------------
c update tracer mass, moments of tracer mass, air mass distribution
c-------------------------------------------------------------------
CALL HALO_UPDATE
(grid, f, FROM=SOUTH)
CALL HALO_UPDATE
(grid, dm, FROM=SOUTH)
CALL HALO_UPDATE_COLUMN
(grid, fmom, FROM=SOUTH)
DO l=1,nlev
if (.not. idx(l)) cycle
DO i = 1, stride
Call update_tracer_mass
()
end do
enddo
return
Contains
Integer Function CheckFlux(flux) Result (sign_of_flux) 2,1
USE QUSDEF
, Only : FLUX_NEGATIVE, FLUX_NONNEGATIVE
Real*8, Intent(In) :: flux
If (flux < 0) Then
sign_of_flux = FLUX_NEGATIVE
Else
sign_of_flux = FLUX_NONNEGATIVE
End If
End Function CheckFlux
Integer Function NeighborByFlux(n, dm) 6,2
USE QUSDEF
, Only : FLUX_NEGATIVE
Integer, Intent(In) :: n
Real*8, Intent(In) :: dm
Integer :: nn
If (CheckFlux
(dm) == FLUX_NEGATIVE) Then ! air mass flux is negative
nn=n+1
else ! air mass flux is positive
nn=n
endif
NeighborByFlux = nn
End Function NeighborByFlux
Function FluxFraction(dm) Result(frac) 3,2
USE QUSDEF
, Only : FLUX_NEGATIVE
Real*8, Intent(In) :: dm
Real*8 :: frac
If (CheckFlux
(dm) == FLUX_NEGATIVE) Then
frac = +1.
Else ! Flux non negative
frac = -1.
End If
End Function FluxFraction
Function MassFraction(dm, mass) Result (fracm) 3
Real*8, Intent(In) :: dm
Real*8, Intent(In) :: mass
Real*8 :: fracm
If (mass > 0.0d0) Then
fracm = dm / mass
Else
fracm = 0.d0
End If
End Function MassFraction
Subroutine calc_tracer_mass_flux() 3,9
Do n = J_0, J_1
nn = NeighborByFlux
(n, DM(i,n))
fracm = MassFraction
(DM(i,n), MASS(i,nn))
frac1 = fracm + FluxFraction
(DM(i,n))
F(i,n)=fracm*(S(i,nn)-frac1*(SMOM(mx,i,nn)-
* (frac1+fracm)*SMOM(mxx,i,nn)))
! temporary storage of fracm in fx, to be used below
FMOM(mx,i,n)=fracm
! temporary storage of frac1 in fxx, to be used below
FMOM(mxx,i,n)=frac1
!
enddo
End Subroutine calc_tracer_mass_flux
Subroutine tracer_slopes_and_curvatures() 1,1
Do n = J_0, J_1
nn = NeighborByFlux
(n, DM(i,n))
! retrieving fracm, which was stored in fx
fracm=FMOM(mx,i,n)
! retrieving frac1, which was stored in fxx
frac1=FMOM(mxx,i,n)
!
FMOM(mx,i,n)=
& DM(i,n)*(fracm*fracm*(SMOM(mx,i,nn)
& -3.*frac1*SMOM(mxx,i,nn))-3.*F(i,n))
FMOM(mxx,i,n)=
& DM(i,n)*(DM(i,n)*fracm**3 *SMOM(mxx,i,nn)
& -5.*(DM(i,n)*F(i,n)+FMOM(mx,i,n)))
! cross moments
FMOM(my,i,n)=
& fracm*(SMOM(my,i,nn)-frac1*SMOM(mxy,i,nn))
FMOM(mxy,i,n)=
& DM(i,n)*(fracm*fracm*SMOM(mxy,i,nn)
& -3.*FMOM(my,i,n))
FMOM(mz,i,n)=
& fracm*(SMOM(mz,i,nn)-frac1*SMOM(mzx,i,nn))
FMOM(mzx,i,n)=
& DM(i,n)*(fracm*fracm*SMOM(mzx,i,nn)
& -3.*FMOM(mz,i,n))
FMOM(myy,i,n) = fracm*SMOM(myy,i,nn)
FMOM(mzz,i,n) = fracm*SMOM(mzz,i,nn)
FMOM(myz,i,n) = fracm*SMOM(myz,i,nn)
enddo
End Subroutine tracer_slopes_and_curvatures
Subroutine apply_limiter 3,4
If (HAVE_SOUTH_POLE) Then
n = J_0
an = FMOM(mx,i,n) ! reading fracm which was stored in fx
anm1 = 0
fn = F(i,n)
fnm1 = 0
sn = S(i,n)
sxn = SMOM(mx,i,n)
sxxn = SMOM(mxx,i,n)
call limitq
(anm1,an,fnm1,fn,sn,sxn,sxxn,ierr)
if (ierr.gt.0) then
err_loc = (/ i, n, l /)
if (ierr.eq.2) return
end if
F(i,n) = fn
SMOM(mx,i,n) = sxn
SMOM(mxx,i,n) = sxxn
End If
nm1=J_0S-1
DO n = J_0S, J_1S+1
an = FMOM(mx,i,n) ! reading fracm which was stored in fx
anm1 = FMOM(mx,i,nm1)
fn = F(i,n)
fnm1 = F(i,nm1)
sn = S(i,n)
sxn = SMOM(mx,i,n)
sxxn = SMOM(mxx,i,n)
call limitq
(anm1,an,fnm1,fn,sn,sxn,sxxn,ierr)
if (ierr.gt.0) then
err_loc = (/ i, n, l /)
if (ierr.eq.2) return
end if
F(i,n) = fn
F(i,nm1) = fnm1
SMOM(mx,i,n) = sxn
SMOM(mxx,i,n) = sxxn
nm1 = n
enddo
End Subroutine apply_limiter
Subroutine update_tracer_mass() 3
If (HAVE_SOUTH_POLE) THEN
n = J_0
tmp=MASS(i,n)
mnew=tmp-DM(i,n)
bymnew = 1./mnew
dm2=DM(i,n)
tmp=S(i,n)
S(i,n)=tmp-F(i,n)
SMOM(mx,i,n)=(SMOM(mx,i,n)*MASS(i,n)-
& 3.*(-dm2*S(i,n)
& +MASS(i,n)*(F(i,n)))+
& (-FMOM(mx,i,n)))*bymnew
SMOM(mxx,i,n)=
& (SMOM(mxx,i,n)*MASS(i,n)*MASS(i,n)
& +2.5*S(i,n)*(MASS(i,n)*MASS(i,n)-mnew*mnew-3.*dm2*dm2)
& +5.*(MASS(i,n)*(MASS(i,n)*(-F(i,n))
& -FMOM(mx,i,n))+dm2*SMOM(mx,i,n)*mnew)
& +(-FMOM(mxx,i,n))) * (bymnew*bymnew)
! cross moments
SMOM(my,i,n)=SMOM(my,i,n)-FMOM(my,i,n)
SMOM(mxy,i,n)=(SMOM(mxy,i,n)*MASS(i,n)
& -3.*(-dm2*SMOM(my,i,n) +
& MASS(i,n)*(FMOM(my,i,n))) +
& (-FMOM(mxy,i,n)))*bymnew
SMOM(mz,i,n)=SMOM(mz,i,n)-FMOM(mz,i,n)
SMOM(mzx,i,n)=(SMOM(mzx,i,n)*MASS(i,n)
& -3.*(-dm2*SMOM(mz,i,n) +
& MASS(i,n)*(+FMOM(mz,i,n))) +
& (-FMOM(mzx,i,n)))*bymnew
!
SMOM(myy,i,n)=SMOM(myy,i,n)-FMOM(myy,i,n)
SMOM(mzz,i,n)=SMOM(mzz,i,n)-FMOM(mzz,i,n)
SMOM(myz,i,n)=SMOM(myz,i,n)-FMOM(myz,i,n)
!
c------------------------------------------------------------------
MASS(i,n) = mnew
if(MASS(i,n).le.0.) then
S(i,n)=0.
SMOM(:,i,n)=0.
endif
c-----------------------------------------------------------------
if (qlimit .and. prather_limits.eq.1) then ! force Prather limits
SMOM(mx,i,n)=
& min(1.5*S(i,n),
& max(-1.5*S(i,n),SMOM(mx,i,n)))
SMOM(mxx,i,n)=
& min(2.*S(i,n)-abs(SMOM(mx,i,n))/3.,
& max(abs(SMOM(mx,i,n))-S(i,n),SMOM(mxx,i,n)))
SMOM(mxy,i,n)=
& min(S(i,n),
& max(-S(i,n),SMOM(mxy,i,n)))
SMOM(mzx,i,n)=
& min(S(i,n),
& max(-S(i,n),SMOM(mzx,i,n)))
end if
End If
Do n=J_0S,J_1
nm1 = n-1
tmp=MASS(i,n)+DM(i,nm1)
mnew=tmp-DM(i,n)
bymnew = 1./mnew
dm2=DM(i,nm1)+DM(i,n)
tmp=S(i,n)+F(i,nm1)
S(i,n)=tmp-F(i,n)
SMOM(mx,i,n)=(SMOM(mx,i,n)*MASS(i,n)-
& 3.*(-dm2*S(i,n)
& +MASS(i,n)*(F(i,nm1)+F(i,n)))+
& (FMOM(mx,i,nm1)-FMOM(mx,i,n)))*bymnew
SMOM(mxx,i,n) = (SMOM(mxx,i,n)*MASS(i,n)*MASS(i,n)
& +2.5*S(i,n)*(MASS(i,n)*MASS(i,n)-mnew*mnew-3.*dm2*dm2)
& +5.*(MASS(i,n)*(MASS(i,n)*
& (F(i,nm1)-F(i,n))-FMOM(mx,i,nm1)
& -FMOM(mx,i,n))+dm2*SMOM(mx,i,n)*mnew)
& +(FMOM(mxx,i,nm1)-
& FMOM(mxx,i,n))) * (bymnew*bymnew)
! cross moments
SMOM(my,i,n)=
& SMOM(my,i,n)+FMOM(my,i,nm1)-FMOM(my,i,n)
SMOM(mxy,i,n)=
& (SMOM(mxy,i,n)*MASS(i,n)
& -3.*(-dm2*SMOM(my,i,n) +
& MASS(i,n)*(FMOM(my,i,nm1)+FMOM(my,i,n))) +
& (FMOM(mxy,i,nm1)-FMOM(mxy,i,n)))*bymnew
SMOM(mz,i,n)=
& SMOM(mz,i,n)+FMOM(mz,i,nm1)-FMOM(mz,i,n)
SMOM(mzx,i,n)=
& (SMOM(mzx,i,n)*MASS(i,n)
& -3.*(-dm2*SMOM(mz,i,n) +
& MASS(i,n)*(FMOM(mz,i,nm1)+FMOM(mz,i,n))) +
& (FMOM(mzx,i,nm1)-FMOM(mzx,i,n)))*bymnew
!
SMOM(myy,i,n)=
& SMOM(myy,i,n)+
& FMOM(myy,i,nm1)-FMOM(myy,i,n)
SMOM(mzz,i,n)=SMOM(mzz,i,n)+
& FMOM(mzz,i,nm1)-FMOM(mzz,i,n)
SMOM(myz,i,n)=SMOM(myz,i,n)+
& FMOM(myz,i,nm1)-FMOM(myz,i,n)
!
c------------------------------------------------------------------
MASS(i,n) = mnew
if(MASS(i,n).le.0.) then
S(i,n)=0.
SMOM(:,i,n)=0.
endif
c-----------------------------------------------------------------
if (qlimit .and. prather_limits.eq.1) then ! force Prather limits
SMOM(mx,i,n)=
& min(1.5*S(i,n),
& max(-1.5*S(i,n),SMOM(mx,i,n)))
SMOM(mxx,i,n)=
& min(2.*S(i,n)-abs(SMOM(mx,i,n))/3.,
& max(abs(SMOM(mx,i,n))-S(i,n),SMOM(mxx,i,n)))
SMOM(mxy,i,n)=
& min(S(i,n),
& max(-S(i,n),SMOM(mxy,i,n)))
SMOM(mzx,i,n)=
& min(S(i,n),
& max(-S(i,n),SMOM(mzx,i,n)))
end if
enddo
End Subroutine Update_Tracer_Mass
end subroutine advection_1D_custom
c************************************************************************
subroutine limitq(anm1,an,fnm1,fn,sn,sx,sxx,ierr) 3
!@sum limitq adjusts moments to maintain non-neg. tracer means/fluxes
!@auth G. Russell, modified by Maxwell Kelley
implicit none
REAL*8 :: anm1,an,fnm1,fn,sn,sx,sxx
c local variables
REAL*8 :: sl,sc,sr, frl,frl1, frr,frr1, gamma,g13ab,
& fr,fr1, fsign,su,sd
integer, intent(out) ::ierr
ierr=0
c****
c**** modify the tracer moments so that the tracer mass in each
c**** division is non-negative
c****
c**** no air leaving the box
if(anm1.ge.0. .and. an.le.0.) return
c**** air is leaving through both the left and right edges
if(anm1.lt.0. .and. an.gt.0.) then
sl = -fnm1
sr = +fn
sc = sn - sl
sc = sc - sr
c**** all three divisions are non-negative
if(sl.ge.0. .and. sr.ge.0. .and. sc.ge.0.) return
c**** first check for the cases when only one of the three is negative
frl = anm1
frl1 = frl+1.
frr = an
frr1 = frr-1.
if(sl.ge.0. .and. sr.ge.0.) then ! center division
gamma = 1.+(frl-frr)
g13ab = gamma*gamma - 1. + 3.*(frl+frr)**2
sxx = sxx - sc*10.*g13ab /
& (gamma*(12.*(frl+frr)**2 + 5.*g13ab*g13ab))
sx = sx + sc*12.*(frl+frr) /
& (gamma*(12.*(frl+frr)**2 + 5.*g13ab*g13ab))
sl = -frl*(sn-frl1*(sx-(frl+frl1)*sxx))
sr = sn-sl
else if(sr.ge.0.) then ! leftmost division
sxx = sxx + sl*(frl+frl1)/(frl*frl1*(.6d0+(frl+frl1)**2))
sx = sn/frl1 + (frl+frl1)*sxx
sr = frr*(sn-frr1*(sx-(frr+frr1)*sxx))
sl = 0.
else if(sl.ge.0.) then ! rightmost division
sxx = sxx - sr*(frr+frr1)/(frr*frr1*(.6d0+(frr+frr1)**2))
sx = sn/frr1 + (frr+frr1)*sxx
sl = -frl*(sn-frl1*(sx-(frl+frl1)*sxx))
sr = 0.
endif
sc = sn - sl
sc = sc - sr
c check for the cases where two of the three divisions are nonpositive
c these cases arise due to adjustments made when only one of the three
c divisions was negative
if(sl.le.0. .and. sr.le.0.) then ! two outer divisions
gamma = 1.+(frl-frr)
sxx = sn*(1.+gamma)/(2.*gamma*frl1*frr1)
sx = sn*(frl+frr)*(1.+2.*gamma)/(2.*gamma*frl1*frr1)
sl = 0.
sr = 0.
else if(sl.le.0. .and. sc.le.0.) then ! center/left divs
sxx = sn/(2.*frr*frl1)
sx = sn*(frl+frr+.5)/(frr*frl1)
sl = 0.
sr = sn
else if(sr.le.0. .and. sc.le.0.) then ! center/right divs
sxx = sn/(2.*frl*frr1)
sx = sn*(frl+frr-.5)/(frl*frr1)
sl = sn
sr = 0.
endif
fnm1 = -sl
fn = +sr
else
c**** air is leaving only through one edge
if(an.gt.0.) then ! right edge
fr=an
sd=fn
fsign=-1.
else ! left edge
fr=anm1
sd=-fnm1
fsign=1.
endif
if(abs(fr).gt.1.) then
c**** give warnings if fractional mass loss > 1
ierr=1
write(6,*) "limitq warning: abs(a)>1",fr,sd
write(6,*) "limitq input: anm1,an,fnm1,fn,sn,sx,sxx",anm1
* ,an,fnm1,fn,sn,sx,sxx
c**** only stop if net tracer mass is negative
if (sn+fnm1-fn.lt.0) then
ierr=2
write(6,*) "limitq error: new sn < 0",sn,sn+fnm1-fn
return
end if
end if
su = sn-sd
if(sd.ge.0. .and. su.ge.0.) return
fr1=fr+fsign
if(sd.lt.0.) then
c**** downstream division is negative
sxx = sxx +fsign*sd*(fr+fr1)/(fr*fr1*(.6d0+(fr+fr1)**2))
sx = sn/fr1 + (fr+fr1)*sxx
su = sn
else
c**** upstream division is negative
sxx = sxx -fsign*su*(fr+fr1)/(fr*fr1*(.6d0+(fr+fr1)**2))
sx = sn/fr + (fr+fr1)*sxx
su = 0.
endif
sd = sn - su
if(an.gt.0.) then
fn=sd
else
fnm1=-sd
endif
endif
return
end subroutine limitq
SUBROUTINE CTMIX (RM,RMOM,FMAIR,FMIX,FRAT) 9,1
!@sum CTMIX Cloud top mixing of tracer moments (incl. q,t) from CONDSE
!@auth Gary Russell, Jean Lerner, Gavin Schmidt
!@ver 1.0
!@var RM,RX,RY,RZ,RXX,RYY,RZZ,RXY,RYZ,RZX mean and moments of tracer
USE QUSDEF
IMPLICIT NONE
REAL*8, DIMENSION(2) :: RM
REAL*8, DIMENSION(NMOM,2) :: RMOM
REAL*8 FMIX !@var FMIX fraction of lower box mixed up
REAL*8 FRAT !@var FRAT fraction of upper box mixed down
REAL*8 FMAIR !@var FMAIR mass of air mixed
REAL*8 RTEMP !@var RTEMP dummy variable
RTEMP = RM(1)*(1.-FMIX)+FRAT*RM(2)
RM(2) = RM(2)*(1.-FRAT)+FMIX*RM(1)
RM(1) = RTEMP
C X
RTEMP = RMOM(MX ,1)*(1.-FMIX)+FRAT*RMOM(MX ,2)
RMOM(MX ,2) = RMOM(MX ,2)*(1.-FRAT)+FMIX*RMOM(MX ,1)
RMOM(MX ,1) = RTEMP
C Y
RTEMP = RMOM(MY ,1)*(1.-FMIX)+FRAT*RMOM(MY ,2)
RMOM(MY ,2) = RMOM(MY ,2)*(1.-FRAT)+FMIX*RMOM(MY ,1)
RMOM(MY ,1) = RTEMP
C XX
RTEMP = RMOM(MXX,1)*(1.-FMIX)+FRAT*RMOM(MXX,2)
RMOM(MXX,2) = RMOM(MXX,2)*(1.-FRAT)+FMIX*RMOM(MXX,1)
RMOM(MXX,1) = RTEMP
C YY
RTEMP = RMOM(MYY,1)*(1.-FMIX)+FRAT*RMOM(MYY,2)
RMOM(MYY,2) = RMOM(MYY,2)*(1.-FRAT)+FMIX*RMOM(MYY,1)
RMOM(MYY,1) = RTEMP
C XY
RTEMP = RMOM(MXY,1)*(1.-FMIX)+FRAT*RMOM(MXY,2)
RMOM(MXY,2) = RMOM(MXY,2)*(1.-FRAT)+FMIX*RMOM(MXY,1)
RMOM(MXY,1) = RTEMP
C Z MOMENTS
RMOM(ZMOMS,:) = RMOM(ZMOMS,:)*(1.-FMAIR)
END SUBROUTINE CTMIX