! $Id$
!
!  This module tests the ghost zones consistency.
!  For more documentation about the ghost zones, please refer to Mpicomm.f90.
!
module Ghost_check
!
  use Cdata
  use Cparam
!
  implicit none
!
  contains
!
!***********************************************************************
    function blocks_equal(msg,a,b)
!
!  Helper routine to check the equality of two arrays.
!
!  07-mar-2011/Bourdin.KIS: coded
!
      use Mpicomm, only: stop_it
!
      character (len=*), intent(in) :: msg
      real, dimension (:,:,:,:), intent(in) :: a, b
      logical :: blocks_equal
!
      integer :: num_x, num_y, num_z, num_a, px, py, pz, pa
!
      num_x = size (a, 1)
      num_y = size (a, 2)
      num_z = size (a, 3)
      num_a = size (a, 4)
!
      if (num_x /= size (b, 1)) call stop_it ('blocks_equal: size mismatch in X')
      if (num_y /= size (b, 2)) call stop_it ('blocks_equal: size mismatch in Y')
      if (num_z /= size (b, 3)) call stop_it ('blocks_equal: size mismatch in Z')
      if (num_a /= size (b, 4)) call stop_it ('blocks_equal: size mismatch in A')
!
      blocks_equal = .true.
      do pa = 1, min (num_a, mcom)
        do pz = 1, num_z
          do py = 1, num_y
            do px = 1, num_x
              if (a(px,py,pz,pa) /= b(px,py,pz,pa)) then
                write (100+iproc,*) msg, ' => ', px, py, pz, pa, ' : ', a(px,py,pz,pa), b(px,py,pz,pa)
                blocks_equal = .false.
              endif
            enddo
          enddo
        enddo
      enddo
!
    endfunction blocks_equal
!***********************************************************************
    subroutine check_ghosts_consistency(f, msg)
!
!  Helper routine to check the consistency of the ghost cell values.
!
!  07-mar-2011/Bourdin.KIS: coded
!
      use Mpicomm, only: stop_it, collect_xy, collect_z
!
      real, dimension (mx,my,mz,mfarray), intent (in) :: f
      character (len=*), intent(in) :: msg
!
      real, dimension (:,:,:,:), allocatable :: buffer, global, lower, middle, upper
      integer :: px, py, pz, lx, ux, ly, uy, lz, uz, alloc_err
      logical :: ok
!
      if (lpencil_check_at_work) return
!
      if (lfirst_proc_xy) then
        allocate (buffer(mx*nprocx,my*nprocy,mz,mfarray), stat=alloc_err)
        if (alloc_err > 0) call stop_it ('check_ghosts_consistency: could not allocate buffer memory.')
      endif
      if (lroot) then
        allocate (global(mx*nprocx,my*nprocy,mz*nprocz,mfarray), stat=alloc_err)
        if (alloc_err > 0) call stop_it ('check_ghosts_consistency: could not allocate global memory.')
        allocate (lower(mx,my,mz,mfarray), middle(mx,my,mz,mfarray), upper(mx,my,mz,mfarray), stat=alloc_err)
        if (alloc_err > 0) call stop_it ('check_ghosts_consistency: could not allocate box memory.')
      endif
!
      call collect_xy (f, buffer)
      if (lfirst_proc_xy) call collect_z (buffer, global)
!
      if (lfirst_proc_xy) deallocate (buffer)
      if (.not. lroot) return
!
      ok = .true.
!
      ! check in X:
      write (100+iproc,*) 'Checking X-direction:'
      do py = 1, nprocy
        do pz = 1, nprocz
          if (nprocx == 1) then
            if (lperi(1)) then
              write (100+iproc,*) 'Subdomain ', 1, py, pz
              middle = global(:,(py-1)*my+1:py*my,(pz-1)*mz+1:pz*mz,:)
              ok = ok .and. blocks_equal ("X  l2i:l2 <>  1:l1-1", middle(l2i:l2,m1:m2,n1:n2,:), middle(1:l1-1,m1:m2,n1:n2,:))
              ok = ok .and. blocks_equal ("X l2+1:mx <> l1:l1i ", middle(l2+1:mx,m1:m2,n1:n2,:), middle(l1:l1i,m1:m2,n1:n2,:))
            endif
          else
            do px = 1, nprocx
              write (100+iproc,*) 'Subdomain ', px, py, pz
              lx = px - 1
              if (lperi(1) .and. (lx < 1)) lx = lx + nprocx
              ux = px + 1
              if (lperi(1) .and. (ux > nprocx)) ux = ux - nprocx
              middle = global((px-1)*mx+1:px*mx,(py-1)*my+1:py*my,(pz-1)*mz+1:pz*mz,:)
              ! check lower neighbor
              if (lx > 0) then
                lower = global((lx-1)*mx+1:lx*mx,(py-1)*my+1:py*my,(pz-1)*mz+1:pz*mz,:)
                ok = ok .and. blocks_equal ("X  l2i:l2 <>  1:l1-1", lower(l2i:l2,m1:m2,n1:n2,:), middle(1:l1-1,m1:m2,n1:n2,:))
              endif
              ! check upper neighbor
              if (ux <= nprocx) then
                upper = global((ux-1)*mx+1:ux*mx,(py-1)*my+1:py*my,(pz-1)*mz+1:pz*mz,:)
                ok = ok .and. blocks_equal ("X l2+1:mx <> l1:l1i ", middle(l2+1:mx,m1:m2,n1:n2,:), upper(l1:l1i,m1:m2,n1:n2,:))
              endif
            enddo
          endif
        enddo
      enddo
!
      ! check in Y:
      write (100+iproc,*) 'Checking Y-direction:'
      do px = 1, nprocx
        do pz = 1, nprocz
          if (nprocy == 1) then
            if (lperi(2)) then
              write (100+iproc,*) 'Subdomain ', px, 1, pz
              middle = global((px-1)*mx+1:px*mx,:,(pz-1)*mz+1:pz*mz,:)
              ok = ok .and. blocks_equal ("Y  m2i:m2 <>  1:m1-1", middle(l1:l2,m2i:m2,n1:n2,:), middle(l1:l2,1:m1-1,n1:n2,:))
              ok = ok .and. blocks_equal ("Y m2+1:my <> m1:m1i ", middle(l1:l2,m2+1:my,n1:n2,:), middle(l1:l2,m1:m1i,n1:n2,:))
            endif
          else
            do py = 1, nprocy
              write (100+iproc,*) 'Subdomain ', px, py, pz
              ly = ipy - 1
              if (lperi(2) .and. (ly < 1)) ly = ly + nprocy
              uy = ipy + 1
              if (lperi(2) .and. (uy > nprocy)) uy = uy - nprocy
              middle = global((px-1)*mx+1:px*mx,(py-1)*my+1:py*my,(pz-1)*mz+1:pz*mz,:)
              ! check lower neighbor
              if (ly > 0) then
                lower = global((px-1)*mx+1:px*mx,(ly-1)*my+1:ly*my,(pz-1)*mz+1:pz*mz,:)
                ok = ok .and. blocks_equal ("Y  m2i:m2 <>  1:m1-1", lower(l1:l2,m2i:m2,n1:n2,:), middle(l1:l2,1:m1-1,n1:n2,:))
              endif
              ! check upper neighbor
              if (uy <= nprocy) then
                upper = global((px-1)*mx+1:px*mx,(uy-1)*my+1:uy*my,(pz-1)*mz+1:pz*mz,:)
                ok = ok .and. blocks_equal ("Y m2+1:my <> m1:m1i ", middle(l1:l2,m2+1:my,n1:n2,:), upper(l1:l2,m1:m1i,n1:n2,:))
              endif
            enddo
          endif
        enddo
      enddo
!
      ! check in z:
      write (100+iproc,*) 'Checking Z-direction:'
      do px = 1, nprocx
        do py = 1, nprocy
          if (nprocz == 1) then
            if (lperi(3)) then
              write (100+iproc,*) 'Subdomain ', px, py, 1
              middle = global((px-1)*mx+1:px*mx,(py-1)*my+1:py*my,:,:)
              ok = ok .and. blocks_equal ("Z  n2i:n2 <>  1:n1-1", middle(l1:l2,m1:m2,n2i:n2,:), middle(l1:l2,m1:m2,1:n1-1,:))
              ok = ok .and. blocks_equal ("Z n2+1:mz <> n1:n1i ", middle(l1:l2,m1:m2,n2+1:mz,:), middle(l1:l2,m1:m2,n1:n1i,:))
            endif
          else
            do pz = 1, nprocz
              write (100+iproc,*) 'Subdomain ', px, py, pz
              lz = pz - 1
              if (lperi(3) .and. (lz < 1)) lz = lz + nprocz
              uz = pz + 1
              if (lperi(3) .and. (uz > nprocz)) uz = uz - nprocz
              middle = global((px-1)*mx+1:px*mx,(py-1)*my+1:py*my,(pz-1)*mz+1:pz*mz,:)
              ! check lower neighbor
              if (lz > 0) then
                lower = global((px-1)*mx+1:px*mx,(py-1)*my+1:py*my,(lz-1)*mz+1:lz*mz,:)
                ok = ok .and. blocks_equal ("Z  n2i:n2 <>  1:n1-1", lower(l1:l2,m1:m2,n2i:n2,:), middle(l1:l2,m1:m2,1:n1-1,:))
              endif
              ! check upper neighbor
              if (uz <= nprocz) then
                upper = global((px-1)*mx+1:px*mx,(py-1)*my+1:py*my,(uz-1)*mz+1:uz*mz,:)
                ok = ok .and. blocks_equal ("Z n2+1:mz <> n1:n1i ", middle(l1:l2,m1:m2,n2+1:mz,:), upper(l1:l2,m1:m2,n1:n1i,:))
              endif
            enddo
          endif
        enddo
      enddo
!
      if (.not. ok) then
        write (*,*) '=> ERROR: found inconsistency in ghost cells!'
        write (*,*) '=> SUBROUTINE: ', msg
        !call sleep (1)
        stop
      endif
!
      if (lroot) deallocate (global, lower, middle, upper)
!
    endsubroutine check_ghosts_consistency
!***********************************************************************
    subroutine check_ghosts_consistency_nompi(f,routine)
!
!  Non-MPI routine to check the consistency of all ghost cell values.
!  This routine will be removed, and the regular routine will be extended.
!
!  07-mar-2011/Bourdin.KIS: coded
!
      real, dimension (mx,my,mz,mfarray), intent (in) :: f
      character (len=*), intent(in) :: routine
!
      logical :: ok
!
      if (lpencil_check_at_work) return
!
      ok = .true.
!
      ! check in X:
      if (lperi(1)) then
        ok = ok .and. blocks_equal ("X  l2i:l2 <>  1:l1-1", f(l2i:l2,m1:m2,n1:n2,:), f(1:l1-1,m1:m2,n1:n2,:))
        ok = ok .and. blocks_equal ("X l2+1:mx <> l1:l1i ", f(l2+1:mx,m1:m2,n1:n2,:), f(l1:l1i,m1:m2,n1:n2,:))
      endif
!
      ! check in Y:
      if (lperi(2)) then
        ok = ok .and. blocks_equal ("Y  m2i:m2 <>  1:m1-1", f(l1:l2,m2i:m2,n1:n2,:), f(l1:l2,1:m1-1,n1:n2,:))
        ok = ok .and. blocks_equal ("Y m2+1:my <> m1:m1i ", f(l1:l2,m2+1:my,n1:n2,:), f(l1:l2,m1:m1i,n1:n2,:))
      endif
!
      ! check in z:
      if (lperi(2)) then
        ok = ok .and. blocks_equal ("Z  n2i:n2 <>  1:n1-1", f(l1:l2,m1:m2,n2i:n2,:), f(l1:l2,m1:m2,1:n1-1,:))
        ok = ok .and. blocks_equal ("Z n2+1:mz <> n1:n1i ", f(l1:l2,m1:m2,n2+1:mz,:), f(l1:l2,m1:m2,n1:n1i,:))
      endif
!
      if (.not. ok) then
        write (*,*) '=> ERROR: found inconsistency in ghost cells!'
        write (*,*) '=> SUBROUTINE: ', routine
        !call sleep (1)
        stop
      endif
!
    endsubroutine check_ghosts_consistency_nompi
!***********************************************************************
endmodule Ghost_check