C $Header: /u/gcmpack/MITgcm/pkg/opps/opps_calc.F,v 1.8 2010/08/24 14:58:48 jmc Exp $
C $Name: $
#include "OPPS_OPTIONS.h"
CBOP
C !ROUTINE: OPPS_CALC
C !INTERFACE: ======================================================
subroutine OPPS_CALC(
U tracerEnv,
I wVel,
I kMax, nTracer, nTracerInuse,
I I, J, bi, bj, myTime, myIter, myThid )
C !DESCRIPTION: \bv
C /=====================================================================\
C | SUBROUTINE OPPS_CALC |
C | o Compute all OPPS fields defined in OPPS.h |
C |=====================================================================|
C | This subroutine is based on the routine 3dconvection.F |
C | by E. Skyllingstad (?) |
C | plenty of modifications to make it work: |
C | - removed many unused parameters and variables |
C | - turned everything (back) into 1D code |
C | - pass variables, that are orginially in common blocks: |
C | maxDepth |
C | - pass vertical velocity, set in OPPS_INTERFACE |
C | - do not use convadj for now (whatever that is) |
C | - changed two .LT. 0 to .LE. 0 statements (because of possible |
C | division) |
C | - replaced statement function state1 by call to a real function |
C | - removed range check, actually moved it up to OPPS_INTERFACE |
C | - avoid division by zero: if (Wd.EQ.0) dt = ...1/Wd |
C | - cleaned-up debugging |
C | - replaced local dz and GridThickness by global drF |
C | - replaced 1/dz by 1*recip_drF |
C | - replaced 9.81 with gravity (=9.81) |
C | - added a lot of comments that relate code to equation in paper |
C | (Paluszkiewicz+Romea, 1997, Dynamics of Atmospheres and Oceans, |
C | 26, pp. 95-130) |
C | - included passive tracer support. This is the main change and may |
C | not improve the readability of the code because of the joint |
C | treatment of active (theta, salt) and passive tracers. The array |
C | tracerEnv(Nr,2+PTRACERS_num) contains |
C | theta = tracerEnv(:,1), |
C | salt = tracerEnv(:,2), and |
C | ptracers = tracerEnv(:,3:PTRACERS_num+2). |
C | All related array names have been changed accordingly, so that |
C | instead of Sd(Nr) and Td(Nr) (plume salinity and temperature), we |
C | have Pd(Nr,nTracer) (tracer in plume), with Sd(:) = Pd(:,2), |
C | Td(:) = Pd(:,1), etc. |
C | o TODO: |
C | clean up the logic of the vertical loops and get rid off the |
C | GOTO statements |
C \=====================================================================/
IMPLICIT NONE
C
C--------------------------------------------------------------------
C \ev
C !USES: ============================================================
#include "SIZE.h"
#include "EEPARAMS.h"
#include "PARAMS.h"
#include "OPPS.h"
#include "FFIELDS.h"
#include "GRID.h"
EXTERNAL
LOGICAL DIFFERENT_MULTIPLE
C !INPUT PARAMETERS: ===================================================
c Routine arguments
c bi, bj - array indices on which to apply calculations
c myTime - Current time in simulation
INTEGER I, J, bi, bj, KMax, nTracer, nTracerInUse
INTEGER myThid, myIter
_RL myTime
_RL tracerEnv(Nr,nTracer),wVel(Nr)
#ifdef ALLOW_OPPS
C !LOCAL VARIABLES: ====================================================
c Local constants
C imin, imax, jmin, jmax - array computation indices
C msgBuf - Informational/error message buffer
CHARACTER*(MAX_LEN_MBUF) msgBuf
INTEGER K, K2, K2m1, K2p1, ktr
INTEGER ntime,nn,kmx,ic
INTEGER maxDepth
_RL wsqr,oldflux,newflux,entrainrate
_RL pmix
_RL D1,D2,state1
_RL dz1,dz2
_RL radius,StartingFlux
_RL dtts,dt
C Arrays
_RL Paa(Nr,nTracer)
_RL wda(Nr), mda(Nr), pda(Nr,nTracer)
C
C Pd, Wd - tracers, vertical velocity in plume
C Md - plume mass flux (?)
C Ad - fractional area covered by plume
C Dd - density in plume
C De - density of environment
C PlumeEntrainment -
_RL Ad(Nr),Wd(Nr),Dd(Nr),Md(Nr)
_RL De(Nr)
_RL PlumeEntrainment(Nr)
_RL Pd(Nr,nTracer)
CEOP
C-- Check to see if should convect now
C IF ( DIFFERENT_MULTIPLE(cAdjFreq,myTime,deltaTClock) ) THEN
IF ( .true. ) THEN
C local initialization
C Copy some arrays
dtts = dTtracerLev(1)
C
C start k-loop
C
DO k=1,KMax-1
c
c initialize the plume T,S,density, and w velocity
c
DO ktr=1,nTracerInUse
Pd(k,ktr) = tracerEnv(k,ktr)
ENDDO
Dd(k)=state1(Pd(k,2),Pd(k,1),i,j,k,bi,bj,myThid)
De(k)=Dd(k)
CML print *, 'ml-opps:', i,j,k,tracerEnv(k,2),tracerEnv(k,1),
CML & Dd(k),Pd(k,1),Pd(k,2)
CML compute vertical velocity at cell centers from GCM velocity
Wd(k)= - .5*(wVel(K)+wVel(K+1))
CML(
CML avoid division by zero
CML IF (Wd(K) .EQ. 0.D0) Wd(K) = 2.23e-16
CML)
c
c guess at initial top grid cell vertical velocity
c
CML Wd(k) = 0.03
c
c these estimates of initial plume velocity based on plume size and
c top grid cell water mass
c
c Wd(k) = 0.5*drF(k)/(dtts*FRACTIONAL_AREA)
c Wd(k) = 0.5*drF(k)/dtts
c
wsqr=Wd(k)*Wd(k)
PlumeEntrainment(k) = 0.0
c
c
c
#ifdef ALLOW_OPPS_DEBUG
IF ( OPPSdebugLevel.GE.debLevB ) THEN
WRITE(msgBuf,'(A,I3)')
& 'S/R OPPS_CALC: doing old lowerparcel', k
CALL PRINT_MESSAGE( msgBuf, standardMessageUnit,
& SQUEEZE_RIGHT , 1)
ENDIF
#endif /* ALLOW_OPPS_DEBUG */
radius=PlumeRadius
StartingFlux=radius*radius*Wd(k)*Dd(k)
oldflux=StartingFlux
dz2=DrF(k)
DO k2=k,KMax-1
D1=state1( Pd(k2,2), Pd(k2,1),i,j,k2+1,bi,bj,myThid)
D2=state1( tracerEnv(k2+1,2), tracerEnv(k2+1,1),
& i,j,k2+1,bi,bj,myThid)
De(k2+1)=D2
c
c To start downward, parcel has to initially be heavier than environment
c but after it has started moving, we continue plume until plume tke or
c flux goes negative
c
CML & _hFacC(i,j,k-1,bi,bj)
CML & *_hFacC(i,j,k,bi,bj) .GT. 0.
CML & .AND.
IF (D2-D1 .LT. STABILITY_THRESHOLD.or.k2.ne.k) THEN
dz1=dz2
dz2=DrF(k2+1)
c
C find mass flux according to eq.(3) from paper by vertical integration
c
newflux=oldflux+e2*radius*Wd(k2)*Dd(k2)*
& .5*(dz1+dz2)
CML print *, 'ml-opps:', i,j,k,oldflux,newflux,e2,radius,
CML & Wd(k2),Dd(k2),Pd(k2,1),Pd(k2,2),dz1,dz2
c
PlumeEntrainment(k2+1) = newflux/StartingFlux
c
IF(newflux.LE.0.0) then
#ifdef ALLOW_OPPS_DEBUG
IF ( OPPSdebugLevel.GE.debLevA ) THEN
WRITE(msgBuf,'(A,I3)')
& 'S/R OPPS_CALC: Plume entrained to zero at level ', k2
CALL PRINT_MESSAGE( msgBuf, standardMessageUnit,
& SQUEEZE_RIGHT , 1)
ENDIF
#endif /* ALLOW_OPPS_DEBUG */
maxdepth = k2
if(maxdepth.eq.k) goto 1000
goto 1
endif
c
c entrainment rate is basically a scaled mass flux dM/M
c
entrainrate = (newflux - oldflux)/newflux
oldflux = newflux
c
c
c mix var(s) are the average environmental values over the two grid levels
c
DO ktr=1,nTracerInUse
pmix=(dz1*tracerEnv(k2,ktr)+dz2*tracerEnv(k2+1,ktr))
& /(dz1+dz2)
Pd(k2+1,ktr)=Pd(k2,ktr)
& - entrainrate*(pmix - Pd(k2,ktr))
ENDDO
c
c compute the density at this level for the buoyancy term in the
c vertical k.e. equation
c
Dd(k2+1)=state1(Pd(k2+1,2),Pd(k2+1,1),i,j,k2+1,bi,bj,myThid)
c
c next, solve for the vertical velocity k.e. using combined eq. (4)
c and eq (5) from the paper
c
#ifdef ALLOW_OPPS_DEBUG
IF ( OPPSdebugLevel.GE.debLevA ) THEN
WRITE(msgBuf,'(A,3E12.4,I3)')
& 'S/R OPPS_CALC: Dd,De,entr,k ',Dd(k2),De(k2),entrainrate,k2
CALL PRINT_MESSAGE( msgBuf, standardMessageUnit,
& SQUEEZE_RIGHT , 1)
ENDIF
#endif /* ALLOW_OPPS_DEBUG */
CML insert Eq. (4) into Eq. (5) to get something like this for wp^2
wsqr = wsqr - wsqr*abs(entrainrate)+ gravity*
& (dz1*(Dd(k2)-De(k2))/De(k2)
& +dz2*(Dd(k2+1)-De(k2+1))/De(k2+1))
c
c if negative k.e. then plume has reached max depth, get out of loop
c
IF(wsqr.LE.0.0)then
maxdepth = k2
#ifdef ALLOW_OPPS_DEBUG
IF ( OPPSdebugLevel.GE.debLevA ) THEN
WRITE(msgBuf,'(A,I3)')
& 'S/R OPPS_CALC: Plume velocity went to zero at level ', k2
CALL PRINT_MESSAGE( msgBuf, standardMessageUnit,
& SQUEEZE_RIGHT , 1)
WRITE(msgBuf,'(A,4A14)')
& 'S/R OPPS_CALC: ', 'wsqr', 'entrainrate',
& '(Dd-De)/De up', '(Dd-De)/De do'
CALL PRINT_MESSAGE( msgBuf, standardMessageUnit,
& SQUEEZE_RIGHT , 1)
WRITE(msgBuf,'(A,4E14.6)')
& 'S/R OPPS_CALC: ', wsqr, entrainrate,
& (Dd(k2)-De(k2))/De(k2), (Dd(k2+1)-De(k2+1))/De(k2+1)
CALL PRINT_MESSAGE( msgBuf, standardMessageUnit,
& SQUEEZE_RIGHT , 1)
ENDIF
#endif /* ALLOW_OPPS_DEBUG */
if(maxdepth.eq.k) goto 1000
goto 1
endif
Wd(k2+1)=sqrt(wsqr)
C
C compute a new radius based on the new mass flux at this grid level
C from Eq. (4)
C
radius=sqrt(newflux/(Wd(k2)*Dd(k2)))
ELSE
maxdepth=k2
if(maxdepth.eq.k) goto 1000
GOTO 1
ENDIF
ENDDO
c
c plume has reached the bottom
c
MaxDepth=kMax
c
1 CONTINUE
c
Ad(k)=FRACTIONAL_AREA
IC=0
c
c start iteration on fractional area, not used in OGCM implementation
c
c
DO IC=1,Max_ABE_Iterations
c
c
c next compute the mass flux beteen each grid box using the entrainment
c
Md(k)=Wd(k)*Ad(k)
c
DO k2=k+1,maxDepth
Md(k2)=Md(k)*PlumeEntrainment(k2)
#ifdef ALLOW_OPPS_DEBUG
IF ( OPPSdebugLevel.GE.debLevA ) THEN
WRITE(msgBuf,'(A,2E12.4,I3)')
& 'S/R OPPS_CALC: Md, Wd, and k are ',Md(k2),Wd(k2),k2
CALL PRINT_MESSAGE( msgBuf, standardMessageUnit,
& SQUEEZE_RIGHT , 1)
ENDIF
#endif /* ALLOW_OPPS_DEBUG */
ENDDO
c
c Now move on to calculate new temperature using flux from
c Td, Sd, Wd, ta, sa, and we. Values for these variables are at
c center of grid cell, use weighted average to get boundary values
c
c use a timestep limited by the GCM model timestep and the maximum plume
c velocity (CFL criteria)
c
c
c calculate the weighted wd, td, and sd
c
dt = dtts
do k2=k,maxDepth-1
IF ( Wd(K2) .NE. 0. _d 0 ) dt = min(dt,drF(k2)/Wd(k2))
c
c time integration will be integer number of steps to get one
c gcm time step
c
ntime = nint(0.5*int(dtts/dt))
if(ntime.eq.0) then
ntime = 1
endif
c
c make sure area weighted vertical velocities match; in other words
c make sure mass in equals mass out at the intersection of each grid
c cell. Eq. (20)
c
mda(k2) = (md(k2)*drF(k2)+md(k2+1)*drF(k2+1))/
* (drF(k2)+drF(k2+1))
c
wda(k2) = (wd(k2)*drF(k2)+wd(k2+1)*drF(k2+1))/
* (drF(k2)+drF(k2+1))
c
DO ktr = 1, nTracerInUse
Pda(k2,ktr) = Pd(k2,ktr)
Paa(k2,ktr) = tracerEnv(k2+1,ktr)
ENDDO
c
enddo
dt = min(dt,dtts)
#ifdef ALLOW_OPPS_DEBUG
IF ( OPPSdebugLevel.GE.debLevA ) THEN
WRITE(msgBuf,'(A,F14.4)')
& 'S/R OPPS_CALC: time step = ', dt
CALL PRINT_MESSAGE( msgBuf, standardMessageUnit,
& SQUEEZE_RIGHT , 1)
ENDIF
#endif /* ALLOW_OPPS_DEBUG */
DO ktr=1,nTracerInUse
Pda(maxdepth,ktr) = Pd(maxdepth,ktr)
ENDDO
C
kmx = maxdepth-1
do nn=1,ntime
C
C top point
C
DO ktr = 1,nTracerInUse
tracerEnv(k,ktr) = tracerEnv(k,ktr)-
& (mda(k)*(Pda(k,ktr)-Paa(k,ktr)))*dt*recip_drF(k)
ENDDO
c
c now do inner points if there are any
c
CML if(Maxdepth-k.gt.1) then
CML This if statement is superfluous
CML IF ( k .LT. Maxdepth-1 ) THEN
CML DO k2=k+1,Maxdepth-1
CML mda(maxDepth) = 0.
DO k2=k+1,kmx
k2m1 = max(k,k2-1)
k2p1 = max(k2+1,maxDepth)
c
DO ktr = 1,nTracerInUse
tracerEnv(k2,ktr) = tracerEnv(k2,ktr) +
& (mda(k2m1)*(Pda(k2m1,ktr)-Paa(k2m1,ktr))
& -mda(k2) *(Pda(k2,ktr) -Paa(k2,ktr)) )
& *dt*recip_drF(k2)
ENDDO
ENDDO
CML This if statement is superfluous
CML ENDIF
C
C bottom point
C
DO ktr=1,nTracerInUse
tracerEnv(kmx+1,ktr) = tracerEnv(kmx+1,ktr)+
& mda(kmx)*(Pda(kmx,ktr)-Paa(kmx,ktr))*dt*recip_drF(kmx+1)
ENDDO
c
c set the environmental temp and salinity to equal new fields
c
DO ktr=1,nTracerInUse
DO k2=1,kmx
paa(k2,ktr) = tracerEnv(k2+1,ktr)
ENDDO
ENDDO
c
c end loop on number of time integration steps
c
enddo
ENDDO
999 continue
C
C count convection event in this grid cell
C
OPPSconvectCount(I,J,K,bi,bj) =
& OPPSconvectCount(I,J,K,bi,bj) + 1. _d 0
C
C jump here if k = maxdepth or if level not unstable, go to next
C profile point
C
1000 continue
c
C
C end of k-loop
C
ENDDO
C-- End IF (DIFFERENT_MULTIPLE)
ENDIF
RETURN
END
_RL FUNCTION STATE1(sLoc,tLoc,I,J,KREF,bi,bj,mythid)
C !DESCRIPTION: \bv
C *===============================================================*
C | o SUBROUTINE STATE1
C | Calculates rho(S,T,p)
C | It is absolutely necessary to compute
C | the full rho and not sigma=rho-rhoConst, because
C | density is used as a scale factor for fluxes and velocities
C *===============================================================*
C \ev
C !USES:
IMPLICIT NONE
C == Global variables ==
#include "SIZE.h"
#include "EEPARAMS.h"
#include "PARAMS.h"
#include "EOS.h"
#include "GRID.h"
#include "DYNVARS.h"
C !INPUT/OUTPUT PARAMETERS:
C == Routine arguments ==
INTEGER I,J,kRef,bi,bj,myThid
_RL tLoc,sLoc
C !LOCAL VARIABLES:
C == Local variables ==
_RL rhoLoc, dRho
_RL pLoc
_RL t1, t2, t3, t4, s1, s3o2, p1, p2, sp5, p1t1
_RL rfresh, rsalt, rhoP0
_RL bMfresh, bMsalt, bMpres, BulkMod
_RL rhoNum, rhoDen, den, epsln
PARAMETER ( epsln = 0.D0 )
character*(max_len_mbuf) msgbuf
CMLC estimate pressure from depth at cell centers
CML mtoSI = gravity*rhoConst
CML pLoc = ABS(rC(kRef))*mtoSI
IF ( buoyancyRelation .EQ. 'OCEANIC' ) THEN
C in Z coordinates the pressure is rho0 * (hydrostatic) Potential
IF ( useDynP_inEos_Zc ) THEN
C----------
C NOTE: For now, totPhiHyd only contains the Potential anomaly
C since PhiRef is not available for Atmos and has not (yet)
C been added in S/R DIAGS_PHI_HYD
C----------
pLoc = rhoConst*( totPhiHyd(i,j,kRef,bi,bj)
& -rC(kRef)*gravity
& )*maskC(i,j,kRef,bi,bj)
ELSE
pLoc = -rhoConst*rC(kRef)*gravity*maskC(i,j,kRef,bi,bj)
ENDIF
ELSEIF ( buoyancyRelation .EQ. 'OCEANICP' ) THEN
C in P coordinates the pressure is just the coordinate of
C the tracer point
pLoc = rC(kRef)* maskC(i,j,kRef,bi,bj)
ENDIF
rhoLoc = 0. _d 0
rhoP0 = 0. _d 0
bulkMod = 0. _d 0
rfresh = 0. _d 0
rsalt = 0. _d 0
bMfresh = 0. _d 0
bMsalt = 0. _d 0
bMpres = 0. _d 0
rhoNum = 0. _d 0
rhoDen = 0. _d 0
den = 0. _d 0
t1 = tLoc
t2 = t1*t1
t3 = t2*t1
t4 = t3*t1
s1 = sLoc
IF ( equationOfState .EQ. 'LINEAR' ) THEN
dRho = rhoNil-rhoConst
rhoLoc=rhoNil* (
& sBeta *(sLoc-sRef(kRef))
& - tAlpha*(tLoc-tRef(KREF)) ) + dRho
ELSEIF (equationOfState.EQ.'POLY3') THEN
C this is not correct, there is a field eosSig0 which should be use here
C but I DO not intent to include the reference level in this routine
WRITE(*,'(a)')
& ' FIND_RHO_SCALAR: for POLY3, the density is not'
WRITE(*,'(a)')
& ' computed correctly in this routine'
rhoLoc = 0. _d 0
ELSEIF ( equationOfState(1:5).EQ.'JMD95'
& .OR. equationOfState.EQ.'UNESCO' ) THEN
C nonlinear equation of state in pressure coordinates
s3o2 = s1*SQRT(s1)
p1 = pLoc*SItoBar
p2 = p1*p1
C density of freshwater at the surface
rfresh =
& eosJMDCFw(1)
& + eosJMDCFw(2)*t1
& + eosJMDCFw(3)*t2
& + eosJMDCFw(4)*t3
& + eosJMDCFw(5)*t4
& + eosJMDCFw(6)*t4*t1
C density of sea water at the surface
rsalt =
& s1*(
& eosJMDCSw(1)
& + eosJMDCSw(2)*t1
& + eosJMDCSw(3)*t2
& + eosJMDCSw(4)*t3
& + eosJMDCSw(5)*t4
& )
& + s3o2*(
& eosJMDCSw(6)
& + eosJMDCSw(7)*t1
& + eosJMDCSw(8)*t2
& )
& + eosJMDCSw(9)*s1*s1
rhoP0 = rfresh + rsalt
C secant bulk modulus of fresh water at the surface
bMfresh =
& eosJMDCKFw(1)
& + eosJMDCKFw(2)*t1
& + eosJMDCKFw(3)*t2
& + eosJMDCKFw(4)*t3
& + eosJMDCKFw(5)*t4
C secant bulk modulus of sea water at the surface
bMsalt =
& s1*( eosJMDCKSw(1)
& + eosJMDCKSw(2)*t1
& + eosJMDCKSw(3)*t2
& + eosJMDCKSw(4)*t3
& )
& + s3o2*( eosJMDCKSw(5)
& + eosJMDCKSw(6)*t1
& + eosJMDCKSw(7)*t2
& )
C secant bulk modulus of sea water at pressure p
bMpres =
& p1*( eosJMDCKP(1)
& + eosJMDCKP(2)*t1
& + eosJMDCKP(3)*t2
& + eosJMDCKP(4)*t3
& )
& + p1*s1*( eosJMDCKP(5)
& + eosJMDCKP(6)*t1
& + eosJMDCKP(7)*t2
& )
& + p1*s3o2*eosJMDCKP(8)
& + p2*( eosJMDCKP(9)
& + eosJMDCKP(10)*t1
& + eosJMDCKP(11)*t2
& )
& + p2*s1*( eosJMDCKP(12)
& + eosJMDCKP(13)*t1
& + eosJMDCKP(14)*t2
& )
bulkMod = bMfresh + bMsalt + bMpres
C density of sea water at pressure p
rhoLoc = rhoP0/(1. _d 0 - p1/bulkMod) - rhoConst
ELSEIF ( equationOfState.EQ.'MDJWF' ) THEN
sp5 = SQRT(s1)
p1 = pLoc*SItodBar
p1t1 = p1*t1
rhoNum = eosMDJWFnum(0)
& + t1*(eosMDJWFnum(1)
& + t1*(eosMDJWFnum(2) + eosMDJWFnum(3)*t1) )
& + s1*(eosMDJWFnum(4)
& + eosMDJWFnum(5)*t1 + eosMDJWFnum(6)*s1)
& + p1*(eosMDJWFnum(7) + eosMDJWFnum(8)*t2
& + eosMDJWFnum(9)*s1
& + p1*(eosMDJWFnum(10) + eosMDJWFnum(11)*t2) )
den = eosMDJWFden(0)
& + t1*(eosMDJWFden(1)
& + t1*(eosMDJWFden(2)
& + t1*(eosMDJWFden(3) + t1*eosMDJWFden(4) ) ) )
& + s1*(eosMDJWFden(5)
& + t1*(eosMDJWFden(6)
& + eosMDJWFden(7)*t2)
& + sp5*(eosMDJWFden(8) + eosMDJWFden(9)*t2) )
& + p1*(eosMDJWFden(10)
& + p1t1*(eosMDJWFden(11)*t2 + eosMDJWFden(12)*p1) )
rhoDen = 1.0/(epsln+den)
rhoLoc = rhoNum*rhoDen - rhoConst
ELSEIF( equationOfState .EQ. 'IDEALG' ) THEN
C
ELSE
WRITE(msgbuf,'(3A)')
& ' STATE1 : equationOfState = "',
& equationOfState,'"'
CALL PRINT_ERROR( msgbuf, mythid )
STOP 'ABNORMAL END: S/R STATE1 in OPPS_CALC'
ENDIF
state1 = rhoLoc + rhoConst
#endif /* ALLOW_OPPS */
RETURN
END
#undef OPPS_ORGCODE
#ifdef OPPS_ORGCODE
c Listed below is the subroutine for use in parallel 3-d circulation code.
c It has been used in the parallel semtner-chervin code and is now being used
c In the POP code. The subroutine is called nlopps (long story to explain why).
c I've attached the version of lopps that we've been using in the simulations.
c There is one common block that is different from the standard model commons
c (countc) and it is not needed if the array convadj is not used. The routine
c does need "kmp" which is why the boundc common is included. For massively
c parallel codes (like POP) we think this will work well when converted from a
c "slab" (i=is,ie) to a column, which just means removing the "do i=is,ie" loop. c There are differences between this
c code and the 1-d code and the earlier scheme implemented in 3-d models. These c differences are described below.
subroutine NLOPPS(j,is,ie,ta,sa,gcmdz)
c
parameter (imt = 361 , jmt = 301 , km = 30 )
c
c Nlopps: E. Skyllingstad and T. Paluszkiewicz
c
c Version: December 11, 1996
c
c Nlopps: This version of lopps is significantly different from
c the original code developed by R. Romea and T. Paluskiewicz. The
c code uses a flux constraint to control the change in T and S at
c each grid level. First, a plume profile of T,S, and W are
c determined using the standard plume model, but with a detraining
c mass instead of entraining. Thus, the T and S plume
c characteristics still change, but the plume contracts in size
c rather than expanding ala classical entraining plumes. This
c is heuristically more in line with large eddy simulation results.
c At each grid level, the convergence of plume velocity determines
c the flux of T and S, which is conserved by using an upstream
c advection. The vertical velocity is balanced so that the area
c weighted upward velocity equals the area weighted downdraft
c velocity, ensuring mass conservation. The present implementation
c adjusts the plume for a time period equal to the time for 1/2 of
c the mass of the fastest moving level to move downward. As a
c consequence, the model does not completely adjust the profile at
c each model time step, but provides a smooth adjustment over time.
c
c
c
c include "params.h"
c include "plume_fast_inc.h"
c include "plume_fast.h"
c #include "loppsd.h"
real ta(imt,km),sa(imt,km),gcmdz(km),dz(km)
real pdensity,wsqr,oldflux,newflux,entrainrate,adtemp
REAL Del,D,dza1,dza2,kd,kd1,Smix,Thmix,PlumeS,PlumeT,PlumeD
c
c
INTEGER i,j,k
clfh
integer is,ie,k2
clfh
REAL D1,D2,state1,Density
REAL dz1,dz2
REAL radius,StartingFlux
real ttemp(km),stemp(km),taa(km),saa(km)
real wda(km),tda(km),sda(km),mda(km)
real dtts,dt,sumo,sumn
integer ntime,nn,kmx,ic
c
c
LOGICAL debug,done
INTEGER MAX_ABE_ITERATIONS
PARAMETER(MAX_ABE_ITERATIONS=1)
REAL PlumeRadius
REAL STABILITY_THRESHOLD
REAL FRACTIONAL_AREA
REAL MAX_FRACTIONAL_AREA
REAL VERTICAL_VELOCITY
REAL ENTRAINMENT_RATE
REAL e2
PARAMETER ( PlumeRadius = 100.D0 )
PARAMETER ( STABILITY_THRESHOLD = -1.E-4 )
PARAMETER ( FRACTIONAL_AREA = .1E0 )
PARAMETER ( MAX_FRACTIONAL_AREA = .8E0 )
PARAMETER ( VERTICAL_VELOCITY = .02E0 )
PARAMETER ( ENTRAINMENT_RATE = -.05E0 )
PARAMETER ( e2 = 2.E0*ENTRAINMENT_RATE )
! Arrays.
REAL Ad(km),Sd(km),Td(km),Wd(km),Dd(km),Md(km)
REAL Se(km),Te(km),We(km),De(km)
REAL PlumeEntrainment(km)
REAL GridThickness(km)
c
c input kmp through a common block
c
common / boundc / wsx(imt,jmt),wsy(imt,jmt),hfs(imt,jmt),
1 ple(imt,jmt),kmp(imt,jmt),kmq(imt,jmt)
cwmseas
& ,wsx1(imt,jmt),wsy1(imt,jmt)
1 ,wsx2(imt,jmt),wsy2(imt,jmt)
c
c input the variables through a common
c
logical problem
common /countc/ convadj(imt,jmt,km),ics,depth(km),problem
c-----may want to setup an option to get this only on first call
c otherwise it is repetive
c griddz is initialize by call to setupgrid
c
c
dtts = 2400
c
do k=1,km
dz(k) = 0.01*gcmdz(k)
enddo
c
do k=1,km
GridThickness(k) = dz(k)
enddo
c
c modified to loop over slab
c
DO i=is,ie
numgridpoints=kmp(i,j)
c
c go to next column if only 1 grid point or on land
c
if(numgridpoints.le.1) goto 1100
c
c loop over depth
c
c
debug = .false.
c
c first save copy of initial profile
c
DO k=1,NumGridPoints
stemp(k)=sa(i,k)
ttemp(k)=ta(i,k)
c
c do a check of t and s range, if out of bounds set flag
c
if(problem) then
write(0,*)"Code in trouble before this nlopps call"
return
endif
c
if(sa(i,k).gt.40..or.ta(i,k).lt.-4.0) then
problem = .true.
write(0,*)"t out of range at j ",j
debug = .true.
return
endif
ENDDO
if(debug) then
write(*,*)"T and S Profile at ",i,j
write(*,*)(ta(i,k),sa(i,k),k=1,NumGridPoints)
endif
DO k=1,NumGridPoints-1
c
c initialize the plume T,S,density, and w velocity
c
Sd(k)=stemp(k)
Td(k)=ttemp(k)
Dd(k)=state1(stemp(k),ttemp(k),k)
De(k)=Dd(k)
c Wd(k)=VERTICAL_VELOCITY
c
c guess at initial top grid cell vertical velocity
c
Wd(k) = 0.03
c
c these estimates of initial plume velocity based on plume size and
c top grid cell water mass
c
c Wd(k) = 0.5*dz(k)/(dtts*FRACTIONAL_AREA)
c Wd(k) = 0.5*dz(k)/dtts
c
wsqr=Wd(k)*Wd(k)
PlumeEntrainment(k) = 0.0
c
c
c
if(debug) write(0,*) 'Doing old lowerparcel'
radius=PlumeRadius
StartingFlux=radius*radius*Wd(k)*Dd(k)
oldflux=StartingFlux
dz2=GridThickness(k)
DO k2=k,NumGridPoints-1
D1=state1(Sd(k2),Td(k2),k2+1)
D2=state1(stemp(k2+1),ttemp(k2+1),k2+1)
De(k2+1)=D2
c
c To start downward, parcel has to initially be heavier than environment
c but after it has started moving, we continue plume until plume tke or
c flux goes negative
c
IF (D2-D1 .LT. STABILITY_THRESHOLD.or.k2.ne.k) THEN
dz1=dz2
dz2=GridThickness(k2+1)
c
c define mass flux according to eq. 4 from paper
c
newflux=oldflux+e2*radius*Wd(k2)*Dd(k2)*0.50*
. (dz1+dz2)
c
PlumeEntrainment(k2+1) = newflux/StartingFlux
c
IF(newflux.LT.0.0) then
if(debug) then
write(0,*)"Plume entrained to zero at ",k2
endif
maxdepth = k2
if(maxdepth.eq.k) goto 1000
goto 1
endif
c
c entrainment rate is basically a scaled mass flux dM/M
c
entrainrate = (newflux - oldflux)/newflux
oldflux = newflux
c
c
c mix var(s) are the average environmental values over the two grid levels
c
smix=(dz1*stemp(k2)+dz2*stemp(k2+1))/(dz1+dz2)
thmix=(dz1*ttemp(k2)+dz2*ttemp(k2+1))/(dz1+dz2)
c
c first compute the new salinity and temperature for this level
c using equations 3.6 and 3.7 from the paper
c
c
c
sd(k2+1)=sd(k2) - entrainrate*(smix - sd(k2))
td(k2+1)=td(k2) - entrainrate*(thmix - td(k2))
c
c
c compute the density at this level for the buoyancy term in the
c vertical k.e. equation
c
Dd(k2+1)=state1(Sd(k2+1),Td(k2+1),k2+1)
c
c next, solve for the vertical velocity k.e. using combined eq. 4
c and eq 5 from the paper
c
if(debug) then
write(0,*)"Dd,De,entr,k ",Dd(k2),De(k2),entrainrate,k2
endif
c
wsqr = wsqr - wsqr*abs(entrainrate)+ 9.81*
. (dz1*(Dd(k2)-De(k2))/De(k2)
. +dz2*(Dd(k2+1)-De(k2+1))/De(k2+1))
c
c if negative k.e. then plume has reached max depth, get out of loop
c
IF(wsqr.LT.0.0)then
maxdepth = k2
if(debug) then
write(0,*)"Plume velocity went to zero at ",k2
endif
if(maxdepth.eq.k) goto 1000
goto 1
endif
Wd(k2+1)=sqrt(wsqr)
c
c compute a new radius based on the new mass flux at this grid level
c
radius=sqrt(newflux/(Wd(k2)*Dd(k2)))
ELSE
maxdepth=k2
if(maxdepth.eq.k) goto 1000
GOTO 1
ENDIF
ENDDO
c
c plume has reached the bottom
c
MaxDepth=NumGridPoints
c
1 continue
c
Ad(k)=FRACTIONAL_AREA
IC=0
c
c start iteration on fractional area, not used in OGCM implementation
c
c
DO IC=1,Max_ABE_Iterations
c
c
c next compute the mass flux beteen each grid box using the entrainment
c
92 continue
Md(k)=Wd(k)*Ad(k)
c
DO k2=k+1,MaxDepth
Md(k2)=Md(k)*PlumeEntrainment(k2)
if(debug) then
write(0,*)"Md, Wd, and k are ",Md(k2),Wd(k2),k2
endif
ENDDO
c
c Now move on to calculate new temperature using flux from
c Td, Sd, Wd, ta, sa, and we. Values for these variables are at
c center of grid cell, use weighted average to get boundary values
c
c use a timestep limited by the GCM model timestep and the maximum plume
c velocity (CFL criteria)
c
c
c calculate the weighted wd, td, and sd
c
dt = dtts
do k2=k,maxdepth-1
dt = min(dt,dz(k2)/wd(k2))
c
c time integration will be integer number of steps to get one
c gcm time step
c
ntime = nint(0.5*int(dtts/dt))
if(ntime.eq.0) then
ntime = 1
endif
c
c make sure area weighted vertical velocities match; in other words
c make sure mass in equals mass out at the intersection of each grid
c cell.
c
mda(k2) = (md(k2)*dz(k2)+md(k2+1)*dz(k2+1))/
* (dz(k2)+dz(k2+1))
c
wda(k2) = (wd(k2)*dz(k2)+wd(k2+1)*dz(k2+1))/
* (dz(k2)+dz(k2+1))
c
tda(k2) = td(k2)
sda(k2) = sd(k2)
c
taa(k2) = ttemp(k2+1)
saa(k2) = stemp(k2+1)
c
enddo
dt = min(dt,dtts)
if(debug) then
write(0,*)"Time step is ", dt
endif
tda(maxdepth) = td(maxdepth)
sda(maxdepth) = sd(maxdepth)
c
c do top and bottom points first
c
kmx = maxdepth-1
do nn=1,ntime
ttemp(k) = ttemp(k)-
* (mda(k)*(tda(k)-taa(k)))*dt*recip_drF(k)
c
stemp(k) = stemp(k)-
* (mda(k)*(sda(k)-saa(k)))*dt*recip_drF(k)
c
c
c now do inner points if there are any
c
if(Maxdepth-k.gt.1) then
do k2=k+1,Maxdepth-1
c
ttemp(k2) = ttemp(k2) +
* (mda(k2-1)*(tda(k2-1)-taa(k2-1))-
* mda(k2)*(tda(k2)-taa(k2)))
* *dt*recip_drF(k2)
c
stemp(k2) = stemp(k2) +
* (mda(k2-1)*(sda(k2-1)-saa(k2-1))-
* mda(k2)*(sda(k2)-saa(k2)))
* *dt*recip_drF(k2)
c
enddo
endif
ttemp(kmx+1) = ttemp(kmx+1)+
* (mda(kmx)*(tda(kmx)-taa(kmx)))*
* dt*recip_drF(kmx+1)
c
stemp(kmx+1) = stemp(kmx+1)+
* (mda(kmx)*(sda(kmx)-saa(kmx)))*
* dt*recip_drF(kmx+1)
c
c set the environmental temp and salinity to equal new fields
c
do k2=1,maxdepth-1
taa(k2) = ttemp(k2+1)
saa(k2) = stemp(k2+1)
enddo
c
c end loop on number of time integration steps
c
enddo
ENDDO
999 continue
c
c assume that it converged, so update the ta and sa with new fields
c
c if(i.gt.180.and.j.gt.200.and.i.lt.240) then
c write(*,*)"Converged ",i,j,k,maxdepth,ttemp(k+1),ta(i,k+1)
c endif
do k2=k,maxdepth
convadj(i,j,k2) = convadj(i,j,k2) + (ttemp(k2)-
* ta(i,k2))
sa(i,k2) = stemp(k2)
ta(i,k2) = ttemp(k2)
c if(i.gt.180.and.j.gt.200.and.i.lt.240) then
c write(*,*)"convadj ",convadj(i,j,k2)
c endif
c
c see if nlopps messed up
c
if(sa(i,k).gt.40..or.ta(i,k).lt.-4.0) then
problem = .true.
write(0,*)"t out of range at j after adjust",j
debug = .true.
endif
c
enddo
c
c jump here if k = maxdepth or if level not unstable, go to next
c profile point
c
1000 continue
c
c
c end loop on k, move on to next possible plume
c
ENDDO
1100 continue
c
c i loop
c
ENDDO
END
#endif /* OPPS_ORGCODE */