!> This module contains variables that describe the connectivity of the mesh and
!> also data structures for connectivity-related communication.
module mod_connectivity
   implicit none

   integer, parameter :: neighbor_boundary = 1
   integer, parameter :: neighbor_coarse = 2
   integer, parameter :: neighbor_sibling = 3
   integer, parameter :: neighbor_fine = 4

   integer, dimension(:,:,:,:,:), allocatable :: neighbor
   integer, dimension(:,:,:,:,:), allocatable :: neighbor_child
   integer, dimension(:,:,:,:), allocatable :: neighbor_type
   logical, dimension(:,:,:,:), allocatable :: neighbor_active
   integer, dimension(:,:,:,:), allocatable :: neighbor_pole
   

   ! grid number array per processor
   integer, dimension(:), allocatable :: igrids
   integer, dimension(:), allocatable :: igrids_active
   integer, dimension(:), allocatable :: igrids_passive
   

   ! phys boundary indices
   integer, dimension(:,:), allocatable :: idphyb
   

   ! number of grids on current processor
   integer :: igridstail
   integer :: igridstail_active
   integer :: igridstail_passive
   

   integer, dimension(3) :: nrecv_fc, nsend_fc
   ! cc for corner coarse
   integer, dimension(3) :: nrecv_cc, nsend_cc

   ! srl neighbor info
   type nbinfo_srl_t
      integer                            :: nigrids=0
      integer, pointer, dimension(:) :: igrid => null(), iencode => null(), isize => null(), ibuf_start => null()
   end type nbinfo_srl_t

   ! c and f neighbor info
   type nbinfo_cf_t
      integer                            :: nigrids=0
      integer, pointer, dimension(:) :: igrid => null(), inc1 => null(), inc2 => null(), inc3 => null(), isize => null()
      integer, pointer, dimension(:) :: i1 => null(), i2 => null(), i3 => null(), ibuf_start => null()
    contains
      procedure, non_overridable         :: init => init_cf_info
   end type nbinfo_cf_t

   ! buffer
   type nbinfo_buffer_t
      double precision, allocatable, dimension(:) :: buffer
      integer                                     :: size = -1
   end type nbinfo_buffer_t

   type nbinfo_buffer_i_t
      integer, allocatable, dimension(:) :: buffer
      integer                            :: size = -1
   end type nbinfo_buffer_i_t

   type cf_nb_t
      type(nbinfo_cf_t)                    :: info            ! info for each nbproc
      type(nbinfo_buffer_t)                :: send            ! double precision send data. One for each nb proc
      type(nbinfo_buffer_t)                :: rcv             ! double precision receive data
      type(nbinfo_buffer_i_t)              :: info_send       ! info package send
      type(nbinfo_buffer_i_t)              :: info_rcv        ! info package receive
   contains
      procedure, non_overridable           :: init => init_cf_nb
   end type cf_nb_t
   type srl_nb_t
      type(nbinfo_srl_t)                   :: info            ! list of the ipelist for each nbproc
      type(nbinfo_buffer_t)                :: send            ! double precision send data. One for each nb proc
      type(nbinfo_buffer_t)                :: rcv             ! double precision receive data
      type(nbinfo_buffer_i_t)              :: info_send       ! info package send
      type(nbinfo_buffer_i_t)              :: info_rcv        ! info package receive
   contains
      procedure, non_overridable           :: init => init_srl_nb
    end type srl_nb_t

   ! neighbor cpu info structure
   type nbprocs_info_t
      ! SRL (neighbor is at same level)
      integer                              :: nbprocs_srl=0        ! number of neighboring processes at srl
      integer                              :: imaxigrids_srl=0     ! max number of igrids over all neighbor processors (for loop collasing)
      integer, pointer                     :: nbprocs_srl_list(:) => null()  ! list of neighboring ipe at srl
      integer, allocatable                 :: ipe_to_inbpe_srl(:)  ! inverse to nbprocs_srl_list
      ! F (neighbor is finer)
      integer                              :: nbprocs_f=0          ! number of neighboring processes with finer grids
      integer                              :: imaxigrids_f=0       ! max number of igrids over all neighbor processors (for loop collasing)
      integer, pointer                     :: nbprocs_f_list(:) => null()  ! list of neighboring ipe at f
      integer, allocatable                 :: ipe_to_inbpe_f(:)    ! inverse to nbprocs_f_list
      ! C (neighbor is coarser)
      integer                              :: nbprocs_c=0          ! number of neighboring processes with coarser grids
      integer                              :: imaxigrids_c=0       ! max number of igrids over all neighbor processors (for loop collasing)
      integer, pointer                     :: nbprocs_c_list(:)  => null()   ! list of neighboring ipe at f
      integer, allocatable                 :: ipe_to_inbpe_c(:)    ! inverse to nbprocs_f_list
      type(cf_nb_t), pointer               :: course_nb(:) => null()        ! Info about course neighbours
      type(cf_nb_t), pointer               :: fine_nb(:) => null()          ! Info about fine neighbours
      type(srl_nb_t), pointer              :: srl_nb(:) => null()           ! Info about same refinment level neighbours
    contains
      procedure, non_overridable :: init, reset
      procedure, non_overridable :: add_igrid_to_srl, add_igrid_to_c, add_igrid_to_f
      procedure, non_overridable :: add_to_srl, add_to_f, add_to_c
      procedure, non_overridable :: alloc_buffers_srl, alloc_buffers_f, alloc_buffers_c
      procedure, non_overridable :: iencode, idecode
   end type nbprocs_info_t

   type(nbprocs_info_t) :: nbprocs_info

   public  :: nbprocs_info_t, nbprocs_info
   private :: reset, init, add_to_srl, add_to_f, add_to_c
   private :: init_cf_nb
   private :: add_igrid_to_srl, alloc_buffers_srl, iencode, idecode
   private :: add_igrid_to_c, add_igrid_to_f


 contains

   subroutine init_srl_nb(self, nigrids)
      class(srl_nb_t) :: self
      integer :: nigrids
      call init_srl_info(self%info, nigrids)
   end subroutine

   subroutine init_srl_info(self, nigrids)
      class(nbinfo_srl_t) :: self
      integer :: nigrids
      integer, pointer, dimension(:) :: igrid, iencode, isize, ibuf_start
      logical :: realloc

      if (associated(self%igrid)) then
        allocate(igrid(nigrids), iencode(nigrids), ibuf_start(nigrids), isize(nigrids))

        igrid(:size(self%igrid)) = self%igrid
        iencode(:size(self%iencode)) = self%iencode
        ibuf_start(:size(self%ibuf_start)) = self%ibuf_start
        isize(:size(self%isize)) = self%isize

        deallocate(self%igrid, self%iencode, self%ibuf_start, self%isize)
        allocate(self%igrid(nigrids), self%iencode(nigrids), self%ibuf_start(nigrids), self%isize(nigrids))

        self%igrid = igrid
        self%iencode = iencode
        self%ibuf_start = ibuf_start
        self%isize = isize
        deallocate(igrid, iencode, ibuf_start, isize)
      else
        allocate(self%igrid(nigrids), self%iencode(nigrids), self%ibuf_start(nigrids), self%isize(nigrids))
      end if
   end subroutine

   subroutine init_cf_nb(self, nigrids)
      class(cf_nb_t) :: self
      integer :: nigrids
      call self%info%init(nigrids)
   end subroutine

   subroutine init_cf_info(self, nigrids)
      class(nbinfo_cf_t)   :: self
      integer, intent(in)  :: nigrids
      integer, pointer, dimension(:) :: igrid, inc1, inc2, inc3, ibuf_start, isize, i1, i2, i3

      if (associated(self%igrid)) then
        allocate(igrid(nigrids), inc1(nigrids), inc2(nigrids), &
                 inc3(nigrids),  ibuf_start(nigrids), isize(nigrids), &
                 i1(nigrids), i2(nigrids), i3(nigrids))

        igrid(:size(self%igrid)) = self%igrid
        inc1(:size(self%inc1)) = self%inc1
        inc2(:size(self%inc2)) = self%inc2
        inc3(:size(self%inc3)) = self%inc3
        ibuf_start(:size(self%ibuf_start)) = self%ibuf_start
        isize(:size(self%isize)) = self%isize
        i1(:size(self%i1)) = self%i1
        i2(:size(self%i2)) = self%i2
        i3(:size(self%i3)) = self%i3

        deallocate(self%igrid, self%inc1, self%inc2, self%inc3, self%ibuf_start, self%isize, self%i1, self%i2, self%i3)
        allocate(self%igrid(nigrids), self%inc1(nigrids), self%inc2(nigrids), &
                 self%inc3(nigrids), self%ibuf_start(nigrids), self%isize(nigrids), &
                 self%i1(nigrids), self%i2(nigrids), self%i3(nigrids))

        self%igrid = igrid
        self%inc1 = inc1
        self%inc2 = inc2
        self%inc3 = inc3
        self%ibuf_start = ibuf_start
        self%isize = isize
        self%i1 = i1
        self%i2 = i2
        self%i3 = i3

        deallocate(igrid, inc1, inc2, &
                 inc3,  ibuf_start, isize, &
                 i1, i2, i3)
      else
        allocate(self%igrid(nigrids), self%inc1(nigrids), self%inc2(nigrids), &
                 self%inc3(nigrids), self%ibuf_start(nigrids), self%isize(nigrids), &
                 self%i1(nigrids), self%i2(nigrids), self%i3(nigrids))

      end if
   end subroutine init_cf_info

   subroutine reset(self)
     class(nbprocs_info_t) :: self
     ! .. local ..
     integer               :: i

     do i=1, self%nbprocs_srl
        
        if ( allocated(self%srl_nb(i)%rcv%buffer) ) then
           deallocate(self%srl_nb(i)%rcv%buffer)
           deallocate(self%srl_nb(i)%send%buffer)
           deallocate(self%srl_nb(i)%info_rcv%buffer)
           deallocate(self%srl_nb(i)%info_send%buffer)
        endif

        self%srl_nb(i)%info%nigrids=0
     end do

     do i=1, self%nbprocs_c
        
        if ( allocated(self%course_nb(i)%rcv%buffer) ) then
           deallocate(self%course_nb(i)%rcv%buffer)
           deallocate(self%course_nb(i)%send%buffer)
           deallocate(self%course_nb(i)%info_rcv%buffer)
           deallocate(self%course_nb(i)%info_send%buffer)
        endif

        self%course_nb(i)%info%nigrids=0
     end do

     do i=1, self%nbprocs_f
        
        if ( allocated(self%fine_nb(i)%rcv%buffer) ) then
           deallocate(self%fine_nb(i)%rcv%buffer)
           deallocate(self%fine_nb(i)%send%buffer)
           deallocate(self%fine_nb(i)%info_rcv%buffer)
           deallocate(self%fine_nb(i)%info_send%buffer)
        endif

        self%fine_nb(i)%info%nigrids=0
     end do

     self%nbprocs_srl         = 0
     self%imaxigrids_srl      = 0
     self%ipe_to_inbpe_srl(:) = -1

     self%nbprocs_c           = 0
     self%imaxigrids_c        = 0
     self%ipe_to_inbpe_c(:)   = -1

     self%nbprocs_f           = 0
     self%imaxigrids_f        = 0
     self%ipe_to_inbpe_f(:)   = -1

   end subroutine reset

   subroutine init(self, npe)
     class(nbprocs_info_t)  :: self
     integer, intent(in)   :: npe
     ! .. local ..
     integer               :: i

     allocate(self%nbprocs_srl_list(1), &
          self%srl_nb(1))

     allocate(self%nbprocs_f_list(1), &
          self%fine_nb(1))

     allocate(self%nbprocs_c_list(1), &
          self%course_nb(1))

     allocate(self%ipe_to_inbpe_srl(0:npe-1))
     self%ipe_to_inbpe_srl(:) = -1

     allocate(self%ipe_to_inbpe_f(0:npe-1))
     self%ipe_to_inbpe_f(:) = -1

     allocate(self%ipe_to_inbpe_c(0:npe-1))
     self%ipe_to_inbpe_c(:) = -1

   end subroutine init

   subroutine add_to_srl(self, ipe, igrid, i1, i2, i3)
     class(nbprocs_info_t) :: self
     integer, intent(in)   :: ipe, igrid, i1, i2, i3
     integer, dimension(:), pointer :: temp_list
     type(srl_nb_t), dimension(:), pointer :: srl_nb_temp

     ! add the procesor id if not already present:
     if (self%ipe_to_inbpe_srl(ipe) == -1) then

        ! enlarge counter
        self%nbprocs_srl = self%nbprocs_srl + 1

        if (self%nbprocs_srl > size(self%nbprocs_srl_list)) then
           allocate(temp_list(self%nbprocs_srl))
           temp_list(:size(self%nbprocs_srl_list)) = self%nbprocs_srl_list
           deallocate(self%nbprocs_srl_list)
           self%nbprocs_srl_list => temp_list

           allocate(srl_nb_temp(self%nbprocs_srl))
           srl_nb_temp(:size(self%srl_nb)) = self%srl_nb
           deallocate(self%srl_nb)
           self%srl_nb => srl_nb_temp
        end if

        ! add the process
        self%nbprocs_srl_list(self%nbprocs_srl) = ipe
        ! add to inverse list
        self%ipe_to_inbpe_srl(ipe) = self%nbprocs_srl

     end if

     call self%add_igrid_to_srl(ipe, igrid, i1, i2, i3)

   end subroutine add_to_srl

   subroutine add_to_c(self, ipe, igrid, i1, i2, i3, inc1, inc2, inc3)
     class(nbprocs_info_t) :: self
     integer, intent(in)   :: ipe, igrid, i1, i2, i3, inc1, inc2, inc3
     integer, dimension(:), pointer :: temp_list
     type(cf_nb_t), dimension(:), pointer :: course_nb_temp

     ! add the procesor id if not already present:
     if (self%ipe_to_inbpe_c(ipe) == -1) then

        ! enlarge counter
        self%nbprocs_c = self%nbprocs_c + 1


        if (self%nbprocs_c > size(self%nbprocs_c_list)) then
           allocate(temp_list(self%nbprocs_c))
           temp_list(:size(self%nbprocs_c_list)) = self%nbprocs_c_list
           deallocate(self%nbprocs_c_list)
           self%nbprocs_c_list => temp_list

           allocate(course_nb_temp(self%nbprocs_c))
           course_nb_temp(:size(self%course_nb)) = self%course_nb
           deallocate(self%course_nb)
           self%course_nb => course_nb_temp
        end if

        ! add the process
        self%nbprocs_c_list(self%nbprocs_c) = ipe
        ! add to inverse list
        self%ipe_to_inbpe_c(ipe) = self%nbprocs_c

     end if

     call self%add_igrid_to_c(ipe, igrid, i1, i2, i3, inc1, inc2, inc3)

   end subroutine add_to_c

   subroutine add_to_f(self, ipe, igrid, inc1, inc2, inc3)
     class(nbprocs_info_t) :: self
     integer, intent(in)   :: ipe, igrid, inc1, inc2, inc3
     integer, dimension(:), pointer :: temp_list
     type(cf_nb_t), dimension(:), pointer :: fine_nb_temp

     ! add the procesor id if not already present:
     if (self%ipe_to_inbpe_f(ipe) == -1) then

        ! enlarge counter
        self%nbprocs_f = self%nbprocs_f + 1

        if (self%nbprocs_f > size(self%nbprocs_f_list)) then
           allocate(temp_list(self%nbprocs_f))
           temp_list(:size(self%nbprocs_f_list)) = self%nbprocs_f_list
           deallocate(self%nbprocs_f_list)
           self%nbprocs_f_list => temp_list

           allocate(fine_nb_temp(self%nbprocs_f))
           fine_nb_temp(:size(self%fine_nb)) = self%fine_nb
           deallocate(self%fine_nb)
           self%fine_nb => fine_nb_temp
        end if

        ! add the process
        self%nbprocs_f_list(self%nbprocs_f) = ipe
        ! add to inverse list
        self%ipe_to_inbpe_f(ipe) = self%nbprocs_f

     end if

     call self%add_igrid_to_f(ipe, igrid, inc1, inc2, inc3)

   end subroutine add_to_f

   subroutine add_igrid_to_srl(self, ipe, igrid, i1, i2, i3)
     class(nbprocs_info_t) :: self
     integer, intent(in)   :: ipe, igrid, i1, i2, i3
     ! .. local ..
     integer               :: inbpe, i

     ! translate to neighbor processor index for srl
     inbpe = self%ipe_to_inbpe_srl(ipe)

     ! enlarge counter
     self%srl_nb(inbpe)%info%nigrids = self%srl_nb(inbpe)%info%nigrids + 1

     ! need to initialize or enlarge storage
     if ( .not. associated(self%srl_nb(inbpe)%info%igrid) ) then
        call init_srl_info(self%srl_nb(inbpe)%info, self%srl_nb(inbpe)%info%nigrids)
     else if ( self%srl_nb(inbpe)%info%nigrids > size(self%srl_nb(inbpe)%info%igrid) ) then
        call init_srl_info(self%srl_nb(inbpe)%info, self%srl_nb(inbpe)%info%nigrids)
     end if


     call self%iencode(i1,i2,i3,i)
     ! add the data at the counter
     self%srl_nb(inbpe)%info%igrid( self%srl_nb(inbpe)%info%nigrids )    = igrid
     self%srl_nb(inbpe)%info%iencode( self%srl_nb(inbpe)%info%nigrids )  = i

   end subroutine add_igrid_to_srl

   subroutine add_igrid_to_c(self, ipe, igrid, i1, i2, i3, inc1, inc2, inc3)
     class(nbprocs_info_t) :: self
     integer, intent(in)   :: ipe, igrid, i1, i2, i3, inc1, inc2, inc3
     ! .. local ..
     integer               :: inbpe, i

     ! translate to neighbor processor index for c
     inbpe = self%ipe_to_inbpe_c(ipe)

     ! enlarge counter
     self%course_nb(inbpe)%info%nigrids = self%course_nb(inbpe)%info%nigrids + 1

     ! need to initialize or enlarge storage
     if ( .not. associated(self%course_nb(inbpe)%info%igrid) ) then
        call init_cf_info(self%course_nb(inbpe)%info, self%course_nb(inbpe)%info%nigrids)
     else if ( self%course_nb(inbpe)%info%nigrids > size(self%course_nb(inbpe)%info%igrid) ) then
        call init_cf_info(self%course_nb(inbpe)%info, self%course_nb(inbpe)%info%nigrids)
     end if

     ! add the data at the counter
     self%course_nb(inbpe)%info%igrid( self%course_nb(inbpe)%info%nigrids )    = igrid
     self%course_nb(inbpe)%info%i1( self%course_nb(inbpe)%info%nigrids )       = i1
     self%course_nb(inbpe)%info%i2( self%course_nb(inbpe)%info%nigrids )       = i2
     self%course_nb(inbpe)%info%i3( self%course_nb(inbpe)%info%nigrids )       = i3
     self%course_nb(inbpe)%info%inc1( self%course_nb(inbpe)%info%nigrids )     = inc1
     self%course_nb(inbpe)%info%inc2( self%course_nb(inbpe)%info%nigrids )     = inc2
     self%course_nb(inbpe)%info%inc3( self%course_nb(inbpe)%info%nigrids )     = inc3

   end subroutine add_igrid_to_c

   subroutine add_igrid_to_f(self, ipe, igrid, inc1, inc2, inc3)
     class(nbprocs_info_t) :: self
     integer, intent(in)   :: ipe, igrid, inc1, inc2, inc3
     ! .. local ..
     integer               :: inbpe, i

     ! translate to neighbor processor index for f
     inbpe = self%ipe_to_inbpe_f(ipe)

     ! enlarge counter
     self%fine_nb(inbpe)%info%nigrids = self%fine_nb(inbpe)%info%nigrids + 1

     ! need to initialize or enlarge storage
     if ( .not. associated(self%fine_nb(inbpe)%info%igrid) ) then
        call init_cf_info(self%fine_nb(inbpe)%info, self%fine_nb(inbpe)%info%nigrids)
     else if ( self%fine_nb(inbpe)%info%nigrids > size(self%fine_nb(inbpe)%info%igrid) ) then
        call init_cf_info(self%fine_nb(inbpe)%info, self%fine_nb(inbpe)%info%nigrids)
     end if

     ! add the data at the counter
     self%fine_nb(inbpe)%info%igrid( self%fine_nb(inbpe)%info%nigrids )    = igrid
     self%fine_nb(inbpe)%info%inc1( self%fine_nb(inbpe)%info%nigrids )     = inc1
     self%fine_nb(inbpe)%info%inc2( self%fine_nb(inbpe)%info%nigrids )     = inc2
     self%fine_nb(inbpe)%info%inc3( self%fine_nb(inbpe)%info%nigrids )     = inc3

   end subroutine add_igrid_to_f

   subroutine alloc_buffers_srl(self, nwgc, &
         ixS_srl_min1, ixS_srl_max1, &
         ixS_srl_min2, ixS_srl_max2, &
         ixS_srl_min3, ixS_srl_max3, &
         ixR_srl_min1, ixR_srl_max1, &
         ixR_srl_min2, ixR_srl_max2, &
         ixR_srl_min3, ixR_srl_max3)

     class(nbprocs_info_t)                     :: self
     integer, intent(in)                       :: nwgc
     integer, dimension(-1:2,-1:1), intent(in) :: &
         ixS_srl_min1, ixS_srl_max1, &
         ixS_srl_min2, ixS_srl_max2, &
         ixS_srl_min3, ixS_srl_max3, &
         ixR_srl_min1, ixR_srl_max1, &
         ixR_srl_min2, ixR_srl_max2, &
         ixR_srl_min3, ixR_srl_max3
     ! .. local ..
     integer         :: inb, igrid, isize_S, isize_R
     integer         :: n_i1, n_i2, n_i3, i1, i2, i3
     integer         :: ixSmin1, ixSmin2, ixSmin3
     integer         :: ixRmin1, ixRmin2, ixRmin3
     integer         :: ixSmax1, ixSmax2, ixSmax3
     integer         :: ixRmax1, ixRmax2, ixRmax3
     integer         :: iib1, iib2, iib3, ibuf_start

     self%imaxigrids_srl = 0
     do inb = 1, self%nbprocs_srl
        self%imaxigrids_srl = max(self%srl_nb(inb)%info%nigrids, self%imaxigrids_srl)

        isize_S = 0; isize_R = 0; ibuf_start = 1
        do igrid = 1, self%srl_nb(inb)%info%nigrids
           iib1 = idphyb(1,self%srl_nb(inb)%info%igrid(igrid))
           iib2 = idphyb(2,self%srl_nb(inb)%info%igrid(igrid))
           iib3 = idphyb(3,self%srl_nb(inb)%info%igrid(igrid))
           call self%idecode( i1, i2, i3, self%srl_nb(inb)%info%iencode(igrid) )

           n_i1=-i1; n_i2=-i2; n_i3=-i3

           ixSmin1=ixS_srl_min1(iib1,i1);ixSmin2=ixS_srl_min2(iib2,i2)
           ixSmin3=ixS_srl_min3(iib3,i3);ixSmax1=ixS_srl_max1(iib1,i1)
           ixSmax2=ixS_srl_max2(iib2,i2);ixSmax3=ixS_srl_max3(iib3,i3)
           ixRmin1=ixR_srl_min1(iib1,n_i1);ixRmin2=ixR_srl_min2(iib2,n_i2)
           ixRmin3=ixR_srl_min3(iib3,n_i3);ixRmax1=ixR_srl_max1(iib1,n_i1)
           ixRmax2=ixR_srl_max2(iib2,n_i2);ixRmax3=ixR_srl_max3(iib3,n_i3)

           isize_S = isize_S + (ixSmax1-ixSmin1+1) * (ixSmax2-ixSmin2+1) * (ixSmax3-ixSmin3+1)
           isize_R = isize_R + (ixRmax1-ixRmin1+1) * (ixRmax2-ixRmin2+1) * (ixRmax3-ixRmin3+1)

           self%srl_nb(inb)%info%ibuf_start(igrid) = ibuf_start
           self%srl_nb(inb)%info%isize(igrid) = (ixSmax1-ixSmin1+1) * (ixSmax2-ixSmin2+1) * (ixSmax3-ixSmin3+1) * nwgc

           ibuf_start = ibuf_start + self%srl_nb(inb)%info%isize(igrid)

        end do

        self%srl_nb(inb)%rcv%size       = isize_R * nwgc
        self%srl_nb(inb)%send%size      = isize_S * nwgc
        self%srl_nb(inb)%info_send%size = 3 * self%srl_nb(inb)%info%nigrids
        self%srl_nb(inb)%info_rcv%size  = 3 * self%srl_nb(inb)%info%nigrids

        allocate(self%srl_nb(inb)%rcv%buffer(isize_R * nwgc))
        allocate(self%srl_nb(inb)%send%buffer(isize_S * nwgc))
        allocate(self%srl_nb(inb)%info_rcv%buffer(3 * self%srl_nb(inb)%info%nigrids))
        allocate(self%srl_nb(inb)%info_send%buffer(3 * self%srl_nb(inb)%info%nigrids))
     end do

   end subroutine alloc_buffers_srl

   subroutine alloc_buffers_f(self, nwgc, &
         ixS_p_min1, ixS_p_max1, &
         ixS_p_min2, ixS_p_max2, &
         ixS_p_min3, ixS_p_max3, &
         ixR_r_min1, ixR_r_max1, &
         ixR_r_min2, ixR_r_max2, &
         ixR_r_min3, ixR_r_max3)
     ! neighbor is fine
     ! required buffers:
     ! send: psb(ixS_p_) with indices type_send_p
     ! receive: psb(ixR_r_) with indices type_recv_r

     class(nbprocs_info_t)                     :: self
     integer, intent(in)                       :: nwgc
     integer, dimension(-1:1,0:3), intent(in)  :: &
         ixS_p_min1, ixS_p_max1, &
         ixS_p_min2, ixS_p_max2, &
         ixS_p_min3, ixS_p_max3, &
         ixR_r_min1, ixR_r_max1, &
         ixR_r_min2, ixR_r_max2, &
         ixR_r_min3, ixR_r_max3
     ! .. local ..
     integer         :: inb, isize_S, isize_R
     integer         :: igrid, inc1, inc2, inc3
     integer         :: iib1, iib2, iib3, ibuf_start
     integer         :: ixSmin1, ixSmin2, ixSmin3
     integer         :: ixRmin1, ixRmin2, ixRmin3
     integer         :: ixSmax1, ixSmax2, ixSmax3
     integer         :: ixRmax1, ixRmax2, ixRmax3

     self%imaxigrids_f = 0
     do inb = 1, self%nbprocs_f
        self%imaxigrids_f = max(self%fine_nb(inb)%info%nigrids, self%imaxigrids_f)

        isize_S = 0; isize_R = 0; ibuf_start = 1
        do igrid = 1, self%fine_nb(inb)%info%nigrids
           iib1 = idphyb(1,self%fine_nb(inb)%info%igrid(igrid))
           iib2 = idphyb(2,self%fine_nb(inb)%info%igrid(igrid))
           iib3 = idphyb(3,self%fine_nb(inb)%info%igrid(igrid))
           inc1 = self%fine_nb(inb)%info%inc1(igrid)
           inc2 = self%fine_nb(inb)%info%inc2(igrid)
           inc3 = self%fine_nb(inb)%info%inc3(igrid)

           ixSmin1=ixS_p_min1(iib1,inc1);ixSmin2=ixS_p_min2(iib2,inc2)
           ixSmin3=ixS_p_min3(iib3,inc3);ixSmax1=ixS_p_max1(iib1,inc1)
           ixSmax2=ixS_p_max2(iib2,inc2);ixSmax3=ixS_p_max3(iib3,inc3)

           ixRmin1=ixR_r_min1(iib1,inc1);ixRmin2=ixR_r_min2(iib2,inc2)
           ixRmin3=ixR_r_min3(iib3,inc3);ixRmax1=ixR_r_max1(iib1,inc1)
           ixRmax2=ixR_r_max2(iib2,inc2);ixRmax3=ixR_r_max3(iib3,inc3)

           isize_S = isize_S + (ixSmax1-ixSmin1+1) * (ixSmax2-ixSmin2+1) * (ixSmax3-ixSmin3+1)
           isize_R = isize_R + (ixRmax1-ixRmin1+1) * (ixRmax2-ixRmin2+1) * (ixRmax3-ixRmin3+1)

           self%fine_nb(inb)%info%ibuf_start(igrid) = ibuf_start
           self%fine_nb(inb)%info%isize(igrid) = (ixSmax1-ixSmin1+1) * (ixSmax2-ixSmin2+1) * (ixSmax3-ixSmin3+1) * nwgc

           ibuf_start = ibuf_start + self%fine_nb(inb)%info%isize(igrid)

        end do

        self%fine_nb(inb)%rcv%size       = isize_R*nwgc
        self%fine_nb(inb)%send%size      = isize_S*nwgc
        self%fine_nb(inb)%info_send%size = 5 * self%fine_nb(inb)%info%nigrids
        self%fine_nb(inb)%info_rcv%size  = 5 * self%fine_nb(inb)%info%nigrids

        allocate(self%fine_nb(inb)%rcv%buffer(isize_R * nwgc))
        allocate(self%fine_nb(inb)%send%buffer(isize_S * nwgc))
        allocate(self%fine_nb(inb)%info_rcv%buffer(5 * self%fine_nb(inb)%info%nigrids))
        allocate(self%fine_nb(inb)%info_send%buffer(5 * self%fine_nb(inb)%info%nigrids))

     end do

   end subroutine alloc_buffers_f

   subroutine alloc_buffers_c(self, nwgc, &
         ixS_r_min1, ixS_r_max1, &
         ixS_r_min2, ixS_r_max2, &
         ixS_r_min3, ixS_r_max3, &
         ixR_p_min1, ixR_p_max1, &
         ixR_p_min2, ixR_p_max2, &
         ixR_p_min3, ixR_p_max3 &
         )
     ! neighbor is coarse
     ! required buffers:
     ! send: psc(ixS_r_) with indices type_send_r
     ! receive: psc(ixR_p_) with indices type_recv_p

     class(nbprocs_info_t)                     :: self
     integer, intent(in)                       :: nwgc
     integer, dimension(-1:1,-1:1), intent(in) :: &
         ixS_r_min1, ixS_r_max1, &
         ixS_r_min2, ixS_r_max2, &
         ixS_r_min3, ixS_r_max3
     integer, dimension(-1:1,0:3), intent(in)  :: &
         ixR_p_min1, ixR_p_max1, &
         ixR_p_min2, ixR_p_max2, &
         ixR_p_min3, ixR_p_max3
     ! .. local ..
     integer         :: inb, isize_S, isize_R
     integer         :: igrid, i1, i2, i3
     integer         :: inc1, inc2, inc3
     integer         :: iib1, iib2, iib3, ibuf_start
     integer         :: ixSmin1, ixSmin2, ixSmin3
     integer         :: ixRmin1, ixRmin2, ixRmin3
     integer         :: ixSmax1, ixSmax2, ixSmax3
     integer         :: ixRmax1, ixRmax2, ixRmax3

     self%imaxigrids_c = 0
     do inb = 1, self%nbprocs_c
        self%imaxigrids_c = max(self%course_nb(inb)%info%nigrids, self%imaxigrids_c)

        isize_S = 0; isize_R = 0; ibuf_start = 1
        do igrid = 1, self%course_nb(inb)%info%nigrids
           iib1 = idphyb(1,self%course_nb(inb)%info%igrid(igrid))
           iib2 = idphyb(2,self%course_nb(inb)%info%igrid(igrid))
           iib3 = idphyb(3,self%course_nb(inb)%info%igrid(igrid))
           i1   = self%course_nb(inb)%info%i1(igrid)
           i2   = self%course_nb(inb)%info%i2(igrid)
           i3   = self%course_nb(inb)%info%i3(igrid)
           inc1 = self%course_nb(inb)%info%inc1(igrid)
           inc2 = self%course_nb(inb)%info%inc2(igrid)
           inc3 = self%course_nb(inb)%info%inc3(igrid)

           ixSmin1=ixS_r_min1(iib1,i1);ixSmin2=ixS_r_min2(iib2,i2)
           ixSmin3=ixS_r_min3(iib3,i3);ixSmax1=ixS_r_max1(iib1,i1)
           ixSmax2=ixS_r_max2(iib2,i2);ixSmax3=ixS_r_max3(iib3,i3)

           ixRmin1=ixR_p_min1(iib1,inc1);ixRmin2=ixR_p_min2(iib2,inc2)
           ixRmin3=ixR_p_min3(iib3,inc3);ixRmax1=ixR_p_max1(iib1,inc1)
           ixRmax2=ixR_p_max2(iib2,inc2);ixRmax3=ixR_p_max3(iib3,inc3)

           isize_S = isize_S + (ixSmax1-ixSmin1+1) * (ixSmax2-ixSmin2+1) * (ixSmax3-ixSmin3+1)
           isize_R = isize_R + (ixRmax1-ixRmin1+1) * (ixRmax2-ixRmin2+1) * (ixRmax3-ixRmin3+1)

           self%course_nb(inb)%info%ibuf_start(igrid) = ibuf_start
           self%course_nb(inb)%info%isize(igrid) = (ixSmax1-ixSmin1+1) * (ixSmax2-ixSmin2+1) * (ixSmax3-ixSmin3+1) * nwgc

           ibuf_start = ibuf_start + self%course_nb(inb)%info%isize(igrid)

        end do

        self%course_nb(inb)%rcv%size       = isize_R*nwgc
        self%course_nb(inb)%send%size      = isize_S*nwgc
        self%course_nb(inb)%info_send%size = 5 * self%course_nb(inb)%info%nigrids
        self%course_nb(inb)%info_rcv%size  = 5 * self%course_nb(inb)%info%nigrids

        allocate(self%course_nb(inb)%rcv%buffer(isize_R * nwgc))
        allocate(self%course_nb(inb)%send%buffer(isize_S * nwgc))
        allocate(self%course_nb(inb)%info_rcv%buffer(5 * self%course_nb(inb)%info%nigrids))
        allocate(self%course_nb(inb)%info_send%buffer(5 * self%course_nb(inb)%info%nigrids))

     end do

   end subroutine alloc_buffers_c

   subroutine iencode(self, i1, i2, i3, i)
     class(nbprocs_info_t)                     :: self
     integer, intent(in)                       :: i1, i2, i3
     integer, intent(out)                      :: i
     ! .. local ..
     integer, parameter                        :: i1min=-1, i2min=-1, i3min=-1

     i = 1 + (i1-i1min) + 3 * (i2-i2min) + 9 * (i3-i3min)

   end subroutine iencode

   subroutine idecode(self, i1, i2, i3, i)
     class(nbprocs_info_t)                     :: self
     integer, intent(in)                       :: i
     integer, intent(out)                      :: i1, i2, i3
     ! .. local ..
     integer, parameter                        :: i1min=-1, i2min=-1, i3min=-1
     integer                                   :: id, idd

     i3  = ceiling(dble(i)/9.0d0) - 1 + i3min
     id  = i - 9 * (i3-i3min)
     i2  = ceiling(dble(id)/3.0d0) -1 + i2min
     idd = id - 3 * (i2-i2min)
     i1  = idd + i1min - 1

   end subroutine idecode


 end module mod_connectivity
