! ! $Id: ppm3d.f90 5246 2024-10-21 12:58:45Z fhourdin $ ! !From lin@explorer.gsfc.nasa.gov Wed Apr 15 17:44:44 1998 !Date: Wed, 15 Apr 1998 11:37:03 -0400 !From: lin@explorer.gsfc.nasa.gov !To: Frederic.Hourdin@lmd.jussieu.fr !Subject: 3D transport module of the GSFC CTM and GEOS GCM !This code is sent to you by S-J Lin, DAO, NASA-GSFC !Note: this version is intended for machines like CRAY !-90. No multitasking directives implemented. ! ******************************************************************** ! ! TransPort Core for Goddard Chemistry Transport Model (G-CTM), Goddard ! Earth Observing System General Circulation Model (GEOS-GCM), and Data ! Assimilation System (GEOS-DAS). ! ! ******************************************************************** ! ! Purpose: given horizontal winds on a hybrid sigma-p surfaces, ! one call to tpcore updates the 3-D mixing ratio ! fields one time step (NDT). [vertical mass flux is computed ! internally consistent with the discretized hydrostatic mass ! continuity equation of the C-Grid GEOS-GCM (for IGD=1)]. ! ! Schemes: Multi-dimensional Flux Form Semi-Lagrangian (FFSL) scheme based ! on the van Leer or PPM. ! (see Lin and Rood 1996). ! Version 4.5 ! Last modified: Dec. 5, 1996 ! Major changes from version 4.0: a more general vertical hybrid sigma- ! pressure coordinate. ! Subroutines modified: xtp, ytp, fzppm, qckxyz ! Subroutines deleted: vanz ! ! Author: Shian-Jiann Lin ! mail address: ! Shian-Jiann Lin* ! Code 910.3, NASA/GSFC, Greenbelt, MD 20771 ! Phone: 301-286-9540 ! E-mail: lin@dao.gsfc.nasa.gov ! ! *affiliation: ! Joint Center for Earth Systems Technology ! The University of Maryland Baltimore County ! NASA - Goddard Space Flight Center ! References: ! ! 1. Lin, S.-J., and R. B. Rood, 1996: Multidimensional flux form semi- ! Lagrangian transport schemes. Mon. Wea. Rev., 124, 2046-2070. ! ! 2. Lin, S.-J., W. C. Chao, Y. C. Sud, and G. K. Walker, 1994: A class of ! the van Leer-type transport schemes and its applications to the moist- ! ure transport in a General Circulation Model. Mon. Wea. Rev., 122, ! 1575-1593. ! ! ****6***0*********0*********0*********0*********0*********0**********72 ! subroutine ppm3d(IGD,Q,PS1,PS2,U,V,W,NDT,IORD,JORD,KORD,NC,IMR, & JNP,j1,NLAY,AP,BP,PT,AE,fill,dum,Umax) implicit none ! rajout de d�clarations ! integer Jmax,kmax,ndt0,nstep,k,j,i,ic,l,js,jn,imh,iad,jad,krd ! integer iu,iiu,j2,jmr,js0,jt ! real dtdy,dtdy5,rcap,iml,jn0,imjm,pi,dl,dp ! real dt,cr1,maxdt,ztc,d5,sum1,sum2,ru ! ! ******************************************************************** ! ! ============= ! INPUT: ! ============= ! ! Q(IMR,JNP,NLAY,NC): mixing ratios at current time (t) ! NC: total # of constituents ! IMR: first dimension (E-W); # of Grid intervals in E-W is IMR ! JNP: 2nd dimension (N-S); # of Grid intervals in N-S is JNP-1 ! NLAY: 3rd dimension (# of layers); vertical index increases from 1 at ! the model top to NLAY near the surface (see fig. below). ! It is assumed that 6 <= NLAY <= JNP (for dynamic memory allocation) ! ! PS1(IMR,JNP): surface pressure at current time (t) ! PS2(IMR,JNP): surface pressure at mid-time-level (t+NDT/2) ! PS2 is replaced by the predicted PS (at t+NDT) on output. ! Note: surface pressure can have any unit or can be multiplied by any ! const. ! ! The pressure at layer edges are defined as follows: ! ! p(i,j,k) = AP(k)*PT + BP(k)*PS(i,j) (1) ! ! Where PT is a constant having the same unit as PS. ! AP and BP are unitless constants given at layer edges ! defining the vertical coordinate. ! BP(1) = 0., BP(NLAY+1) = 1. ! The pressure at the model top is PTOP = AP(1)*PT ! ! For pure sigma system set AP(k) = 1 for all k, PT = PTOP, ! BP(k) = sige(k) (sigma at edges), PS = Psfc - PTOP. ! ! Note: the sigma-P coordinate is a subset of Eq. 1, which in turn ! is a subset of the following even more general sigma-P-thelta coord. ! currently under development. ! p(i,j,k) = (AP(k)*PT + BP(k)*PS(i,j))/(D(k)-C(k)*TE**(-1/kapa)) ! ! ///////////////////////////////// ! / \ ------------- PTOP -------------- AP(1), BP(1) ! | ! delp(1) | ........... Q(i,j,1) ............ ! | ! W(1) \ / --------------------------------- AP(2), BP(2) ! ! ! ! W(k-1) / \ --------------------------------- AP(k), BP(k) ! | ! delp(K) | ........... Q(i,j,k) ............ ! | ! W(k) \ / --------------------------------- AP(k+1), BP(k+1) ! ! ! ! / \ --------------------------------- AP(NLAY), BP(NLAY) ! | ! delp(NLAY) | ........... Q(i,j,NLAY) ......... ! | ! W(NLAY)=0 \ / ------------- surface ----------- AP(NLAY+1), BP(NLAY+1) ! ////////////////////////////////// ! ! U(IMR,JNP,NLAY) & V(IMR,JNP,NLAY):winds (m/s) at mid-time-level (t+NDT/2) ! U and V may need to be polar filtered in advance in some cases. ! ! IGD: grid type on which winds are defined. ! IGD = 0: A-Grid [all variables defined at the same point from south ! pole (j=1) to north pole (j=JNP) ] ! ! IGD = 1 GEOS-GCM C-Grid ! [North] ! ! V(i,j) ! | ! | ! | ! U(i-1,j)---Q(i,j)---U(i,j) [EAST] ! | ! | ! | ! V(i,j-1) ! ! U(i, 1) is defined at South Pole. ! V(i, 1) is half grid north of the South Pole. ! V(i,JMR) is half grid south of the North Pole. ! ! V must be defined at j=1 and j=JMR if IGD=1 ! V at JNP need not be given. ! ! NDT: time step in seconds (need not be constant during the course of ! the integration). Suggested value: 30 min. for 4x5, 15 min. for 2x2.5 ! (Lat-Lon) resolution. Smaller values are recommanded if the model ! has a well-resolved stratosphere. ! ! J1 defines the size of the polar cap: ! South polar cap edge is located at -90 + (j1-1.5)*180/(JNP-1) deg. ! North polar cap edge is located at 90 - (j1-1.5)*180/(JNP-1) deg. ! There are currently only two choices (j1=2 or 3). ! IMR must be an even integer if j1 = 2. Recommended value: J1=3. ! ! IORD, JORD, and KORD are integers controlling various options in E-W, N-S, ! and vertical transport, respectively. Recommended values for positive ! definite scalars: IORD=JORD=3, KORD=5. Use KORD=3 for non- ! positive definite scalars or when linear correlation between constituents ! is to be maintained. ! ! _ORD= ! 1: 1st order upstream scheme (too diffusive, not a useful option; it ! can be used for debugging purposes; this is THE only known "linear" ! monotonic advection scheme.). ! 2: 2nd order van Leer (full monotonicity constraint; ! see Lin et al 1994, MWR) ! 3: monotonic PPM* (slightly improved PPM of Collela & Woodward 1984) ! 4: semi-monotonic PPM (same as 3, but overshoots are allowed) ! 5: positive-definite PPM (constraint on the subgrid distribution is ! only strong enough to prevent generation of negative values; ! both overshoots & undershoots are possible). ! 6: un-constrained PPM (nearly diffusion free; slightly faster but ! positivity not quaranteed. Use this option only when the fields ! and winds are very smooth). ! ! *PPM: Piece-wise Parabolic Method ! ! Note that KORD <=2 options are no longer supported. DO not use option 4 or 5. ! for non-positive definite scalars (such as Ertel Potential Vorticity). ! ! The implicit numerical diffusion decreases as _ORD increases. ! The last two options (ORDER=5, 6) should only be used when there is ! significant explicit diffusion (such as a turbulence parameterization). You ! might get dispersive results otherwise. ! No filter of any kind is applied to the constituent fields here. ! ! AE: Radius of the sphere (meters). ! Recommended value for the planet earth: 6.371E6 ! ! fill(logical): flag to do filling for negatives (see note below). ! ! Umax: Estimate (upper limit) of the maximum U-wind speed (m/s). ! (220 m/s is a good value for troposphere model; 280 m/s otherwise) ! ! ============= ! Output ! ============= ! ! Q: mixing ratios at future time (t+NDT) (original values are over-written) ! W(NLAY): large-scale vertical mass flux as diagnosed from the hydrostatic ! relationship. W will have the same unit as PS1 and PS2 (eg, mb). ! W must be divided by NDT to get the correct mass-flux unit. ! The vertical Courant number C = W/delp_UPWIND, where delp_UPWIND ! is the pressure thickness in the "upwind" direction. For example, ! C(k) = W(k)/delp(k) if W(k) > 0; ! C(k) = W(k)/delp(k+1) if W(k) < 0. ! ( W > 0 is downward, ie, toward surface) ! PS2: predicted PS at t+NDT (original values are over-written) ! ! ******************************************************************** ! NOTES: ! This forward-in-time upstream-biased transport scheme reduces to ! the 2nd order center-in-time center-in-space mass continuity eqn. ! if Q = 1 (constant fields will remain constant). This also ensures ! that the computed vertical velocity to be identical to GEOS-1 GCM ! for on-line transport. ! ! A larger polar cap is used if j1=3 (recommended for C-Grid winds or when ! winds are noisy near poles). ! ! Flux-Form Semi-Lagrangian transport in the East-West direction is used ! when and where Courant # is greater than one. ! ! The user needs to change the parameter Jmax or Kmax if the resolution ! is greater than 0.5 deg in N-S or 150 layers in the vertical direction. ! (this TransPort Core is otherwise resolution independent and can be used ! as a library routine). ! ! PPM is 4th order accurate when grid spacing is uniform (x & y); 3rd ! order accurate for non-uniform grid (vertical sigma coord.). ! ! Time step is limitted only by transport in the meridional direction. ! (the FFSL scheme is not implemented in the meridional direction). ! ! Since only 1-D limiters are applied, negative values could ! potentially be generated when large time step is used and when the ! initial fields contain discontinuities. ! This does not necessarily imply the integration is unstable. ! These negatives are typically very small. A filling algorithm is ! activated if the user set "fill" to be true. ! ! The van Leer scheme used here is nearly as accurate as the original PPM ! due to the use of a 4th order accurate reference slope. The PPM imple- ! mented here is an improvement over the original and is also based on ! the 4th order reference slope. ! ! ****6***0*********0*********0*********0*********0*********0**********72 ! ! User modifiable parameters ! integer,parameter :: Jmax = 361, kmax = 150 ! ! ****6***0*********0*********0*********0*********0*********0**********72 ! ! Input-Output arrays ! real :: Q(IMR,JNP,NLAY,NC),PS1(IMR,JNP),PS2(IMR,JNP), & U(IMR,JNP,NLAY),V(IMR,JNP,NLAY),AP(NLAY+1), & BP(NLAY+1),W(IMR,JNP,NLAY),NDT,val(NLAY),Umax integer :: IGD,IORD,JORD,KORD,NC,IMR,JNP,j1,NLAY,AE integer :: IMRD2 real :: PT logical :: cross, fill, dum ! ! Local dynamic arrays ! real :: CRX(IMR,JNP),CRY(IMR,JNP),xmass(IMR,JNP),ymass(IMR,JNP), & fx1(IMR+1),DPI(IMR,JNP,NLAY),delp1(IMR,JNP,NLAY), & WK1(IMR,JNP,NLAY),PU(IMR,JNP),PV(IMR,JNP),DC2(IMR,JNP), & delp2(IMR,JNP,NLAY),DQ(IMR,JNP,NLAY,NC),VA(IMR,JNP), & UA(IMR,JNP),qtmp(-IMR:2*IMR) ! ! Local static arrays ! real :: DTDX(Jmax), DTDX5(Jmax), acosp(Jmax), & cosp(Jmax), cose(Jmax), DAP(kmax),DBK(Kmax) data NDT0, NSTEP /0, 0/ data cross /.true./ REAL :: DTDY, DTDY5, RCAP INTEGER :: JS0, JN0, IML, JMR, IMJM SAVE DTDY, DTDY5, RCAP, JS0, JN0, IML, & DTDX, DTDX5, ACOSP, COSP, COSE, DAP,DBK ! INTEGER :: NDT0, NSTEP, j2, k,j,i,ic,l,JS,JN,IMH INTEGER :: IU,IIU,JT,iad,jad,krd REAL :: r23,r3,PI,DL,DP,DT,CR1,MAXDT,ZTC,D5 REAL :: sum1,sum2,ru JMR = JNP -1 IMJM = IMR*JNP j2 = JNP - j1 + 1 NSTEP = NSTEP + 1 ! ! *********** Initialization ********************** if(NSTEP.eq.1) then ! write(6,*) '------------------------------------ ' write(6,*) 'NASA/GSFC Transport Core Version 4.5' write(6,*) '------------------------------------ ' ! WRITE(6,*) 'IMR=',IMR,' JNP=',JNP,' NLAY=',NLAY,' j1=',j1 WRITE(6,*) 'NC=',NC,IORD,JORD,KORD,NDT ! ! controles sur les parametres if(NLAY.LT.6) then write(6,*) 'NLAY must be >= 6' stop endif if (JNP.LT.NLAY) then write(6,*) 'JNP must be >= NLAY' stop endif IMRD2=mod(IMR,2) if (j1.eq.2.and.IMRD2.NE.0) then write(6,*) 'if j1=2 IMR must be an even integer' stop endif ! if(Jmax.lt.JNP .or. Kmax.lt.NLAY) then write(6,*) 'Jmax or Kmax is too small' stop endif ! DO k=1,NLAY DAP(k) = (AP(k+1) - AP(k))*PT DBK(k) = BP(k+1) - BP(k) ENDDO ! PI = 4. * ATAN(1.) DL = 2.*PI / REAL(IMR) DP = PI / REAL(JMR) ! if(IGD.eq.0) then ! Compute analytic cosine at cell edges call cosa(cosp,cose,JNP,PI,DP) else ! Define cosine consistent with GEOS-GCM (using dycore2.0 or later) call cosc(cosp,cose,JNP,PI,DP) endif ! do J=2,JMR acosp(j) = 1. / cosp(j) end do ! ! Inverse of the Scaled polar cap area. ! RCAP = DP / (IMR*(1.-COS((j1-1.5)*DP))) acosp(1) = RCAP acosp(JNP) = RCAP endif ! if(NDT0 .ne. NDT) then DT = NDT NDT0 = NDT if(Umax .lt. 180.) then write(6,*) 'Umax may be too small!' endif CR1 = abs(Umax*DT)/(DL*AE) MaxDT = DP*AE / abs(Umax) + 0.5 write(6,*)'Largest time step for max(V)=',Umax,' is ',MaxDT if(MaxDT .lt. abs(NDT)) then write(6,*) 'Warning!!! NDT maybe too large!' endif ! if(CR1.ge.0.95) then JS0 = 0 JN0 = 0 IML = IMR-2 ZTC = 0. else ZTC = acos(CR1) * (180./PI) ! JS0 = REAL(JMR)*(90.-ZTC)/180. + 2 JS0 = max(JS0, J1+1) IML = min(6*JS0/(J1-1)+2, 4*IMR/5) JN0 = JNP-JS0+1 endif ! ! do J=2,JMR DTDX(j) = DT / ( DL*AE*COSP(J) ) ! print*,'dtdx=',dtdx(j) DTDX5(j) = 0.5*DTDX(j) enddo ! DTDY = DT /(AE*DP) ! print*,'dtdy=',dtdy DTDY5 = 0.5*DTDY ! ! write(6,*) 'J1=',J1,' J2=', J2 endif ! ! *********** End Initialization ********************** ! ! delp = pressure thickness: the psudo-density in a hydrostatic system. do k=1,NLAY do j=1,JNP do i=1,IMR delp1(i,j,k)=DAP(k)+DBK(k)*PS1(i,j) delp2(i,j,k)=DAP(k)+DBK(k)*PS2(i,j) enddo enddo enddo ! if(j1.ne.2) then DO IC=1,NC DO L=1,NLAY DO I=1,IMR Q(I, 2,L,IC) = Q(I, 1,L,IC) Q(I,JMR,L,IC) = Q(I,JNP,L,IC) END DO END DO END DO endif ! ! Compute "tracer density" DO IC=1,NC DO k=1,NLAY DO j=1,JNP DO i=1,IMR DQ(i,j,k,IC) = Q(i,j,k,IC)*delp1(i,j,k) END DO END DO END DO END DO ! do k=1,NLAY ! if(IGD.eq.0) then ! Convert winds on A-Grid to Courant # on C-Grid. call A2C(U(1,1,k),V(1,1,k),IMR,JMR,j1,j2,CRX,CRY,dtdx5,DTDY5) else ! Convert winds on C-grid to Courant # do j=j1,j2 do i=2,IMR CRX(i,J) = dtdx(j)*U(i-1,j,k) end do end do ! do j=j1,j2 CRX(1,J) = dtdx(j)*U(IMR,j,k) 50 continue end do ! do i=1,IMR*JMR CRY(i,2) = DTDY*V(i,1,k) end do endif ! ! Determine JS and JN JS = j1 JN = j2 ! do j=JS0,j1+1,-1 do i=1,IMR if(abs(CRX(i,j)).GT.1.) then JS = j go to 2222 endif enddo enddo ! 2222 continue do j=JN0,j2-1 do i=1,IMR if(abs(CRX(i,j)).GT.1.) then JN = j go to 2233 endif enddo enddo 2233 continue ! if(j1.ne.2) then ! Enlarged polar cap. do i=1,IMR DPI(i, 2,k) = 0. DPI(i,JMR,k) = 0. enddo endif ! ! ******* Compute horizontal mass fluxes ************ ! ! N-S component do j=j1,j2+1 D5 = 0.5 * COSE(j) do i=1,IMR ymass(i,j) = CRY(i,j)*D5*(delp2(i,j,k) + delp2(i,j-1,k)) enddo enddo ! do j=j1,j2 DO i=1,IMR DPI(i,j,k) = (ymass(i,j) - ymass(i,j+1)) * acosp(j) END DO end do ! ! Poles sum1 = ymass(IMR,j1 ) sum2 = ymass(IMR,J2+1) do i=1,IMR-1 sum1 = sum1 + ymass(i,j1 ) sum2 = sum2 + ymass(i,J2+1) enddo ! sum1 = - sum1 * RCAP sum2 = sum2 * RCAP do i=1,IMR DPI(i, 1,k) = sum1 DPI(i,JNP,k) = sum2 enddo ! ! E-W component ! do j=j1,j2 do i=2,IMR PU(i,j) = 0.5 * (delp2(i,j,k) + delp2(i-1,j,k)) enddo enddo ! do j=j1,j2 PU(1,j) = 0.5 * (delp2(1,j,k) + delp2(IMR,j,k)) enddo ! do j=j1,j2 DO i=1,IMR xmass(i,j) = PU(i,j)*CRX(i,j) END DO end do ! DO j=j1,j2 DO i=1,IMR-1 DPI(i,j,k) = DPI(i,j,k) + xmass(i,j) - xmass(i+1,j) END DO END DO ! DO j=j1,j2 DPI(IMR,j,k) = DPI(IMR,j,k) + xmass(IMR,j) - xmass(1,j) END DO ! DO j=j1,j2 do i=1,IMR-1 UA(i,j) = 0.5 * (CRX(i,j)+CRX(i+1,j)) enddo enddo ! DO j=j1,j2 UA(imr,j) = 0.5 * (CRX(imr,j)+CRX(1,j)) enddo !cccccccccccccccccccccccccccccccccccccccccccccccccccccc ! Rajouts pour LMDZ.3.3 !cccccccccccccccccccccccccccccccccccccccccccccccccccccc do i=1,IMR do j=1,JNP VA(i,j)=0. enddo enddo do i=1,imr*(JMR-1) VA(i,2) = 0.5*(CRY(i,2)+CRY(i,3)) enddo ! if(j1.eq.2) then IMH = IMR/2 do i=1,IMH VA(i, 1) = 0.5*(CRY(i,2)-CRY(i+IMH,2)) VA(i+IMH, 1) = -VA(i,1) VA(i, JNP) = 0.5*(CRY(i,JNP)-CRY(i+IMH,JMR)) VA(i+IMH,JNP) = -VA(i,JNP) enddo VA(IMR,1)=VA(1,1) VA(IMR,JNP)=VA(1,JNP) endif ! ! ****6***0*********0*********0*********0*********0*********0**********72 do IC=1,NC ! do i=1,IMJM wk1(i,1,1) = 0. wk1(i,1,2) = 0. enddo ! ! E-W advective cross term do j=J1,J2 if(J.GT.JS .and. J.LT.JN) GO TO 250 ! do i=1,IMR qtmp(i) = q(i,j,k,IC) enddo ! do i=-IML,0 qtmp(i) = q(IMR+i,j,k,IC) qtmp(IMR+1-i) = q(1-i,j,k,IC) enddo ! DO i=1,IMR iu = UA(i,j) ru = UA(i,j) - iu iiu = i-iu if(UA(i,j).GE.0.) then wk1(i,j,1) = qtmp(iiu)+ru*(qtmp(iiu-1)-qtmp(iiu)) else wk1(i,j,1) = qtmp(iiu)+ru*(qtmp(iiu)-qtmp(iiu+1)) endif wk1(i,j,1) = wk1(i,j,1) - qtmp(i) END DO 250 continue end do ! if(JN.ne.0) then do j=JS+1,JN-1 ! do i=1,IMR qtmp(i) = q(i,j,k,IC) enddo ! qtmp(0) = q(IMR,J,k,IC) qtmp(IMR+1) = q( 1,J,k,IC) ! do i=1,imr iu = i - UA(i,j) wk1(i,j,1) = UA(i,j)*(qtmp(iu) - qtmp(iu+1)) enddo enddo endif ! ****6***0*********0*********0*********0*********0*********0**********72 ! Contribution from the N-S advection do i=1,imr*(j2-j1+1) JT = REAL(J1) - VA(i,j1) wk1(i,j1,2) = VA(i,j1) * (q(i,jt,k,IC) - q(i,jt+1,k,IC)) enddo ! do i=1,IMJM wk1(i,1,1) = q(i,1,k,IC) + 0.5*wk1(i,1,1) wk1(i,1,2) = q(i,1,k,IC) + 0.5*wk1(i,1,2) enddo ! if(cross) then ! Add cross terms in the vertical direction. if(IORD .GE. 2) then iad = 2 else iad = 1 endif ! if(JORD .GE. 2) then jad = 2 else jad = 1 endif call xadv(IMR,JNP,j1,j2,wk1(1,1,2),UA,JS,JN,IML,DC2,iad) call yadv(IMR,JNP,j1,j2,wk1(1,1,1),VA,PV,W,jad) do j=1,JNP do i=1,IMR q(i,j,k,IC) = q(i,j,k,IC) + DC2(i,j) + PV(i,j) enddo enddo endif ! call xtp(IMR,JNP,IML,j1,j2,JN,JS,PU,DQ(1,1,k,IC),wk1(1,1,2) & ,CRX,fx1,xmass,IORD) call ytp(IMR,JNP,j1,j2,acosp,RCAP,DQ(1,1,k,IC),wk1(1,1,1),CRY, & DC2,ymass,WK1(1,1,3),wk1(1,1,4),WK1(1,1,5),WK1(1,1,6),JORD) ! end do end do ! ! ******* Compute vertical mass flux (same unit as PS) *********** ! ! 1st step: compute total column mass CONVERGENCE. ! do j=1,JNP do i=1,IMR CRY(i,j) = DPI(i,j,1) end do end do ! do k=2,NLAY do j=1,JNP do i=1,IMR CRY(i,j) = CRY(i,j) + DPI(i,j,k) end do end do end do ! do j=1,JNP do i=1,IMR ! ! 2nd step: compute PS2 (PS at n+1) using the hydrostatic assumption. ! Changes (increases) to surface pressure = total column mass convergence ! PS2(i,j) = PS1(i,j) + CRY(i,j) ! ! 3rd step: compute vertical mass flux from mass conservation principle. ! W(i,j,1) = DPI(i,j,1) - DBK(1)*CRY(i,j) W(i,j,NLAY) = 0. end do end do ! do k=2,NLAY-1 do j=1,JNP do i=1,IMR W(i,j,k) = W(i,j,k-1) + DPI(i,j,k) - DBK(k)*CRY(i,j) end do end do end do ! DO k=1,NLAY DO j=1,JNP DO i=1,IMR delp2(i,j,k) = DAP(k) + DBK(k)*PS2(i,j) END DO END DO END DO ! KRD = max(3, KORD) do IC=1,NC ! !****6***0*********0*********0*********0*********0*********0**********72 call FZPPM(IMR,JNP,NLAY,j1,DQ(1,1,1,IC),W,Q(1,1,1,IC),WK1,DPI, & DC2,CRX,CRY,PU,PV,xmass,ymass,delp1,KRD) ! if(fill) call qckxyz(DQ(1,1,1,IC),DC2,IMR,JNP,NLAY,j1,j2, & cosp,acosp,.false.,IC,NSTEP) ! ! Recover tracer mixing ratio from "density" using predicted ! "air density" (pressure thickness) at time-level n+1 ! DO k=1,NLAY DO j=1,JNP DO i=1,IMR Q(i,j,k,IC) = DQ(i,j,k,IC) / delp2(i,j,k) ! print*,'i=',i,'j=',j,'k=',k,'Q(i,j,k,IC)=',Q(i,j,k,IC) enddo enddo enddo ! if(j1.ne.2) then DO k=1,NLAY DO I=1,IMR ! j=1 c'est le p�le Sud, j=JNP c'est le p�le Nord Q(I, 2,k,IC) = Q(I, 1,k,IC) Q(I,JMR,k,IC) = Q(I,JNP,k,IC) END DO END DO endif end do ! if(j1.ne.2) then DO k=1,NLAY DO i=1,IMR W(i, 2,k) = W(i, 1,k) W(i,JMR,k) = W(i,JNP,k) END DO END DO endif ! RETURN END SUBROUTINE ppm3d ! !****6***0*********0*********0*********0*********0*********0**********72 subroutine FZPPM(IMR,JNP,NLAY,j1,DQ,WZ,P,DC,DQDT,AR,AL,A6, & flux,wk1,wk2,wz2,delp,KORD) implicit none integer,parameter :: kmax = 150 real,parameter :: R23 = 2./3., R3 = 1./3. integer :: IMR,JNP,NLAY,J1,KORD real :: WZ(IMR,JNP,NLAY),P(IMR,JNP,NLAY),DC(IMR,JNP,NLAY), & wk1(IMR,*),delp(IMR,JNP,NLAY),DQ(IMR,JNP,NLAY), & DQDT(IMR,JNP,NLAY) ! Assuming JNP >= NLAY real :: AR(IMR,*),AL(IMR,*),A6(IMR,*),flux(IMR,*),wk2(IMR,*), & wz2(IMR,*) integer :: JMR,IMJM,NLAYM1,LMT,K,I,J real :: c0,c1,c2,tmp,qmax,qmin,a,b,fct,a1,a2,cm,cp ! JMR = JNP - 1 IMJM = IMR*JNP NLAYM1 = NLAY - 1 ! LMT = KORD - 3 ! ! ****6***0*********0*********0*********0*********0*********0**********72 ! Compute DC for PPM ! ****6***0*********0*********0*********0*********0*********0**********72 ! do k=1,NLAYM1 do i=1,IMJM DQDT(i,1,k) = P(i,1,k+1) - P(i,1,k) end do end do ! DO k=2,NLAYM1 DO I=1,IMJM c0 = delp(i,1,k) / (delp(i,1,k-1)+delp(i,1,k)+delp(i,1,k+1)) c1 = (delp(i,1,k-1)+0.5*delp(i,1,k))/(delp(i,1,k+1)+delp(i,1,k)) c2 = (delp(i,1,k+1)+0.5*delp(i,1,k))/(delp(i,1,k-1)+delp(i,1,k)) tmp = c0*(c1*DQDT(i,1,k) + c2*DQDT(i,1,k-1)) Qmax = max(P(i,1,k-1),P(i,1,k),P(i,1,k+1)) - P(i,1,k) Qmin = P(i,1,k) - min(P(i,1,k-1),P(i,1,k),P(i,1,k+1)) DC(i,1,k) = sign(min(abs(tmp),Qmax,Qmin), tmp) END DO END DO ! ! ****6***0*********0*********0*********0*********0*********0**********72 ! Loop over latitudes (to save memory) ! ****6***0*********0*********0*********0*********0*********0**********72 ! DO j=1,JNP if((j.eq.2 .or. j.eq.JMR) .and. j1.ne.2) goto 2000 ! DO k=1,NLAY DO i=1,IMR wz2(i,k) = WZ(i,j,k) wk1(i,k) = P(i,j,k) wk2(i,k) = delp(i,j,k) flux(i,k) = DC(i,j,k) !this flux is actually the monotone slope enddo enddo ! !****6***0*********0*********0*********0*********0*********0**********72 ! Compute first guesses at cell interfaces ! First guesses are required to be continuous. ! ****6***0*********0*********0*********0*********0*********0**********72 ! ! three-cell parabolic subgrid distribution at model top ! two-cell parabolic with zero gradient subgrid distribution ! at the surface. ! ! First guess top edge value DO i=1,IMR ! three-cell PPM ! Compute a,b, and c of q = aP**2 + bP + c using cell averages and delp a = 3.*( DQDT(i,j,2) - DQDT(i,j,1)*(wk2(i,2)+wk2(i,3))/ & (wk2(i,1)+wk2(i,2)) ) / & ( (wk2(i,2)+wk2(i,3))*(wk2(i,1)+wk2(i,2)+wk2(i,3)) ) b = 2.*DQDT(i,j,1)/(wk2(i,1)+wk2(i,2)) - & R23*a*(2.*wk2(i,1)+wk2(i,2)) AL(i,1) = wk1(i,1) - wk2(i,1)*(R3*a*wk2(i,1) + 0.5*b) AL(i,2) = wk2(i,1)*(a*wk2(i,1) + b) + AL(i,1) ! ! Check if change sign if(wk1(i,1)*AL(i,1).le.0.) then AL(i,1) = 0. flux(i,1) = 0. else flux(i,1) = wk1(i,1) - AL(i,1) endif END DO ! ! Bottom DO i=1,IMR ! 2-cell PPM with zero gradient right at the surface ! fct = DQDT(i,j,NLAYM1)*wk2(i,NLAY)**2 / & ( (wk2(i,NLAY)+wk2(i,NLAYM1))*(2.*wk2(i,NLAY)+wk2(i,NLAYM1))) AR(i,NLAY) = wk1(i,NLAY) + fct AL(i,NLAY) = wk1(i,NLAY) - (fct+fct) if(wk1(i,NLAY)*AR(i,NLAY).le.0.) AR(i,NLAY) = 0. flux(i,NLAY) = AR(i,NLAY) - wk1(i,NLAY) END DO ! !****6***0*********0*********0*********0*********0*********0**********72 ! 4th order interpolation in the interior. !****6***0*********0*********0*********0*********0*********0**********72 ! DO k=3,NLAYM1 DO i=1,IMR c1 = DQDT(i,j,k-1)*wk2(i,k-1) / (wk2(i,k-1)+wk2(i,k)) c2 = 2. / (wk2(i,k-2)+wk2(i,k-1)+wk2(i,k)+wk2(i,k+1)) A1 = (wk2(i,k-2)+wk2(i,k-1)) / (2.*wk2(i,k-1)+wk2(i,k)) A2 = (wk2(i,k )+wk2(i,k+1)) / (2.*wk2(i,k)+wk2(i,k-1)) AL(i,k) = wk1(i,k-1) + c1 + c2 * & ( wk2(i,k )*(c1*(A1 - A2)+A2*flux(i,k-1)) - & wk2(i,k-1)*A1*flux(i,k) ) ! print *,'AL1',i,k, AL(i,k) END DO END DO ! do i=1,IMR*NLAYM1 AR(i,1) = AL(i,2) ! print *,'AR1',i,AR(i,1) end do ! do i=1,IMR*NLAY A6(i,1) = 3.*(wk1(i,1)+wk1(i,1) - (AL(i,1)+AR(i,1))) ! print *,'A61',i,A6(i,1) end do ! !****6***0*********0*********0*********0*********0*********0**********72 ! Top & Bot always monotonic call lmtppm(flux(1,1),A6(1,1),AR(1,1),AL(1,1),wk1(1,1),IMR,0) call lmtppm(flux(1,NLAY),A6(1,NLAY),AR(1,NLAY),AL(1,NLAY), & wk1(1,NLAY),IMR,0) ! ! Interior depending on KORD if(LMT.LE.2) & call lmtppm(flux(1,2),A6(1,2),AR(1,2),AL(1,2),wk1(1,2), & IMR*(NLAY-2),LMT) ! !****6***0*********0*********0*********0*********0*********0**********72 ! DO i=1,IMR*NLAYM1 IF(wz2(i,1).GT.0.) then CM = wz2(i,1) / wk2(i,1) flux(i,2) = AR(i,1)+0.5*CM*(AL(i,1)-AR(i,1)+A6(i,1)*(1.-R23*CM)) else ! print *,'test2-0',i,j,wz2(i,1),wk2(i,2) CP= wz2(i,1) / wk2(i,2) ! print *,'testCP',CP flux(i,2) = AL(i,2)+0.5*CP*(AL(i,2)-AR(i,2)-A6(i,2)*(1.+R23*CP)) ! print *,'test2',i, AL(i,2),AR(i,2),A6(i,2),R23 endif END DO ! DO i=1,IMR*NLAYM1 flux(i,2) = wz2(i,1) * flux(i,2) 250 CONTINUE END DO ! do i=1,IMR DQ(i,j, 1) = DQ(i,j, 1) - flux(i, 2) DQ(i,j,NLAY) = DQ(i,j,NLAY) + flux(i,NLAY) end do ! do k=2,NLAYM1 do i=1,IMR DQ(i,j,k) = DQ(i,j,k) + flux(i,k) - flux(i,k+1) end do end do 2000 CONTINUE END DO return end subroutine fzppm ! subroutine xtp(IMR,JNP,IML,j1,j2,JN,JS,PU,DQ,Q,UC, & fx1,xmass,IORD) implicit none integer :: IMR,JNP,IML,j1,j2,JN,JS,IORD real :: PU,DQ,Q,UC,fx1,xmass real :: dc,qtmp integer :: ISAVE(IMR) dimension UC(IMR,*),DC(-IML:IMR+IML+1),xmass(IMR,JNP) & ,fx1(IMR+1),DQ(IMR,JNP),qtmp(-IML:IMR+1+IML) dimension PU(IMR,JNP),Q(IMR,JNP) integer :: jvan,j1vl,j2vl,j,i,iu,itmp,ist,imp real :: rut ! IMP = IMR + 1 ! ! van Leer at high latitudes jvan = max(1,JNP/18) j1vl = j1+jvan j2vl = j2-jvan ! do j=j1,j2 ! do i=1,IMR qtmp(i) = q(i,j) enddo ! if(j.ge.JN .or. j.le.JS) goto 2222 ! ************* Eulerian ********** ! qtmp(0) = q(IMR,J) qtmp(-1) = q(IMR-1,J) qtmp(IMP) = q(1,J) qtmp(IMP+1) = q(2,J) ! IF(IORD.eq.1 .or. j.eq.j1.OR. j.eq.j2) THEN DO i=1,IMR iu = REAL(i) - uc(i,j) fx1(i) = qtmp(iu) END DO ELSE call xmist(IMR,IML,Qtmp,DC) DC(0) = DC(IMR) ! if(IORD.eq.2 .or. j.le.j1vl .or. j.ge.j2vl) then DO i=1,IMR iu = REAL(i) - uc(i,j) fx1(i) = qtmp(iu) + DC(iu)*(sign(1.,uc(i,j))-uc(i,j)) END DO else call fxppm(IMR,IML,UC(1,j),Qtmp,DC,fx1,IORD) endif ! ENDIF ! DO i=1,IMR fx1(i) = fx1(i)*xmass(i,j) END DO ! goto 1309 ! ! ***** Conservative (flux-form) Semi-Lagrangian transport ***** ! 2222 continue ! do i=-IML,0 qtmp(i) = q(IMR+i,j) qtmp(IMP-i) = q(1-i,j) enddo ! IF(IORD.eq.1 .or. j.eq.j1.OR. j.eq.j2) THEN DO i=1,IMR itmp = INT(uc(i,j)) ISAVE(i) = i - itmp iu = i - uc(i,j) fx1(i) = (uc(i,j) - itmp)*qtmp(iu) END DO ELSE call xmist(IMR,IML,Qtmp,DC) ! do i=-IML,0 DC(i) = DC(IMR+i) DC(IMP-i) = DC(1-i) enddo ! DO i=1,IMR itmp = INT(uc(i,j)) rut = uc(i,j) - itmp ISAVE(i) = i - itmp iu = i - uc(i,j) fx1(i) = rut*(qtmp(iu) + DC(iu)*(sign(1.,rut) - rut)) END DO ENDIF ! do i=1,IMR IF(uc(i,j).GT.1.) then !DIR$ NOVECTOR do ist = ISAVE(i),i-1 fx1(i) = fx1(i) + qtmp(ist) enddo elseIF(uc(i,j).LT.-1.) then do ist = i,ISAVE(i)-1 fx1(i) = fx1(i) - qtmp(ist) enddo !DIR$ VECTOR endif end do do i=1,IMR fx1(i) = PU(i,j)*fx1(i) enddo ! ! *************************************** ! 1309 fx1(IMP) = fx1(1) DO i=1,IMR DQ(i,j) = DQ(i,j) + fx1(i)-fx1(i+1) END DO ! ! *************************************** ! end do return end subroutine xtp ! subroutine fxppm(IMR,IML,UT,P,DC,flux,IORD) implicit none integer :: IMR,IML,IORD real :: UT,P,DC,flux real,parameter :: R3 = 1./3., R23 = 2./3. DIMENSION UT(*),flux(*),P(-IML:IMR+IML+1),DC(-IML:IMR+IML+1) REAL :: AR(0:IMR),AL(0:IMR),A6(0:IMR) integer :: LMT,IMP,JLVL,i ! logical first ! data first /.true./ ! SAVE LMT ! if(first) then ! ! correction calcul de LMT a chaque passage pour pouvoir choisir ! plusieurs schemas PPM pour differents traceurs ! IF (IORD.LE.0) then ! if(IMR.GE.144) then ! LMT = 0 ! elseif(IMR.GE.72) then ! LMT = 1 ! else ! LMT = 2 ! endif ! else ! LMT = IORD - 3 ! endif ! LMT = IORD - 3 ! write(6,*) 'PPM option in E-W direction = ', LMT ! first = .false. ! endif ! DO i=1,IMR AL(i) = 0.5*(p(i-1)+p(i)) + (DC(i-1) - DC(i))*R3 END DO ! do i=1,IMR-1 AR(i) = AL(i+1) end do AR(IMR) = AL(1) ! do i=1,IMR A6(i) = 3.*(p(i)+p(i) - (AL(i)+AR(i))) end do ! if(LMT.LE.2) call lmtppm(DC(1),A6(1),AR(1),AL(1),P(1),IMR,LMT) ! AL(0) = AL(IMR) AR(0) = AR(IMR) A6(0) = A6(IMR) ! DO i=1,IMR IF(UT(i).GT.0.) then flux(i) = AR(i-1) + 0.5*UT(i)*(AL(i-1) - AR(i-1) + & A6(i-1)*(1.-R23*UT(i)) ) else flux(i) = AL(i) - 0.5*UT(i)*(AR(i) - AL(i) + & A6(i)*(1.+R23*UT(i))) endif enddo return end subroutine fxppm ! subroutine xmist(IMR,IML,P,DC) implicit none integer :: IMR,IML real,parameter :: R24 = 1./24. real :: P(-IML:IMR+1+IML),DC(-IML:IMR+1+IML) integer :: i real :: tmp,pmax,pmin ! do i=1,IMR tmp = R24*(8.*(p(i+1) - p(i-1)) + p(i-2) - p(i+2)) Pmax = max(P(i-1), p(i), p(i+1)) - p(i) Pmin = p(i) - min(P(i-1), p(i), p(i+1)) DC(i) = sign(min(abs(tmp),Pmax,Pmin), tmp) end do return end subroutine xmist ! subroutine ytp(IMR,JNP,j1,j2,acosp,RCAP,DQ,P,VC,DC2 & ,ymass,fx,A6,AR,AL,JORD) implicit none integer :: IMR,JNP,j1,j2,JORD real :: acosp,RCAP,DQ,P,VC,DC2,ymass,fx,A6,AR,AL dimension P(IMR,JNP),VC(IMR,JNP),ymass(IMR,JNP) & ,DC2(IMR,JNP),DQ(IMR,JNP),acosp(JNP) ! Work array DIMENSION fx(IMR,JNP),AR(IMR,JNP),AL(IMR,JNP),A6(IMR,JNP) integer :: JMR,len,i,jt,j real :: sum1,sum2 ! JMR = JNP - 1 len = IMR*(J2-J1+2) ! if(JORD.eq.1) then DO i=1,len JT = REAL(J1) - VC(i,J1) fx(i,j1) = p(i,JT) END DO else call ymist(IMR,JNP,j1,P,DC2,4) ! if(JORD.LE.0 .or. JORD.GE.3) then call fyppm(VC,P,DC2,fx,IMR,JNP,j1,j2,A6,AR,AL,JORD) else DO i=1,len JT = REAL(J1) - VC(i,J1) fx(i,j1) = p(i,JT) + (sign(1.,VC(i,j1))-VC(i,j1))*DC2(i,JT) END DO endif endif ! DO i=1,len fx(i,j1) = fx(i,j1)*ymass(i,j1) END DO ! DO j=j1,j2 DO i=1,IMR DQ(i,j) = DQ(i,j) + (fx(i,j) - fx(i,j+1)) * acosp(j) END DO END DO ! ! Poles sum1 = fx(IMR,j1 ) sum2 = fx(IMR,J2+1) do i=1,IMR-1 sum1 = sum1 + fx(i,j1 ) sum2 = sum2 + fx(i,J2+1) enddo ! sum1 = DQ(1, 1) - sum1 * RCAP sum2 = DQ(1,JNP) + sum2 * RCAP do i=1,IMR DQ(i, 1) = sum1 DQ(i,JNP) = sum2 enddo ! if(j1.ne.2) then do i=1,IMR DQ(i, 2) = sum1 DQ(i,JMR) = sum2 enddo endif ! return end subroutine ytp ! subroutine ymist(IMR,JNP,j1,P,DC,ID) implicit none integer :: IMR,JNP,j1,ID real,parameter :: R24 = 1./24. real :: P(IMR,JNP),DC(IMR,JNP) integer :: iimh,jmr,ijm3,imh,i real :: pmax,pmin,tmp ! IMH = IMR / 2 JMR = JNP - 1 IJM3 = IMR*(JMR-3) ! IF(ID.EQ.2) THEN do i=1,IMR*(JMR-1) tmp = 0.25*(p(i,3) - p(i,1)) Pmax = max(p(i,1),p(i,2),p(i,3)) - p(i,2) Pmin = p(i,2) - min(p(i,1),p(i,2),p(i,3)) DC(i,2) = sign(min(abs(tmp),Pmin,Pmax),tmp) end do ELSE do i=1,IMH ! J=2 tmp = (8.*(p(i,3) - p(i,1)) + p(i+IMH,2) - p(i,4))*R24 Pmax = max(p(i,1),p(i,2),p(i,3)) - p(i,2) Pmin = p(i,2) - min(p(i,1),p(i,2),p(i,3)) DC(i,2) = sign(min(abs(tmp),Pmin,Pmax),tmp) ! J=JMR tmp=(8.*(p(i,JNP)-p(i,JMR-1))+p(i,JMR-2)-p(i+IMH,JMR))*R24 Pmax = max(p(i,JMR-1),p(i,JMR),p(i,JNP)) - p(i,JMR) Pmin = p(i,JMR) - min(p(i,JMR-1),p(i,JMR),p(i,JNP)) DC(i,JMR) = sign(min(abs(tmp),Pmin,Pmax),tmp) end do do i=IMH+1,IMR ! J=2 tmp = (8.*(p(i,3) - p(i,1)) + p(i-IMH,2) - p(i,4))*R24 Pmax = max(p(i,1),p(i,2),p(i,3)) - p(i,2) Pmin = p(i,2) - min(p(i,1),p(i,2),p(i,3)) DC(i,2) = sign(min(abs(tmp),Pmin,Pmax),tmp) ! J=JMR tmp=(8.*(p(i,JNP)-p(i,JMR-1))+p(i,JMR-2)-p(i-IMH,JMR))*R24 Pmax = max(p(i,JMR-1),p(i,JMR),p(i,JNP)) - p(i,JMR) Pmin = p(i,JMR) - min(p(i,JMR-1),p(i,JMR),p(i,JNP)) DC(i,JMR) = sign(min(abs(tmp),Pmin,Pmax),tmp) end do ! do i=1,IJM3 tmp = (8.*(p(i,4) - p(i,2)) + p(i,1) - p(i,5))*R24 Pmax = max(p(i,2),p(i,3),p(i,4)) - p(i,3) Pmin = p(i,3) - min(p(i,2),p(i,3),p(i,4)) DC(i,3) = sign(min(abs(tmp),Pmin,Pmax),tmp) end do ENDIF ! if(j1.ne.2) then do i=1,IMR DC(i,1) = 0. DC(i,JNP) = 0. enddo else ! Determine slopes in polar caps for scalars! ! do i=1,IMH ! South tmp = 0.25*(p(i,2) - p(i+imh,2)) Pmax = max(p(i,2),p(i,1), p(i+imh,2)) - p(i,1) Pmin = p(i,1) - min(p(i,2),p(i,1), p(i+imh,2)) DC(i,1)=sign(min(abs(tmp),Pmax,Pmin),tmp) ! North. tmp = 0.25*(p(i+imh,JMR) - p(i,JMR)) Pmax = max(p(i+imh,JMR),p(i,jnp), p(i,JMR)) - p(i,JNP) Pmin = p(i,JNP) - min(p(i+imh,JMR),p(i,jnp), p(i,JMR)) DC(i,JNP) = sign(min(abs(tmp),Pmax,pmin),tmp) end do ! do i=imh+1,IMR DC(i, 1) = - DC(i-imh, 1) DC(i,JNP) = - DC(i-imh,JNP) end do endif return end subroutine ymist ! subroutine fyppm(VC,P,DC,flux,IMR,JNP,j1,j2,A6,AR,AL,JORD) implicit none integer :: IMR,JNP,j1,j2,JORD real,parameter :: R3 = 1./3., R23 = 2./3. real :: VC(IMR,*),flux(IMR,*),P(IMR,*),DC(IMR,*) ! Local work arrays. real :: AR(IMR,JNP),AL(IMR,JNP),A6(IMR,JNP) integer :: LMT,i integer :: IMH,JMR,j11,IMJM1,len ! logical first ! data first /.true./ ! SAVE LMT ! IMH = IMR / 2 JMR = JNP - 1 j11 = j1-1 IMJM1 = IMR*(J2-J1+2) len = IMR*(J2-J1+3) ! if(first) then ! IF(JORD.LE.0) then ! if(JMR.GE.90) then ! LMT = 0 ! elseif(JMR.GE.45) then ! LMT = 1 ! else ! LMT = 2 ! endif ! else ! LMT = JORD - 3 ! endif ! ! first = .false. ! endif ! ! modifs pour pouvoir choisir plusieurs schemas PPM LMT = JORD - 3 ! DO i=1,IMR*JMR AL(i,2) = 0.5*(p(i,1)+p(i,2)) + (DC(i,1) - DC(i,2))*R3 AR(i,1) = AL(i,2) END DO ! !Poles: ! DO i=1,IMH AL(i,1) = AL(i+IMH,2) AL(i+IMH,1) = AL(i,2) ! AR(i,JNP) = AR(i+IMH,JMR) AR(i+IMH,JNP) = AR(i,JMR) ENDDO !cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc ! Rajout pour LMDZ.3.3 !ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc AR(IMR,1)=AL(1,1) AR(IMR,JNP)=AL(1,JNP) !cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc do i=1,len A6(i,j11) = 3.*(p(i,j11)+p(i,j11) - (AL(i,j11)+AR(i,j11))) end do ! if(LMT.le.2) call lmtppm(DC(1,j11),A6(1,j11),AR(1,j11) & ,AL(1,j11),P(1,j11),len,LMT) ! DO i=1,IMJM1 IF(VC(i,j1).GT.0.) then flux(i,j1) = AR(i,j11) + 0.5*VC(i,j1)*(AL(i,j11) - AR(i,j11) + & A6(i,j11)*(1.-R23*VC(i,j1)) ) else flux(i,j1) = AL(i,j1) - 0.5*VC(i,j1)*(AR(i,j1) - AL(i,j1) + & A6(i,j1)*(1.+R23*VC(i,j1))) endif END DO return end subroutine fyppm ! subroutine yadv(IMR,JNP,j1,j2,p,VA,ady,wk,IAD) implicit none integer :: IMR,JNP,j1,j2,IAD REAL :: p(IMR,JNP),ady(IMR,JNP),VA(IMR,JNP) REAL :: WK(IMR,-1:JNP+2) INTEGER :: JMR,IMH,i,j,jp REAL :: rv,a1,b1,sum1,sum2 ! JMR = JNP-1 IMH = IMR/2 do j=1,JNP do i=1,IMR wk(i,j) = p(i,j) enddo enddo ! Poles: do i=1,IMH wk(i, -1) = p(i+IMH,3) wk(i+IMH,-1) = p(i,3) wk(i, 0) = p(i+IMH,2) wk(i+IMH,0) = p(i,2) wk(i,JNP+1) = p(i+IMH,JMR) wk(i+IMH,JNP+1) = p(i,JMR) wk(i,JNP+2) = p(i+IMH,JNP-2) wk(i+IMH,JNP+2) = p(i,JNP-2) enddo ! write(*,*) 'toto 1' ! -------------------------------- IF(IAD.eq.2) then do j=j1-1,j2+1 do i=1,IMR ! write(*,*) 'avt NINT','i=',i,'j=',j JP = NINT(VA(i,j)) rv = JP - VA(i,j) ! write(*,*) 'VA=',VA(i,j), 'JP1=',JP,'rv=',rv JP = j - JP ! write(*,*) 'JP2=',JP a1 = 0.5*(wk(i,jp+1)+wk(i,jp-1)) - wk(i,jp) b1 = 0.5*(wk(i,jp+1)-wk(i,jp-1)) ! write(*,*) 'a1=',a1,'b1=',b1 ady(i,j) = wk(i,jp) + rv*(a1*rv + b1) - wk(i,j) enddo enddo ! write(*,*) 'toto 2' ! ELSEIF(IAD.eq.1) then do j=j1-1,j2+1 do i=1,imr JP = REAL(j)-VA(i,j) ady(i,j) = VA(i,j)*(wk(i,jp)-wk(i,jp+1)) enddo enddo ENDIF ! if(j1.ne.2) then sum1 = 0. sum2 = 0. do i=1,imr sum1 = sum1 + ady(i,2) sum2 = sum2 + ady(i,JMR) enddo sum1 = sum1 / IMR sum2 = sum2 / IMR ! do i=1,imr ady(i, 2) = sum1 ady(i,JMR) = sum2 ady(i, 1) = sum1 ady(i,JNP) = sum2 enddo else ! Poles: sum1 = 0. sum2 = 0. do i=1,imr sum1 = sum1 + ady(i,1) sum2 = sum2 + ady(i,JNP) enddo sum1 = sum1 / IMR sum2 = sum2 / IMR ! do i=1,imr ady(i, 1) = sum1 ady(i,JNP) = sum2 enddo endif ! return end subroutine yadv ! subroutine xadv(IMR,JNP,j1,j2,p,UA,JS,JN,IML,adx,IAD) implicit none INTEGER :: IMR,JNP,j1,j2,JS,JN,IML,IAD REAL :: p(IMR,JNP),adx(IMR,JNP),qtmp(-IMR:IMR+IMR),UA(IMR,JNP) INTEGER :: JMR,j,i,ip,iu,iiu REAL :: ru,a1,b1 ! JMR = JNP-1 do j=j1,j2 if(J.GT.JS .and. J.LT.JN) GO TO 1309 ! do i=1,IMR qtmp(i) = p(i,j) enddo ! do i=-IML,0 qtmp(i) = p(IMR+i,j) qtmp(IMR+1-i) = p(1-i,j) enddo ! IF(IAD.eq.2) THEN DO i=1,IMR IP = NINT(UA(i,j)) ru = IP - UA(i,j) IP = i - IP a1 = 0.5*(qtmp(ip+1)+qtmp(ip-1)) - qtmp(ip) b1 = 0.5*(qtmp(ip+1)-qtmp(ip-1)) adx(i,j) = qtmp(ip) + ru*(a1*ru + b1) enddo ELSEIF(IAD.eq.1) then DO i=1,IMR iu = UA(i,j) ru = UA(i,j) - iu iiu = i-iu if(UA(i,j).GE.0.) then adx(i,j) = qtmp(iiu)+ru*(qtmp(iiu-1)-qtmp(iiu)) else adx(i,j) = qtmp(iiu)+ru*(qtmp(iiu)-qtmp(iiu+1)) endif enddo ENDIF ! do i=1,IMR adx(i,j) = adx(i,j) - p(i,j) enddo 1309 continue end do ! ! Eulerian upwind ! do j=JS+1,JN-1 ! do i=1,IMR qtmp(i) = p(i,j) enddo ! qtmp(0) = p(IMR,J) qtmp(IMR+1) = p(1,J) ! IF(IAD.eq.2) THEN qtmp(-1) = p(IMR-1,J) qtmp(IMR+2) = p(2,J) do i=1,imr IP = NINT(UA(i,j)) ru = IP - UA(i,j) IP = i - IP a1 = 0.5*(qtmp(ip+1)+qtmp(ip-1)) - qtmp(ip) b1 = 0.5*(qtmp(ip+1)-qtmp(ip-1)) adx(i,j) = qtmp(ip)- p(i,j) + ru*(a1*ru + b1) enddo ELSEIF(IAD.eq.1) then ! 1st order DO i=1,IMR IP = i - UA(i,j) adx(i,j) = UA(i,j)*(qtmp(ip)-qtmp(ip+1)) enddo ENDIF enddo ! if(j1.ne.2) then do i=1,IMR adx(i, 2) = 0. adx(i,JMR) = 0. enddo endif ! set cross term due to x-adv at the poles to zero. do i=1,IMR adx(i, 1) = 0. adx(i,JNP) = 0. enddo return end subroutine xadv ! subroutine lmtppm(DC,A6,AR,AL,P,IM,LMT) implicit none ! ! A6 = CURVATURE OF THE TEST PARABOLA ! AR = RIGHT EDGE VALUE OF THE TEST PARABOLA ! AL = LEFT EDGE VALUE OF THE TEST PARABOLA ! DC = 0.5 * MISMATCH ! P = CELL-AVERAGED VALUE ! IM = VECTOR LENGTH ! ! OPTIONS: ! ! LMT = 0: FULL MONOTONICITY ! LMT = 1: SEMI-MONOTONIC CONSTRAINT (NO UNDERSHOOTS) ! LMT = 2: POSITIVE-DEFINITE CONSTRAINT ! real,parameter :: R12 = 1./12. real :: A6(IM),AR(IM),AL(IM),P(IM),DC(IM) integer :: IM,LMT INTEGER :: i REAL :: da1,da2,a6da,fmin ! if(LMT.eq.0) then ! Full constraint do i=1,IM if(DC(i).eq.0.) then AR(i) = p(i) AL(i) = p(i) A6(i) = 0. else da1 = AR(i) - AL(i) da2 = da1**2 A6DA = A6(i)*da1 if(A6DA .lt. -da2) then A6(i) = 3.*(AL(i)-p(i)) AR(i) = AL(i) - A6(i) elseif(A6DA .gt. da2) then A6(i) = 3.*(AR(i)-p(i)) AL(i) = AR(i) - A6(i) endif endif end do elseif(LMT.eq.1) then ! Semi-monotonic constraint do i=1,IM if(abs(AR(i)-AL(i)) .GE. -A6(i)) go to 150 if(p(i).lt.AR(i) .and. p(i).lt.AL(i)) then AR(i) = p(i) AL(i) = p(i) A6(i) = 0. elseif(AR(i) .gt. AL(i)) then A6(i) = 3.*(AL(i)-p(i)) AR(i) = AL(i) - A6(i) else A6(i) = 3.*(AR(i)-p(i)) AL(i) = AR(i) - A6(i) endif 150 continue end do elseif(LMT.eq.2) then do i=1,IM if(abs(AR(i)-AL(i)) .GE. -A6(i)) go to 250 fmin = p(i) + 0.25*(AR(i)-AL(i))**2/A6(i) + A6(i)*R12 if(fmin.ge.0.) go to 250 if(p(i).lt.AR(i) .and. p(i).lt.AL(i)) then AR(i) = p(i) AL(i) = p(i) A6(i) = 0. elseif(AR(i) .gt. AL(i)) then A6(i) = 3.*(AL(i)-p(i)) AR(i) = AL(i) - A6(i) else A6(i) = 3.*(AR(i)-p(i)) AL(i) = AR(i) - A6(i) endif 250 continue end do endif return end subroutine lmtppm ! subroutine A2C(U,V,IMR,JMR,j1,j2,CRX,CRY,dtdx5,DTDY5) implicit none integer :: IMR,JMR,j1,j2 real :: U(IMR,*),V(IMR,*),CRX(IMR,*),CRY(IMR,*),DTDX5(*),DTDY5 integer :: i,j ! do j=j1,j2 do i=2,IMR CRX(i,J) = dtdx5(j)*(U(i,j)+U(i-1,j)) end do end do ! do j=j1,j2 CRX(1,J) = dtdx5(j)*(U(1,j)+U(IMR,j)) end do ! do i=1,IMR*JMR CRY(i,2) = DTDY5*(V(i,2)+V(i,1)) end do return end subroutine a2c ! subroutine cosa(cosp,cose,JNP,PI,DP) implicit none integer :: JNP real :: cosp(*),cose(*),PI,DP integer :: JMR,j,jeq real :: ph5 JMR = JNP-1 do j=2,JNP ph5 = -0.5*PI + (REAL(J-1)-0.5)*DP cose(j) = cos(ph5) end do ! JEQ = (JNP+1) / 2 if(JMR .eq. 2*(JMR/2) ) then do j=JNP, JEQ+1, -1 cose(j) = cose(JNP+2-j) enddo else ! cell edge at equator. cose(JEQ+1) = 1. do j=JNP, JEQ+2, -1 cose(j) = cose(JNP+2-j) enddo endif ! do j=2,JMR cosp(j) = 0.5*(cose(j)+cose(j+1)) end do cosp(1) = 0. cosp(JNP) = 0. return end subroutine cosa ! subroutine cosc(cosp,cose,JNP,PI,DP) implicit none integer :: JNP real :: cosp(*),cose(*),PI,DP real :: phi integer :: j ! phi = -0.5*PI do j=2,JNP-1 phi = phi + DP cosp(j) = cos(phi) end do cosp( 1) = 0. cosp(JNP) = 0. ! do j=2,JNP cose(j) = 0.5*(cosp(j)+cosp(j-1)) end do ! do j=2,JNP-1 cosp(j) = 0.5*(cose(j)+cose(j+1)) end do return end subroutine cosc ! SUBROUTINE qckxyz (Q,qtmp,IMR,JNP,NLAY,j1,j2,cosp,acosp, & cross,IC,NSTEP) ! real,parameter :: tiny = 1.E-60 INTEGER :: IMR,JNP,NLAY,j1,j2,IC,NSTEP REAL :: Q(IMR,JNP,NLAY),qtmp(IMR,JNP),cosp(*),acosp(*) logical :: cross INTEGER :: NLAYM1,len,ip,L,icr,ipy,ipx,i real :: qup,qly,dup,sum ! NLAYM1 = NLAY-1 len = IMR*(j2-j1+1) ip = 0 ! ! Top layer L = 1 icr = 1 call filns(q(1,1,L),IMR,JNP,j1,j2,cosp,acosp,ipy,tiny) if(ipy.eq.0) goto 50 call filew(q(1,1,L),qtmp,IMR,JNP,j1,j2,ipx,tiny) if(ipx.eq.0) goto 50 ! if(cross) then call filcr(q(1,1,L),IMR,JNP,j1,j2,cosp,acosp,icr,tiny) endif if(icr.eq.0) goto 50 ! ! Vertical filling... do i=1,len IF( Q(i,j1,1).LT.0.) THEN ip = ip + 1 Q(i,j1,2) = Q(i,j1,2) + Q(i,j1,1) Q(i,j1,1) = 0. endif enddo ! 50 continue DO L = 2,NLAYM1 icr = 1 ! call filns(q(1,1,L),IMR,JNP,j1,j2,cosp,acosp,ipy,tiny) if(ipy.eq.0) goto 225 call filew(q(1,1,L),qtmp,IMR,JNP,j1,j2,ipx,tiny) if(ipx.eq.0) go to 225 if(cross) then call filcr(q(1,1,L),IMR,JNP,j1,j2,cosp,acosp,icr,tiny) endif if(icr.eq.0) goto 225 ! do i=1,len IF( Q(I,j1,L).LT.0.) THEN ! ip = ip + 1 ! From above qup = Q(I,j1,L-1) qly = -Q(I,j1,L) dup = min(qly,qup) Q(I,j1,L-1) = qup - dup Q(I,j1,L ) = dup-qly ! Below Q(I,j1,L+1) = Q(I,j1,L+1) + Q(I,j1,L) Q(I,j1,L) = 0. ENDIF ENDDO 225 CONTINUE END DO ! ! BOTTOM LAYER sum = 0. L = NLAY ! call filns(q(1,1,L),IMR,JNP,j1,j2,cosp,acosp,ipy,tiny) if(ipy.eq.0) goto 911 call filew(q(1,1,L),qtmp,IMR,JNP,j1,j2,ipx,tiny) if(ipx.eq.0) goto 911 ! call filcr(q(1,1,L),IMR,JNP,j1,j2,cosp,acosp,icr,tiny) if(icr.eq.0) goto 911 ! DO I=1,len IF( Q(I,j1,L).LT.0.) THEN ip = ip + 1 ! ! From above ! qup = Q(I,j1,NLAYM1) qly = -Q(I,j1,L) dup = min(qly,qup) Q(I,j1,NLAYM1) = qup - dup ! From "below" the surface. sum = sum + qly-dup Q(I,j1,L) = 0. ENDIF ENDDO ! 911 continue ! if(ip.gt.IMR) then write(6,*) 'IC=',IC,' STEP=',NSTEP, & ' Vertical filling pts=',ip endif ! if(sum.gt.1.e-25) then write(6,*) IC,NSTEP,' Mass source from the ground=',sum endif RETURN END SUBROUTINE qckxyz ! subroutine filcr(q,IMR,JNP,j1,j2,cosp,acosp,icr,tiny) implicit none integer :: IMR,JNP,j1,j2,icr real :: q(IMR,*),cosp(*),acosp(*),tiny integer :: i,j real :: dq,dn,d0,d1,ds,d2 icr = 0 do j=j1+1,j2-1 DO i=1,IMR-1 IF(q(i,j).LT.0.) THEN icr = 1 dq = - q(i,j)*cosp(j) ! N-E dn = q(i+1,j+1)*cosp(j+1) d0 = max(0.,dn) d1 = min(dq,d0) q(i+1,j+1) = (dn - d1)*acosp(j+1) dq = dq - d1 ! S-E ds = q(i+1,j-1)*cosp(j-1) d0 = max(0.,ds) d2 = min(dq,d0) q(i+1,j-1) = (ds - d2)*acosp(j-1) q(i,j) = (d2 - dq)*acosp(j) + tiny endif 50 CONTINUE END DO if(icr.eq.0 .and. q(IMR,j).ge.0.) goto 65 DO i=2,IMR IF(q(i,j).LT.0.) THEN icr = 1 dq = - q(i,j)*cosp(j) ! N-W dn = q(i-1,j+1)*cosp(j+1) d0 = max(0.,dn) d1 = min(dq,d0) q(i-1,j+1) = (dn - d1)*acosp(j+1) dq = dq - d1 ! S-W ds = q(i-1,j-1)*cosp(j-1) d0 = max(0.,ds) d2 = min(dq,d0) q(i-1,j-1) = (ds - d2)*acosp(j-1) q(i,j) = (d2 - dq)*acosp(j) + tiny endif END DO ! ***************************************** ! i=1 i=1 IF(q(i,j).LT.0.) THEN icr = 1 dq = - q(i,j)*cosp(j) ! N-W dn = q(IMR,j+1)*cosp(j+1) d0 = max(0.,dn) d1 = min(dq,d0) q(IMR,j+1) = (dn - d1)*acosp(j+1) dq = dq - d1 ! S-W ds = q(IMR,j-1)*cosp(j-1) d0 = max(0.,ds) d2 = min(dq,d0) q(IMR,j-1) = (ds - d2)*acosp(j-1) q(i,j) = (d2 - dq)*acosp(j) + tiny endif ! ***************************************** ! i=IMR i=IMR IF(q(i,j).LT.0.) THEN icr = 1 dq = - q(i,j)*cosp(j) ! N-E dn = q(1,j+1)*cosp(j+1) d0 = max(0.,dn) d1 = min(dq,d0) q(1,j+1) = (dn - d1)*acosp(j+1) dq = dq - d1 ! S-E ds = q(1,j-1)*cosp(j-1) d0 = max(0.,ds) d2 = min(dq,d0) q(1,j-1) = (ds - d2)*acosp(j-1) q(i,j) = (d2 - dq)*acosp(j) + tiny endif ! ***************************************** 65 continue end do ! do i=1,IMR if(q(i,j1).lt.0. .or. q(i,j2).lt.0.) then icr = 1 goto 80 endif enddo ! 80 continue ! if(q(1,1).lt.0. .or. q(1,jnp).lt.0.) then icr = 1 endif ! return end subroutine filcr ! subroutine filns(q,IMR,JNP,j1,j2,cosp,acosp,ipy,tiny) implicit none integer :: IMR,JNP,j1,j2,ipy real :: q(IMR,*),cosp(*),acosp(*),tiny real :: DP,CAP1,dq,dn,d0,d1,ds,d2 INTEGER :: i,j ! logical first ! data first /.true./ ! save cap1 ! ! if(first) then DP = 4.*ATAN(1.)/REAL(JNP-1) CAP1 = IMR*(1.-COS((j1-1.5)*DP))/DP ! first = .false. ! endif ! ipy = 0 do j=j1+1,j2-1 DO i=1,IMR IF(q(i,j).LT.0.) THEN ipy = 1 dq = - q(i,j)*cosp(j) ! North dn = q(i,j+1)*cosp(j+1) d0 = max(0.,dn) d1 = min(dq,d0) q(i,j+1) = (dn - d1)*acosp(j+1) dq = dq - d1 ! South ds = q(i,j-1)*cosp(j-1) d0 = max(0.,ds) d2 = min(dq,d0) q(i,j-1) = (ds - d2)*acosp(j-1) q(i,j) = (d2 - dq)*acosp(j) + tiny endif END DO end do ! do i=1,imr IF(q(i,j1).LT.0.) THEN ipy = 1 dq = - q(i,j1)*cosp(j1) ! North dn = q(i,j1+1)*cosp(j1+1) d0 = max(0.,dn) d1 = min(dq,d0) q(i,j1+1) = (dn - d1)*acosp(j1+1) q(i,j1) = (d1 - dq)*acosp(j1) + tiny endif enddo ! j = j2 do i=1,imr IF(q(i,j).LT.0.) THEN ipy = 1 dq = - q(i,j)*cosp(j) ! South ds = q(i,j-1)*cosp(j-1) d0 = max(0.,ds) d2 = min(dq,d0) q(i,j-1) = (ds - d2)*acosp(j-1) q(i,j) = (d2 - dq)*acosp(j) + tiny endif enddo ! ! Check Poles. if(q(1,1).lt.0.) then dq = q(1,1)*cap1/REAL(IMR)*acosp(j1) do i=1,imr q(i,1) = 0. q(i,j1) = q(i,j1) + dq if(q(i,j1).lt.0.) ipy = 1 enddo endif ! if(q(1,JNP).lt.0.) then dq = q(1,JNP)*cap1/REAL(IMR)*acosp(j2) do i=1,imr q(i,JNP) = 0. q(i,j2) = q(i,j2) + dq if(q(i,j2).lt.0.) ipy = 1 enddo endif ! return end subroutine filns ! subroutine filew(q,qtmp,IMR,JNP,j1,j2,ipx,tiny) implicit none integer :: IMR,JNP,j1,j2,ipx real :: q(IMR,*),qtmp(JNP,IMR),tiny integer :: i,j real :: d0,d1,d2 ! ipx = 0 ! Copy & swap direction for vectorization. do i=1,imr do j=j1,j2 qtmp(j,i) = q(i,j) end do end do ! do i=2,imr-1 do j=j1,j2 if(qtmp(j,i).lt.0.) then ipx = 1 ! west d0 = max(0.,qtmp(j,i-1)) d1 = min(-qtmp(j,i),d0) qtmp(j,i-1) = qtmp(j,i-1) - d1 qtmp(j,i) = qtmp(j,i) + d1 ! east d0 = max(0.,qtmp(j,i+1)) d2 = min(-qtmp(j,i),d0) qtmp(j,i+1) = qtmp(j,i+1) - d2 qtmp(j,i) = qtmp(j,i) + d2 + tiny endif end do end do ! i=1 do j=j1,j2 if(qtmp(j,i).lt.0.) then ipx = 1 ! west d0 = max(0.,qtmp(j,imr)) d1 = min(-qtmp(j,i),d0) qtmp(j,imr) = qtmp(j,imr) - d1 qtmp(j,i) = qtmp(j,i) + d1 ! east d0 = max(0.,qtmp(j,i+1)) d2 = min(-qtmp(j,i),d0) qtmp(j,i+1) = qtmp(j,i+1) - d2 ! qtmp(j,i) = qtmp(j,i) + d2 + tiny endif 65 continue end do i=IMR do j=j1,j2 if(qtmp(j,i).lt.0.) then ipx = 1 ! west d0 = max(0.,qtmp(j,i-1)) d1 = min(-qtmp(j,i),d0) qtmp(j,i-1) = qtmp(j,i-1) - d1 qtmp(j,i) = qtmp(j,i) + d1 ! east d0 = max(0.,qtmp(j,1)) d2 = min(-qtmp(j,i),d0) qtmp(j,1) = qtmp(j,1) - d2 ! qtmp(j,i) = qtmp(j,i) + d2 + tiny endif end do ! if(ipx.ne.0) then do j=j1,j2 do i=1,imr q(i,j) = qtmp(j,i) end do end do else ! ! Poles. if(q(1,1).lt.0.OR. q(1,JNP).lt.0.) ipx = 1 endif return end subroutine filew ! subroutine zflip(q,im,km,nc) implicit none ! This routine flip the array q (in the vertical). integer :: im,km,nc real :: q(im,km,nc) ! local dynamic array real :: qtmp(im,km) integer :: IC,k,i ! do IC = 1, nc ! do k=1,km do i=1,im qtmp(i,k) = q(i,km+1-k,IC) end do end do ! do i=1,im*km q(i,1,IC) = qtmp(i,1) 2000 continue end do end do return end subroutine zflip