!### macro's #####################################################
!
#define TRACEBACK write (gol,'("in ",a," (",a,", line",i5,")")') rname, __FILE__, __LINE__; call goErr
#define IF_NOTOK_RETURN(action) if (status/=0) then; TRACEBACK; action; return; end if
#define IF_ERROR_RETURN(action) if (status> 0) then; TRACEBACK; action; return; end if
!
#include "tm5.inc"
!
!#################################################################

module zoom_tools

  use GO, only : gol, goPr, goErr
  
use go, only : gol, gobug
use partools, only : tracer_active
use tracer_data, only : tracer_print
use meteodata, only : m_dat

  implicit none

  ! --- interface ---

  private

  public :: mix_edges, coarsen_region, update_parent


contains



  subroutine mix_edges(region)
    !
    ! distribute data at the x-edges of the zoom region
    ! over the whole interface cell
    ! written by mike botchev, march-june 1999
    ! adapted for the CRAY by Maarten Krol, 3-2000
    !
    use dims
    use global_data, only : mass_dat, region_dat
    use chem_param,  only : ntracet
    use toolbox,     only : escape_tm
    use partools   , only : myid
    use partools   , only : ntracetloc
    use partools   , only : lmloc, offsetl
    use partools   , only : which_par, previous_par
    use meteodata  , only : m_dat

    ! input
    integer,intent(in)            ::  region

    ! local
    real,dimension(:,:,:,:),pointer    :: rm
#ifdef slopes
    real,dimension(:,:,:,:),pointer    :: rxm,rym,rzm
#endif
    real,dimension(:,:,:),  pointer    :: m
    integer,dimension(:,:), pointer    :: edge

    integer    :: imr, i, j, l, n, jmr, lmr, ip, jp, imp, jmp, nt
    integer    :: xref_, yref_, zref_, xyzref_, xyref_
    real       :: m_edge, rm_edge, rxm_edge, rym_edge, rzm_edge
    logical    :: xedges , ynp,ysp

    ! relaxation factor for slopes; relax=0.0 means upwinding
    real,parameter:: relax=1.0
    real,dimension(:,:),allocatable   :: to_parent
#ifdef slopes
    real,dimension(:,:),allocatable   :: to_parentx
    real,dimension(:,:),allocatable   :: to_parenty
    real,dimension(:,:),allocatable   :: to_parentz
#endif

    ! start

    xedges = .true.
    ysp = .true.  !mix also NP/SP?
    ynp = .true.

    which_par=previous_par(region)
    if ( which_par == 'tracer' .and. ntracetloc == 0 ) return 
    if ( which_par == 'levels' .and. lmloc == 0 ) return  !WP! 

    imr = im(region)
    jmr = jm(region)

    m => m_dat(region)%data
    
    if ( which_par == 'tracer') then

       rm => mass_dat(region)%rm_t
#ifdef slopes
       rxm => mass_dat(region)%rxm_t
       rym => mass_dat(region)%rym_t
       rzm => mass_dat(region)%rzm_t
#endif

       lmr = lm(region)
       nt=ntracetloc

    else if ( which_par == 'levels' ) then

       rm => mass_dat(region)%rm_k
#ifdef slopes
       rxm => mass_dat(region)%rxm_k
       rym => mass_dat(region)%rym_k
       rzm => mass_dat(region)%rzm_k
#endif

       lmr = lmloc
       nt=ntracet
    end if

    edge => region_dat(region)%edge

    xref_ = xref(region)/xref(parent(region))
    yref_ = yref(region)/yref(parent(region))
    zref_ = zref(region)/zref(parent(region))
    if ( zref_ /= 1 ) call escape_tm( &
         'mix_edges: zooming in z direction not supported!')

    xyref_ = xref_*yref_

    if ( okdebug ) then
       write (gol,*) 'mix_edges: region=',region; call goPr
    end if
    if ( (xref_ == 1) .or. (im(region)/xref(region) == im(1)) ) then
       if ( okdebug ) then
         write (gol,*) 'mix_edges: no refinement or periodic bc, skipping x-walls'; call goPr
       end if
       xedges = .false. 
       if ( region == 1 ) then
          if ( okdebug ) then
            write (gol,*) 'mix_edges: region = 1, returning'; call goPr
          end if
          return
       end if
    end if

    if ( touch_sp(region) == 1) then
       ysp = .false.
       if ( okdebug ) then 
         write (gol,*) 'mix_edges: SP touching...skip SP y-walls'; call goPr
       end if
    end if
    if(touch_np(region) == 1) then
       ynp = .false.
       if ( okdebug ) then
         write (gol,*) 'mix_edges: NP touching...skip NP y-walls'; call goPr
       end if
    end if
    imp = imr/xref_   !in 'coarse' resolution
    jmp = jmr/yref_

    ! ~~ mass (all levels!)

    allocate(to_parent(imp,jmp))

    !do l=1,lmr !WP! depends on tracers or levels
    do l=1,lm(region)
       to_parent = 0.0
       do j=1,jmr
          jp = 1 + (j-1)/yref_
          do i=1,imr
             if ( edge(i,j) /= -1 ) cycle   !do only for edge of child...
             ip = 1 + (i-1)/xref_
             to_parent(ip,jp) = to_parent(ip,jp) + m(i,j,l)/xyref_
          end do
       end do
       if ( xedges ) then     !mix the x-wall of the zoom region.
          do j = 1,jmr
             jp = 1 + (j-1)/yref_
             do i = 1, xref_
                m(i,j,l) = to_parent(1,jp)
             end do
             do i = imr-xref_+1,imr
                m(i,j,l) = to_parent(imp,jp)
             end do
          end do
       end if    !xedges
       if ( ysp ) then
          do j = 1,yref_
             do i = 1, imr
                ip = 1 + (i-1)/xref_
                m(i,j,l) = to_parent(ip,1)
             end do
          end do
       end if
       if ( ynp ) then
          do j = jmr-yref_+1,jmr
             do i = 1, imr
                ip = 1 + (i-1)/xref_
                m(i,j,l) = to_parent(ip,jmp)
             end do
          end do
       end if
    end do  !l

    deallocate(to_parent)
    
    ! ~~ tracers

    allocate(to_parent(imp,jmp))
#ifdef slopes
    allocate(to_parentx(imp,jmp))
    allocate(to_parenty(imp,jmp))
    allocate(to_parentz(imp,jmp))
#endif

    do n=1,nt  !WP! depends on tracers or levels
       do l=1,lmr !WP! depends on tracers or levels
          to_parent = 0.0
#ifdef slopes
          to_parentx = 0.0
          to_parenty = 0.0
          to_parentz = 0.0
#endif
          do j=1,jmr
             jp = 1 + (j-1)/yref_
             do i=1,imr
                if ( edge(i,j) /= -1 ) cycle   !do only for edge of child...
                ip = 1 + (i-1)/xref_
                to_parent(ip,jp) = to_parent(ip,jp) + rm(i,j,l,n)/xyref_
#ifdef slopes
                to_parentx(ip,jp) = to_parentx(ip,jp) + rxm(i,j,l,n)/xyref_
                to_parenty(ip,jp) = to_parenty(ip,jp) + rym(i,j,l,n)/xyref_
                to_parentz(ip,jp) = to_parentz(ip,jp) + rzm(i,j,l,n)/xyref_
#endif
             end do
          end do
          if ( xedges ) then     !mix the x-wall of the zoom region.
             do j = 1,jmr
                jp = 1 + (j-1)/yref_
                do i = 1, xref_
                   rm(i,j,l,n) = to_parent(1,jp)
#ifdef slopes
                   rxm(i,j,l,n) = to_parentx(1,jp)
                   rym(i,j,l,n) = to_parenty(1,jp)
                   rzm(i,j,l,n) = to_parentz(1,jp)
#endif
                end do
                do i = imr-xref_+1,imr
                   rm(i,j,l,n) = to_parent(imp,jp)
#ifdef slopes
                   rxm(i,j,l,n) = to_parentx(imp,jp)
                   rym(i,j,l,n) = to_parenty(imp,jp)
                   rzm(i,j,l,n) = to_parentz(imp,jp)
#endif
                end do
             end do
          end if !xedges
          if ( ysp ) then
             do j = 1,yref_
                do i = 1, imr
                   ip = 1 + (i-1)/xref_
                   rm(i,j,l,n) = to_parent(ip,1)
#ifdef slopes
                   rxm(i,j,l,n) = to_parentx(ip,1)
                   rym(i,j,l,n) = to_parenty(ip,1)
                   rzm(i,j,l,n) = to_parentz(ip,1)
#endif
                end do
             end do
          end if
          if ( ynp ) then
             do j = jmr-yref_+1,jmr
                do i = 1, imr
                   ip = 1 + (i-1)/xref_
                   rm(i,j,l,n) = to_parent(ip,jmp)
#ifdef slopes
                   rxm(i,j,l,n) = to_parentx(ip,jmp)
                   rym(i,j,l,n) = to_parenty(ip,jmp)
                   rzm(i,j,l,n) = to_parentz(ip,jmp)
#endif
                end do
             end do
          end if
       end do   !l
    end do   !nt

    deallocate(to_parent)
#ifdef slopes
    deallocate(to_parentx)
    deallocate(to_parenty)
    deallocate(to_parentz)
#endif

    nullify(m)
    nullify(rm)
#ifdef slopes
    nullify(rxm)
    nullify(rym)
    nullify(rzm)
#endif
    nullify(edge)

  end subroutine mix_edges



  recursive subroutine coarsen_region(region)
    !
    ! calculates values in the parent identical to the child. 
    ! Avoids small numerical differences that are caused by the 
    ! limited accuracy of the stored pressure and wind-data.
    ! written by Maarten Krol, december 1999
    !
    use dims
    use global_data,   only : wind_dat
    use meteodata    , only : m_dat
    use advect_tools,  only : m2phlb, m2phlb1
    use toolbox,       only : escape_tm
#ifdef MPI
    use mpi_comm, only : barrier,barrier_t
#endif
    use partools   , only : myid, root_t, root_k
    use partools   , only : ntracetloc, lmar, lmloc, ntracet_ar
    use tracer_data, only : Par_Check_Domain

    implicit none

    ! in/out:
    integer,intent(in)   :: region 

    ! local:
    real,dimension(:,:,:),pointer     :: am,bm,cm,m,amp,bmp,cmp,mp
    real,dimension(:,:,:),allocatable :: top
    integer i,j,l,ip,jp,lp,regiop,imp,jmp,lmp,imr,jmr,lmr
    integer xref_,yref_,zref_,tref_
    real,dimension(0:im(region)+1,0:jm(region)+1,0:lm(region) ) :: fieldglob

    ! start

    regiop = parent(region) 
#ifdef MPI
    !WP! check whether region and
    call Par_Check_Domain( region, 'p', 'tracer' ) 
#endif
    !parents are on tracer domain
    imr = im(region)
    jmr = jm(region)
    lmr = lm(region)
    if ( regiop > 0 ) then 
#ifdef MPI 
       if ( ntracetloc /= 0 ) then !only PE's with nonzero ntracetloc continue
#endif
          if ( okdebug .and. myid == root_t ) then
            write (gol,*) 'coarsen_region: region:',region; call goPr
          end if
          xref_ = xref(region)/xref(regiop)
          yref_ = yref(region)/yref(regiop)
          zref_ = zref(region)/zref(regiop)
          tref_ = tref(region)/tref(regiop)
          imp = imr/xref_
          jmp = jmr/yref_
          lmp = lmr/zref_

          if ( ibeg(region) < iend(region) .and. &
               imp /= iend(region)-ibeg(region)+1 ) then
            write (gol,'("wrong region i dimensions:")'); call goErr
            write (gol,'("  ibeg, iend, imp : ",3i4)') ibeg(region), iend(region), imp; call goErr
            call escape_tm('coarsen_region: stop')
          end if
          if ( jmp /= jend(region)-jbeg(region)+1 ) then
            write (gol,'("wrong region j dimensions:")'); call goErr
            write (gol,'("  jbeg, jend, jmp : ",3i4)') jbeg(region), jend(region), jmp; call goErr
            call escape_tm('coarsen_region: stop')
          end if
          if ( lmp /= lend(region)-lbeg(region)+1 ) then
            write (gol,'("wrong region l dimensions:")'); call goErr
            write (gol,'("  lbeg, lend, lmp : ",3i4)') lbeg(region), lend(region), lmp; call goErr
            call escape_tm('coarsen_region: stop')
          end if
          write (gol,*) 'coarsen_region: coarsening parent',regiop,'with child',region; call goPr

          allocate(top(0:imp,jmp,lmp))
          amp => wind_dat(regiop)%am_t
          am => wind_dat(region)%am_t
          top = 0.0
          do l=1,lmr
             lp = 1 + (l-1)/zref_
             do j=1,jmr
                jp = 1 + (j-1)/yref_
                do ip=0,imp
                   top(ip,jp,lp) = top(ip,jp,lp) + am(ip*xref_,j,l)*tref_
                end do
             end do
          end do
          !   copy top to parent
          do l=1,lmp
             lp = lbeg(region)-1+l
             do j=1,jmp
                jp = jbeg(region)-1+j
                do i=0,imp
                   ip = mod(ibeg(region)-2+i,im(regiop))+1
                   amp(ip,jp,lp) = top(i,j,l)
                end do
             end do
          end do

          deallocate(top)
          nullify(am)
          nullify(amp)

          bm => wind_dat(region)%bm_t
          bmp => wind_dat(regiop)%bm_t
          allocate(top(imp,jmp+1,lmp))
          top = 0.0
          do l=1,lmr
             lp = 1 + (l-1)/zref_
             do i=1,imr
                ip = 1 + (i-1)/xref_
                do jp = 1,jmp+1
                   top(ip,jp,lp) = top(ip,jp,lp) + bm(i,1 + &
                        (jp-1)*yref_,l)*tref_
                end do
             end do
          end do
          do l=1,lmp
             lp = lbeg(region)-1+l
             do j=1,jmp+1
                jp = jbeg(region)-1+j
                do i=1,imp
                   ip = mod(ibeg(region)-2+i,im(regiop))+1
                   bmp(ip,jp,lp) = top(i,j,l)
                end do
             end do
          end do

          deallocate(top)
          nullify(bm)
          nullify(bmp)

          allocate(top(imp,jmp,lmp))
          cm => wind_dat(region)%cm_t
          cmp => wind_dat(regiop)%cm_t
          top = 0.0
          do i=1,imr
             ip = 1 + (i-1)/xref_
             do j = 1,jmr
                jp = 1 + (j-1)/yref_  
                do lp=1,lmp
                   top(ip,jp,lp) = top(ip,jp,lp) + cm(i,j,lp*zref_)*tref_
                end do
             end do
          end do
          do l=1,lmp
             lp = lbeg(region)-1+l
             do j=1,jmp
                jp = jbeg(region)-1+j
                do i=1,imp
                   ip = mod(ibeg(region)-2+i,im(regiop))+1
                   cmp(ip,jp,lp) = top(i,j,l)
                end do
             end do
          end do

          nullify(cm)
          nullify(cmp)

          top = 0.0

          m  => m_dat(region)%data
          mp => m_dat(regiop)%data

          do l=1,lmr
             lp = 1 + (l-1)/zref_
             do j = 1,jmr
                jp = 1 + (j-1)/yref_  
                do i=1,imr
                   ip = 1 + (i-1)/xref_
                   top(ip,jp,lp) = top(ip,jp,lp) + m(i,j,l)
                end do
             end do
          end do

          do l=1,lmp
             lp = lbeg(region)-1+l
             do j=1,jmp
                jp = jbeg(region)-1+j
                do i=1,imp
                   ip = mod(ibeg(region)-2+i,im(regiop))+1
                   mp(ip,jp,lp) = top(i,j,l)
                end do
             end do
          end do

          deallocate(top)
          nullify(m)
          nullify(mp)

#ifdef MPI 
       end if ! all PE's from here
#endif

       if ( regiop /= 1 ) then
          call m2phlb(regiop)
       else
          call m2phlb1(regiop)
       end if
       ! call grand-parent recursively...
       call coarsen_region(regiop)
#ifdef MPI
       call barrier   
#endif

    end if !regiop>0


  end subroutine coarsen_region



  subroutine update_parent(region)
    !
    ! update rm of its parent in correspondence with itself
    ! written by mike botchev, march-june 1999
    !
    use dims
    use global_data  ,only : mass_dat
    use meteodata   , only : m_dat
#ifdef with_budgets
    use budget_global,only : sum_update
#endif
    use chem_param,   only : ntracet
    use toolbox,      only : escape_tm
#ifdef MPI
    use mpi_const
#endif
    use partools   , only : which_par, previous_par
    use partools   , only : lmloc, offsetl
    use partools   , only : ntracetloc
    use tracer_data, only : Par_Check_Domain

    implicit none

    ! input
    integer,intent(in)   :: region

    ! local
    real,dimension(:,:,:,:),pointer          :: rm, rmp
#ifdef slopes
    real,dimension(:,:,:,:),pointer          :: rxm,rym,rzm, rxmp,rymp,rzmp
#endif
    real,dimension(:,:,:),pointer            :: m,mp
    real,dimension(:,:,:),allocatable        :: to_parent

    real,dimension(xref(region)/xref(parent(region)), &
         yref(region)/yref(parent(region)), &
         zref(region)/zref(parent(region)))   :: slope
    real    :: dummy
    real    :: sum_parent, sum_to_parent, sum_parent_all, sum_to_parent_all
    integer :: i, iend_loop, iip, ip, j, jp, l, lp, n
    integer :: lb, le, ib, ie, jb, je
    integer :: my_parent, xref_, yref_, zref_
    integer :: imp, jmp, lmp, imr, jmr, lmr, nt
    real    :: m_value, mxval, sum_mass
    integer,dimension(3) :: mxloc
    integer :: communicator, root_id
    integer :: lmpa

    ! start

    if (region == 1) return

    which_par=previous_par(region)

    !WP! make sure parents are on same domain
    call Par_Check_Domain( region, 'p', which_par )

    if ( which_par == 'tracer' .and. ntracetloc == 0 ) return 
    if ( which_par == 'levels' .and. lmloc == 0 ) return  !WP! 

    imr = im(region)
    jmr = jm(region)
    my_parent = parent(region)
    xref_ = xref(region)/xref(my_parent)
    yref_ = yref(region)/yref(my_parent)
    zref_ = zref(region)/zref(my_parent)

    m  => m_dat(region   )%data
    mp => m_dat(my_parent)%data
       
    if ( which_par == 'tracer' ) then

       rm => mass_dat(region)%rm_t
#ifdef slopes
       rxm => mass_dat(region)%rxm_t
       rym => mass_dat(region)%rym_t
       rzm => mass_dat(region)%rzm_t
#endif

       rmp => mass_dat(my_parent)%rm_t
#ifdef slopes
       rxmp => mass_dat(my_parent)%rxm_t
       rymp => mass_dat(my_parent)%rym_t
       rzmp => mass_dat(my_parent)%rzm_t
#endif

       lmr = lm(region)
       nt = ntracetloc
#ifdef MPI  
       communicator=com_trac  !WP! assign com_trac as communicator
       root_id=root_t
#endif
    else if ( which_par == 'levels' ) then

       rm => mass_dat(region)%rm_k
#ifdef slopes
       rxm => mass_dat(region)%rxm_k
       rym => mass_dat(region)%rym_k
       rzm => mass_dat(region)%rzm_k
#endif

       rmp => mass_dat(my_parent)%rm_k
#ifdef slopes
       rxmp => mass_dat(my_parent)%rxm_k
       rymp => mass_dat(my_parent)%rym_k
       rzmp => mass_dat(my_parent)%rzm_k
#endif

       lmr = lmloc
       nt=ntracet
#ifdef MPI  
       communicator=com_lev  !WP! assign com_lev as communicator
       root_id=root_k
#endif
    end if

    if ( okdebug ) then
       write (gol,*) 'update_parent: my_parent=',my_parent, &
            ' x-,y-,zref_: ',xref_,yref_,zref_ ; call goPr
    end if

    imp = im(region)/xref_
    jmp = jm(region)/yref_
    lmp = lmr

    if ( ibeg(region) < iend(region) .and. imp /= iend(region)-ibeg(region)+1 )  &
         call escape_tm('update_parent: stop')
    if ( jmp /= jend(region)-jbeg(region)+1 ) &
         call escape_tm('update_parent: stop')

    ! ~~ mass (all levels!)

    lmpa = lm(region)
    allocate(to_parent(imp,jmp,lmpa))

    to_parent = 0.0
    do i=1,imr
       ip = 1 + (i-1)/xref_
       do j=1,jmr
          jp = 1 + (j-1)/yref_
          do l=1,lmpa
             to_parent(ip,jp,l) = to_parent(ip,jp,l) + m(i,j,l)
          end do
       end do
    end do
    if ( ibeg(region) < iend(region) ) then   !no dateline crossing!
       mp(ibeg(region):iend(region),jbeg(region):jend(region),1:lmpa) = &
            to_parent 
    else
       mp(ibeg(region):im(region),jbeg(region):jend(region),1:lmpa)  = &
            to_parent(1:im(region)-ibeg(region)+1,:,:)
       mp(1:iend(region),jbeg(region):jend(region),1:lmpa)  = &
            to_parent(im(region)-ibeg(region)+2:,:,:)
    end if

    deallocate(to_parent)
    
    ! ~~ tracers

    allocate(to_parent(imp,jmp,lmp))

    sum_parent = 0.0
    sum_to_parent = 0.0
    sum_parent_all = 0.0
    sum_to_parent_all = 0.0
    do n=1,nt
       to_parent = 0.0
       do i=1,imr
          ip = 1 + (i-1)/xref_
          do j=1,jmr
             jp = 1 + (j-1)/yref_
             do l=1,lmr
                lp = 1 + (l-1)/zref_
                to_parent(ip,jp,lp) = to_parent(ip,jp,lp) + rm(i,j,l,n)
                if ( n == 1 ) sum_to_parent = sum_to_parent + rm(i,j,l,1)
#ifdef MPI         
                if ( which_par == 'tracer' .and. myid/=pe_first_tracer) &
                     sum_to_parent=0.0 !WP! only on one when in tracer
#endif
             end do
          end do
       end do
       if ( ibeg(region) < iend(region) ) then   !no dateline crossing!
          if ( n == 1 ) then
             sum_parent = sum(rmp(ibeg(region):iend(region), &
                  jbeg(region):jend(region), 1:lmr,1) )
#ifdef MPI     
             if ( which_par == 'tracer' .and. myid /= pe_first_tracer ) &
                  sum_parent=0.0 !WP! only on one when in tracer
#endif
          end if
          ! CMK this line gave a problem 1:lmr /= to_parent dimension!
          rmp(ibeg(region):iend(region),jbeg(region):jend(region),1:lmr,n) = &
               to_parent 
       else
          if (n == 1) then
             sum_parent = sum(rmp(ibeg(region):im(region), &
                  jbeg(region):jend(region),   &
                  1:lmr,1) ) + &
                  sum(rmp(1:iend(region), &
                  jbeg(region):jend(region),   &
                  1:lmr,1) ) 
#ifdef MPI     
             if ( which_par == 'tracer' .and. myid /= pe_first_tracer ) &
                  sum_parent=0.0 !WP! only on one when in tracer
#endif
          end if
          rmp(ibeg(region):im(region),jbeg(region):jend(region),1:lmr,n)  = &
               to_parent(1:im(region)-ibeg(region)+1,:,:)
          rmp(1:iend(region),jbeg(region):jend(region),1:lmr,n)  = &
               to_parent(im(region)-ibeg(region)+2:,:,:)
       end if
    end do   ! ntracet

#ifdef with_budgets

#ifdef MPI
    call mpi_allreduce(sum_parent,sum_parent_all,1,my_real, &
         mpi_sum,communicator,ierr)
    call mpi_allreduce(sum_to_parent,sum_to_parent_all,1,my_real, &
         mpi_sum,communicator,ierr)
#else
    sum_parent_all = sum_parent
    sum_to_parent_all = sum_to_parent
#endif

    sum_update(my_parent) = sum_update(my_parent) &
         + sum_to_parent_all - sum_parent_all

#endif

    deallocate(to_parent)

    nullify(m)
    nullify(rm)
#ifdef slopes
    nullify(rxm)
    nullify(rym)
    nullify(rzm)
#endif

    nullify(mp)
    nullify(rmp)
#ifdef slopes
    nullify(rxmp)
    nullify(rymp)
    nullify(rzmp)
#endif

    ! mk still to do : limit the slopes after rm update!

  end subroutine update_parent



end module zoom_tools
