!
MODULE climb_wind_mod
!
! Module to solve the verctical diffusion of the wind components "u" and "v".
!
  USE dimphy

  IMPLICIT NONE

  SAVE
  PRIVATE
  
  REAL, DIMENSION(:),   ALLOCATABLE  :: alf1, alf2
  !$OMP THREADPRIVATE(alf1,alf2)
  REAL, DIMENSION(:,:), ALLOCATABLE  :: Kcoefm
  !$OMP THREADPRIVATE(Kcoefm)
  REAL, DIMENSION(:,:), ALLOCATABLE  :: Ccoef_U, Dcoef_U
  !$OMP THREADPRIVATE(Ccoef_U, Dcoef_U)
  REAL, DIMENSION(:,:), ALLOCATABLE  :: Ccoef_V, Dcoef_V
  !$OMP THREADPRIVATE(Ccoef_V, Dcoef_V)
  REAL, DIMENSION(:), ALLOCATABLE   :: Acoef_U, Bcoef_U
  !$OMP THREADPRIVATE(Acoef_U, Bcoef_U)
  REAL, DIMENSION(:), ALLOCATABLE   :: Acoef_V, Bcoef_V
  !$OMP THREADPRIVATE(Acoef_V, Bcoef_V)
  LOGICAL                            :: firstcall=.TRUE.
  !$OMP THREADPRIVATE(firstcall)

  
  PUBLIC :: climb_wind_down, climb_wind_up, climb_wind_init

CONTAINS
!
!****************************************************************************************
!
  SUBROUTINE climb_wind_init

    INTEGER             :: ierr
    CHARACTER(len = 20) :: modname = 'climb_wind_init'    


!****************************************************************************************
! Initialize module
    
    IF (firstcall) THEN

      !****************************************************************************************
      ! Allocation of global module variables
      !
      !****************************************************************************************
  
      ALLOCATE(alf1(klon), stat=ierr)
      IF (ierr /= 0) CALL abort_physic(modname,'Pb in allocate alf1',1)
      alf1(:) = 0.
  
      ALLOCATE(alf2(klon), stat=ierr)
      IF (ierr /= 0) CALL abort_physic(modname,'Pb in allocate alf2',1)
      alf2(:) = 0.
  
      ALLOCATE(Kcoefm(klon,klev), stat=ierr)
      IF (ierr /= 0) CALL abort_physic(modname,'Pb in allocate Kcoefm',1)
      Kcoefm(:,:) = 0.
  
      ALLOCATE(Ccoef_U(klon,klev), stat=ierr)
      IF (ierr /= 0) CALL abort_physic(modname,'Pb in allocate Ccoef_U',1)
      Ccoef_U(:,:) = 0.
  
      ALLOCATE(Dcoef_U(klon,klev), stat=ierr)
      IF (ierr /= 0) CALL abort_physic(modname,'Pb in allocation Dcoef_U',1)
      Dcoef_U(:,:) = 0.
  
      ALLOCATE(Ccoef_V(klon,klev), stat=ierr)
      IF (ierr /= 0) CALL abort_physic(modname,'Pb in allocation Ccoef_V',1)
      Ccoef_V(:,:) = 0.
  
      ALLOCATE(Dcoef_V(klon,klev), stat=ierr)
      IF (ierr /= 0) CALL abort_physic(modname,'Pb in allocation Dcoef_V',1)
      Dcoef_V(:,:) = 0.
  
      ALLOCATE(Acoef_U(klon), Bcoef_U(klon), Acoef_V(klon), Bcoef_V(klon), STAT=ierr)
      IF ( ierr /= 0 )  PRINT*,' pb in allloc Acoef_U and Bcoef_U, ierr=', ierr
      Acoef_U(:) = 0. ; Bcoef_U(:) = 0. ; Acoef_V(:) = 0. ; Bcoef_V(:) = 0. ;
  
      firstcall=.FALSE.
    
    ENDIF

  END SUBROUTINE climb_wind_init
!
!****************************************************************************************
!
  SUBROUTINE climb_wind_down(knon, ni, dtime, coef_in, pplay, paprs, temp, delp, u_old, v_old, &
!!! nrlmd le 02/05/2011
       Ccoef_U_out, Ccoef_V_out, Dcoef_U_out, Dcoef_V_out, &
       Kcoef_m_out, alf_1_out, alf_2_out, &
!!!
       Acoef_U_out, Acoef_V_out, Bcoef_U_out, Bcoef_V_out)
!$gpum horizontal knon

!
! This routine calculates for the wind components u and v,
! recursivly the coefficients C and D in equation 
! X(k) = C(k) + D(k)*X(k-1), X=[u,v], k=[1,klev] is the vertical layer.
!
!
USE yomcst_mod_h
USE compbl_mod_h
! Input arguments
!****************************************************************************************
    INTEGER, INTENT(IN)                      :: knon
    INTEGER, INTENT(IN)                      :: ni(knon)
    REAL, INTENT(IN)                         :: dtime
    REAL, DIMENSION(knon,klev), INTENT(IN)   :: coef_in
    REAL, DIMENSION(knon,klev), INTENT(IN)   :: pplay ! pres au milieu de couche (Pa)
    REAL, DIMENSION(knon,klev+1), INTENT(IN) :: paprs ! pression a inter-couche (Pa)
    REAL, DIMENSION(knon,klev), INTENT(IN)   :: temp  ! temperature
    REAL, DIMENSION(knon,klev), INTENT(IN)   :: delp
    REAL, DIMENSION(knon,klev), INTENT(IN)   :: u_old
    REAL, DIMENSION(knon,klev), INTENT(IN)   :: v_old

! Output arguments
!****************************************************************************************
    REAL, DIMENSION(knon), INTENT(OUT)       :: Acoef_U_out
    REAL, DIMENSION(knon), INTENT(OUT)       :: Acoef_V_out
    REAL, DIMENSION(knon), INTENT(OUT)       :: Bcoef_U_out
    REAL, DIMENSION(knon), INTENT(OUT)       :: Bcoef_V_out

!!! nrlmd le 02/05/2011
    REAL, DIMENSION(knon,klev), INTENT(OUT)  :: Ccoef_U_out
    REAL, DIMENSION(knon,klev), INTENT(OUT)  :: Ccoef_V_out
    REAL, DIMENSION(knon,klev), INTENT(OUT)  :: Dcoef_U_out
    REAL, DIMENSION(knon,klev), INTENT(OUT)  :: Dcoef_V_out
    REAL, DIMENSION(knon,klev), INTENT(OUT)  :: Kcoef_m_out
    REAL, DIMENSION(knon), INTENT(OUT)       :: alf_1_out
    REAL, DIMENSION(knon), INTENT(OUT)       :: alf_2_out
!!!

! Local variables
!****************************************************************************************
    REAL :: yalf1(knon)
    REAL :: yalf2(knon)
    REAL :: yKcoefm(knon,klev)
    REAL :: yCcoef_U(knon,klev)
    REAL :: yDcoef_U(knon,klev)
    REAL :: yCcoef_V(knon,klev)
    REAL :: yDcoef_V(knon,klev)
    REAL :: yAcoef_U(knon), yBcoef_U(knon), yAcoef_V(knon), yBcoef_V(knon)

    REAL, DIMENSION(knon)                    :: u1lay, v1lay
    INTEGER                                  :: k, i, j


!****************************************************************************************
! Calculate the coefficients C and D in : u(k) = C(k) + D(k)*u(k-1)
!
!****************************************************************************************
! - Define alpha (alf1 and alf2) 
    yalf1(:) = 1.0
    yalf2(:) = 1.0 - yalf1(:)

! - Calculate the coefficients K
    yKcoefm(:,:) = 0.0
    DO k = 2, klev
       DO i=1,knon
          yKcoefm(i,k) = coef_in(i,k)*RG*RG*dtime/(pplay(i,k-1)-pplay(i,k)) &
               *(paprs(i,k)*2/(temp(i,k)+temp(i,k-1))/RD)**2
       END DO
    END DO

! - Calculate the coefficients C and D, component "u"
    CALL calc_coef(knon, yKcoefm, delp, &
         u_old, yalf1, yalf2,  &
         yCcoef_U, yDcoef_U, yAcoef_U, yBcoef_U)

! - Calculate the coefficients C and D, component "v"
    CALL calc_coef(knon, yKcoefm, delp, &
         v_old, yalf1, yalf2,  &
         yCcoef_V, yDcoef_V, yAcoef_V, yBcoef_V)

!****************************************************************************************
! 6)
! Return the first layer in output variables
!
!****************************************************************************************
    Acoef_U_out = yAcoef_U
    Bcoef_U_out = yBcoef_U
    Acoef_V_out = yAcoef_V
    Bcoef_V_out = yBcoef_V

!****************************************************************************************
! 7)
! If Pbl is split, return also the other layers in output variables
!
!****************************************************************************************
!!! jyg le 07/02/2012
!!jyg       IF (mod(iflag_pbl_split,2) .eq.1) THEN
       IF (mod(iflag_pbl_split,10) .ge.1) THEN
!!! nrlmd le 02/05/2011
    DO k= 1, klev
      DO i= 1, knon
        Ccoef_U_out(i,k) = yCcoef_U(i,k)
        Ccoef_V_out(i,k) = yCcoef_V(i,k)
        Dcoef_U_out(i,k) = yDcoef_U(i,k)
        Dcoef_V_out(i,k) = yDcoef_V(i,k)
        Kcoef_m_out(i,k) = yKcoefm(i,k)
      ENDDO
    ENDDO
    DO i= 1, knon
      alf_1_out(i)   = yalf1(i)
      alf_2_out(i)   = yalf2(i)
    ENDDO
!!!      
       ENDIF  ! (mod(iflag_pbl_split,2) .ge.1)
!!!
    DO j= 1, knon
      i=ni(j)
      Acoef_U(i) = yAcoef_U(j)
      Bcoef_U(i) = yBcoef_U(j)
      Acoef_V(i) = yAcoef_V(j)
      Bcoef_V(i) = yBcoef_V(j)
    ENDDO

    DO k= 1, klev
      DO j= 1, knon
        i=ni(j)
        Ccoef_U(i,k) = yCcoef_U(j,k)
        Ccoef_V(i,k) = yCcoef_V(j,k)
        Dcoef_U(i,k) = yDcoef_U(j,k)
        Dcoef_V(i,k) = yDcoef_V(j,k)
        Kcoefm(i,k) = yKcoefm(j,k)
        alf1(i)   = yalf1(j)
        alf2(i)   = yalf2(j)
      ENDDO
    ENDDO
  END SUBROUTINE climb_wind_down
!
!****************************************************************************************
!
  SUBROUTINE calc_coef(knon, Kcoef, delp, X, alfa1, alfa2, Ccoef, Dcoef, Acoef, Bcoef)
!$gpum horizontal knon

!
! Find the coefficients C and D in fonction of alfa, K and delp
USE yomcst_mod_h
! Input arguments
!****************************************************************************************
    INTEGER, INTENT(IN)                      :: knon
    REAL, DIMENSION(knon,klev), INTENT(IN)   :: Kcoef, delp
    REAL, DIMENSION(knon,klev), INTENT(IN)   :: X
    REAL, DIMENSION(knon), INTENT(IN)        :: alfa1, alfa2

! Output arguments
!****************************************************************************************
    REAL, DIMENSION(knon), INTENT(OUT)       :: Acoef, Bcoef
    REAL, DIMENSION(knon,klev), INTENT(OUT)  :: Ccoef, Dcoef
  
! local variables
!****************************************************************************************
    INTEGER                                  :: k, i, j
    REAL                                     :: buf
    !****************************************************************************************
! 

! Calculate coefficients C and D at top level, k=klev
!
    Ccoef(:,:) = 0.0
    Dcoef(:,:) = 0.0

    DO i = 1, knon
       buf = delp(i,klev) + Kcoef(i,klev)

       Ccoef(i,klev) = X(i,klev)*delp(i,klev)/buf 
       Dcoef(i,klev) = Kcoef(i,klev)/buf
    END DO
    
! 
! Calculate coefficients C and D at top level (klev-1) <= k <= 2
!
    DO k=(klev-1),2,-1
       DO i = 1, knon
          buf = delp(i,k) + Kcoef(i,k) + Kcoef(i,k+1)*(1.-Dcoef(i,k+1))
          
          Ccoef(i,k) = (X(i,k)*delp(i,k) + Kcoef(i,k+1)*Ccoef(i,k+1))/buf
          Dcoef(i,k) = Kcoef(i,k)/buf
       END DO
    END DO

!
! Calculate coeffiecent A and B at surface
!
    DO i = 1, knon
       buf = delp(i,1) + Kcoef(i,2)*(1-Dcoef(i,2))
       Acoef(i) = (X(i,1)*delp(i,1) + Kcoef(i,2)*Ccoef(i,2))/buf
       Bcoef(i) = -RG/buf
    END DO

  END SUBROUTINE calc_coef
!
!****************************************************************************************
!

  SUBROUTINE climb_wind_up(knon, ni, dtime, u_old, v_old, flx_u1, flx_v1,  &
!!! nrlmd le 02/05/2011
       Acoef_U_in, Acoef_V_in, Bcoef_U_in, Bcoef_V_in, &
       Ccoef_U_in, Ccoef_V_in, Dcoef_U_in, Dcoef_V_in, &
       Kcoef_m_in, &
!!!
       flx_u_new, flx_v_new, d_u_new, d_v_new)
!$gpum horizontal knon

!
! Diffuse the wind components from the surface layer and up to the top layer. 
! Coefficents A, B, C and D are known from before. Start values for the diffusion are the
! momentum fluxes at surface.
!
! u(k=1) = A + B*flx*dtime
! u(k)   = C(k) + D(k)*u(k-1)  [2 <= k <= klev]
!
!****************************************************************************************
USE yomcst_mod_h
USE compbl_mod_h
! Input arguments
!****************************************************************************************
    INTEGER, INTENT(IN)                     :: knon
    INTEGER, INTENT(IN)                     :: ni(knon)
    REAL, INTENT(IN)                        :: dtime
    REAL, DIMENSION(knon,klev), INTENT(IN)  :: u_old
    REAL, DIMENSION(knon,klev), INTENT(IN)  :: v_old
    REAL, DIMENSION(knon), INTENT(IN)       :: flx_u1, flx_v1 ! momentum flux

!!! nrlmd le 02/05/2011
    REAL, DIMENSION(knon), INTENT(IN)       :: Acoef_U_in,Acoef_V_in, Bcoef_U_in, Bcoef_V_in
    REAL, DIMENSION(knon,klev), INTENT(IN)  :: Ccoef_U_in, Ccoef_V_in, Dcoef_U_in, Dcoef_V_in
    REAL, DIMENSION(knon,klev), INTENT(IN)  :: Kcoef_m_in
!!!

! Output arguments
!****************************************************************************************
    REAL, DIMENSION(knon,klev), INTENT(OUT) :: flx_u_new, flx_v_new
    REAL, DIMENSION(knon,klev), INTENT(OUT) :: d_u_new, d_v_new

! Local variables
!****************************************************************************************
    REAL :: yalf1(knon)
    REAL :: yalf2(knon)
    REAL :: yKcoefm(knon,klev)
    REAL :: yCcoef_U(knon,klev)
    REAL :: yDcoef_U(knon,klev)
    REAL :: yCcoef_V(knon,klev)
    REAL :: yDcoef_V(knon,klev)
    REAL :: yAcoef_U(knon), yBcoef_U(knon), yAcoef_V(knon), yBcoef_V(knon)

    REAL, DIMENSION(knon,klev)              :: u_new, v_new
    INTEGER                                 :: k, i, j
!****************************************************************************************

!!! jyg le 07/02/2012
!!jyg       IF (mod(iflag_pbl_split,2) .eq.1) THEN
       IF (mod(iflag_pbl_split,10) .ge.1) THEN
!!! nrlmd le 02/05/2011
    DO i = 1, knon
      yAcoef_U(i)=Acoef_U_in(i)
      yAcoef_V(i)=Acoef_V_in(i)
      yBcoef_U(i)=Bcoef_U_in(i)
      yBcoef_V(i)=Bcoef_V_in(i)
    ENDDO
    DO k = 1, klev
      DO i = 1, knon
        yCcoef_U(i,k)=Ccoef_U_in(i,k)
        yCcoef_V(i,k)=Ccoef_V_in(i,k)
        yDcoef_U(i,k)=Dcoef_U_in(i,k)
        yDcoef_V(i,k)=Dcoef_V_in(i,k)
        yKcoefm(i,k)=Kcoef_m_in(i,k)
      ENDDO
    ENDDO
   ELSE
    DO j = 1, knon
      i=ni(j)
      yAcoef_U(j)=Acoef_U(i)
      yAcoef_V(j)=Acoef_V(i)
      yBcoef_U(j)=Bcoef_U(i)
      yBcoef_V(j)=Bcoef_V(i)
    ENDDO
    DO k = 1, klev
      DO j = 1, knon
        i=ni(j)
        yCcoef_U(j,k)=Ccoef_U(i,k)
        yCcoef_V(j,k)=Ccoef_V(i,k)
        yDcoef_U(j,k)=Dcoef_U(i,k)
        yDcoef_V(j,k)=Dcoef_V(i,k)
        yKcoefm(j,k)=Kcoefm(i,k)
      ENDDO
    ENDDO    
!!!
       ENDIF  ! (mod(iflag_pbl_split,2) .ge.1)
!!!

! Niveau 1
    DO i = 1, knon
       u_new(i,1) = yAcoef_U(i) + yBcoef_U(i)*flx_u1(i)*dtime
       v_new(i,1) = yAcoef_V(i) + yBcoef_V(i)*flx_v1(i)*dtime
    END DO

! Niveau 2 jusqu'au sommet klev
    DO k = 2, klev
       DO i=1, knon
          u_new(i,k) = yCcoef_U(i,k) + yDcoef_U(i,k) * u_new(i,k-1)
          v_new(i,k) = yCcoef_V(i,k) + yDcoef_V(i,k) * v_new(i,k-1)
       END DO
    END DO

!****************************************************************************************
! Calcul flux
!
!== flux_u/v est le flux de moment angulaire (positif vers bas)
!== dont l'unite est: (kg m/s)/(m**2 s) 
!
!****************************************************************************************
!
    flx_u_new(:,:) = 0.0
    flx_v_new(:,:) = 0.0

    flx_u_new(1:knon,1)=flx_u1(1:knon)
    flx_v_new(1:knon,1)=flx_v1(1:knon)

! Niveau 2->klev
    DO k = 2, klev
       DO i = 1, knon
          flx_u_new(i,k) = yKcoefm(i,k)/RG/dtime * &
               (u_new(i,k)-u_new(i,k-1))
          
          flx_v_new(i,k) = yKcoefm(i,k)/RG/dtime * &
               (v_new(i,k)-v_new(i,k-1))
       END DO
    END DO

!****************************************************************************************
! Calcul tendances
!
!****************************************************************************************
    d_u_new(:,:) = 0.0
    d_v_new(:,:) = 0.0
    DO k = 1, klev
       DO i = 1, knon
          d_u_new(i,k) = u_new(i,k) - u_old(i,k)
          d_v_new(i,k) = v_new(i,k) - v_old(i,k)
       END DO
    END DO

    DO j = 1, knon
      i=ni(j)
      Acoef_U(i)=yAcoef_U(j)
      Acoef_V(i)=yAcoef_V(j)
      Bcoef_U(i)=yBcoef_U(j)
      Bcoef_V(i)=yBcoef_V(j)
    ENDDO

    DO k = 1, klev
      DO j = 1, knon
        i=ni(j)
        Ccoef_U(i,k) = yCcoef_U(j,k)
        Ccoef_V(i,k) = yCcoef_V(j,k)
        Dcoef_U(i,k) = yDcoef_U(j,k)
        Dcoef_V(i,k) = yDcoef_V(j,k)
        Kcoefm(i,k) = yKcoefm(j,k) 
      ENDDO
    ENDDO    
  
  END SUBROUTINE climb_wind_up
!
!****************************************************************************************
!
END MODULE climb_wind_mod
