!
! $Id$
!
MODULE grid_noro_m
!
!*******************************************************************************

  USE print_control_mod, ONLY: lunout
  USE assert_eq_m,       ONLY: assert_eq
  PRIVATE
  PUBLIC :: grid_noro, grid_noro0, read_noro


CONTAINS


!-------------------------------------------------------------------------------
!
SUBROUTINE grid_noro(xd,yd,zd,x,y,zphi,zmea,zstd,zsig,zgam,zthe,zpic,zval,mask)
!
!-------------------------------------------------------------------------------
! Author: F. Lott (see also Z.X. Li, A. Harzallah et L. Fairhead)
!-------------------------------------------------------------------------------
! Purpose: Compute the Parameters of the SSO scheme as described in LOTT &MILLER
!         (1997) and LOTT(1999).
!-------------------------------------------------------------------------------
! Comments:
!  * Target points are on a rectangular grid:
!      iim+1 latitudes including North and South Poles;
!      jjm+1 longitudes, with periodicity jjm+1=1.
!  * At the poles, the fields value is repeated jjm+1 time.
!  * The parameters a,b,c,d represent the limits of the target gridpoint region.
!    The means over this region are calculated from USN data, ponderated by a
!    weight proportional to the surface occupated by the data inside the model
!    gridpoint area. In most circumstances, this weight is the ratio between the
!    surfaces of the USN gridpoint area and the model gridpoint area. 
!
!           (c)
!        ----d-----
!        | . . . .|
!        |        |
!     (b)a . * . .b(a)
!        |        |
!        | . . . .|
!        ----c-----
!           (d)
!  * Hard-coded US Navy dataset dimensions (imdp=2160 ; jmdp=1080) have been
!    removed (ALLOCATABLE used).
!  * iext (currently 10% of imdp) represents the margin to ensure output cells
!    on the edge are contained in input cells.
!  * March 2022: modif by E Vignon, V Wiener, L Pauget to apply a detrending 
!                before zstd, zpic, zval calculation
!===============================================================================
  IMPLICIT NONE
!-------------------------------------------------------------------------------
! Arguments:
  REAL, INTENT(IN)  :: xd(:), yd(:)  !--- INPUT  COORDINATES     (imdp) (jmdp)
  REAL, INTENT(IN)  :: zd(:,:)       !--- INPUT  FIELD           (imdp,jmdp)
  REAL, INTENT(IN)  :: x(:), y(:)    !--- OUTPUT COORDINATES     (imar+1) (jmar)
  REAL, INTENT(OUT) :: zphi(:,:)     !--- GEOPOTENTIAL           (imar+1,jmar)
  REAL, INTENT(OUT) :: zmea(:,:)     !--- MEAN OROGRAPHY         (imar+1,jmar)
  REAL, INTENT(OUT) :: zstd(:,:)     !--- STANDARD DEVIATION     (imar+1,jmar)
  REAL, INTENT(OUT) :: zsig(:,:)     !--- SLOPE                  (imar+1,jmar)
  REAL, INTENT(OUT) :: zgam(:,:)     !--- ANISOTROPY             (imar+1,jmar)
  REAL, INTENT(OUT) :: zthe(:,:)     !--- SMALL AXIS ORIENTATION (imar+1,jmar)
  REAL, INTENT(OUT) :: zpic(:,:)     !--- MAXIMUM ALTITITUDE     (imar+1,jmar)
  REAL, INTENT(OUT) :: zval(:,:)     !--- MINIMUM ALTITITUDE     (imar+1,jmar)
  REAL, INTENT(OUT) :: mask(:,:)     !--- MASK                   (imar+1,jmar)
!-------------------------------------------------------------------------------
! Local variables:
  CHARACTER(LEN=256) :: modname="grid_noro"
  REAL, ALLOCATABLE :: xusn(:), yusn(:)           ! dim (imdp+2*iext) (jmdp+2)
  REAL, ALLOCATABLE :: zusn(:,:), planusn(:,:)    ! dim (imdp+2*iext,jmdp+2)
! CORRELATIONS OF OROGRAPHY GRADIENT              ! dim (imar+1,jmar)
  REAL, ALLOCATABLE :: ztz(:,:), zxtzx(:,:), zytzy(:,:), zxtzy(:,:), weight(:,:)
! CORRELATIONS OF USN OROGRAPHY GRADIENTS         ! dim (imar+2*iext,jmdp+2)
  REAL, ALLOCATABLE :: zxtzxusn(:,:), zytzyusn(:,:), zxtzyusn(:,:)
  REAL, ALLOCATABLE :: num_tot(:,:), num_lan(:,:) ! dim (imar+1,jmar)
  REAL, ALLOCATABLE :: a(:), b(:)                 ! dim (imar+1)
  REAL, ALLOCATABLE :: c(:), d(:)                 ! dim (jmar)
  LOGICAL :: masque_lu
  INTEGER :: i, ii, imdp, imar, iext
  INTEGER :: j, jj, jmdp, jmar, nn
  REAL    :: xpi, zdeltax, zlenx, xincr,  zweinor, xk, xl, xm
  REAL    :: rad, zdeltay, zleny, masque, zweisud, xp, xq, xw
! remove mean slope to calculate zstd, zpic and zval
  LOGICAL :: detrend=.true.
  INTEGER, ALLOCATABLE :: istart(:,:),iend(:,:),jstart(:,:),jend(:,:)
  REAL, ALLOCATABLE  :: zmeaplan(:,:),weighx(:,:), weighy(:,:)
  REAL               :: tmp
!-------------------------------------------------------------------------------
  imdp=assert_eq(SIZE(xd),SIZE(zd,1),TRIM(modname)//" imdp")
  jmdp=assert_eq(SIZE(yd),SIZE(zd,2),TRIM(modname)//" jmdp")
  imar=assert_eq([SIZE(x),SIZE(zphi,1),SIZE(zmea,1),SIZE(zstd,1),SIZE(zsig,1), &
                          SIZE(zgam,1),SIZE(zthe,1),SIZE(zpic,1),SIZE(zval,1), &
                          SIZE(mask,1)],TRIM(modname)//" imar")-1
  jmar=assert_eq([SIZE(y),SIZE(zphi,2),SIZE(zmea,2),SIZE(zstd,2),SIZE(zsig,2), &
                          SIZE(zgam,2),SIZE(zthe,2),SIZE(zpic,2),SIZE(zval,2), &
                          SIZE(mask,2)],TRIM(modname)//" jmar")
!  IF(imar/=iim)   CALL abort_physic(TRIM(modname),'imar/=iim'  ,1)
!  IF(jmar/=jjm+1) CALL abort_physic(TRIM(modname),'jmar/=jjm+1',1)
  iext=imdp/10                                !--- OK up to 36 degrees cell
  xpi = ACOS(-1.)
  rad = 6371229.
  zdeltay=2.*xpi/REAL(jmdp)*rad
  WRITE(lunout,*)"*** Orography parameters at sub-cell scale ***"

!--- ARE WE USING A READ MASK ?
  masque_lu=ANY(mask/=-99999.); IF(.NOT.masque_lu) mask=0.0
  WRITE(lunout,*)'Masque lu: ',masque_lu

!--- EXTENSION OF THE INPUT DATABASE TO PROCEED COMPUTATIONS AT BOUNDARIES:
  ALLOCATE(xusn(imdp+2*iext))
  xusn(1     +iext:imdp  +iext)=xd(:)
  xusn(1          :       iext)=xd(1+imdp-iext:imdp)-2.*xpi
  xusn(1+imdp+iext:imdp+2*iext)=xd(1          :iext)+2.*xpi

  ALLOCATE(yusn(jmdp+2))
  yusn(1       )=yd(1)   +(yd(1)   -yd(2))
  yusn(2:jmdp+1)=yd(:)
  yusn(  jmdp+2)=yd(jmdp)+(yd(jmdp)-yd(jmdp-1))

  ALLOCATE(zusn(imdp+2*iext,jmdp+2), planusn(imdp+2*iext,jmdp+2))
  zusn(1       +iext:imdp  +iext,2:jmdp+1)=zd  (:                   ,     :)
  zusn(1            :       iext,2:jmdp+1)=zd  (imdp-iext+1:imdp    ,     :)
  zusn(1+imdp  +iext:imdp+2*iext,2:jmdp+1)=zd  (1:iext              ,     :)
  zusn(1            :imdp/2+iext,       1)=zusn(1+imdp/2:imdp  +iext,     2)
  zusn(1+imdp/2+iext:imdp+2*iext,       1)=zusn(1       :imdp/2+iext,     2)
  zusn(1            :imdp/2+iext,  jmdp+2)=zusn(1+imdp/2:imdp  +iext,jmdp+1)
  zusn(1+imdp/2+iext:imdp+2*iext,  jmdp+2)=zusn(1       :imdp/2+iext,jmdp+1)
  planusn(:,:)=0.

  ALLOCATE(weighx(imdp+2*iext,jmdp+2),weighy(imdp+2*iext,jmdp+2))
  weighx(:,:)=0.
  weighy(:,:)=0.

!--- COMPUTE LIMITS OF MODEL GRIDPOINT AREA (REGULAR GRID)
  ALLOCATE(a(imar+1),b(imar+1))
  b(1:imar)=(x(1:imar  )+ x(2:imar+1))/2.0
  b(imar+1)= x(  imar+1)+(x(  imar+1)-x(imar))/2.0
  a(1)=x(1)-(x(2)-x(1))/2.0
  a(2:imar+1)= b(1:imar)

  ALLOCATE(c(jmar),d(jmar))
  d(1:jmar-1)=(y(1:jmar-1)+ y(2:jmar))/2.0
  d(  jmar  )= y(  jmar  )+(y(  jmar)-y(jmar-1))/2.0
  c(1)=y(1)-(y(2)-y(1))/2.0
  c(2:jmar)=d(1:jmar-1)

!--- INITIALIZATIONS:
  ALLOCATE(weight(imar+1,jmar)); weight(:,:)= 0.0
  ALLOCATE(zxtzx (imar+1,jmar)); zxtzx (:,:)= 0.0
  ALLOCATE(zytzy (imar+1,jmar)); zytzy (:,:)= 0.0
  ALLOCATE(zxtzy (imar+1,jmar)); zxtzy (:,:)= 0.0
  ALLOCATE(ztz   (imar+1,jmar)); ztz   (:,:)= 0.0
  ALLOCATE(istart(imar+1,jmar)); istart(:,:)=1
  ALLOCATE(iend(imar+1,jmar)); iend(:,:)=1
  ALLOCATE(jstart(imar+1,jmar)); jstart(:,:)=1
  ALLOCATE(jend(imar+1,jmar)); jend(:,:)=1
  ALLOCATE(zmeaplan(imar+1,jmar))

  zmea(:,:)= 0.0
  zmeaplan(:,:)=0.0
  zpic(:,:)=-1.E+10
  zval(:,:)= 1.E+10

!--- COMPUTE SLOPES CORRELATIONS ON USN GRID
! CORRELATIONS OF USN OROGRAPHY GRADIENTS  ! dim (imdp+2*iext,jmdp+2)
  ALLOCATE(zytzyusn(imdp+2*iext,jmdp+2)); zytzyusn(:,:)=0.0
  ALLOCATE(zxtzxusn(imdp+2*iext,jmdp+2)); zxtzxusn(:,:)=0.0
  ALLOCATE(zxtzyusn(imdp+2*iext,jmdp+2)); zxtzyusn(:,:)=0.0
  DO j = 2, jmdp+1
    zdeltax=zdeltay*cos(yusn(j))
    DO i = 2, imdp+2*iext-1
      zytzyusn(i,j)=(zusn(i,j+1)-zusn(i,j-1))**2/zdeltay**2
      zxtzxusn(i,j)=(zusn(i+1,j)-zusn(i-1,j))**2/zdeltax**2
      zxtzyusn(i,j)=(zusn(i,j+1)-zusn(i,j-1))   /zdeltay  &
     &             *(zusn(i+1,j)-zusn(i-1,j))   /zdeltax
    END DO
  END DO

!--- SUMMATION OVER GRIDPOINT AREA
  zleny=xpi/REAL(jmdp)*rad
  xincr=xpi/REAL(jmdp)/2.
  ALLOCATE(num_tot(imar+1,jmar)); num_tot(:,:)=0.
  ALLOCATE(num_lan(imar+1,jmar)); num_lan(:,:)=0.
  DO ii = 1, imar+1
      DO jj = 1, jmar
      
      istart(ii,jj)=imdp+2*iext-1
      iend(ii,jj)=2
      jstart(ii,jj)=jmdp+1
      jend(ii,jj)=2


      DO j = 2,jmdp+1 
        zlenx=zleny*COS(yusn(j))
        zdeltax=zdeltay*COS(yusn(j))
        tmp=(xincr+AMIN1(c(jj)-yusn(j),yusn(j)-d(jj)))*rad
        tmp=AMAX1(0.,AMIN1(tmp,zleny))
        weighy(:,j)=tmp

        IF(tmp==0.) THEN 
            CYCLE
        ELSE
           jend(ii,jj)=MAX(jend(ii,jj),j)
           jstart(ii,jj)=MIN(jstart(ii,jj), j) 
        ENDIF
                   
        DO i = 2, imdp+2*iext-1
          tmp=(xincr+AMIN1(xusn(i)-a(ii),b(ii)-xusn(i)))*rad*COS(yusn(j))
          tmp=AMAX1(0.,AMIN1(tmp,zlenx))
          weighx(i,j)=tmp
          IF(tmp==0.) THEN 
              CYCLE
          ELSE
              iend(ii,jj)=MAX(iend(ii,jj),i)
              istart(ii,jj)=MIN(istart(ii,jj), i)
          ENDIF

          num_tot(ii,jj)=num_tot(ii,jj)+1.0
          IF(zusn(i,j)>=1.)num_lan(ii,jj)=num_lan(ii,jj)+1.0
          weight(ii,jj)=weight(ii,jj)+weighx(i,j)*weighy(i,j)
          zxtzx(ii,jj)=zxtzx(ii,jj)+zxtzxusn(i,j)*weighx(i,j)*weighy(i,j)
          zytzy(ii,jj)=zytzy(ii,jj)+zytzyusn(i,j)*weighx(i,j)*weighy(i,j)
          zxtzy(ii,jj)=zxtzy(ii,jj)+zxtzyusn(i,j)*weighx(i,j)*weighy(i,j)
          ztz  (ii,jj)=ztz(ii,jj)+zusn(i,j)*zusn(i,j)*weighx(i,j)*weighy(i,j)
          zmea (ii,jj)=zmea(ii,jj)+zusn(i,j)*weighx(i,j)*weighy(i,j) !--- MEAN
          zpic (ii,jj)=AMAX1(zpic(ii,jj),zusn(i,j)) !--- PEAKS
          zval (ii,jj)=AMIN1(zval(ii,jj),zusn(i,j)) !--- VALLEYS
        END DO
      END DO
    END DO
  END DO


  IF (detrend) THEN ! recalculate zstd (ztz), zpic and zval
   ztz(:,:)=0.
   zpic(:,:)=-1.E+10
   zval(:,:)= 1.E+10

   DO ii = 1, imar+1
    DO jj = 1, jmar
      CALL fplan(xusn(istart(ii,jj):iend(ii,jj)),                                 &
                 yusn(jstart(ii,jj):jend(ii,jj)),                                 &
                 zusn(istart(ii,jj):iend(ii,jj),jstart(ii,jj):jend(ii,jj)),       &
                 weighx(istart(ii,jj):iend(ii,jj),jstart(ii,jj):jend(ii,jj)),     &
                 weighy(istart(ii,jj):iend(ii,jj),jstart(ii,jj):jend(ii,jj)),     &
                 planusn(istart(ii,jj):iend(ii,jj),jstart(ii,jj):jend(ii,jj)))

      DO j = 2,jmdp+1
        zlenx=zleny*COS(yusn(j))
        zdeltax=zdeltay*COS(yusn(j))
        tmp=(xincr+AMIN1(c(jj)-yusn(j),yusn(j)-d(jj)))*rad
        tmp=AMAX1(0.,AMIN1(tmp,zleny))
        weighy(:,j)=tmp
        IF(tmp==0.)  CYCLE
        DO i = 2, imdp+2*iext-1
          tmp=(xincr+AMIN1(xusn(i)-a(ii),b(ii)-xusn(i)))*rad*COS(yusn(j))
          tmp=AMAX1(0.,AMIN1(tmp,zlenx))
          weighx(i,j)=tmp
          IF(tmp==0.) CYCLE
          zmeaplan (ii,jj)=zmeaplan(ii,jj)+planusn(i,j)*weighx(i,j)*weighy(i,j)            !--- MEAN PLANE
          ztz  (ii,jj)=ztz(ii,jj)+((zusn(i,j)-planusn(i,j))**2)*weighx(i,j)*weighy(i,j)
          zpic (ii,jj)=AMAX1(zpic(ii,jj),zusn(i,j)-planusn(i,j)) !--- PEAKS
          zval (ii,jj)=AMIN1(zval(ii,jj),zusn(i,j)-planusn(i,j)) !--- VALLEYS
        END DO
      END DO
     END DO
    END DO
    
    DO ii = 1, imar+1
    DO jj = 1, jmar
    IF (weight(ii,jj).NE.0.0) THEN 
        zpic(ii,jj)=MAX(0.0, zpic(ii,jj)+zmeaplan(ii,jj)/weight(ii,jj))     
        zval(ii,jj)=MAX(0.0, zval(ii,jj)+zmeaplan(ii,jj)/weight(ii,jj))
        ztz(ii,jj)=ztz(ii,jj)+zmea(ii,jj)*zmea(ii,jj)/weight(ii,jj) ! add this term to anticipate next step (useless if detrend)
    ENDIF
    ENDDO
    ENDDO

  ENDIF


!--- COMPUTE PARAMETERS NEEDED BY LOTT & MILLER (1997) AND LOTT (1999) SSO SCHEME
  IF(.NOT.masque_lu) THEN
    WHERE(weight(:,:)/=0.0) mask=num_lan(:,:)/num_tot(:,:)
  END IF
  nn=COUNT(weight(:,:)==0.0)
  IF(nn/=0) WRITE(lunout,*)'Problem with weight ; vanishing occurrences: ',nn
  WHERE(weight(:,:)/=0.0)
    zmea (:,:)=zmea (:,:)/weight(:,:)
    zxtzx(:,:)=zxtzx(:,:)/weight(:,:)
    zytzy(:,:)=zytzy(:,:)/weight(:,:)
    zxtzy(:,:)=zxtzy(:,:)/weight(:,:)
    ztz  (:,:)=ztz  (:,:)/weight(:,:)
    zstd (:,:)=ztz(:,:)-zmea(:,:)**2
  END WHERE

  WHERE(zstd(:,:)<0) zstd(:,:)=0.
  zstd (:,:)=SQRT(zstd(:,:))

!--- CORRECT VALUES OF HORIZONTAL SLOPE NEAR THE POLES:
  zxtzx(:,   1)=zxtzx(:,     2)
  zxtzx(:,jmar)=zxtzx(:,jmar-1)
  zxtzy(:,   1)=zxtzy(:,     2)
  zxtzy(:,jmar)=zxtzy(:,jmar-1)
  zytzy(:,   1)=zytzy(:,     2)
  zytzy(:,jmar)=zytzy(:,jmar-1)

!=== FILTERS TO SMOOTH OUT FIELDS FOR INPUT INTO SSO SCHEME.
!--- FIRST FILTER, MOVING AVERAGE OVER 9 POINTS.
!-------------------------------------------------------------------------------
  zphi(:,:)=zmea(:,:)                           ! GK211005 (CG) UNSMOOTHED TOPO

  CALL MVA9(zmea);  CALL MVA9(zstd);  CALL MVA9(zpic);  CALL MVA9(zval)
  CALL MVA9(zxtzx); CALL MVA9(zxtzy); CALL MVA9(zytzy)

!--- MASK BASED ON GROUND MAXIMUM, 10% THRESHOLD. (SURFACE PARAMS MEANINGLESS)
  WHERE(weight(:,:)==0.0.OR.mask<0.1)
    zphi(:,:)=0.0; zmea(:,:)=0.0; zpic(:,:)=0.0; zval(:,:)=0.0; zstd(:,:)=0.0
  END WHERE
  DO ii = 1, imar
    DO jj = 1, jmar
      IF(weight(ii,jj)==0.0) CYCLE
    !--- Coefficients K, L et M:
      xk=(zxtzx(ii,jj)+zytzy(ii,jj))/2.
      xl=(zxtzx(ii,jj)-zytzy(ii,jj))/2.
      xm=zxtzy(ii,jj)
      xp=xk-SQRT(xl**2+xm**2)
      xq=xk+SQRT(xl**2+xm**2)
      xw=1.e-8
      IF(xp<=xw) xp=0.
      IF(xq<=xw) xq=xw
      IF(ABS(xm)<=xw) xm=xw*SIGN(1.,xm)
    !--- SLOPE, ANISOTROPY AND THETA ANGLE
      zsig(ii,jj)=SQRT(xq)
      zgam(ii,jj)=xp/xq
      zthe(ii,jj)=90.*ATAN2(xm,xl)/xpi
    END DO
  END DO
  WHERE(weight(:,:)==0.0.OR.mask<0.1)
    zsig(:,:)=0.0; zgam(:,:)=0.0; zthe(:,:)=0.0
  END WHERE

  WRITE(lunout,*)'  MEAN ORO:' ,MAXVAL(zmea)
  WRITE(lunout,*)'  ST. DEV.:' ,MAXVAL(zstd)
  WRITE(lunout,*)'  PENTE:'    ,MAXVAL(zsig)
  WRITE(lunout,*)'  ANISOTROP:',MAXVAL(zgam)
  WRITE(lunout,*)'  ANGLE:'    ,MINVAL(zthe),MAXVAL(zthe)
  WRITE(lunout,*)'  pic:'      ,MAXVAL(zpic)
  WRITE(lunout,*)'  val:'      ,MAXVAL(zval)
      
!--- Values at redundant longitude
  zmea(imar+1,:)=zmea(1,:)
  zphi(imar+1,:)=zphi(1,:)
  zpic(imar+1,:)=zpic(1,:)
  zval(imar+1,:)=zval(1,:)
  zstd(imar+1,:)=zstd(1,:)
  zsig(imar+1,:)=zsig(1,:)
  zgam(imar+1,:)=zgam(1,:)
  zthe(imar+1,:)=zthe(1,:)

!--- Values at north pole
  zweinor  =SUM(weight(1:imar,1))
  zmea(:,1)=SUM(weight(1:imar,1)*zmea(1:imar,1))/zweinor
  zphi(:,1)=SUM(weight(1:imar,1)*zphi(1:imar,1))/zweinor
  zpic(:,1)=SUM(weight(1:imar,1)*zpic(1:imar,1))/zweinor
  zval(:,1)=SUM(weight(1:imar,1)*zval(1:imar,1))/zweinor
  zstd(:,1)=SUM(weight(1:imar,1)*zstd(1:imar,1))/zweinor
  zsig(:,1)=SUM(weight(1:imar,1)*zsig(1:imar,1))/zweinor
  zgam(:,1)=1.; zthe(:,1)=0.

!--- Values at south pole
  zweisud     =SUM(weight(1:imar,jmar),DIM=1)
  zmea(:,jmar)=SUM(weight(1:imar,jmar)*zmea(1:imar,jmar))/zweisud
  zphi(:,jmar)=SUM(weight(1:imar,jmar)*zphi(1:imar,jmar))/zweisud
  zpic(:,jmar)=SUM(weight(1:imar,jmar)*zpic(1:imar,jmar))/zweisud
  zval(:,jmar)=SUM(weight(1:imar,jmar)*zval(1:imar,jmar))/zweisud
  zstd(:,jmar)=SUM(weight(1:imar,jmar)*zstd(1:imar,jmar))/zweisud
  zsig(:,jmar)=SUM(weight(1:imar,jmar)*zsig(1:imar,jmar))/zweisud
  zgam(:,jmar)=1.; zthe(:,jmar)=0.

END SUBROUTINE grid_noro
!
!-------------------------------------------------------------------------------
!-------------------------------------------------------------------------------
!
SUBROUTINE fplan(xx,yy,zz,weighx,weighy,plan)
!
!===============================================================================
! Purpose: computes the mean plane fitted on a zd topography field
!          L Pauget, V Wiener, E Vignon
!          the fitting plane is z=a0x+a1y+b
!===============================================================================
  IMPLICIT NONE
!-------------------------------------------------------------------------------
! Arguments:
  REAL, INTENT(IN)  :: xx(:), yy(:)  !--- INPUT  COORDINATES     
  REAL, INTENT(IN)  :: zz(:,:)       !--- INPUT  FIELD    
  REAL, INTENT(IN)  :: weighx(:,:)   !--- WEIGHT X POINTS
  REAL, INTENT(IN)  :: weighy(:,:)   !--- WEIGHT Y POINTS
  REAL, INTENT(OUT) :: plan(:,:)     !--- PLANAR SURFACE         
!-------------------------------------------------------------------------------
! Local variables:
INTEGER           :: i, llm, ppm, m
REAL              :: a0, a1, b
REAL              :: a0bar, a1bar, bbar
REAL              :: xbar, ybar, zbar
REAL              :: l00, l01, l11, r0, r1
REAL, ALLOCATABLE :: xx_ar(:,:), yy_ar(:,:), ww_ar(:,:), zz_ar(:,:)
REAL, ALLOCATABLE :: xx_vec(:), yy_vec(:), zz_vec(:), ww_vec(:)


llm=size(zz,1)
ppm=size(zz,2)
m=size(zz)


ALLOCATE(xx_ar(llm,ppm),yy_ar(llm,ppm),zz_ar(llm,ppm),ww_ar(llm,ppm))
xx_ar= spread(xx,dim=2,ncopies=ppm)
yy_ar= spread(yy,dim=1,ncopies=llm)
zz_ar=zz
ww_ar=weighx*weighy/sum(weighx*weighy)
! important: at the moment we assume all weight =1
! to retrieve the mean plance (more stable solution)
ww_ar(:,:)=1.

ALLOCATE(xx_vec(m), yy_vec(m),zz_vec(m))
xx_vec=reshape(xx_ar,(/m/))
yy_vec=reshape(yy_ar,(/m/))
zz_vec=reshape(zz_ar,(/m/))
ww_vec=reshape(ww_ar,(/m/))

xbar=1./size(xx_vec)*sum(xx_vec)
ybar=1./size(yy_vec)*sum(yy_vec)
zbar=1./size(zz_vec)*sum(zz_vec)

l00=0.
l01=0.
l11=0.
r0=0.
r1=0.

DO i=1,m
   l00=l00+ww_vec(i)*(xx_vec(i)-xbar)**2
   l01=l01+ww_vec(i)*(xx_vec(i)-xbar)*(yy_vec(i)-ybar)
   l11=l11+ww_vec(i)*(yy_vec(i)-ybar)**2
   r0=r0+ww_vec(i)*(xx_vec(i)-xbar)*(zz_vec(i)-zbar)
   r1=r1+ww_vec(i)*(yy_vec(i)-ybar)*(zz_vec(i)-zbar)
END DO

a0bar=(l11*r0-l01*r1)/(l00*l11-l01*l01)
a1bar=(l00*r1-l01*r0)/(l00*l11-l01*l01)
bbar=0.

a0=a0bar
a1=a1bar
b=zbar-a0bar*xbar-a1bar*ybar

plan(:,:)=a0*xx_ar(:,:)+a1*yy_ar(:,:)+b


END SUBROUTINE fplan

!-------------------------------------------------------------------------------
!
SUBROUTINE grid_noro0(xd,yd,zd,x,y,zphi,mask)
!
!===============================================================================
! Purpose: Extracted from grid_noro to provide geopotential height for dynamics
!          without any call to physics subroutines.
!===============================================================================
  IMPLICIT NONE 
!-------------------------------------------------------------------------------
! Arguments:
  REAL, INTENT(IN)  :: xd(:), yd(:) !--- INPUT  COORDINATES     (imdp) (jmdp)
  REAL, INTENT(IN)  :: zd(:,:)      !--- INPUT  FIELD           (imdp,  jmdp)
  REAL, INTENT(IN)  :: x(:), y(:)   !--- OUTPUT COORDINATES     (imar+1) (jmar)
  REAL, INTENT(OUT) :: zphi(:,:)    !--- GEOPOTENTIAL           (imar+1,jmar)
  REAL, INTENT(OUT) :: mask(:,:)    !--- MASK                   (imar+1,jmar)
!-------------------------------------------------------------------------------
! Local variables:
  CHARACTER(LEN=256) :: modname="grid_noro0"
  REAL, ALLOCATABLE :: xusn(:), yusn(:)           ! dim (imdp+2*iext) (jmdp+2)
  REAL, ALLOCATABLE :: zusn(:,:)                  ! dim (imdp+2*iext,  jmdp+2)
  REAL, ALLOCATABLE :: weight(:,:)                ! dim (imar+1,jmar)
  REAL, ALLOCATABLE :: num_tot(:,:), num_lan(:,:) ! dim (imar+1,jmar)
  REAL, ALLOCATABLE :: a(:), b(:)                 ! dim (imar+1)
  REAL, ALLOCATABLE :: c(:), d(:)                 ! dim (jmar)

  LOGICAL :: masque_lu
  INTEGER :: i, ii, imdp, imar, iext
  INTEGER :: j, jj, jmdp, jmar, nn
  REAL    :: xpi, zlenx, zleny, weighx, weighy, xincr, masque, rad

!-------------------------------------------------------------------------------
  imdp=assert_eq(SIZE(xd),SIZE(zd,1),TRIM(modname)//" imdp")
  jmdp=assert_eq(SIZE(yd),SIZE(zd,2),TRIM(modname)//" jmdp")
  imar=assert_eq(SIZE(x),SIZE(zphi,1),SIZE(mask,1),TRIM(modname)//" imar")-1
  jmar=assert_eq(SIZE(y),SIZE(zphi,2),SIZE(mask,2),TRIM(modname)//" jmar")
  iext=imdp/10
  xpi = ACOS(-1.)
  rad = 6371229.

!--- ARE WE USING A READ MASK ?
  masque_lu=ANY(mask/=-99999.); IF(.NOT.masque_lu) mask=0.0
  WRITE(lunout,*)'Masque lu: ',masque_lu

!--- EXTENSION OF THE INPUT DATABASE TO PROCEED COMPUTATIONS AT BOUNDARIES:
  ALLOCATE(xusn(imdp+2*iext))
  xusn(1     +iext:imdp  +iext)=xd(:)
  xusn(1          :       iext)=xd(1+imdp-iext:imdp)-2.*xpi
  xusn(1+imdp+iext:imdp+2*iext)=xd(1          :iext)+2.*xpi

  ALLOCATE(yusn(jmdp+2))
  yusn(1       )=yd(1)   +(yd(1)   -yd(2))
  yusn(2:jmdp+1)=yd(:)
  yusn(  jmdp+2)=yd(jmdp)+(yd(jmdp)-yd(jmdp-1))

  ALLOCATE(zusn(imdp+2*iext,jmdp+2))
  zusn(1       +iext:imdp  +iext,2:jmdp+1)=zd  (:                   ,     :)
  zusn(1            :       iext,2:jmdp+1)=zd  (imdp-iext+1:imdp    ,     :)
  zusn(1+imdp  +iext:imdp+2*iext,2:jmdp+1)=zd  (1:iext              ,     :)
  zusn(1            :imdp/2+iext,       1)=zusn(1+imdp/2:imdp  +iext,     2)
  zusn(1+imdp/2+iext:imdp+2*iext,       1)=zusn(1       :imdp/2+iext,     2)
  zusn(1            :imdp/2+iext,  jmdp+2)=zusn(1+imdp/2:imdp  +iext,jmdp+1)
  zusn(1+imdp/2+iext:imdp+2*iext,  jmdp+2)=zusn(1       :imdp/2+iext,jmdp+1)

!--- COMPUTE LIMITS OF MODEL GRIDPOINT AREA (REGULAR GRID)
  ALLOCATE(a(imar+1),b(imar+1))
  b(1:imar)=(x(1:imar  )+ x(2:imar+1))/2.0
  b(imar+1)= x(  imar+1)+(x(  imar+1)-x(imar))/2.0
  a(1)=x(1)-(x(2)-x(1))/2.0
  a(2:imar+1)= b(1:imar)

  ALLOCATE(c(jmar),d(jmar))
  d(1:jmar-1)=(y(1:jmar-1)+ y(2:jmar))/2.0
  d(  jmar  )= y(  jmar  )+(y(  jmar)-y(jmar-1))/2.0
  c(1)=y(1)-(y(2)-y(1))/2.0
  c(2:jmar)=d(1:jmar-1)

!--- INITIALIZATIONS:
  ALLOCATE(weight(imar+1,jmar)); weight(:,:)=0.0; zphi(:,:)=0.0

!--- SUMMATION OVER GRIDPOINT AREA
  zleny=xpi/REAL(jmdp)*rad
  xincr=xpi/REAL(jmdp)/2.
  ALLOCATE(num_tot(imar+1,jmar)); num_tot(:,:)=0.
  ALLOCATE(num_lan(imar+1,jmar)); num_lan(:,:)=0.
  DO ii = 1, imar+1
    DO jj = 1, jmar
      DO j = 2,jmdp+1 
        zlenx=zleny*COS(yusn(j))
        weighy=(xincr+AMIN1(c(jj)-yusn(j),yusn(j)-d(jj)))*rad
        weighy=AMAX1(0.,AMIN1(weighy,zleny))
        IF(weighy/=0) CYCLE
        DO i = 2, imdp+2*iext-1
          weighx=(xincr+AMIN1(xusn(i)-a(ii),b(ii)-xusn(i)))*rad*COS(yusn(j))
          weighx=AMAX1(0.,AMIN1(weighx,zlenx))
          IF(weighx/=0) CYCLE
          num_tot(ii,jj)=num_tot(ii,jj)+1.0
          IF(zusn(i,j)>=1.)num_lan(ii,jj)=num_lan(ii,jj)+1.0
          weight(ii,jj)=weight(ii,jj)+weighx*weighy
          zphi  (ii,jj)=zphi  (ii,jj)+zusn(i,j)*weighx*weighy !--- MEAN
        END DO
      END DO
    END DO
  END DO

!--- COMPUTE PARAMETERS NEEDED BY LOTT & MILLER (1997) AND LOTT (1999) SSO SCHEME
  IF(.NOT.masque_lu) THEN
    WHERE(weight(:,:)/=0.0) mask=num_lan(:,:)/num_tot(:,:)
  END IF
  nn=COUNT(weight(:,:)==0.0)
  IF(nn/=0) WRITE(lunout,*)'Problem with weight ; vanishing occurrences: ',nn
  WHERE(weight/=0.0) zphi(:,:)=zphi(:,:)/weight(:,:)

!--- MASK BASED ON GROUND MAXIMUM, 10% THRESHOLD (<10%: SURF PARAMS MEANINGLESS)
  WHERE(weight(:,:)==0.0.OR.mask<0.1) zphi(:,:)=0.0
  WRITE(lunout,*)'  MEAN ORO:' ,MAXVAL(zphi)

!--- Values at redundant longitude and at poles
  zphi(imar+1,:)=zphi(1,:)
  zphi(:,   1)=SUM(weight(1:imar,   1)*zphi(1:imar,   1))/SUM(weight(1:imar,   1))
  zphi(:,jmar)=SUM(weight(1:imar,jmar)*zphi(1:imar,jmar))/SUM(weight(1:imar,jmar))

END SUBROUTINE grid_noro0
!
!-------------------------------------------------------------------------------


!-------------------------------------------------------------------------------
!
SUBROUTINE read_noro(x,y,fname,zphi,zmea,zstd,zsig,zgam,zthe,zpic,zval,mask)
!
!-------------------------------------------------------------------------------
! Purpose: Read parameters usually determined with grid_noro from a file.
!===============================================================================
  USE netcdf, ONLY: NF90_OPEN,  NF90_INQ_DIMID, NF90_INQUIRE_DIMENSION,        &
        NF90_NOERR, NF90_CLOSE, NF90_INQ_VARID, NF90_GET_VAR, NF90_STRERROR,   &
        NF90_NOWRITE
  IMPLICIT NONE 
!-------------------------------------------------------------------------------
! Arguments:
  REAL, INTENT(IN)  :: x(:), y(:)    !--- OUTPUT COORDINATES     (imar+1) (jmar)
  CHARACTER(LEN=*), INTENT(IN) :: fname ! PARAMETERS FILE NAME
  REAL, INTENT(OUT) :: zphi(:,:)     !--- GEOPOTENTIAL           (imar+1,jmar)
  REAL, INTENT(OUT) :: zmea(:,:)     !--- MEAN OROGRAPHY         (imar+1,jmar)
  REAL, INTENT(OUT) :: zstd(:,:)     !--- STANDARD DEVIATION     (imar+1,jmar)
  REAL, INTENT(OUT) :: zsig(:,:)     !--- SLOPE                  (imar+1,jmar)
  REAL, INTENT(OUT) :: zgam(:,:)     !--- ANISOTROPY             (imar+1,jmar)
  REAL, INTENT(OUT) :: zthe(:,:)     !--- SMALL AXIS ORIENTATION (imar+1,jmar)
  REAL, INTENT(OUT) :: zpic(:,:)     !--- MAXIMUM ALTITUDE       (imar+1,jmar)
  REAL, INTENT(OUT) :: zval(:,:)     !--- MINIMUM ALTITUDE       (imar+1,jmar)
  REAL, INTENT(OUT) :: mask(:,:)     !--- MASK                   (imar+1,jmar)
!-------------------------------------------------------------------------------
! Local variables:
  CHARACTER(LEN=256) :: modname="read_noro"
  INTEGER :: imar, jmar, fid, did, vid
  LOGICAL :: masque_lu
  REAL :: xpi, d2r
!-------------------------------------------------------------------------------
  imar=assert_eq([SIZE(x),SIZE(zphi,1),SIZE(zmea,1),SIZE(zstd,1),SIZE(zsig,1), &
                          SIZE(zgam,1),SIZE(zthe,1),SIZE(zpic,1),SIZE(zval,1), &
                          SIZE(mask,1)],TRIM(modname)//" imar")-1
  jmar=assert_eq([SIZE(y),SIZE(zphi,2),SIZE(zmea,2),SIZE(zstd,2),SIZE(zsig,2), &
                          SIZE(zgam,2),SIZE(zthe,2),SIZE(zpic,2),SIZE(zval,2), &
                          SIZE(mask,2)],TRIM(modname)//" jmar")
  xpi=ACOS(-1.0); d2r=xpi/180.
  WRITE(lunout,*)"*** Orography parameters at sub-cell scale from file ***"

!--- ARE WE USING A READ MASK ?
  masque_lu=ANY(mask/=-99999.); IF(.NOT.masque_lu) mask=0.0
  WRITE(lunout,*)'Masque lu: ',masque_lu
  CALL ncerr(NF90_OPEN(fname,NF90_NOWRITE,fid))
  CALL check_dim('x','longitude',x(1:imar))
  CALL check_dim('y','latitude' ,y(1:jmar))
  IF(.NOT.masque_lu) CALL get_fld('mask',mask)
  CALL get_fld('Zphi',zphi)
  CALL get_fld('Zmea',zmea)
  CALL get_fld('mu'  ,zstd)
  CALL get_fld('Zsig',zsig)
  CALL get_fld('Zgam',zgam)
  CALL get_fld('Zthe',zthe)
  zpic=zmea+2*zstd
  zval=MAX(0.,zmea-2.*zstd)
  CALL ncerr(NF90_CLOSE(fid))
  WRITE(lunout,*)'  MEAN ORO:' ,MAXVAL(zmea)
  WRITE(lunout,*)'  ST. DEV.:' ,MAXVAL(zstd)
  WRITE(lunout,*)'  PENTE:'    ,MAXVAL(zsig)
  WRITE(lunout,*)'  ANISOTROP:',MAXVAL(zgam)
  WRITE(lunout,*)'  ANGLE:'    ,MINVAL(zthe),MAXVAL(zthe)
  WRITE(lunout,*)'  pic:'      ,MAXVAL(zpic)
  WRITE(lunout,*)'  val:'      ,MAXVAL(zval)

CONTAINS


SUBROUTINE get_fld(var,fld)
  CHARACTER(LEN=*), INTENT(IN)    :: var
  REAL,             INTENT(INOUT) :: fld(:,:)
  CALL ncerr(NF90_INQ_VARID(fid,var,vid),var)
  CALL ncerr(NF90_GET_VAR(fid,vid,fld(1:imar,:)),var)
  fld(imar+1,:)=fld(1,:)
END SUBROUTINE get_fld

SUBROUTINE check_dim(dimd,nam,dimv)
  CHARACTER(LEN=*), INTENT(IN) :: dimd
  CHARACTER(LEN=*), INTENT(IN) :: nam
  REAL,             INTENT(IN) :: dimv(:)
  REAL, ALLOCATABLE :: tmp(:)
  INTEGER :: n
  CALL ncerr(NF90_INQ_DIMID(fid,dimd,did))
  CALL ncerr(NF90_INQUIRE_DIMENSION(fid,did,len=n)); ALLOCATE(tmp(n))
  CALL ncerr(NF90_INQ_VARID(fid,dimd,did))
  CALL ncerr(NF90_GET_VAR(fid,did,tmp))
  IF(MAXVAL(tmp)>xpi) tmp=tmp*d2r
  IF(n/=SIZE(dimv).OR.ANY(ABS(tmp-dimv)>1E-6)) THEN
    WRITE(lunout,*)'Problem with file "'//TRIM(fname)//'".'
    CALL abort_physic(modname,'Grid differs from LMDZ for '//TRIM(nam)//'.',1)
  END IF
END SUBROUTINE check_dim

SUBROUTINE ncerr(ncres,var)
  IMPLICIT NONE
  INTEGER,          INTENT(IN) :: ncres
  CHARACTER(LEN=*), INTENT(IN), OPTIONAL :: var
  CHARACTER(LEN=256) :: mess
  IF(ncres/=NF90_NOERR) THEN
    mess='Problem with file "'//TRIM(fname)//'"'
    IF(PRESENT(var)) mess=TRIM(mess)//' and variable "'//TRIM(var)//'"'
    WRITE(lunout,*)TRIM(mess)//'.'
    CALL abort_physic(modname,NF90_STRERROR(ncres),1)
  END IF
END SUBROUTINE ncerr

END SUBROUTINE read_noro
!
!-------------------------------------------------------------------------------


!-------------------------------------------------------------------------------
!
SUBROUTINE MVA9(x)
!
!-------------------------------------------------------------------------------
  IMPLICIT NONE
! MAKE A MOVING AVERAGE OVER 9 GRIDPOINTS OF THE X FIELDS
!-------------------------------------------------------------------------------
! Arguments:
  REAL, INTENT(INOUT) :: x(:,:)
!-------------------------------------------------------------------------------
! Local variables:
  REAL    :: xf(SIZE(x,DIM=1),SIZE(x,DIM=2)), WEIGHTpb(-1:1,-1:1)
  INTEGER :: i, j, imar, jmar
!-------------------------------------------------------------------------------
  WEIGHTpb=RESHAPE([((1./REAL((1+i**2)*(1+j**2)),i=-1,1),j=-1,1)],SHAPE=[3,3])
  WEIGHTpb=WEIGHTpb/SUM(WEIGHTpb)
  imar=SIZE(X,DIM=1); jmar=SIZE(X,DIM=2)
  DO j=2,jmar-1
    DO i=2,imar-1
      xf(i,j)=SUM(x(i-1:i+1,j-1:j+1)*WEIGHTpb(:,:))
    END DO
  END DO
  DO j=2,jmar-1
    xf(1,j)=SUM(x(imar-1,j-1:j+1)*WEIGHTpb(-1,:))
    xf(1,j)=xf(1,j)+SUM(x(1:2,j-1:j+1)*WEIGHTpb(0:1,-1:1))
    xf(imar,j)=xf(1,j)
  END DO
  xf(:,   1)=xf(:,     2)
  xf(:,jmar)=xf(:,jmar-1)
  x(:,:)=xf(:,:)

END SUBROUTINE MVA9
!
!-------------------------------------------------------------------------------


END MODULE grid_noro_m


