module mod_Hallo
USE parallel_lmdz
implicit none
  logical,save :: use_mpi_alloc
  integer, parameter :: MaxProc=512
  integer, parameter :: DefaultMaxBufferSize=1024*1024*100
  integer, SAVE :: MaxBufferSize=0
  integer, parameter :: ListSize=1000
  
  integer,save       :: MaxBufferSize_Used
!$OMP THREADPRIVATE( MaxBufferSize_Used)

   real,save,pointer,dimension(:) :: Buffer
!$OMP THREADPRIVATE(Buffer)

   integer,save,dimension(Listsize) :: Buffer_Pos
   integer,save :: Index_Pos
!$OMP THREADPRIVATE(Buffer_Pos,Index_pos)
   
  type Hallo
    real, dimension(:,:),pointer :: Field
    integer :: offset
    integer :: size
    integer :: NbLevel
    integer :: Stride
  end type Hallo
  
  type request_SR
    integer :: NbRequest=0
    integer :: NbRequestMax=0
    integer :: BufferSize
    integer :: Pos
    integer :: Index 
    type(Hallo), POINTER :: Hallo(:)
    integer :: MSG_Request
  end type request_SR

  type request
    type(request_SR),dimension(0:MaxProc-1) :: RequestSend
    type(request_SR),dimension(0:MaxProc-1) :: RequestRecv
    integer :: tag=1
  end type request
  
   TYPE(distrib),SAVE :: distrib_gather


  INTERFACE Register_SwapField_u
    MODULE PROCEDURE Register_SwapField1d_u,Register_SwapField2d_u1d,Register_SwapField3d_u, &
                     Register_SwapField1d_u_bis,Register_SwapField2d_u1d_bis,Register_SwapField3d_u_bis
  END INTERFACE Register_SwapField_u

  INTERFACE Register_SwapField_v
    MODULE PROCEDURE Register_SwapField1d_v,Register_SwapField2d_v1d,Register_SwapField3d_v,&
                     Register_SwapField1d_v_bis,Register_SwapField2d_v1d_bis,Register_SwapField3d_v_bis
  END INTERFACE Register_SwapField_v

  INTERFACE Register_SwapField2d_u
    MODULE PROCEDURE Register_SwapField1d_u2d,Register_SwapField2d_u2d,Register_SwapField3d_u2d, &
                     Register_SwapField1d_u2d_bis,Register_SwapField2d_u2d_bis,Register_SwapField3d_u2d_bis
  END INTERFACE Register_SwapField2d_u

  INTERFACE Register_SwapField2d_v
    MODULE PROCEDURE Register_SwapField1d_v2d,Register_SwapField2d_v2d,Register_SwapField3d_v2d, &
                     Register_SwapField1d_v2d_bis,Register_SwapField2d_v2d_bis,Register_SwapField3d_v2d_bis
  END INTERFACE Register_SwapField2d_v

  contains

  subroutine Init_mod_hallo
    USE dimensions_mod, ONLY: iim, jjm, llm, ndm
    USE paramet_mod_h
    USE IOIPSL
    implicit none
    integer :: jj_nb_gather(0:mpi_size-1)
    
    Index_Pos=1
    Buffer_Pos(Index_Pos)=1
    MaxBufferSize_Used=0
!$OMP MASTER     
    MaxBufferSize=DefaultMaxBufferSize
    CALL getin("mpi_buffer_size",MaxBufferSize)
!$OMP END MASTER
!$OMP BARRIER
    
    IF (use_mpi_alloc .AND. using_mpi) THEN
      CALL create_global_mpi_buffer
    ELSE 
      CALL create_standard_mpi_buffer
    ENDIF
     
!$OMP MASTER     
     jj_nb_gather(:)=0
     jj_nb_gather(0)=jjp1
     
     CALL create_distrib(jj_nb_gather,distrib_gather) 
!$OMP END MASTER
!$OMP BARRIER

  end subroutine init_mod_hallo

  SUBROUTINE create_standard_mpi_buffer
  IMPLICIT NONE
    
    ALLOCATE(Buffer(MaxBufferSize))
    
  END SUBROUTINE create_standard_mpi_buffer
  
  SUBROUTINE create_global_mpi_buffer
  USE lmdz_mpi
  IMPLICIT NONE
    POINTER (Pbuffer,MPI_Buffer(MaxBufferSize))
    REAL :: MPI_Buffer
    INTEGER(KIND=MPI_ADDRESS_KIND) :: BS 
    INTEGER :: i,ierr

!  Allocation du buffer MPI
      Bs=8*MaxBufferSize
!$OMP CRITICAL (MPI)
      CALL MPI_ALLOC_MEM(BS,MPI_INFO_NULL,Pbuffer,ierr)
!$OMP END CRITICAL (MPI)
      DO i=1,MaxBufferSize
	MPI_Buffer(i)=i
      ENDDO
     
      CALL  Associate_buffer(MPI_Buffer)
      
  CONTAINS
     
     SUBROUTINE Associate_buffer(MPI_Buffer)
     IMPLICIT NONE
       REAL,DIMENSION(:),target :: MPI_Buffer  

         Buffer=>MPI_Buffer
 
      END SUBROUTINE  Associate_buffer
                                      
  END SUBROUTINE create_global_mpi_buffer
 
      
  subroutine allocate_buffer(Size,Index,Pos)
  implicit none
    integer :: Size
    integer :: Index
    integer :: Pos

    if (Buffer_pos(Index_pos)+Size>MaxBufferSize_Used) MaxBufferSize_Used=Buffer_pos(Index_pos)+Size  
    if (Buffer_pos(Index_pos)+Size>MaxBufferSize) then
      print *,'STOP :: La taille de MaxBufferSize dans mod_hallo.F90 est trop petite !!!!'
      CALL abort_gcm("mod_hallo","stopped",1)
    endif
    
    if (Index_pos>=ListSize) then
      print *,'STOP :: La taille de ListSize dans mod_hallo.F90 est trop petite !!!!'
      CALL abort_gcm("mod_hallo","stopped",1)
    endif
     
    Pos=Buffer_Pos(Index_Pos)
    Buffer_Pos(Index_pos+1)=Buffer_Pos(Index_Pos)+Size
    Index_Pos=Index_Pos+1
    Index=Index_Pos
    
  end subroutine allocate_buffer
     
  subroutine deallocate_buffer(Index)
  implicit none
    integer :: Index
    
    Buffer_Pos(Index)=-1
    
    do while (Buffer_Pos(Index_Pos)==-1 .and. Index_Pos>1)
      Index_Pos=Index_Pos-1
    end do

  end subroutine deallocate_buffer  
  
  subroutine SetTag(a_request,tag)
  implicit none
    type(request):: a_request
    integer :: tag
    
    a_request%tag=tag
  end subroutine SetTag
  
  
  subroutine New_Hallo(Field,Stride,NbLevel,offset,size,Ptr_request)
    integer :: Stride
    integer :: NbLevel
    integer :: size
    integer :: offset
    real, dimension(Stride,NbLevel),target :: Field
    type(request_SR),pointer :: Ptr_request
    type(Hallo),POINTER :: NewHallos(:),HalloSwitch(:), NewHallo
    
    Ptr_Request%NbRequest=Ptr_Request%NbRequest+1
    IF(Ptr_Request%NbRequestMax==0) THEN
       Ptr_Request%NbRequestMax=10
       ALLOCATE(Ptr_Request%Hallo(Ptr_Request%NbRequestMax))
    ELSE IF ( Ptr_Request%NbRequest > Ptr_Request%NbRequestMax) THEN
      Ptr_Request%NbRequestMax=INT(Ptr_Request%NbRequestMax*1.2)
      ALLOCATE(NewHallos(Ptr_Request%NbRequestMax))
      NewHallos(1:Ptr_Request%NbRequest-1)=Ptr_Request%hallo(1:Ptr_Request%NbRequest-1)
      HalloSwitch=>Ptr_Request%hallo
      Ptr_Request%hallo=>NewHallos
      DEALLOCATE(HalloSwitch)
    ENDIF
    
    NewHallo=>Ptr_Request%hallo(Ptr_Request%NbRequest)
          
    NewHallo%Field=>Field
    NewHallo%Stride=Stride
    NewHallo%NbLevel=NbLevel
    NewHallo%size=size
    NewHallo%offset=offset
    
  end subroutine New_Hallo
  
  subroutine Register_SendField(Field,ij,ll,offset,size,target,a_request)
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
  implicit none

    
      INTEGER :: ij,ll,offset,size,target
      REAL, dimension(ij,ll) :: Field
      type(request),target :: a_request
      type(request_SR),pointer :: Ptr_request

      Ptr_Request=>a_request%RequestSend(target)

      call New_Hallo(Field,ij,ll,offset,size,Ptr_request)
      
   end subroutine Register_SendField      
      
  subroutine Register_RecvField(Field,ij,ll,offset,size,target,a_request)
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
  implicit none

   
      INTEGER :: ij,ll,offset,size,target
      REAL, dimension(ij,ll) :: Field
      type(request),target :: a_request
      type(request_SR),pointer :: Ptr_request

      Ptr_Request=>a_request%RequestRecv(target)
            
      call New_Hallo(Field,ij,ll,offset,size,Ptr_request)

      
   end subroutine Register_RecvField      
  
  subroutine Register_SwapField(FieldS,FieldR,ij,ll,jj_Nb_New,a_request)
    USE dimensions_mod, ONLY: iim, jjm, llm, ndm
    USE paramet_mod_h
      implicit none

    
    INTEGER :: ij,ll
    REAL, dimension(ij,ll) :: FieldS
    REAL, dimension(ij,ll) :: FieldR
    type(request) :: a_request
    integer,dimension(0:MPI_Size-1) :: jj_Nb_New   
    integer,dimension(0:MPI_Size-1) :: jj_Begin_New,jj_End_New
    
    integer ::i,jje,jjb
    
    jj_begin_New(0)=1
    jj_End_New(0)=jj_Nb_New(0)
    do i=1,MPI_Size-1
      jj_begin_New(i)=jj_end_New(i-1)+1
      jj_end_New(i)=jj_begin_new(i)+jj_Nb_New(i)-1
    enddo
    
    do i=0,MPI_Size-1
      if (i /= MPI_Rank) then
        jjb=max(jj_begin_new(i),jj_begin)
        jje=min(jj_end_new(i),jj_end)
        
        if (ij==ip1jm .and. jje==jjp1) jje=jjm
        
        if (jje >= jjb) then
          call Register_SendField(FieldS,ij,ll,jjb,jje-jjb+1,i,a_request) 
        endif
        
        jjb=max(jj_begin_new(MPI_Rank),jj_begin_Para(i))
        jje=min(jj_end_new(MPI_Rank),jj_end_Para(i))
        
        if (ij==ip1jm .and. jje==jjp1) jje=jjm
        
        if (jje >= jjb) then
          call Register_RecvField(FieldR,ij,ll,jjb,jje-jjb+1,i,a_request) 
        endif
        
      endif
    enddo
    
  end subroutine Register_SwapField    
  

  
  subroutine Register_SwapFieldHallo(FieldS,FieldR,ij,ll,jj_Nb_New,Up,Down,a_request)
    USE dimensions_mod, ONLY: iim, jjm, llm, ndm
    USE paramet_mod_h
      implicit none
    
    INTEGER :: ij,ll,Up,Down
    REAL, dimension(ij,ll) :: FieldS
    REAL, dimension(ij,ll) :: FieldR
    type(request) :: a_request
    integer,dimension(0:MPI_Size-1) :: jj_Nb_New   
    integer,dimension(0:MPI_Size-1) :: jj_Begin_New,jj_End_New
    
    integer ::i,jje,jjb
    
    jj_begin_New(0)=1
    jj_End_New(0)=jj_Nb_New(0)
    do i=1,MPI_Size-1
      jj_begin_New(i)=jj_end_New(i-1)+1
      jj_end_New(i)=jj_begin_new(i)+jj_Nb_New(i)-1
    enddo
    
    do i=0,MPI_Size-1
      jj_begin_New(i)=max(1,jj_begin_New(i)-Up)
      jj_end_New(i)=min(jjp1,jj_end_new(i)+Down)
    enddo
   
    do i=0,MPI_Size-1
      if (i /= MPI_Rank) then
        jjb=max(jj_begin_new(i),jj_begin)
        jje=min(jj_end_new(i),jj_end)
        
        if (ij==ip1jm .and. jje==jjp1) jje=jjm
        
        if (jje >= jjb) then
          call Register_SendField(FieldS,ij,ll,jjb,jje-jjb+1,i,a_request) 
        endif
        
        jjb=max(jj_begin_new(MPI_Rank),jj_begin_Para(i))
        jje=min(jj_end_new(MPI_Rank),jj_end_Para(i))
        
        if (ij==ip1jm .and. jje==jjp1) jje=jjm
        
        if (jje >= jjb) then
          call Register_RecvField(FieldR,ij,ll,jjb,jje-jjb+1,i,a_request) 
        endif
        
      endif
    enddo
    
  end subroutine Register_SwapFieldHallo



  SUBROUTINE Register_SwapField1d_u(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    REAL, DIMENSION(current_dist%ijb_u:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_u:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down

    CALL  Register_SwapField_gen_u(FieldS,FieldR,1,current_dist,new_dist,halo_up,halo_down,a_request)
        
  END SUBROUTINE  Register_SwapField1d_u 

  SUBROUTINE Register_SwapField1d_u_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    TYPE(distrib),INTENT(IN)          :: old_dist
    REAL, DIMENSION(old_dist%ijb_u:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_u:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down

    CALL  Register_SwapField_gen_u(FieldS,FieldR,1,old_dist,new_dist,halo_up,halo_down,a_request)
        
  END SUBROUTINE  Register_SwapField1d_u_bis 


  SUBROUTINE Register_SwapField2d_u1d(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
    IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    REAL, DIMENSION(current_dist%ijb_u:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_u:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,2)
    
    CALL  Register_SwapField_gen_u(FieldS,FieldR,ll,current_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField2d_u1d

  SUBROUTINE Register_SwapField2d_u1d_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
    IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    TYPE(distrib),INTENT(IN) :: old_dist
    REAL, DIMENSION(old_dist%ijb_u:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_u:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,2)
    
    CALL  Register_SwapField_gen_u(FieldS,FieldR,ll,old_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField2d_u1d_bis
   

  SUBROUTINE Register_SwapField3d_u(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    REAL, DIMENSION(current_dist%ijb_u:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_u:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,2)*size(FieldS,3)
    
    CALL  Register_SwapField_gen_u(FieldS,FieldR,ll,current_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField3d_u 

  SUBROUTINE Register_SwapField3d_u_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    TYPE(distrib),INTENT(IN) :: old_dist
    REAL, DIMENSION(old_dist%ijb_u:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_u:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,2)*size(FieldS,3)
    
    CALL  Register_SwapField_gen_u(FieldS,FieldR,ll,old_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField3d_u_bis 
  


 SUBROUTINE Register_SwapField1d_u2d(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE

    TYPE(distrib),INTENT(IN)          :: new_dist !LF
    REAL, DIMENSION(current_dist%jjb_u:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_u:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down

    CALL  Register_SwapField_gen_u(FieldS,FieldR,1,current_dist,new_dist,halo_up,halo_down,a_request)
        
  END SUBROUTINE  Register_SwapField1d_u2d 

 SUBROUTINE Register_SwapField1d_u2d_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE

    TYPE(distrib),INTENT(IN)          :: new_dist !LF
    TYPE(distrib),INTENT(IN)          :: old_dist
    REAL, DIMENSION(old_dist%jjb_u:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_u:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down

    CALL  Register_SwapField_gen_u(FieldS,FieldR,1,old_dist,new_dist,halo_up,halo_down,a_request)
        
  END SUBROUTINE  Register_SwapField1d_u2d_bis 


  SUBROUTINE Register_SwapField2d_u2d(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    REAL, DIMENSION(current_dist%jjb_u:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_u:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,3)
    
    CALL  Register_SwapField_gen_u(FieldS,FieldR,ll,current_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField2d_u2d

  SUBROUTINE Register_SwapField2d_u2d_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    TYPE(distrib),INTENT(IN) :: old_dist
    REAL, DIMENSION(old_dist%jjb_u:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_u:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,3)
    
    CALL  Register_SwapField_gen_u(FieldS,FieldR,ll,old_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField2d_u2d_bis
   

  SUBROUTINE Register_SwapField3d_u2d(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    REAL, DIMENSION(current_dist%jjb_u:,:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_u:,:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,3)*size(FieldS,4)
    
    CALL  Register_SwapField_gen_u(FieldS,FieldR,ll,current_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField3d_u2d 

  SUBROUTINE Register_SwapField3d_u2d_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    TYPE(distrib),INTENT(IN) :: old_dist
    REAL, DIMENSION(old_dist%jjb_u:,:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_u:,:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,3)*size(FieldS,4)
    
    CALL  Register_SwapField_gen_u(FieldS,FieldR,ll,old_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField3d_u2d_bis 







  SUBROUTINE Register_SwapField1d_v(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    REAL, DIMENSION(current_dist%ijb_v:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_v:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down

    CALL  Register_SwapField_gen_v(FieldS,FieldR,1,current_dist,new_dist,halo_up,halo_down,a_request)
        
  END SUBROUTINE  Register_SwapField1d_v 

  SUBROUTINE Register_SwapField1d_v_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    TYPE(distrib),INTENT(IN) :: old_dist
    REAL, DIMENSION(old_dist%ijb_v:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_v:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down

    CALL  Register_SwapField_gen_v(FieldS,FieldR,1,old_dist,new_dist,halo_up,halo_down,a_request)
        
  END SUBROUTINE  Register_SwapField1d_v_bis 


  SUBROUTINE Register_SwapField2d_v1d(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
   
    TYPE(distrib),INTENT(IN)          :: new_dist
    REAL, DIMENSION(current_dist%ijb_v:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_v:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,2)
    
    CALL  Register_SwapField_gen_v(FieldS,FieldR,ll,current_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField2d_v1d
  
  SUBROUTINE Register_SwapField2d_v1d_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
   
    TYPE(distrib),INTENT(IN)          :: new_dist
    TYPE(distrib),INTENT(IN)          :: old_dist
    REAL, DIMENSION(old_dist%ijb_v:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_v:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,2)
    
    CALL  Register_SwapField_gen_v(FieldS,FieldR,ll,old_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField2d_v1d_bis
  
   

  SUBROUTINE Register_SwapField3d_v(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    REAL, DIMENSION(current_dist%ijb_v:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_v:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,2)*size(FieldS,3)
    
    CALL  Register_SwapField_gen_v(FieldS,FieldR,ll,current_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField3d_v 

  SUBROUTINE Register_SwapField3d_v_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    TYPE(distrib),INTENT(IN) :: old_dist
    REAL, DIMENSION(old_dist%ijb_v:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%ijb_v:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,2)*size(FieldS,3)
    
    CALL  Register_SwapField_gen_v(FieldS,FieldR,ll,old_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField3d_v_bis 




  SUBROUTINE Register_SwapField1d_v2d(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist !LF
    REAL, DIMENSION(current_dist%jjb_v:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_v:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down

    CALL  Register_SwapField_gen_v(FieldS,FieldR,1,current_dist,new_dist,halo_up,halo_down,a_request)
        
  END SUBROUTINE  Register_SwapField1d_v2d

  SUBROUTINE Register_SwapField1d_v2d_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist !LF
    TYPE(distrib),INTENT(IN) :: old_dist
    REAL, DIMENSION(old_dist%jjb_v:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_v:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down

    CALL  Register_SwapField_gen_v(FieldS,FieldR,1,old_dist,new_dist,halo_up,halo_down,a_request)
        
  END SUBROUTINE  Register_SwapField1d_v2d_bis


  SUBROUTINE Register_SwapField2d_v2d(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    REAL, DIMENSION(current_dist%jjb_v:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_v:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,3)
    
    CALL  Register_SwapField_gen_v(FieldS,FieldR,ll,current_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField2d_v2d
   
  SUBROUTINE Register_SwapField2d_v2d_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    TYPE(distrib),INTENT(IN) :: old_dist
    REAL, DIMENSION(old_dist%jjb_v:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_v:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,3)
    
    CALL  Register_SwapField_gen_v(FieldS,FieldR,ll,old_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField2d_v2d_bis
   

  SUBROUTINE Register_SwapField3d_v2d(FieldS,FieldR,new_dist,a_request,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    REAL, DIMENSION(current_dist%jjb_v:,:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_v:,:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,3)*size(FieldS,4)
    
    CALL  Register_SwapField_gen_v(FieldS,FieldR,ll,current_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField3d_v2d 
  
  SUBROUTINE Register_SwapField3d_v2d_bis(FieldS,FieldR,new_dist,a_request,old_dist,up,down)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
      IMPLICIT NONE
    
    TYPE(distrib),INTENT(IN)          :: new_dist
    TYPE(distrib),INTENT(IN) :: old_dist
    REAL, DIMENSION(old_dist%jjb_v:,:,:,:),INTENT(IN)     :: FieldS
    REAL, DIMENSION(new_dist%jjb_v:,:,:,:),INTENT(OUT)    :: FieldR
    INTEGER,OPTIONAL,INTENT(IN)       :: up
    INTEGER,OPTIONAL,INTENT(IN)       :: down      
    TYPE(request),INTENT(INOUT)         :: a_request

    INTEGER                           :: halo_up
    INTEGER                           :: halo_down
    INTEGER                           :: ll
        
    
    halo_up=0
    halo_down=0
    IF (PRESENT(up))   halo_up=up
    IF (PRESENT(down)) halo_down=down
    
    ll=size(FieldS,3)*size(FieldS,4)
    
    CALL  Register_SwapField_gen_v(FieldS,FieldR,ll,old_dist,new_dist,halo_up,halo_down,a_request)
    
  END SUBROUTINE  Register_SwapField3d_v2d_bis 
  
  

  SUBROUTINE Register_SwapField_gen_u(FieldS,FieldR,ll,old_dist,new_dist,Up,Down,a_request)
    USE parallel_lmdz
    USE dimensions_mod, ONLY: iim, jjm, llm, ndm
    USE paramet_mod_h
      IMPLICIT NONE
   
    INTEGER :: ll,Up,Down
    TYPE(distrib)  :: old_dist
    TYPE(distrib)  :: new_dist
    REAL, DIMENSION(old_dist%ijb_u:old_dist%ije_u,ll) :: FieldS
    REAL, DIMENSION(new_dist%ijb_u:new_dist%ije_u,ll) :: FieldR
    TYPE(request) :: a_request
    INTEGER,DIMENSION(0:MPI_Size-1) :: jj_Nb_New   
    INTEGER,DIMENSION(0:MPI_Size-1) :: jj_Begin_New,jj_End_New
    
    INTEGER ::i,l,jje,jjb,ijb,ije
    
    DO i=0,MPI_Size-1
      jj_begin_New(i)=max(1,new_dist%jj_begin_para(i)-Up)
      jj_end_New(i)=min(jjp1,new_dist%jj_end_para(i)+Down)
    ENDDO
   
    DO i=0,MPI_Size-1
      IF (i /= MPI_Rank) THEN
        jjb=max(jj_begin_new(i),old_dist%jj_begin)
        jje=min(jj_end_new(i),old_dist%jj_end)
        
        IF (jje >= jjb) THEN
          CALL Register_SendField(FieldS,old_dist%ijnb_u,ll,jjb-old_dist%jjb_u+1,jje-jjb+1,i,a_request) 
        ENDIF
        
        jjb=max(jj_begin_new(MPI_Rank),old_dist%jj_begin_Para(i))
        jje=min(jj_end_new(MPI_Rank),old_dist%jj_end_Para(i))
        
        IF (jje >= jjb) THEN
          CALL Register_RecvField(FieldR,new_dist%ijnb_u,ll,jjb-new_dist%jjb_u+1,jje-jjb+1,i,a_request) 
        ENDIF
      ELSE
        jjb=max(jj_begin_new(i),old_dist%jj_begin)
        jje=min(jj_end_new(i),old_dist%jj_end)
        ijb=(jjb-1)*iip1+1
        ije=jje*iip1
!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)           
        DO l=1,ll
          FieldR(ijb:ije,l)=FieldS(ijb:ije,l)              
        ENDDO
!$OMP END DO NOWAIT
      ENDIF
    ENDDO
    
  END SUBROUTINE Register_SwapField_gen_u



  SUBROUTINE Register_SwapField_gen_v(FieldS,FieldR,ll,old_dist,new_dist,Up,Down,a_request)
  USE parallel_lmdz
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
  USE paramet_mod_h
    IMPLICIT NONE
    
    INTEGER :: ll,Up,Down
    TYPE(distrib)  :: old_dist
    TYPE(distrib)  :: new_dist
    REAL, DIMENSION(old_dist%ijb_v:old_dist%ije_v,ll) :: FieldS
    REAL, DIMENSION(new_dist%ijb_v:new_dist%ije_v,ll) :: FieldR
    TYPE(request) :: a_request
    INTEGER,DIMENSION(0:MPI_Size-1) :: jj_Nb_New   
    INTEGER,DIMENSION(0:MPI_Size-1) :: jj_Begin_New,jj_End_New
    
    INTEGER ::i,l,jje,jjb,ijb,ije
    
    DO i=0,MPI_Size-1
      jj_begin_New(i)=max(1,new_dist%jj_begin_para(i)-Up)
      jj_end_New(i)=min(jjp1,new_dist%jj_end_para(i)+Down)
    ENDDO
   
    DO i=0,MPI_Size-1
      IF (i /= MPI_Rank) THEN
        jjb=max(jj_begin_new(i),old_dist%jj_begin)
        jje=min(jj_end_new(i),old_dist%jj_end)

        IF (jje==jjp1) jje=jjm        

        IF (jje >= jjb) THEN
          CALL Register_SendField(FieldS,old_dist%ijnb_v,ll,jjb-old_dist%jjb_v+1,jje-jjb+1,i,a_request) 
        ENDIF
        
        jjb=max(jj_begin_new(MPI_Rank),old_dist%jj_begin_Para(i))
        jje=min(jj_end_new(MPI_Rank),old_dist%jj_end_Para(i))

        IF (jje==jjp1) jje=jjm
        
        IF (jje >= jjb) THEN
          CALL Register_RecvField(FieldR,new_dist%ijnb_v,ll,jjb-new_dist%jjb_v+1,jje-jjb+1,i,a_request) 
        ENDIF
      ELSE
        jjb=max(jj_begin_new(i),old_dist%jj_begin)
        jje=min(jj_end_new(i),old_dist%jj_end)
        IF (jje==jjp1) jje=jjm
        ijb=(jjb-1)*iip1+1
        ije=jje*iip1
!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)           
        DO l=1,ll
          FieldR(ijb:ije,l)=FieldS(ijb:ije,l)
        ENDDO              
!$OMP END DO NOWAIT
      ENDIF
    ENDDO
    
  END SUBROUTINE Register_SwapField_gen_v


 

  
  subroutine Register_Hallo(Field,ij,ll,RUp,Rdown,SUp,SDown,a_request)
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
  USE lmdz_mpi
      implicit none

      INTEGER :: ij,ll
      REAL, dimension(ij,ll) :: Field
      INTEGER :: Sup,Sdown,rup,rdown
      type(request) :: a_request
      type(Hallo),pointer :: PtrHallo
      LOGICAL :: SendUp,SendDown
      LOGICAL :: RecvUp,RecvDown
   
 
      SendUp=.TRUE.
      SendDown=.TRUE.
      RecvUp=.TRUE.
      RecvDown=.TRUE.
        
      IF (pole_nord) THEN
        SendUp=.FALSE.
        RecvUp=.FALSE.
      ENDIF
  
      IF (pole_sud) THEN
        SendDown=.FALSE.
        RecvDown=.FALSE.
      ENDIF
      
      if (Sup.eq.0) then
        SendUp=.FALSE.
       endif
      
      if (Sdown.eq.0) then
        SendDown=.FALSE.
      endif

      if (Rup.eq.0) then
        RecvUp=.FALSE.
      endif
      
      if (Rdown.eq.0) then
        RecvDown=.FALSE.
      endif
      
      IF (SendUp) THEN
        call Register_SendField(Field,ij,ll,jj_begin,SUp,MPI_Rank-1,a_request)
      ENDIF
  
      IF (SendDown) THEN
        call Register_SendField(Field,ij,ll,jj_end-SDown+1,SDown,MPI_Rank+1,a_request)
      ENDIF
    
  
      IF (RecvUp) THEN
        call Register_RecvField(Field,ij,ll,jj_begin-Rup,RUp,MPI_Rank-1,a_request)
      ENDIF
  
      IF (RecvDown) THEN
        call Register_RecvField(Field,ij,ll,jj_end+1,RDown,MPI_Rank+1,a_request)
      ENDIF
  
    end subroutine Register_Hallo


  subroutine Register_Hallo_u(Field,ll,RUp,Rdown,SUp,SDown,a_request)
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
  USE lmdz_mpi
      implicit none
      INTEGER :: ll
      REAL, dimension(ijb_u:ije_u,ll) :: Field
      INTEGER :: Sup,Sdown,rup,rdown
      type(request) :: a_request
      type(Hallo),pointer :: PtrHallo
      LOGICAL :: SendUp,SendDown
      LOGICAL :: RecvUp,RecvDown
   
 
      SendUp=.TRUE.
      SendDown=.TRUE.
      RecvUp=.TRUE.
      RecvDown=.TRUE.
        
      IF (pole_nord) THEN
        SendUp=.FALSE.
        RecvUp=.FALSE.
      ENDIF
  
      IF (pole_sud) THEN
        SendDown=.FALSE.
        RecvDown=.FALSE.
      ENDIF
      
      if (Sup.eq.0) then
        SendUp=.FALSE.
       endif
      
      if (Sdown.eq.0) then
        SendDown=.FALSE.
      endif

      if (Rup.eq.0) then
        RecvUp=.FALSE.
      endif
      
      if (Rdown.eq.0) then
        RecvDown=.FALSE.
      endif
      
      IF (SendUp) THEN
        call Register_SendField(Field,ijnb_u,ll,jj_begin-jjb_u+1,SUp,MPI_Rank-1,a_request)
      ENDIF
  
      IF (SendDown) THEN
        call Register_SendField(Field,ijnb_u,ll,jj_end-SDown+1-jjb_u+1,SDown,MPI_Rank+1,a_request)
      ENDIF
    
  
      IF (RecvUp) THEN
        call Register_RecvField(Field,ijnb_u,ll,jj_begin-Rup-jjb_u+1,RUp,MPI_Rank-1,a_request)
      ENDIF
  
      IF (RecvDown) THEN
        call Register_RecvField(Field,ijnb_u,ll,jj_end+1-jjb_u+1,RDown,MPI_Rank+1,a_request)
      ENDIF
  
    end subroutine Register_Hallo_u

  subroutine Register_Hallo_v(Field,ll,RUp,Rdown,SUp,SDown,a_request)
  USE dimensions_mod, ONLY: iim, jjm, llm, ndm
  USE lmdz_mpi
      implicit none
      INTEGER :: ll
      REAL, dimension(ijb_v:ije_v,ll) :: Field
      INTEGER :: Sup,Sdown,rup,rdown
      type(request) :: a_request
      type(Hallo),pointer :: PtrHallo
      LOGICAL :: SendUp,SendDown
      LOGICAL :: RecvUp,RecvDown
   
 
      SendUp=.TRUE.
      SendDown=.TRUE.
      RecvUp=.TRUE.
      RecvDown=.TRUE.
        
      IF (pole_nord) THEN
        SendUp=.FALSE.
        RecvUp=.FALSE.
      ENDIF
  
      IF (pole_sud) THEN
        SendDown=.FALSE.
        RecvDown=.FALSE.
      ENDIF
      
      if (Sup.eq.0) then
        SendUp=.FALSE.
       endif
      
      if (Sdown.eq.0) then
        SendDown=.FALSE.
      endif

      if (Rup.eq.0) then
        RecvUp=.FALSE.
      endif
      
      if (Rdown.eq.0) then
        RecvDown=.FALSE.
      endif
      
      IF (SendUp) THEN
        call Register_SendField(Field,ijnb_v,ll,jj_begin-jjb_v+1,SUp,MPI_Rank-1,a_request)
      ENDIF
  
      IF (SendDown) THEN
        call Register_SendField(Field,ijnb_v,ll,jj_end-SDown+1-jjb_v+1,SDown,MPI_Rank+1,a_request)
      ENDIF
    
  
      IF (RecvUp) THEN
        call Register_RecvField(Field,ijnb_v,ll,jj_begin-Rup-jjb_v+1,RUp,MPI_Rank-1,a_request)
      ENDIF
  
      IF (RecvDown) THEN
        call Register_RecvField(Field,ijnb_v,ll,jj_end+1-jjb_v+1,RDown,MPI_Rank+1,a_request)
      ENDIF
  
    end subroutine Register_Hallo_v
    
    subroutine SendRequest(a_Request)
    USE dimensions_mod, ONLY: iim, jjm, llm, ndm
    USE paramet_mod_h
    USE lmdz_mpi
      implicit none

      type(request),target :: a_request
      type(request_SR),pointer :: Req
      type(Hallo),pointer :: PtrHallo
      integer :: SizeBuffer
      integer :: i,rank,l,ij,Pos,ierr
      integer :: offset
      real,dimension(:,:),pointer :: Field
      integer :: Nb
       
      do rank=0,MPI_SIZE-1
      
        Req=>a_Request%RequestSend(rank)
        
        SizeBuffer=0
        do i=1,Req%NbRequest
          PtrHallo=>Req%Hallo(i)
!$OMP DO SCHEDULE(STATIC,OMP_CHUNK) 
          DO l=1,PtrHallo%NbLevel
            SizeBuffer=SizeBuffer+PtrHallo%size*iip1
          ENDDO
!$OMP ENDDO NOWAIT          
        enddo
      
         Req%BufferSize=SizeBuffer
         if (Req%NbRequest>0) then
       
          call allocate_buffer(SizeBuffer,Req%Index,Req%pos)

          Pos=Req%Pos
          do i=1,Req%NbRequest
            PtrHallo=>Req%Hallo(i)
            offset=(PtrHallo%offset-1)*iip1+1
            Nb=iip1*PtrHallo%size-1
            Field=>PtrHallo%Field

!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)           
            do l=1,PtrHallo%NbLevel
!cdir NODEP
              do ij=0,Nb
	        Buffer(Pos+ij)=Field(Offset+ij,l)
	      enddo
              
              Pos=Pos+Nb+1
            enddo
!$OMP END DO NOWAIT            
          enddo
    
         if (SizeBuffer>0) then
!$OMP CRITICAL (MPI)
         
         call MPI_ISEND(Buffer(req%Pos),SizeBuffer,MPI_REAL_LMDZ,rank,a_request%tag+1000*omp_rank,     &
                         COMM_LMDZ,Req%MSG_Request,ierr)
         IF (.NOT.using_mpi) THEN
           PRINT *,'Erreur, echange MPI en mode sequentiel !!!'
           CALL abort_gcm("mod_hallo","stopped",1)
         ENDIF
!         PRINT *,"-------------------------------------------------------------------"
!         PRINT *,"Process de rang",mpi_rank,"Task : ",omp_rank,"--->"
!         PRINT *,"Requete envoye au proc :",rank,"tag :",a_request%tag+1000*omp_rank
!         PRINT *,"Taille du message :",SizeBuffer,"requete no :",Req%MSG_Request
!         PRINT *,"-------------------------------------------------------------------"
!$OMP END CRITICAL (MPI)
        endif
       endif
    enddo
   
           
      do rank=0,MPI_SIZE-1
         
          Req=>a_Request%RequestRecv(rank)
          SizeBuffer=0
          
	  do i=1,Req%NbRequest
            PtrHallo=>Req%Hallo(i)

!$OMP DO SCHEDULE(STATIC,OMP_CHUNK) 
            DO l=1,PtrHallo%NbLevel
              SizeBuffer=SizeBuffer+PtrHallo%size*iip1
            ENDDO
!$OMP ENDDO NOWAIT          
          enddo
          
          Req%BufferSize=SizeBuffer
          
          if (Req%NbRequest>0) then
          call allocate_buffer(SizeBuffer,Req%Index,Req%Pos)
   
          if (SizeBuffer>0) then

!$OMP CRITICAL (MPI)

             call MPI_IRECV(Buffer(Req%Pos),SizeBuffer,MPI_REAL_LMDZ,rank,a_request%tag+1000*omp_rank,     &
                           COMM_LMDZ,Req%MSG_Request,ierr)

             IF (.NOT.using_mpi) THEN
               PRINT *,'Erreur, echange MPI en mode sequentiel !!!'
               CALL abort_gcm("mod_hallo","stopped",1)
             ENDIF

!         PRINT *,"-------------------------------------------------------------------"
!         PRINT *,"Process de rang",mpi_rank,"Task : ",omp_rank,"--->"
!         PRINT *,"Requete en attente du proc :",rank,"tag :",a_request%tag+1000*omp_rank
!         PRINT *,"Taille du message :",SizeBuffer,"requete no :",Req%MSG_Request
!         PRINT *,"-------------------------------------------------------------------"

!$OMP END CRITICAL (MPI)
          endif
        endif
      
      enddo
                        
   end subroutine SendRequest 
   
   subroutine WaitRequest(a_Request)
   USE dimensions_mod, ONLY: iim, jjm, llm, ndm
   USE paramet_mod_h
   USE lmdz_mpi
   implicit none
      
      type(request),target :: a_request
      type(request_SR),pointer :: Req
      type(Hallo),pointer :: PtrHallo
      integer, dimension(2*mpi_size) :: TabRequest
      integer, dimension(MPI_STATUS_SIZE,2*mpi_size) :: TabStatus
      integer :: NbRequest
      integer :: i,rank,pos,ij,l,ierr
      integer :: offset
      integer :: Nb
      
      
      NbRequest=0
      do rank=0,MPI_SIZE-1
        Req=>a_request%RequestSend(rank)
        if (Req%NbRequest>0 .AND. Req%BufferSize > 0) then
          NbRequest=NbRequest+1
          TabRequest(NbRequest)=Req%MSG_Request
        endif
      enddo
      
      do rank=0,MPI_SIZE-1
        Req=>a_request%RequestRecv(rank)
        if (Req%NbRequest>0 .AND. Req%BufferSize > 0 ) then
          NbRequest=NbRequest+1
          TabRequest(NbRequest)=Req%MSG_Request
        endif
      enddo
     
      if (NbRequest>0) then
!$OMP CRITICAL (MPI)
!        PRINT *,"-------------------------------------------------------------------"
!        PRINT *,"Process de rang",mpi_rank,"Task : ",omp_rank,"--->",NbRequest,"en attente"
!        PRINT *,"No des requetes :",TabRequest(1:NbRequest)
        call MPI_WAITALL(NbRequest,TabRequest,TabStatus,ierr)
!        PRINT *,"Process de rang",mpi_rank,"Task : ",omp_rank,"--->",NbRequest,"complete"
!        PRINT *,"-------------------------------------------------------------------"
!$OMP END CRITICAL (MPI)
      endif
      do rank=0,MPI_Size-1
        Req=>a_request%RequestRecv(rank)
        if (Req%NbRequest>0) then
          Pos=Req%Pos
          do i=1,Req%NbRequest
            PtrHallo=>Req%Hallo(i)
            offset=(PtrHallo%offset-1)*iip1+1
	    Nb=iip1*PtrHallo%size-1

!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)            
	    do l=1,PtrHallo%NbLevel
!cdir NODEP
              do ij=0,Nb
	        PtrHallo%Field(offset+ij,l)=Buffer(Pos+ij)
	      enddo

              Pos=Pos+Nb+1
	    enddo
!$OMP ENDDO NOWAIT	    
          enddo
        endif
      enddo
      
      do rank=0,MPI_SIZE-1
        Req=>a_request%RequestSend(rank)
        if (Req%NbRequest>0) then
          call deallocate_buffer(Req%Index)
          Req%NbRequest=0 
        endif
      enddo
              
      do rank=0,MPI_SIZE-1
        Req=>a_request%RequestRecv(rank)
        if (Req%NbRequest>0) then
          call deallocate_buffer(Req%Index)
          Req%NbRequest=0 
        endif
      enddo
     
      a_request%tag=1
    end subroutine WaitRequest
     
   subroutine WaitSendRequest(a_Request)
   USE lmdz_mpi
   USE dimensions_mod, ONLY: iim, jjm, llm, ndm
   implicit none
   
      type(request),target :: a_request
      type(request_SR),pointer :: Req
      type(Hallo),pointer :: PtrHallo
      integer, dimension(mpi_size) :: TabRequest
      integer, dimension(MPI_STATUS_SIZE,mpi_size) :: TabStatus
      integer :: NbRequest
      integer :: i,rank,pos,ij,l,ierr
      integer :: offset
      
      
      NbRequest=0
      do rank=0,MPI_SIZE-1
        Req=>a_request%RequestSend(rank)
        if (Req%NbRequest>0) then
          NbRequest=NbRequest+1
          TabRequest(NbRequest)=Req%MSG_Request
        endif
      enddo
      

      if (NbRequest>0 .AND. Req%BufferSize > 0 ) THEN 
!$OMP CRITICAL (MPI)     
!        PRINT *,"-------------------------------------------------------------------"
!        PRINT *,"Process de rang",mpi_rank,"Task : ",omp_rank,"--->",NbRequest,"en attente"
!        PRINT *,"No des requetes :",TabRequest(1:NbRequest)
         call MPI_WAITALL(NbRequest,TabRequest,TabStatus,ierr)
!        PRINT *,"Process de rang",mpi_rank,"Task : ",omp_rank,"--->",NbRequest,"complete"
!        PRINT *,"-------------------------------------------------------------------"

!$OMP END CRITICAL (MPI)
      endif      
      
      do rank=0,MPI_SIZE-1
        Req=>a_request%RequestSend(rank)
        if (Req%NbRequest>0) then
          call deallocate_buffer(Req%Index)
          Req%NbRequest=0 
        endif
      enddo
              
      a_request%tag=1
    end subroutine WaitSendRequest
    
   subroutine WaitRecvRequest(a_Request)
   USE dimensions_mod, ONLY: iim, jjm, llm, ndm
   USE paramet_mod_h
   USE lmdz_mpi
   implicit none
      type(request),target :: a_request
      type(request_SR),pointer :: Req
      type(Hallo),pointer :: PtrHallo
      integer, dimension(mpi_size) :: TabRequest
      integer, dimension(MPI_STATUS_SIZE,mpi_size) :: TabStatus
      integer :: NbRequest
      integer :: i,rank,pos,ij,l,ierr
      integer :: offset,Nb
      
      
      NbRequest=0
      
      do rank=0,MPI_SIZE-1
        Req=>a_request%RequestRecv(rank)
        if (Req%NbRequest>0 .AND. Req%BufferSize > 0 ) then
          NbRequest=NbRequest+1
          TabRequest(NbRequest)=Req%MSG_Request
        endif
      enddo
     
      
      if (NbRequest>0) then
!$OMP CRITICAL (MPI)     
!        PRINT *,"-------------------------------------------------------------------"
!        PRINT *,"Process de rang",mpi_rank,"Task : ",omp_rank,"--->",NbRequest,"en attente"
!        PRINT *,"No des requetes :",TabRequest(1:NbRequest)
         call MPI_WAITALL(NbRequest,TabRequest,TabStatus,ierr)
!        PRINT *,"Process de rang",mpi_rank,"Task : ",omp_rank,"--->",NbRequest,"complete"
!        PRINT *,"-------------------------------------------------------------------"
!$OMP END CRITICAL (MPI)     
      endif
      
      do rank=0,MPI_Size-1
        Req=>a_request%RequestRecv(rank)
        if (Req%NbRequest>0) then
          Pos=Req%Pos
          do i=1,Req%NbRequest
            PtrHallo=>Req%Hallo(i)
            offset=(PtrHallo%offset-1)*iip1+1
	    Nb=iip1*PtrHallo%size-1
!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)            
	    do l=1,PtrHallo%NbLevel
!cdir NODEP
              do ij=0,Nb
	        PtrHallo%Field(offset+ij,l)=Buffer(Pos+ij)
	      enddo
                 Pos=Pos+Nb+1
            enddo
!$OMP END DO NOWAIT
          enddo
        endif
      enddo
      
           
      do rank=0,MPI_SIZE-1
        Req=>a_request%RequestRecv(rank)
        if (Req%NbRequest>0) then
          call deallocate_buffer(Req%Index)
          Req%NbRequest=0 
        endif
      enddo
     
      a_request%tag=1
    end subroutine WaitRecvRequest
    
    
    
    subroutine CopyField(FieldS,FieldR,ij,ll,jj_Nb_New)
    USE dimensions_mod, ONLY: iim, jjm, llm, ndm
    USE paramet_mod_h
      implicit none
    
    INTEGER :: ij,ll,l
    REAL, dimension(ij,ll) :: FieldS
    REAL, dimension(ij,ll) :: FieldR
    integer,dimension(0:MPI_Size-1) :: jj_Nb_New   
    integer,dimension(0:MPI_Size-1) :: jj_Begin_New,jj_End_New
    
    integer ::i,jje,jjb,ijb,ije
    
    jj_begin_New(0)=1
    jj_End_New(0)=jj_Nb_New(0)
    do i=1,MPI_Size-1
      jj_begin_New(i)=jj_end_New(i-1)+1
      jj_end_New(i)=jj_begin_new(i)+jj_Nb_New(i)-1
    enddo
    
    jjb=max(jj_begin,jj_begin_new(MPI_Rank))
    jje=min(jj_end,jj_end_new(MPI_Rank))
    if (ij==ip1jm) jje=min(jje,jjm)

    if (jje >= jjb) then
      ijb=(jjb-1)*iip1+1
      ije=jje*iip1

!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)
      do l=1,ll
        FieldR(ijb:ije,l)=FieldS(ijb:ije,l)
      enddo
!$OMP ENDDO NOWAIT
    endif


  end subroutine CopyField    

  subroutine CopyFieldHallo(FieldS,FieldR,ij,ll,jj_Nb_New,Up,Down)
    USE dimensions_mod, ONLY: iim, jjm, llm, ndm
    USE paramet_mod_h
      implicit none
    
    INTEGER :: ij,ll,Up,Down
    REAL, dimension(ij,ll) :: FieldS
    REAL, dimension(ij,ll) :: FieldR
    integer,dimension(0:MPI_Size-1) :: jj_Nb_New   
    integer,dimension(0:MPI_Size-1) :: jj_Begin_New,jj_End_New

    integer ::i,jje,jjb,ijb,ije,l

     
    jj_begin_New(0)=1
    jj_End_New(0)=jj_Nb_New(0)
    do i=1,MPI_Size-1
      jj_begin_New(i)=jj_end_New(i-1)+1
      jj_end_New(i)=jj_begin_new(i)+jj_Nb_New(i)-1
    enddo

        
    jjb=max(jj_begin,jj_begin_new(MPI_Rank)-Up)
    jje=min(jj_end,jj_end_new(MPI_Rank)+Down)
    if (ij==ip1jm) jje=min(jje,jjm)
    
    
    if (jje >= jjb) then
      ijb=(jjb-1)*iip1+1
      ije=jje*iip1

!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)
      do l=1,ll
        FieldR(ijb:ije,l)=FieldS(ijb:ije,l)
      enddo
!$OMP ENDDO NOWAIT

    endif
   end subroutine CopyFieldHallo        

   subroutine Gather_field_u(field_loc,field_glo,ll)
   USE dimensions_mod, ONLY: iim, jjm, llm, ndm
   USE paramet_mod_h
   implicit none
     integer :: ll
     real :: field_loc(ijb_u:ije_u,ll)
     real :: field_glo(ip1jmp1,ll)
     type(request) :: request_gather
     integer       :: l


!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)
     DO l=1,ll
       field_glo(ij_begin:ij_end,l)=field_loc(ij_begin:ij_end,l)
     ENDDO
     
     call register_SwapField(field_glo,field_glo,ip1jmp1,ll,distrib_gather%jj_nb_para,request_gather)
     call SendRequest(request_gather)
!$OMP BARRIER
     call WaitRequest(request_gather)       
!$OMP BARRIER

    end subroutine Gather_field_u
        
   subroutine Gather_field_v(field_loc,field_glo,ll)
   USE dimensions_mod, ONLY: iim, jjm, llm, ndm
   USE paramet_mod_h
   implicit none
     integer :: ll
     real :: field_loc(ijb_v:ije_v,ll)
     real :: field_glo(ip1jm,ll)
     type(request) :: request_gather
     integer :: ijb,ije
     integer       :: l
     
   
     ijb=ij_begin
     ije=ij_end
     if (pole_sud) ije=ij_end-iip1
        
!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)
     DO l=1,ll
       field_glo(ijb:ije,l)=field_loc(ijb:ije,l)
     ENDDO
     
     call register_SwapField(field_glo,field_glo,ip1jm,ll,distrib_gather%jj_nb_para,request_gather)
     call SendRequest(request_gather)
!$OMP BARRIER
     call WaitRequest(request_gather)       
!$OMP BARRIER

    end subroutine Gather_field_v
     
   subroutine Scatter_field_u(field_glo,field_loc,ll)
   USE dimensions_mod, ONLY: iim, jjm, llm, ndm
   USE paramet_mod_h
   implicit none
     integer :: ll
     real :: field_glo(ip1jmp1,ll)
     real :: field_loc(ijb_u:ije_u,ll)
     type(request) :: request_gather
     TYPE(distrib) :: distrib_swap
     integer       :: l
     
!$OMP BARRIER
!$OMP MASTER     
     call get_current_distrib(distrib_swap)
     call set_Distrib(distrib_gather)
!$OMP END MASTER
!$OMP BARRIER
 
     call register_SwapField(field_glo,field_glo,ip1jmp1,ll,distrib_swap%jj_nb_para,request_gather)
     call SendRequest(request_gather)
!$OMP BARRIER
     call WaitRequest(request_gather)       
!$OMP BARRIER
!$OMP MASTER     
     call set_Distrib(distrib_swap)
!$OMP END MASTER
!$OMP BARRIER

!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)
       DO l=1,ll
         field_loc(ij_begin:ij_end,l)=field_glo(ij_begin:ij_end,l)
       ENDDO

    end subroutine Scatter_field_u

   subroutine Scatter_field_v(field_glo,field_loc,ll)
   USE dimensions_mod, ONLY: iim, jjm, llm, ndm
   USE paramet_mod_h
   implicit none
     integer :: ll
     real :: field_glo(ip1jmp1,ll)
     real :: field_loc(ijb_v:ije_v,ll)
     type(request) :: request_gather
     TYPE(distrib) :: distrib_swap
     integer       :: ijb,ije,l
     

!$OMP BARRIER
!$OMP MASTER     
     call get_current_distrib(distrib_swap)
     call set_Distrib(distrib_gather)
!$OMP END MASTER
!$OMP BARRIER
     call register_SwapField(field_glo,field_glo,ip1jm,ll,distrib_swap%jj_nb_para,request_gather)
     call SendRequest(request_gather)
!$OMP BARRIER
     call WaitRequest(request_gather)       
!$OMP BARRIER
!$OMP MASTER
     call set_Distrib(distrib_swap)
!$OMP END MASTER
!$OMP BARRIER
     ijb=ij_begin
     ije=ij_end
     if (pole_sud) ije=ij_end-iip1
     
!$OMP DO SCHEDULE(STATIC,OMP_CHUNK)
       DO l=1,ll
         field_loc(ijb:ije,l)=field_glo(ijb:ije,l)
       ENDDO

    end subroutine Scatter_field_v
              
end module mod_Hallo 
   
