Changeset 5607


Ignore:
Timestamp:
Apr 9, 2025, 9:47:14 PM (2 months ago)
Author:
dcugnet
Message:

Rewritten dynamical tropopause routine:

  • accomodate very low tropopauses
  • no weird indexes used (even overwritten) => no failure in debug compilaton mode
  • general binomial filter for vertical profiles smoothing with adjsutable width
  • correct order for loops on cells (at the cost of a 2D indexes table)
File:
1 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified LMDZ6/trunk/libf/phylmd/tropopause_m.f90 ΒΆ

    r5578 r5607  
    11MODULE tropopause_m
    22
    3   USE yomcst_mod_h
    4 IMPLICIT NONE
     3  IMPLICIT NONE
     4
    55  PRIVATE
     6
    67  PUBLIC :: dyn_tropopause
    78
     9  REAL,    PARAMETER :: DynPTrMin = 8.E+3                  !--- Dyn tropopause pressures < DynPTrMin are set to DynPTrMin  (Pa)
     10  REAL,    PARAMETER :: DynPTrMax = 4.E+4                  !--- Dyn tropopause pressures > DynPTrMax are set to DynPTrMax  (Pa)
     11  REAL,    PARAMETER :: theta0 = 380.                      !--- Default threshold for theta-defined tropopause              (K)
     12  REAL,    PARAMETER :: pVort0 = 2.0                       !--- Default threshold for PV-defined tropopause               (PVU)
     13  REAL,    PARAMETER :: sg0  = 0.75                        !--- Bottom->top PV=pv0e search loop starts at sigma=sg0 level
     14  INTEGER, PARAMETER :: nadj = 3                           !--- Threshold must be exceeded on nadj adjacent levels
     15  INTEGER, PARAMETER :: ns   = 2                           !--- Number of neighbours used each side for vertical smoothing
     16
    817CONTAINS
    918
    10 !-------------------------------------------------------------------------------
    11 !
    12 FUNCTION dyn_tropopause(t, ts, paprs, pplay, rot, itrop, thet0, pvor0)
    13 !
    14 !-------------------------------------------------------------------------------
     19!===============================================================================================================================
     20FUNCTION dyn_tropopause(t, ts, paprs, pplay, rot, itrop, thet0, potV0) RESULT(pTrop)
    1521  USE assert_m,     ONLY: assert
    1622  USE assert_eq_m,  ONLY: assert_eq
    1723  USE dimphy,       ONLY: klon, klev
    1824  USE geometry_mod, ONLY: latitude_deg, longitude_deg
    19   USE vertical_layers_mod, ONLY: aps, bps, preff
     25  USE strings_mod,  ONLY: maxlen
     26  USE yomcst_mod_h, ONLY: ROMEGA, RPI, RKAPPA, RG
     27  USE vertical_layers_mod,    ONLY: aps, bps, preff
    2028  USE lmdz_reprobus_wrappers, ONLY: itroprep
    21   USE lmdz_cppkeys_wrapper, ONLY: CPPKEY_REPROBUS
    22   USE print_control_mod, ONLY: lunout
    23 
    24 !-------------------------------------------------------------------------------
    25 ! Arguments:
    26   REAL ::     dyn_tropopause(klon) !--- Pressure at tropopause
    27   REAL, INTENT(IN)  ::      t(:,:) !--- Cells-centers temperature
    28   REAL, INTENT(IN)  ::     ts(:)   !--- Surface       temperature
    29   REAL, INTENT(IN)  ::  paprs(:,:) !--- Cells-edges   pressure
    30   REAL, INTENT(IN)  ::  pplay(:,:) !--- Cells-centers pressure
    31   REAL, INTENT(IN)  ::    rot(:,:) !--- Cells-centers relative vorticity
    32   INTEGER, INTENT(OUT), OPTIONAL :: itrop(klon) !--- Last tropospheric layer idx
    33   REAL,    INTENT(IN),  OPTIONAL :: thet0, pvor0
    34 !-------------------------------------------------------------------------------
    35 ! Local variables:
    36 
    37   REAL, PARAMETER :: DynPTrMin =8.E+3 !--- Thresholds for minimum and maximum
    38   REAL, PARAMETER :: DynPTrMax =4.E+4 !    dynamical tropopause pressure (Pa).
    39   CHARACTER(LEN=80)  :: sub
    40   INTEGER :: i, k, kb, kt, kp, ib, ie, nw
    41   REAL    :: al, th0, pv0
    42   REAL,    DIMENSION(klon,klev) :: tpot_cen, tpot_edg, pvor_cen
    43   REAL,    PARAMETER :: sg0=0.75  !--- Start level for PV=cte search loop
    44   INTEGER, PARAMETER :: nadj=3    !--- Adjacent levs nb for thresholds detection
    45   REAL,    PARAMETER :: w(5)=[0.1,0.25,0.3,0.25,0.1] !--- Vertical smoothing
    46   INTEGER, SAVE :: k0
    47   INTEGER :: savkt
    48   LOGICAL, SAVE :: first=.TRUE.
    49 !$OMP THREADPRIVATE(k0,first)
    50 !-------------------------------------------------------------------------------
    51   sub='dyn_tropopause'
    52   CALL assert(SIZE(t ,1)==klon, TRIM(sub)//" t klon")
    53   CALL assert(SIZE(t ,2)==klev, TRIM(sub)//" t klev")
    54   CALL assert(SIZE(ts,1)==klon, TRIM(sub)//" ts klon")
    55   CALL assert(SHAPE(paprs)==[klon,klev+1],TRIM(sub)//" paprs shape")
    56   CALL assert(SHAPE(pplay)==[klon,klev  ],TRIM(sub)//" pplay shape")
    57   CALL assert(SHAPE(rot)  ==[klon,klev  ],TRIM(sub)//" rot shape")
    58 
    59   !--- DEFAULT THRESHOLDS
    60   th0=380.; IF(PRESENT(thet0)) th0=thet0   !--- In kelvins
    61   pv0=  2.; IF(PRESENT(pvor0)) pv0=pvor0   !--- In PVU
    62   IF(first) THEN
    63     DO k0=1,klev; IF(aps(k0)/preff+bps(k0)<sg0) EXIT; END DO; first=.FALSE.
     29  USE lmdz_cppkeys_wrapper,   ONLY: CPPKEY_REPROBUS
     30  USE mod_phys_lmdz_para,     ONLY: is_master
     31  USE mod_phys_lmdz_transfert_para, ONLY : bcast
     32  IMPLICIT NONE
     33  REAL                           :: pTrop(klon)            !--- Pressure at dynamical tropopause                           (Pa)
     34  REAL,              INTENT(IN)  ::      t(:,:)            !--- Temperature at layers centers                               (K)
     35  REAL,              INTENT(IN)  ::     ts(:)              !--- Temperature on surface layer interface                      (K)
     36  REAL,              INTENT(IN)  ::  paprs(:,:)            !--- Pressure at layers interfaces                              (Pa)
     37  REAL,              INTENT(IN)  ::  pplay(:,:)            !--- Pressure at layers centers                                 (Pa)
     38  REAL,              INTENT(IN)  ::    rot(:,:)            !--- Relative vorticity at layers centers                      (s-1)
     39  INTEGER, OPTIONAL, INTENT(OUT) :: itrop(klon)            !--- Last tropospheric layer idx
     40  REAL,    OPTIONAL, INTENT(IN)  :: thet0                  !--- Potential temperature at the tropopause (tropical region)   (K)
     41  REAL,    OPTIONAL, INTENT(IN)  :: potV0                  !--- Potential vorticity   at the tropopause (rest of globe)   (PVU)
     42!------------------------------------------------------------------------------------------------------------------------------
     43  CHARACTER(LEN=maxlen) :: modname                         !--- Current routine name
     44  REAL                  ::    Temp_edg(klon,klev)          !--- Regular   temperature at layers interfaces (except last one)(K)
     45  REAL                  :: potTemp_edg(klon,klev)          !--- Potential temperature at layers interfaces (except last one)(K)
     46  REAL                  :: potTemp_cen(klon,klev)          !--- Potential temperature at layers centers                     (K)
     47  REAL                  :: potVort_cen(klon,klev)          !--- Potential vorticity   at layers centers                     (K)
     48  REAL                  :: p_th0(klon)                     !--- Pressures at theta=380K                                    (Pa)
     49  REAL                  :: p_pv0(klon)                     !--- Pressures at PV=2PVU                                       (Pa)
     50  REAL                  :: al, th0, pv0                    !--- Interpolation coefficient + potential temp. and PV thresholds
     51  INTEGER               :: i, k, kb, kt, kp, ib, ie, nw, n
     52  INTEGER               :: ith(klon)                       !--- Indices of first TH=380K layers (top -> bottom search)
     53  INTEGER               :: ipv(klon)                       !--- Indices of first PV=2PVU layers (top -> bottom search)
     54  INTEGER               :: ipv0(klon)                      !--- Indices of first PV=2PVU layers (bottom -> top search)
     55  INTEGER               :: ncons(klon)                     !--- Number of consecutive matching values found in vertical loops
     56  INTEGER               :: itr(klon)                       !--- Index of last layer with a center pressure lower than pTrop
     57  INTEGER               :: co(2*ns+1)                      !--- Binomial coefficients used compute smoothing weights "w(:,:)"
     58  INTEGER,           SAVE :: k0                            !--- Start index (sigma=sg0) for 2PVU bottom->top search loop
     59  REAL, ALLOCATABLE, SAVE :: fac(:)                        !--- Coriolis parameter: 2*ROMEGA*SIN(cells centers latitudes) (s-1)
     60  REAL, ALLOCATABLE, SAVE :: w(:,:)                        !--- Coefficients for vertical smoothing froutine "smooth"
     61  LOGICAL,           SAVE :: lFirst = .TRUE.
     62!$OMP THREADPRIVATE(k0, fac, w, lFirst)
     63!------------------------------------------------------------------------------------------------------------------------------
     64  modname = 'dyn_tropopause'
     65  CALL assert(SIZE(t,  DIM=1) == klon,        TRIM(modname)//" t klon")
     66  CALL assert(SIZE(t,  DIM=2) == klev,        TRIM(modname)//" t klev")
     67  CALL assert(SIZE(ts, DIM=1) == klon,        TRIM(modname)//" ts klon")
     68  CALL assert(SHAPE(paprs) == [klon, klev+1], TRIM(modname)//" paprs shape")
     69  CALL assert(SHAPE(pplay) == [klon, klev  ], TRIM(modname)//" pplay shape")
     70  CALL assert(SHAPE(rot)   == [klon, klev  ], TRIM(modname)//" rot shape")
     71
     72  !--- MODIFY THE THRESHOLDS FOR THE DYNAMICAL TROPOPAUSE DEFINITION IN CASE THE CORRESPONDING OPTIONAL ARGUMENTS ARE USED
     73  th0 = theta0; IF(PRESENT(thet0)) th0 = thet0            !--- Potential temperature at the tropopause (tropical region)   (K)
     74  pv0 = pVort0; IF(PRESENT(potV0)) pv0 = potV0            !--- Potential vorticity   at the tropopause (rest of globe)   (PVU)
     75
     76  IF(lFirst) THEN
     77     ALLOCATE(fac(klon), w(ns+1, ns+1))
     78!$OMP BARRIER
     79     IF(is_master) THEN
     80
     81       !--- GET THE INDEX "k0" OF THE FIRST LOWER INTERFACE LAYER WITH SIGMA COORDINATE LOWER THAN "sg0"
     82       !--- NOTE: "k0" DEPENDS ON VERTICAL DISCRETIZATION ONLY (VIA HYBRID COEFFS aps, bps) AND IS NOT SIMULATION-DEPENDENT
     83        DO k0 = 1, klev; IF( aps(k0) / preff + bps(k0) < sg0 ) EXIT; END DO     !--- START INDEX FOR BOTTOM->TOP PV SEARCH LOOP
     84
     85        !--- COMPUTE THE CORIOLIS PARAMETER FOR PV ALCULATION ROUTINE "potentialVorticity"
     86        DO i = 1, klon
     87           fac(:) = 2. * ROMEGA * SIN(latitude_deg(:) * RPI/180.)
     88        END DO
     89
     90        !--- COMPUTE THE WEIGHTS FOR THE VERTICAL SMOOTHING ROUTINE "smooth"
     91        co(:) = 0;  w(:, :) = 0.
     92        co(1) = 1;  w(1, 1) = 1.
     93        DO i = 1, ns
     94           co(2:2*ns+1) = co(2:2*ns+1) + co(1:2*ns)        !--- C(n+1,p+1) = C(n,p+1) + C(n,p)
     95           co(2:2*ns+1) = co(2:2*ns+1) + co(1:2*ns)        !--- C(n+1,p+1) = C(n,p+1) + C(n,p) AGAIN
     96           w(i+1, 1:i+1) = REAL(co(i+1:2*i+1))/REAL(SUM(co(i+1:2*i+1)))
     97        END DO
     98
     99        lFirst=.FALSE.
     100     END IF
     101     CALL bcast(k0)
     102     CALL bcast(fac)
     103     CALL bcast(w)
     104     CALL bcast(lFirst)
    64105  END IF
    65106
    66   !--- POTENTIAL TEMPERATURE AT CELLS CENTERS AND INTERFACES
    67   DO i = 1,klon
    68     tpot_cen(i,1) = t(i,1)*(preff/pplay(i,1))**RKAPPA
    69     tpot_edg(i,1) = ts(i) *(preff/paprs(i,1))**RKAPPA
    70     DO k=2,klev
    71       al = LOG(pplay(i,k-1)/paprs(i,k))/LOG(pplay(i,k-1)/pplay(i,k))
    72       tpot_cen(i,k) =  t(i,k)                        *(preff/pplay(i,k))**RKAPPA
    73       tpot_edg(i,k) = (t(i,k-1)+al*(t(i,k)-t(i,k-1)))*(preff/paprs(i,k))**RKAPPA
    74       !--- FORCE QUANTITIES TO BE GROWING
    75       IF(tpot_edg(i,k)<tpot_edg(i,k-1)) tpot_edg(i,k)=tpot_edg(i,k-1)+1.E-5
    76       IF(tpot_cen(i,k)<tpot_cen(i,k-1)) tpot_cen(i,k)=tpot_cen(i,k-1)+1.E-5
    77     END DO
    78     !--- VERTICAL SMOOTHING
    79     tpot_cen(i,:)=smooth(tpot_cen(i,:),w)
    80     tpot_edg(i,:)=smooth(tpot_edg(i,:),w)
    81   END DO
    82 
    83   !--- ERTEL POTENTIAL VORTICITY AT CELLS CENTERS (except in top layer)
    84   DO i = 1, klon
    85     DO k= 1, klev-1
    86       pvor_cen(i,k)=-1.E6*RG*(rot(i,k)+2.*ROMEGA*SIN(latitude_deg(i)*RPI/180.))&
    87                    * (tpot_edg(i,k+1)-tpot_edg(i,k)) / (paprs(i,k+1)-paprs(i,k))
    88     END DO
    89     !--- VERTICAL SMOOTHING
    90     pvor_cen(i,1:klev-1)=smooth(pvor_cen(i,1:klev-1),w)
    91   END DO
    92 
    93   !--- LOCATE TROPOPAUSE: LOWEST POINT BETWEEN THETA=380K AND PV=2PVU SURFACES.
    94   DO i = 1, klon
    95     !--- UPPER TROPOPAUSE: |PV|=2PVU POINT STARTING FROM TOP
    96 !    DO kt=klev-1,1,-1
    97 !      savkt = kt
    98 !      IF (kt-nadj == 0) THEN
    99 !        WRITE(lunout,*)'ABORT_PHYSIC tropopause_m kt= ',kt
    100 !        call abort_physic("tropopause_m", " kt = nadj", 1)
    101 !      ENDIF
    102 !      IF(ALL(ABS(pvor_cen(i,kt-nadj:kt))<=pv0)) THEN
    103 !        EXIT
    104 !      ENDIF
    105 !    END DO
    106     DO kt=klev-1,nadj+1,-1; savkt = kt; IF(ALL(ABS(pvor_cen(i,kt-nadj:kt))<=pv0))  EXIT; END DO
    107     kt = savkt
    108     !--- LOWER TROPOPAUSE: |PV|=2PVU POINT STARTING FROM BOTTOM
    109     DO kb=k0,klev-1;   IF(ALL(ABS(pvor_cen(i,kb:kb+nadj))> pv0)) EXIT; END DO; kb=kb-1
    110     !--- ISO-THETA POINT: THETA=380K       STARTING FROM TOP
    111     DO kp=klev-1,1,-1; IF(ALL(ABS(tpot_cen(i,kp-nadj:kp))<=th0)) EXIT; END DO
    112     !--- CHOOSE BETWEEN LOWER AND UPPER TROPOPAUSE
    113     IF(2*COUNT(ABS(pvor_cen(i,kb:kt))>pv0)>kt-kb+1) kt=kb
    114     !--- PV-DEFINED TROPOPAUSE
    115     al = (ABS(pvor_cen(i,kt+1))-pv0)/ABS(pvor_cen(i,kt+1)-pvor_cen(i,kt))
    116     dyn_tropopause(i) = pplay(i,kt+1)*(pplay(i,kt)/pplay(i,kt+1))**al
    117     !--- THETA=380K IN THE TROPICAL REGION
    118     al = (tpot_cen(i,kp+1)-th0)/(tpot_cen(i,kp+1)-tpot_cen(i,kp))
    119     dyn_tropopause(i) = MAX( pplay(i,kp+1)*(pplay(i,kp)/pplay(i,kp+1))**al,    &
    120                             dyn_tropopause(i) )
    121     !--- UNREALISTIC VALUES DETECTION
    122     IF(dyn_tropopause(i)<DynPTrMin.OR.dyn_tropopause(i)>DynPTrMax) THEN
    123       dyn_tropopause(i)=MIN(MAX(dyn_tropopause(i),DynPTrMax),DynPTrMin)
    124       DO kt=1,klev-1; IF(pplay(i,kt+1)>dyn_tropopause(i)) EXIT; END DO; kp=kt
    125     END IF
    126 IF (CPPKEY_REPROBUS) THEN
    127     itroprep(i)=MAX(kt,kp)
    128 END IF
    129     !--- LAST TROPOSPHERIC LAYER INDEX NEEDED
    130     IF(PRESENT(itrop)) itrop(i)=MAX(kt,kp)
    131   END DO
     107  !=== DETERMINE THE PRESSURE AT WHICH THETA = th0 ============================================================================
     108  CALL potentialTemperature(pplay, t, potTemp_cen)                             !--- POTENTIAL TEMPERATURE @ LAYERS CENTERS
     109
     110  !--- INDEX OF FIRST LAYERS WITH THETA<380K @ CENTER ON "nadj" CONSECUTIVE LAYERS
     111  CALL getLayerIdx(potTemp_cen, th0, -1, nadj, ith)                            !--- FROM TOP TO BOTTOM
     112
     113  CALL getPressure(potTemp_cen, th0, ith, pplay, paprs, p_th0)                 !--- PRESSURE @ THETA = th0 SURFACE
     114
     115  !=== DETERMINE THE PRESSURE AT WHICH PV = pv0 ===============================================================================
     116  CALL cen2edg(t, ts, pplay, paprs(:,1:klev), temp_edg)                        !--- TEMP @ LAYERS INTERFACES (EXCEPT LAST ONE)
     117
     118  CALL potentialTemperature (paprs(:,1:klev), temp_edg, potTemp_edg)           !--- TPOT @ LAYERS INTERFACES (EXCEPT LAST ONE)
     119
     120  CALL potentialVorticity(rot, potTemp_edg, paprs(:,1:klev), potVort_cen)      !--- ERTEL POTENTIAL VORTICITY @ LAYERS CENTERS
     121
     122  !--- INDEX OF FIRST LAYERS WITH PV<=2PVU @ CENTER ON "nadj" CONSECUTIVE LAYERS
     123  CALL getLayerIdx(potVort_cen, pv0, -1, nadj, ipv)                            !--- FROM TOP TO BOTTOM
     124  CALL getLayerIdx(potVort_cen, pv0, k0, nadj, ipv0)                           !--- FROM LAYER @ sig=sig0 TO TOP
     125  DO i = 1, klon; n = 0                                                        !--- CHOOSE BETWEEN BOTTOM AND TOP INDEX
     126     IF(ipv0(i) == k0-1 .OR. ipv0(i) > ipv(i)) CYCLE                           !--- ipv0 CAN'T BE USED
     127     DO k = ipv0(i), ipv(i); IF(potVort_cen(i, k) > pv0) n = n+1; END DO       !--- NUMBER OF POINTS WITH PV>2PVU
     128     IF(2 * n >= ipv(i)-ipv0(i)+1) ipv(i) = ipv0(i)                            !--- MORE THAN 50% > pv0 => LOWER POINT KEPT
     129  END DO
     130
     131  CALL getPressure(potVort_cen, pv0, ipv, pplay, paprs, p_pv0)                  !--- PRESSURE @ PV = pv0 SURFACE
     132
     133  !=== DETERMINE THE UNFILTERED DYNAMICAL TROPOPAUSE PRESSURE FIELD (LOWER POINT BETWEEN THETA=380K AND PV=2PVU) ==============
     134  DO i = 1, klon
     135     pTrop(i) = MAX(p_th0(i), p_pv0(i))
     136  END DO
     137
     138  !=== FILTER THE PRESSURE FIELD: TOO HIGH AND TOO LOW VALUES ARE CLIPPED =====================================================
     139  DO i = 1, klon
     140     IF(pTrop(i) < DynPTrMin) pTrop(i) = DynPTrMin
     141     IF(pTrop(i) > DynPTrMax) pTrop(i) = DynPTrMax
     142  END DO
     143
     144  !=== LAST VERTICAL INDEX WITH A PRESSURE HIGHER THAN TROPOPAUSE PRESSURE ====================================================
     145  IF(.NOT.(PRESENT(itrop) .OR. CPPKEY_REPROBUS)) RETURN
     146  DO i = 1, klon
     147     DO k = 1, klev
     148        IF(pplay(i,k+1) <= pTrop(i)) EXIT
     149     END DO
     150     IF(PRESENT(itrop )) itrop(i)    = k
     151     IF(CPPKEY_REPROBUS) itroprep(i) = k
     152  END DO
     153
     154CONTAINS
     155
     156!===============================================================================================================================
     157SUBROUTINE cen2edg(v_cen, v0_edg, p_cen, p_edg, v_edg)
     158  IMPLICIT NONE
     159  REAL, DIMENSION(klon, klev), INTENT(IN)  :: v_cen, p_cen, p_edg
     160  REAL, DIMENSION(klon),       INTENT(IN)  :: v0_edg
     161  REAL, DIMENSION(klon, klev), INTENT(OUT) :: v_edg
     162  INTEGER :: i, k
     163  DO i = 1, klon
     164     v_edg(i, 1) = v0_edg(i)
     165  END DO
     166  DO k = 2, klev
     167     DO i = 1, klon
     168        al = LOG(p_edg(i, k-1)/p_cen(i, k)) / LOG(p_cen(i, k-1)/p_cen(i, k))   !--- CENTER -> INTERFACE INTERPOLATION COEFF
     169        v_edg(i, k) = v_cen(i, k-1) + al * (v_cen(i, k) - v_cen(i, k-1))       !--- FIELD AT LAYER INTERFACE
     170     END DO
     171  END DO
     172END SUBROUTINE cen2edg
     173!===============================================================================================================================
     174SUBROUTINE getPressure(v_cen, v0, ix, p_cen, p_int, pre_v0)
     175  IMPLICIT NONE
     176  REAL,    INTENT(IN)  :: v_cen(klon, klev), v0
     177  INTEGER, INTENT(IN)  ::    ix(klon)
     178  REAL,    INTENT(IN)  :: p_cen(klon, klev), p_int(klon, klev+1)
     179  REAL,    INTENT(OUT) :: pre_v0(klon)
     180  REAL    :: al
     181  INTEGER :: i, k
     182  DO i = 1, klon; k = ix(i)
     183     IF(k == 0) THEN
     184        pre_v0(i) = p_int(i,1)
     185     ELSE IF(k == klev) THEN
     186        pre_v0(i) = p_int(i,klev+1)
     187     ELSE
     188        al =  (v0 - v_cen(i, k+1)) / (v_cen(i, k) - v_cen(i, k+1))
     189        pre_v0(i) = p_cen(i, k+1)  * (p_cen(i, k) / p_cen(i, k+1))**al
     190     END IF
     191  END DO
     192END SUBROUTINE getPressure
     193!===============================================================================================================================
     194SUBROUTINE getLayerIdx(v, v0, k0, nadj, ix)
     195! Purpose: Search for the index of the last layer ix(i) with a value v(i,k) lower than or equal to v0.
     196!          At least nadj adjacent layers must satisfy the criterium (less - as much as possible - near top or bottom).
     197!          The search is done from:    * top to bottom if k0 < 0 (from k=klev to k=|k0|)
     198!                                      * bottom to top if k0 > 0 (from k=k0   to k=klev)
     199!          - nominal case: k0 <= ix(i) < klev
     200!          - special case: ix(i) == klev:   ALL(v(i,k0:klev) <= v0)
     201!          - special case: ix(i) == |k0|-1: ALL(v(i,k0:klev) >  v0)
     202  IMPLICIT NONE
     203  REAL,    INTENT(IN)  ::  v(klon, klev), v0
     204  INTEGER, INTENT(IN)  :: k0, nadj
     205  INTEGER, INTENT(OUT) :: ix(klon)
     206  INTEGER :: i, k, nc(klon)
     207  nc(:) = 0
     208  ix(:) = 0
     209  IF(k0 < 0) THEN
     210     !=== SEARCH FROM TOP TO BOTTOM: klev -> -k0
     211     !--- ix(i) depends on nc(i), the number of adjacent layers with v(i,:) <= v0 (k is the index of the last tested layer)
     212     !---  *     nc(i) == nadj   nominal case: enough matching values   => ix(i) = k+nadj-1   (|k0|+nadj-1 <= k <= klev-nadj+1)
     213     !---                     particular case: all values are matching  => ix(i) = klev       (k = klev-nadj+1)
     214     !---  * 0 < nc(i) < nadj  bottom reached: nc<nadj matching values  => ix(i) = k+nc(i)-1  (k = |k0|)
     215     !---  *     nc(i) == 0    bottom reached:      no matching values  => ix(i) = k          (k = |k0|-1)
     216     !--- So ix(i) = MAX(k, k+nc(i)-1) fits for each case.
     217     DO k = klev, -1, -k0
     218        DO i = 1, klon
     219           IF(ix(i) /= 0) CYCLE                                                !--- ADEQUATE LAYER ALREADY FOUND
     220           nc(i) = nc(i) + 1
     221           IF(ABS(v(i, k)) > v0) nc(i) = 0
     222           IF(nc(i) /= nadj) CYCLE                                             !--- nc<nadj ADJACENT LAYERS WITH v<=v0 FOUND
     223           ix(i) = 1                                                           !--- FAKE /=0 VALUE TO SKIP FOLLOWING ITERATIONS
     224        END DO
     225     END DO
     226     DO i = 1, klon
     227        ix(i) = MAX(k, k+nc(i)-1)                                              !--- INDEX OF LOWEST LAYER WITH v<=v0
     228     END DO
     229  ELSE
     230     !=== SEARCH FROM BOTTOM TO TOP: k0 -> klev
     231     !--- ix(i) depends on nc(i), the number of adjacent layers with v(i,:) > v0 (k is the index of the last tested layer)
     232     !---  *     nc(i) == nadj   nominal case: enough matching values   => ix(i) = k-nadj     ( k0 +nadj-1 <= k <= klev-nadj+1)
     233     !---                     particular case: all values are matching  => ix(i) = k0-1       (k = k0+nadj-1)
     234     !---  * 0 < nc(i) < nadj     top reached: nc<nadj matching values  => ix(i) = k-nc(i)    (k = klev)
     235     !---  *     nc(i) == 0       top reached:      no matching values  => ix(i) = k          (k = klev)
     236     !--- So ix(i) = k-nc(i) fits for each case.
     237     DO k = k0, klev
     238        DO i = 1, klon
     239           IF(ix(i) /= 0) CYCLE                                                !--- ADEQUATE LAYER ALREADY FOUND
     240           nc(i) = nc(i) + 1
     241           IF(ABS(v(i, k)) <= v0) nc(i) = 0
     242           IF(nc(i) /= nadj) CYCLE                                             !--- nc<nadj ADJACENT LAYERS WITH v<=v0 FOUND
     243           ix(i) = 1                                                           !--- FAKE /=0 VALUE TO SKIP FOLLOWING ITERATIONS
     244        END DO
     245     END DO
     246     DO i = 1, klon
     247        ix(i) = k-nc(i)                                                        !--- INDEX OF LOWEST LAYER WITH v<=v0
     248     END DO
     249  END IF
     250END SUBROUTINE getLayerIdx
     251!===============================================================================================================================
     252SUBROUTINE potentialTemperature(pre, temp, tPot)
     253  IMPLICIT NONE
     254  REAL, DIMENSION(:, :),                       INTENT(IN)  :: pre, temp
     255  REAL, DIMENSION(SIZE(pre, 1), SIZE(pre, 2)), INTENT(OUT) :: tPot
     256  REAL, ALLOCATABLE :: tmp(:,:)
     257  CHARACTER(LEN=maxlen) :: modname
     258  INTEGER :: i, k, ni, nk
     259  modname = 'potentialTemperature'
     260  ni = SIZE(pre, 1)
     261  nk = SIZE(pre, 2)
     262  CALL assert(SIZE(temp, DIM=1) == ni, TRIM(modname)//" SIZE(temp,1) SIZE(pre,1)")
     263  CALL assert(SIZE(temp, DIM=2) == nk, TRIM(modname)//" SIZE(temp,2) SIZE(pre,2)")
     264  ALLOCATE(tmp(ni, nk))
     265  DO k = 1, nk                                                                 !--- COMPUTE RAW FIELD
     266     DO i = 1, ni
     267        tmp(i, k) = temp(i, k) * (100000. / pre(i, k))**RKAPPA
     268     END DO
     269  END DO
     270  DO k = 2, nk                                                                 !--- ENSURE GROWING FIELD WITH ALTITUDE
     271     DO i = 1, ni
     272        IF(tmp(i, k)< tmp(i, k-1)) tmp(i, k) = tmp(i, k-1) + 1.E-5
     273     END DO
     274  END DO
     275  CALL smooth(tmp, tPot)                                                       !--- FILTER THE FIELD
     276END SUBROUTINE potentialTemperature
     277!===============================================================================================================================
     278SUBROUTINE potentialVorticity(rot_cen, th_int, pint, pVor_cen)
     279  IMPLICIT NONE
     280  REAL, DIMENSION(klon, klev), INTENT(IN)  :: rot_cen, th_int, pint
     281  REAL, DIMENSION(klon, klev), INTENT(OUT) :: pVor_cen
     282  REAL ::     tmp(klon, klev)
     283  INTEGER :: i, k, kp
     284  DO k = 1, klev-1                                                             !--- COMPUTE RAW FIELD
     285     DO i = 1, klon
     286        tmp(i, k) = -1.E6 * RG * (rot_cen(i, k) + fac(i)) * (th_int(i, k+1)-th_int(i, k)) / (pint(i, k+1)-pint(i, k))
     287     END DO
     288  END DO
     289  DO i = 1, klon
     290     tmp(i, klev) = tmp(i, klev-1)
     291  END DO
     292  CALL smooth(tmp, pVor_cen)                                                   !--- FILTER THE FIELD
     293END SUBROUTINE potentialVorticity
     294!===============================================================================================================================
     295SUBROUTINE smooth(v, vs)
     296! Purpose: Vertical smoothing of each profile v(i,:) using 2*ns+1 centered binomial weights (+/- ns points).
     297! Note:    For levels near the bottom (k <= ns) or the top (k > klev-ns), a narrower set of weights (n<ns) is used.
     298!          => in particular, first and last levels are left untouched.
     299  IMPLICIT NONE
     300  REAL,    INTENT(IN)  :: v (klon, klev)
     301  REAL,    INTENT(OUT) :: vs(klon, klev)
     302  INTEGER :: i, j, k
     303  vs(:, :) = 0.
     304  DO k = 1, klev
     305     n = MIN(k-1, klev-k, ns)
     306     DO j = k-n, k+n
     307        DO i = 1, klon
     308           vs(i, k) = vs(i, k) + v(i, j) * w(n+1, 1+ABS(j-k))
     309        END DO
     310     END DO
     311  END DO
     312END SUBROUTINE smooth
    132313
    133314END FUNCTION dyn_tropopause
    134315
    135 
    136 !-------------------------------------------------------------------------------
    137 !
    138 FUNCTION smooth(v,w)
    139 !
    140 !-------------------------------------------------------------------------------
    141 ! Arguments:
    142   REAL, INTENT(IN)         :: v(:), w(:)
    143   REAL, DIMENSION(SIZE(v)) :: smooth
    144 !-------------------------------------------------------------------------------
    145 ! Local variables:
    146   INTEGER :: nv, nw, k, kb, ke, lb, le
    147 !-------------------------------------------------------------------------------
    148   nv=SIZE(v); nw=(SIZE(w)-1)/2
    149   DO k=1,nv
    150     kb=MAX(k-nw,1 ); lb=MAX(2+nw   -k,1)
    151     ke=MIN(k+nw,nv); le=MIN(1+nw+nv-k,1+2*nw)
    152     smooth(k)=SUM(v(kb:ke)*w(lb:le))/SUM(w(lb:le))
    153   END DO
    154 
    155 END FUNCTION smooth
    156 !
    157 !-------------------------------------------------------------------------------
    158 
    159316END MODULE tropopause_m
Note: See TracChangeset for help on using the changeset viewer.