!-------------------------------------------

! NEW OCTREEBIT LIBRARY (WRITTEN 26-27/02/2004) BY JEAN BRAUN

! This version addresses problems that arose in the computation
! of the icon array for very large octrees. This new version is much
! more efficient and uses the same amount of memory (sometimes less).
! Many of the querry routines have been greatly improved by storing
! additional information in the octree structure. Data storage and
! navigation within the octree are based on the methods described by
! Frisken and Perry from Mitsubishi Electric Research Lab.
! The computation of the icon array is based on a three stage method:
! (1) Compute icon and a nodal position array disregarding the nodal connectivity
! (2) Sort the nodal arrays by ordering along x, y and z coordinates
! (3) Remove the spurious nodes and modify the icon array accordingly
! Because a fast sorting method is used (O(n log log n)), this approach
! is rather efficient.


!=======================!
!=====[OCTREE_INIT]=====!
!=======================!

subroutine octree_init (octree,noctree)

! This routine must be called before any other routine when an octree is created
! the basic structure of the octree is 
! octree(1)=maximum level (unit cube is level 0)
! octree(2)=number of leaves
! octree(3)=total length of octree
! for each cube in the octree (at location loc)
! octree(loc)=level
! octree(loc+1)=address of parent
! octree(loc+2 to loc+9)=address of children (if negative the child is a leaf and the
!         value is the leaf number in the sequence of leaves)

implicit none

integer noctree,octree(noctree)

integer levelmax,nleaves,length,loc,k

levelmax=1
nleaves=8
length=13

octree(1)=levelmax
octree(2)=nleaves
octree(3)=length

loc=4

octree(loc)=1
octree(loc+1)=0
  do k=1,8
  octree(loc+1+k)=-k
  enddo

return
end

!====================================!
!=====[FIND_INTEGER_COORDINATES]=====!
!====================================!

subroutine find_integer_coordinates (x,y,z,ix,iy,iz,levelmax)

! returns the integer coordinates of point (x,y,z) in (ix,iy,iz)
! the integer coordinates are determined by the maximum level in the octree
! levelmax

! the integer coordinate is comprise between 1 and 2**levelmax
! and corresponds to the location of the leaf containing the
! point of given coordinates

implicit none

double precision x,y,z
integer ix,iy,iz,levelmax

double precision powermax

powermax=2.d0**levelmax
ix=int(x*powermax)
iy=int(y*powermax)
iz=int(z*powermax)

return
end

!=================================!
!=====[FIND_REAL_COORDINATES]=====!
!=================================!

subroutine find_real_coordinates (ix,iy,iz,x,y,z,levelmax)

! returns the real coordinates of a point of integer coordinates (ix,iy,iz)
! the real coordinates are determined by the maximum level in the octree

implicit none

double precision x,y,z
integer ix,iy,iz,levelmax

double precision powermax

powermax=2**levelmax
x=dfloat(ix)/powermax
y=dfloat(iy)/powermax
z=dfloat(iz)/powermax

return
end

!========================================!
!=====[OCTREE_CREATE_FROM_PARTICLE]=====!
!========================================!

subroutine octree_create_from_particle (octree,noctree,x,y,z,np,level)

! updates the octree by creating a leaf at point (x,y,z)
! of level level
! if the leaf (or a cube of smaller level) exists, the routine has no effect on the octree
! note that x,y,z must belong to [0,1[

implicit none

integer noctree,octree(noctree),np
double precision x,y,z
integer level
double precision xp,yp,zp

integer levelin,ix,iy,iz,levelmax,loc,ip

levelmax=octree(1)

xp=x
yp=y
zp=z
levelin=0
loc=4
  if (xp.eq.1.d0) xp=1.d0-1.d-20
  if (yp.eq.1.d0) yp=1.d0-1.d-20
  if (zp.eq.1.d0) zp=1.d0-1.d-20
  if (xp.eq.0.d0) xp=1.d-20
  if (yp.eq.0.d0) yp=1.d-20
  if (zp.eq.0.d0) zp=1.d-20
  if (xp*(xp-1.d0).lt.0.d0 .and. yp*(yp-1.d0).lt.0.d0 .and. zp*(zp-1.d0).lt.0.d0) &
    call update (octree,noctree,xp,yp,zp,level,levelin,loc)

return
end

!========================================!
!=====[OCTREE_CREATE_FROM_PARTICLES]=====!
!========================================!

subroutine octree_create_from_particles (octree,noctree,x,y,z,np,level)

! updates the octree by creating a leaf at points (x(1:np),y(1:np),z(1:np))
! of level level(1:np)
! if the leaf (or a cube of smaller level) exists, the routine has no effect on the octree
! note that x,y,z must belong to [0,1[

implicit none

integer noctree,octree(noctree),np
double precision x(np),y(np),z(np)
integer level(np)
double precision xp,yp,zp

integer levelin,ix,iy,iz,levelmax,loc,ip

levelmax=octree(1)

do ip=1,np
xp=x(ip)
yp=y(ip)
zp=z(ip)
levelin=0
loc=4
  if (xp.eq.1.d0) xp=1.d0-1.d-20
  if (yp.eq.1.d0) yp=1.d0-1.d-20
  if (zp.eq.1.d0) zp=1.d0-1.d-20
  if (xp.eq.0.d0) xp=1.d-20
  if (yp.eq.0.d0) yp=1.d-20
  if (zp.eq.0.d0) zp=1.d-20
  if (xp*(xp-1.d0).lt.0.d0 .and. yp*(yp-1.d0).lt.0.d0 .and. zp*(zp-1.d0).lt.0.d0) &
    call update (octree,noctree,xp,yp,zp,level(ip),levelin,loc)
enddo

return
end

!==================!
!=====[UPDATE]=====!
!==================!

recursive subroutine update (octree,noctree,x,y,z,level,levelin,loc)

! DO NOT USE
! internal routine called by OctreeUpdate
! this routine divides a cube of the octree in 8
! if the requested level is greater than0
! and if the requested level is stricly greater than levelin
! loc is the location in the octree of the current cube (to be divided)

implicit none

integer noctree,octree(noctree),ix,iy,iz,levelin,level,loc
double precision x,y,z
integer ibits_jean
external ibits_jean

integer levelmax,ibitx,ibity,ibitz,ichild,iaddress,newaddress

if (level.eq.0) return

if (levelin+1.eq.level) return

levelmax=octree(1)
call find_integer_coordinates (x,y,z,ix,iy,iz,levelmax)
ibitx=ibits_jean(ix,levelmax-levelin-1)
ibity=ibits_jean(iy,levelmax-levelin-1)
ibitz=ibits_jean(iz,levelmax-levelin-1)

ichild=loc+2+ibitz*4+ibity*2+ibitx

if (ichild.gt.octree(3)) then
print*,ichild,' requested ',octree(3),' available'
stop 'octree needs to grow...'
endif

iaddress=octree(ichild)

  if (iaddress.lt.0) then
!adds a level
  newaddress=octree(3)+1
! updates current level
  octree(ichild)=newaddress
! updates octree length
  octree(3)=octree(3)+10
! updates number of leaves
  octree(2)=octree(2)+7
! updates maximum level
  if (levelin+2.gt.octree(1)) octree(1)=levelin+2
! creates new division
  octree(newaddress)=levelin+2
  octree(newaddress+1)=ichild
  octree(newaddress+2)=iaddress
  octree(newaddress+3)=-(octree(2)-6)
  octree(newaddress+4)=-(octree(2)-5)
  octree(newaddress+5)=-(octree(2)-4)
  octree(newaddress+6)=-(octree(2)-3)
  octree(newaddress+7)=-(octree(2)-2)
  octree(newaddress+8)=-(octree(2)-1)
  octree(newaddress+9)=-(octree(2))
  call update (octree,noctree,x,y,z,level,levelin+1,newaddress)
  else
  call update (octree,noctree,x,y,z,level,levelin+1,iaddress)
  endif

return
end


!=================!
!=====[IBITS]=====!
!=================!

integer function ibits_jean(i,ipos)

!internal function, do not modify

integer j
j=i/2**ipos
ibits_jean=j-(j/2)*2
return
end

!============================!
!=====[OCTREE_FIND_LEAF]=====!
!============================!

subroutine octree_find_leaf (octree,noctree,x,y,z,leaf,level,loc,x0,y0,z0,dxyz)

! given an octree of size noctree
! this routine returns the leaf number in which a point (x,y,z) resides
! the level of the leaf (0 is unit cube)
! the location in the octree of the part describing the parent of the leaf (loc)
! the centroid of the leaf (x0,y0,z0) and its size (dxyz)

implicit none

integer noctree,octree(noctree)
double precision x,y,z,x0,y0,z0,dxyz
integer leaf,level,loc

integer ix,iy,iz,levelin,locin,levelmax

levelmax=octree(1)
call find_integer_coordinates (x,y,z,ix,iy,iz,levelmax)

levelin=0
locin=4
leaf=0
call find_leaf (octree,noctree,ix,iy,iz,levelin,locin,leaf,level,loc)

call find_integer_coordinates (x,y,z,ix,iy,iz,level)
call find_real_coordinates (ix,iy,iz,x0,y0,z0,level)
dxyz=1.d0/2.d0**level

return
end

!=====================!
!=====[FIND_LEAF]=====!
!=====================!

recursive subroutine find_leaf (octree,noctree,ix,iy,iz,levelin,locin,leaf,level,loc)

! DO NOT USE
! internal routine used by octree_find_leaf

implicit none

integer noctree,octree(noctree),ix,iy,iz,levelin,leaf,level,loc,locin
integer levelmax,ibitx,ibity,ibitz,ichild,iaddress
integer ibits_jean
external ibits_jean

levelmax=octree(1)
!ibitx=ibits(ix,levelmax-levelin-1,1)
!ibity=ibits(iy,levelmax-levelin-1,1)
!ibitz=ibits(iz,levelmax-levelin-1,1)
ibitx=ibits_jean(ix,levelmax-levelin-1)
ibity=ibits_jean(iy,levelmax-levelin-1)
ibitz=ibits_jean(iz,levelmax-levelin-1)

ichild=locin+2+ibitz*4+ibity*2+ibitx

if (ichild.gt.octree(3)) stop 'octree needs to grow...'
iaddress=octree(ichild)

  if (iaddress.lt.0) then
  leaf=-iaddress
  level=levelin+1
  loc=locin
  return
  else
  call find_leaf (octree,noctree,ix,iy,iz,levelin+1,iaddress,leaf,level,loc)
  endif

return
end

!===============================!
!=====[OCTREETHROUGHLEAVES]=====!
!===============================!

subroutine OctreeThroughLeaves (octree,noctree)

! DO NOT USE
! this is a general routine that simply goes through the leaves
! this routine is used as a template to build other routines.
! it should not be used

implicit none

integer noctree,octree(noctree)

integer loc,ix,iy,iz

loc=4
ix=0
iy=0
iz=0
call throughleaves (octree,noctree,loc,ix,iy,iz)

return
end

!=========================!
!=====[THROUGHLEAVES]=====!
!=========================!

recursive subroutine throughleaves (octree,noctree,loc,ix,iy,iz)

! DO NOT USE
! internal routine
! on entry we have the address of the current cube
! and the binary coordinates of its bottom corner

implicit none

integer noctree,octree(noctree),loc,ix,iy,iz

integer level,levelmax,ipower,ixn,iyn,izn,locn
integer idx,idy,idz,k

level=octree(loc)
levelmax=octree(1)

  do idz=0,1
  do idy=0,1
  do idx=0,1
  k=idx+idy*2+idz*4
  locn=octree(loc+2+k)
  ipower=2**(levelmax-level)
  ixn=ix+idx*ipower
  iyn=iy+idy*ipower
  izn=iz+idz*ipower
    if (locn.lt.0) then
! here i am going through the leaves one by one and i know
! their coordinates (ixn,iyn,izn)
! their address (loc+2+k)
! their leaf number (-locn)
! their level (level)
    else
    call throughleaves (octree,noctree,locn,ixn,iyn,izn)
    endif
  enddo
  enddo
  enddo

return
end

!===========================!
!=====[OCTREE_SMOOTHEN]=====!
!===========================!

subroutine octree_smoothen (octree,noctree)

! as it names indicates this routine smoothens the octree
! ie it ensures that no two adjacent leaves are more than one level apart

implicit none

integer noctree,octree(noctree)

integer loc,ix,iy,iz,nleaves,nleaves0
integer ioctree_number_of_elements

nleaves=ioctree_number_of_elements (octree,noctree)

do while (nleaves.ne.nleaves0)
nleaves0=nleaves
loc=4
ix=0
iy=0
iz=0
call smooth (octree,noctree,loc,ix,iy,iz)
nleaves=ioctree_number_of_elements (octree,noctree)
enddo

return
end

!==================!
!=====[SMOOTH]=====!
!==================!

recursive subroutine smooth (octree,noctree,loc,ix,iy,iz)

! DO NOT USE
! internal routine
! on entry we have the address of the current cube
! and the binary coordinates of its bottom corner

implicit none

integer noctree,octree(noctree),loc,ix,iy,iz

integer level,levelmax,ipower,ixn,iyn,izn,locn
integer k,idx,idy,idz,ip
integer ipmax,iddx,iddy,iddz,ixp,iyp,izp,levelin,locp
double precision xp,yp,zp

level=octree(loc)
levelmax=octree(1)

  do idz=0,1
  do idy=0,1
  do idx=0,1
  k=idx+idy*2+idz*4
  locn=octree(loc+2+k)
  ipower=2**(levelmax-level)
  ixn=ix+idx*ipower
  iyn=iy+idy*ipower
  izn=iz+idz*ipower
    if (locn.lt.0) then
! here i am going through the leaves one by one and i know
! the binary coordinate of their bottom corner (ixn,iyn,izn)
! their address (loc+2+k)
! their leaf number (-locn)
! their level (level)
! the address of their parent (loc)

! first check that `right' neighbours are not down by more than 1 level
    ipmax=2**levelmax
    ip=2**level
    izp=izn+ipower
    iyp=iyn+ipower
    ixp=ixn+ipower
      if (izp.lt.ipmax) then
      zp=dfloat(izp)/ipmax
      yp=dfloat(iyn)/ipmax
      xp=dfloat(ixn)/ipmax
      levelin=0
      locp=4
      call update (octree,noctree,xp,yp,zp,level-1,levelin,locp)
      endif
      if (iyp.lt.ipmax) then
      zp=dfloat(izn)/ipmax
      yp=dfloat(iyp)/ipmax
      xp=dfloat(ixn)/ipmax
      levelin=0
      locp=4
      call update (octree,noctree,xp,yp,zp,level-1,levelin,locp)
      endif
      if (ixp.lt.ipmax) then
      zp=dfloat(izn)/ipmax
      yp=dfloat(iyn)/ipmax
      xp=dfloat(ixp)/ipmax
      levelin=0
      locp=4
      call update (octree,noctree,xp,yp,zp,level-1,levelin,locp)
      endif

! second check if the 'left' neighbours are not down by more than one level
    izp=izn-1
    iyp=iyn-1
    ixp=ixn-1
      if (izp.ge.0) then
      zp=dfloat(izp)/ipmax
      yp=dfloat(iyn)/ipmax
      xp=dfloat(ixn)/ipmax
      levelin=0
      locp=4
      call update (octree,noctree,xp,yp,zp,level-1,levelin,locp)
      endif
      if (iyp.ge.0) then
      zp=dfloat(izn)/ipmax
      yp=dfloat(iyp)/ipmax
      xp=dfloat(ixn)/ipmax
      levelin=0
      locp=4
      call update (octree,noctree,xp,yp,zp,level-1,levelin,locp)
      endif
      if (ixp.ge.0) then
      zp=dfloat(izn)/ipmax
      yp=dfloat(iyn)/ipmax
      xp=dfloat(ixp)/ipmax
      levelin=0
      locp=4
      call update (octree,noctree,xp,yp,zp,level-1,levelin,locp)
      endif

    else
    call smooth(octree,noctree,locn,ixn,iyn,izn)
    endif
  enddo
  enddo
  enddo

return
end

!=================================!
!=====[OCTREE_SUPER_SMOOTHEN]=====!
!=================================!

subroutine octree_super_smoothen (octree,noctree)

! as it names indicates this routine further smoothens the octree
! ie it ensures that for any given leaf and its six closest neighbouring
! leaves, ther is no difference in level larger than 1

! Note that contrary to octree_smoothen, octree_super_smoothen can
! be used iteratively to increase the smoothness of the octree

implicit none

integer noctree,octree(noctree)

integer loc,ix,iy,iz,nleaves,nleaves0
integer ioctree_number_of_elements

nleaves=ioctree_number_of_elements (octree,noctree)

do while (nleaves.ne.nleaves0)
nleaves0=nleaves
loc=4
ix=0
iy=0
iz=0
call super_smooth (octree,noctree,loc,ix,iy,iz,nleaves)
nleaves=ioctree_number_of_elements (octree,noctree)
enddo

call octree_smoothen (octree,noctree)

return
end

!========================!
!=====[SUPER_SMOOTH]=====!
!========================!

recursive subroutine super_smooth (octree,noctree,loc,ix,iy,iz,nleaves0)

! DO NOT USE
! internal routine
! on entry we have the address of the current cube
! and the binary coordinates of its bottom corner

implicit none

integer noctree,octree(noctree),loc,ix,iy,iz

integer level,levelmax,ipower,ixn,iyn,izn,locn
integer k,idx,idy,idz,ip
integer ipmax,iddx,iddy,iddz,ixp,iyp,izp,levelin,locp
double precision xp,yp,zp
integer levelout(6),levelset,leaf,locin,nleaves0

level=octree(loc)
levelmax=octree(1)

  do idz=0,1
  do idy=0,1
  do idx=0,1
  k=idx+idy*2+idz*4
  locn=octree(loc+2+k)
  ipower=2**(levelmax-level)
  ixn=ix+idx*ipower
  iyn=iy+idy*ipower
  izn=iz+idz*ipower
    if (locn.lt.0) then
! here i am going through the leaves one by one and i know
! the binary coordinate of their bottom corner (ixn,iyn,izn)
! their address (loc+2+k)
! their leaf number (-locn)
! their level (level)
! the address of their parent (loc)

! first find `right' neighbours levels
    ipmax=2**levelmax
    ip=2**level
    izp=izn+ipower
    iyp=iyn+ipower
    ixp=ixn+ipower
    levelout=level
      if (izp.lt.ipmax) then
      levelin=0
      locin=4
      leaf=0
      call find_leaf (octree,noctree,ixn,iyn,izp,levelin,locin,leaf,levelout(1),locp)
      if (leaf.gt.nleaves0) levelout(1)=level
      endif
      if (iyp.lt.ipmax) then
      levelin=0
      locin=4
      leaf=0
      call find_leaf (octree,noctree,ixn,iyp,izn,levelin,locin,leaf,levelout(2),locp)
      if (leaf.gt.nleaves0) levelout(2)=level
      endif
      if (ixp.lt.ipmax) then
      levelin=0
      locin=4
      leaf=0
      call find_leaf (octree,noctree,ixp,iyn,izn,levelin,locin,leaf,levelout(3),locp)
      if (leaf.gt.nleaves0) levelout(3)=level
      endif

! second find 'left' neighbours levels
    izp=izn-1
    iyp=iyn-1
    ixp=ixn-1
      if (izp.ge.0) then
      levelin=0
      locin=4
      leaf=0
      call find_leaf (octree,noctree,ixn,iyn,izp,levelin,locin,leaf,levelout(4),locp)
      if (leaf.gt.nleaves0) levelout(4)=level
      endif
      if (iyp.ge.0) then
      levelin=0
      locin=4
      leaf=0
      call find_leaf (octree,noctree,ixn,iyp,izn,levelin,locin,leaf,levelout(5),locp)
      if (leaf.gt.nleaves0) levelout(5)=level
      endif
      if (ixp.ge.0) then
      levelin=0
      locin=4
      leaf=0
      call find_leaf (octree,noctree,ixp,iyn,izn,levelin,locin,leaf,levelout(6),locp)
      if (leaf.gt.nleaves0) levelout(6)=level
      endif

! if the difference in level between the leaf and any of its neighbours is greater
! than 1, all leaves are set to the maximum level minus one at least

      if (maxval(levelout)-minval(levelout).gt.1) then
      levelset=maxval(levelout)-1
! now check that `right' neighbours are at least at this level
      izp=izn+ipower
      iyp=iyn+ipower
      ixp=ixn+ipower
        if (izp.lt.ipmax) then
        zp=dfloat(izp)/ipmax
        yp=dfloat(iyn)/ipmax
        xp=dfloat(ixn)/ipmax
        levelin=0
        locp=4
        call update (octree,noctree,xp,yp,zp,levelset,levelin,locp)
        endif
        if (iyp.lt.ipmax) then
        zp=dfloat(izn)/ipmax
        yp=dfloat(iyp)/ipmax
        xp=dfloat(ixn)/ipmax
        levelin=0
        locp=4
        call update (octree,noctree,xp,yp,zp,levelset,levelin,locp)
        endif
        if (ixp.lt.ipmax) then
        zp=dfloat(izn)/ipmax
        yp=dfloat(iyn)/ipmax
        xp=dfloat(ixp)/ipmax
        levelin=0
        locp=4
        call update (octree,noctree,xp,yp,zp,levelset,levelin,locp)
        endif
  ! now check that 'left' neighbours are at least at this level
      izp=izn-1
      iyp=iyn-1
      ixp=ixn-1
        if (izp.ge.0) then
        zp=dfloat(izp)/ipmax
        yp=dfloat(iyn)/ipmax
        xp=dfloat(ixn)/ipmax
        levelin=0
        locp=4
        call update (octree,noctree,xp,yp,zp,levelset,levelin,locp)
        endif
        if (iyp.ge.0) then
        zp=dfloat(izn)/ipmax
        yp=dfloat(iyp)/ipmax
        xp=dfloat(ixn)/ipmax
        levelin=0
        locp=4
        call update (octree,noctree,xp,yp,zp,levelset,levelin,locp)
        endif
        if (ixp.ge.0) then
        zp=dfloat(izn)/ipmax
        yp=dfloat(iyn)/ipmax
        xp=dfloat(ixp)/ipmax
        levelin=0
        locp=4
        call update (octree,noctree,xp,yp,zp,levelset,levelin,locp)
        endif
  
      endif

    else
    call super_smooth(octree,noctree,locn,ixn,iyn,izn,nleaves0)
    endif
  enddo
  enddo
  enddo

return
end

!===============================!
!=====[OCTREE_SHOW_AS_MESH]=====!
!===============================!

subroutine octree_show_as_mesh (octree,noctree)

! routine to show the octree as a mesh in vrml
! produces a VRML file called mesh.wrl
! this is mostly used for debugging purposes

implicit none

integer noctree,octree(noctree)

integer loc,ix,iy,iz

open (7,file='mesh.wrl',status='unknown')
write (7,'(a)') '#VRML V2.0 utf8'
write (7,'(a)') 'Transform { children ['
write (7,'(a)') 'NavigationInfo { '
write (7,'(a)') 'type ["EXAMINE"]'
write (7,'(a)') 'headlight FALSE}'
write (7,'(a)') 'Background{groundColor 1 1 1 skyColor 1 1 1}'
write (7,'(a)') 'DirectionalLight {ambientIntensity  0.2'
write (7,'(a)') '                  color      1 1 1'
write (7,'(a)') '                  direction  .8 1 .5}'
write (7,'(a)') 'DirectionalLight {ambientIntensity  0.2'
write (7,'(a)') '                  color      1 1 1'
write (7,'(a)') '                  direction  -.8 -1 -.5}'
write (7,'(a)') 'Transform { children Viewpoint {'
write (7,'(a)') '      description "Starting"'
write (7,'(a)') '      fieldOfView 1'
write (7,'(a,3f12.8,a)') '      position    ',-.41885125637054443, -.8311104774475098, 1.5406757593154907
write (7,'(a,4f12.8,a)') '      orientation ', .7352051138877869, -.10698327422142029, -.669348955154419, 1.3260198831558228,'}}'
write (7,'(a,a)') 'DEF Node0 Shape{geometry Sphere{radius 0.0075', &
                ' }appearance Appearance{material Material{diffuseColor 1 0 0}}}'
write (7,'(a,3f10.6,a)') 'Transform{translation',0.,0.,0., &
                         ' children [USE Node0]}'
loc=4
ix=0
iy=0
iz=0
call show (octree,noctree,loc,ix,iy,iz)
write (7,'(a)') ']}'
close (7)

return
end

!================!
!=====[SHOW]=====!
!================!

recursive subroutine show (octree,noctree,loc,ix,iy,iz)

! DO NOT USE
! internal routine
! to show the octree as a mesh

implicit none

integer noctree,octree(noctree),loc,ix,iy,iz

integer level,levelmax,ipower,ixn,iyn,izn,locn
integer idx,idy,idz,k
double precision x1,y1,z1,x2,y2,z2,dxyz

level=octree(loc)
levelmax=octree(1)

  do idz=0,1
  do idy=0,1
  do idx=0,1
  k=idx+idy*2+idz*4
  locn=octree(loc+2+k)
  ipower=2**(levelmax-level)
  ixn=ix+idx*ipower
  iyn=iy+idy*ipower
  izn=iz+idz*ipower
    if (locn.lt.0) then
! here i am going through the leaves one by one and i know
! their coordinates (ixn,iyn,izn)
! their address (loc+2+k)
! their leaf number (-locn)
! their level (level)
    call find_real_coordinates (ixn,iyn,izn,x1,y1,z1,levelmax)
    dxyz=1.d0/(2.d0**level)
    x2=x1+dxyz
    y2=y1+dxyz
    z2=z1+dxyz
    write (7,'(a,24f10.3,a,a,a)') &
         'Shape { geometry IndexedLineSet { coord Coordinate { point [', &
          x1,y1,z1,x2,y1,z1,x2,y2,z1,x1,y2,z1,x1,y1,z2,x2,y1,z2,x2,y2,z2,x1,y2,z2, &
          ']} coordIndex [0 1 2 3 0 -1 4 5 6 7 4 -1 0 4 -1 1 5 -1 2 6 -1 3 7 -1', &
          ']}appearance Appearance { material Material { emissiveColor 0 0 0}}}'
    else
    call show (octree,noctree,locn,ixn,iyn,izn)
    endif
  enddo
  enddo
  enddo

return
end

!=====================================!
!=====[OCTREE_FIND_ELEMENT_LEVEL]=====!
!=====================================!

subroutine octree_find_element_level (octree,noctree,levs,nleaves)

! routine to return the level of each leaf
! the result is returned in the array levs of dimension nleaves

! the function ioctree_number_of_elements should be called first to
! find nleaves and dimension levs accordingly

implicit none

integer noctree,octree(noctree),nleaves,levs(nleaves)

integer loc,ix,iy,iz

loc=4
ix=0
iy=0
iz=0
call levels (octree,noctree,loc,ix,iy,iz,levs,nleaves)

return
end

!==================!
!=====[LEVELS]=====!
!==================!

recursive subroutine levels (octree,noctree,loc,ix,iy,iz,levs,nleaves)

! DO NOT USE
! internal routine
! on entry we have the address of the current cube
! and the binary coordinates of its bottom corner

implicit none

integer noctree,octree(noctree),loc,ix,iy,iz
integer nleaves,levs(nleaves)

integer level,levelmax,ipower,ixn,iyn,izn,locn
integer idx,idy,idz,k

level=octree(loc)
levelmax=octree(1)

  do idz=0,1
  do idy=0,1
  do idx=0,1
  k=idx+idy*2+idz*4
  locn=octree(loc+2+k)
  ipower=2**(levelmax-level)
  ixn=ix+idx*ipower
  iyn=iy+idy*ipower
  izn=iz+idz*ipower
    if (locn.lt.0) then
! here i am going through the leaves one by one and i know
! their coordinates (ixn,iyn,izn)
! their address (loc+2+k)
! their leaf number (-locn)
! their level (level)
    if (-locn.gt.nleaves) stop 'array level needs to grow'
    levs(-locn)=level
    else
    call levels (octree,noctree,locn,ixn,iyn,izn,levs,nleaves)
    endif
  enddo
  enddo
  enddo

return
end

!======================================!
!=====[IOCTREE_NUMBER_OF_ELEMENTS]=====!
!======================================!

integer function ioctree_number_of_elements (octree,noctree)

! function that returns (in ioctree_number_of_elements) the number of leaves in the octree

implicit none

integer noctree,octree(noctree)

ioctree_number_of_elements=octree(2)

return
end

!=================================!
!=====[IOCTREE_MAXIMUM_LEVEL]=====!
!=================================!

integer function ioctree_maximum_level (octree,noctree)

! function that returns (in ioctree_maximum_level) the maximum level in the octree

implicit none

integer noctree,octree(noctree)

ioctree_maximum_level=octree(1)

return
end

!========================!
!=====[IOCTREE_SIZE]=====!
!========================!

integer function ioctree_size (octree,noctree)

! function that returns (in ioctree_size) the size of the octree

implicit none

integer noctree,octree(noctree)

ioctree_size=octree(3)

return
end

!=================================!
!=====[OCTREE_CREATE_UNIFORM]=====!
!=================================!

subroutine octree_create_uniform (octree,noctree,levelt)

! routine to generate a uniform octree down to level levelt

implicit none

integer noctree,octree(noctree),levelt

integer nl,levelin,loc,iz,ix,iy
double precision x,y,z

nl=2**levelt
do iz=0,nl-1
z=dfloat(iz)/nl
do iy=0,nl-1
y=dfloat(iy)/nl
do ix=0,nl-1
x=dfloat(ix)/nl
levelin=0
loc=4
call update (octree,noctree,x,y,z,levelt,levelin,loc)
enddo
enddo
enddo

return
end


!=================================!
!=====[OCTREE_RENUMBER_NODES]=====!
!=================================!

subroutine octree_renumber_nodes (icon,nleaves,xa,ya,za,na)

! This routine renumbers the nodes to minimize the maximum
! difference (in the least square sense) between the numbers
! of the nodes belonging to any given element.

! This is useful is one wishes to use the octree mesh for FE
! calculations.

! This uses SLOAN's algorithm and routines

! in output, the icon array is modified as well as the node
! location arrays (xa,ya,za) that have been reordered

implicit none

integer nleaves,icon(8,nleaves),na
double precision xa(na),ya(na),za(na)
integer,dimension(:),allocatable::npn,xnpn,adj,xadj,sort,working
integer,dimension(:,:),allocatable::jcon
double precision,dimension(:),allocatable::xyz
integer ie,k,inpn,iadj,oldpro,newpro

inpn=nleaves*8
iadj=nleaves*8*7

allocate (xnpn(nleaves+1),npn(inpn),adj(iadj),xadj(na+1))
allocate (sort(na),working(3*na+1))

xnpn(1)=1
  do ie=1,nleaves
  xnpn(ie+1)=xnpn(ie)+8-1
    do k=1,8
    npn(xnpn(ie)+k-1)=icon(k,ie)
    enddo
  enddo

call graph_sloan (na,nleaves,inpn,npn,xnpn,iadj,adj,xadj)

call label_sloan (na,xadj(na+1)-1,adj,xadj,sort,working,oldpro,newpro)

deallocate (xnpn,npn,adj,xadj,working)

allocate (jcon(8,nleaves),xyz(na))

  do ie=1,nleaves
    do k=1,8
    jcon(k,ie)=sort(icon(k,ie))
    enddo
  enddo
icon=jcon

  do k=1,na
  xyz(sort(k))=xa(k)
  enddo
xa=xyz

  do k=1,na
  xyz(sort(k))=ya(k)
  enddo
ya=xyz

  do k=1,na
  xyz(sort(k))=za(k)
  enddo
za=xyz

deallocate (sort,jcon,xyz)

return
end

!=========================================!
!=====[OCTREE_FIND_NODE_CONNECTIVITY]=====!
!=========================================!

subroutine octree_find_node_connectivity (octree,noctree,icon,nleaves,xa,ya,za,na)

! This routine finds the number (na) and locations (xa,ya,za)
! of the nodes of the octree
! It also computes the connectivity array between nodes and leaves
! (icon). Icon is dimensioned icon(8,nleaves) and contains the number
! of the 8 nodes connected by each leaf
! When calling this routine, na shold have the dimension used to dimension
! the coordinate arrys in the calling routine
! on return na contains the true dimension of these array (ie how many nodes 
! there are in the octree

implicit none

integer noctree,octree(noctree),nleaves,icon(8,nleaves),na
double precision xa(*),ya(*),za(*)
integer,dimension(:),allocatable::kx,ky,kz,isort,jsort,ksort
integer loc,ix,iy,iz,nk
integer k1,nnk,kk1,nnnk
integer i0,in,levelmax,npower,il,i,k,l,ii

allocate (kx(8*nleaves),ky(8*nleaves),kz(8*nleaves))

! first build a general/redondant icon array

loc=4
ix=0
iy=0
iz=0
nk=0
call iconfind (octree,noctree,loc,ix,iy,iz,icon,kx,ky,kz,nk)

if (nk.ne.8*nleaves) stop 'error in iconfind'

allocate (isort(nk),jsort(nk),ksort(nk))

! here we rank the nodes according to their x, y and then z coordinates

call mrgrnk (kx,isort,nk)
call sort (kx,isort,nk)
call sort (ky,isort,nk)
call sort (kz,isort,nk)

jsort=isort

k1=1
do i=1,nk
  if (kx(i).gt.kx(k1).or.i.eq.nk) then
  nnk=i-k1
    if (nnk.gt.1) then
    call mrgrnk (ky(k1),isort(k1),nnk)
    call sort (kx(k1),isort(k1),nnk)
    call sort (ky(k1),isort(k1),nnk)
    call sort (kz(k1),isort(k1),nnk)
      do l=k1,k1+nnk-1
      ksort(l)=jsort(k1+isort(l)-1)
      enddo
      do l=k1,k1+nnk-1
      jsort(l)=ksort(l)
      enddo
    kk1=k1
    do ii=k1,k1+nnk-1
      if (ky(ii).gt.ky(kk1).or.ii.eq.k1+nnk-1) then
      nnnk=ii-kk1
        if (nnnk.gt.1) then
        call mrgrnk (kz(kk1),isort(kk1),nnnk)
        call sort (kx(kk1),isort(kk1),nnnk)
        call sort (ky(kk1),isort(kk1),nnnk)
        call sort (kz(kk1),isort(kk1),nnnk)
          do l=kk1,kk1+nnnk-1
          ksort(l)=jsort(kk1+isort(l)-1)
          enddo
          do l=kk1,kk1+nnnk-1
          jsort(l)=ksort(l)
          enddo
        endif
      kk1=ii
      endif
    enddo
    endif
  k1=i
  endif
enddo

isort=jsort

do i=1,nk
jsort(isort(i))=i
enddo

! at this point isort(i) is the location of the point of rank i
! whereas jsort(i) is the rank of the point of location i

! we modify isort to remove reference to identical points

! we introduce ksort to reorder the node into consecutive numbers

i0=1
na=1
do i=1,nk
in=0
if (kz(i).eq.kz(i0)) then
if (ky(i).eq.ky(i0)) then
if (kx(i).eq.kx(i0)) then
isort(i)=isort(i0)
ksort(isort(i))=na
in=1
endif
endif
endif
  if (in.eq.0) then
  i0=i
  na=na+1
  isort(i)=isort(i0)
  ksort(isort(i))=na
  endif
enddo

!print*,'There are ',na,' nodes'

! we modify icon to represent the new node representation

do il=1,nleaves
do k=1,8
icon(k,il)=ksort(isort(jsort(icon(k,il))))
enddo
enddo

! we build coordinates arrays

deallocate (jsort)

allocate (jsort(na))

! we define the correspondence between global and reduced node set

do i=1,nk
jsort(ksort(isort(i)))=i
enddo

! we compute the geometry of the nodes

levelmax=octree(1)
npower=2**levelmax
do i=1,na
xa(i)=dfloat(kx(jsort(i)))/npower
ya(i)=dfloat(ky(jsort(i)))/npower
za(i)=dfloat(kz(jsort(i)))/npower
enddo

deallocate (kx,ky,kz,isort,jsort,ksort)

return
end

!================!
!=====[SORT]=====!
!================!

subroutine sort (k,is,n)

! DO NOT USE
! this routine is used to sort an array according to an sorting array

implicit none

integer n,k(n),is(n)
integer,dimension(:),allocatable::kk
integer i

allocate (kk(n))

do i=1,n
kk(i)=k(is(i))
enddo

do i=1,n
k(i)=kk(i)
enddo

deallocate(kk)

return
end

!====================!
!=====[ICONFIND]=====!
!====================!

recursive subroutine iconfind (octree,noctree,loc,ix,iy,iz,icon,kx,ky,kz,nk)

! DO NOT USE
! internal routine
! on entry we have the address of the current cube
! and the binary coordinates of its bottom corner

implicit none

integer noctree,octree(noctree),loc,ix,iy,iz
integer icon(8,*),kx(*),ky(*),kz(*),nk

integer level,levelmax,ipower,ixn,iyn,izn,locn
integer idx,idy,idz,k,kkx,kky,kkz

level=octree(loc)
levelmax=octree(1)

  do idz=0,1
  do idy=0,1
  do idx=0,1
  k=idx+idy*2+idz*4
  locn=octree(loc+2+k)
  ipower=2**(levelmax-level)
  ixn=ix+idx*ipower
  iyn=iy+idy*ipower
  izn=iz+idz*ipower
    if (locn.lt.0) then
! here i am going through the leaves one by one and i know
! their coordinates (ixn,iyn,izn)
! their address (loc+2+k)
! their leaf number (-locn)
! their level (level)
      do kkz=0,1
      do kky=0,1
      do kkx=0,1
      nk=nk+1
      k=kkx+kky*2+kkz*4
      icon(k+1,-locn)=nk
      kx(nk)=ixn+kkx*ipower
      ky(nk)=iyn+kky*ipower
      kz(nk)=izn+kkz*ipower
      enddo
      enddo
      enddo
    else
    call iconfind (octree,noctree,locn,ixn,iyn,izn,icon,kx,ky,kz,nk)
    endif
  enddo
  enddo
  enddo

return
end

!============================!
!=====[OCTREE_SHOW_ICON]=====!
!============================!

subroutine octree_show_icon (icon,nelem,x,y,z,nnode)

! this routine creates a VRML file called icon.wrl
! that shows the octree as a mesh and the nodes as small spheres
! note that this routine does use icon
! note that it uses the octree definition for icon

! it creates a VRML file called icon.wrl

implicit none

integer nelem,icon(8,nelem),nnode
double precision x(nnode),y(nnode),z(nnode)
integer ie,i,kkk

open (7,file='icon.wrl',status='unknown')
write (7,'(a)') '#VRML V2.0 utf8'
write (7,'(a)') 'Transform { children ['
write (7,'(a)') 'NavigationInfo { '
write (7,'(a)') 'type ["EXAMINE"]'
write (7,'(a)') 'headlight FALSE}'
write (7,'(a)') 'Background{groundColor 1 1 1 skyColor 1 1 1}'
write (7,'(a)') 'DirectionalLight {ambientIntensity  0.2'
write (7,'(a)') '                  color      1 1 1'
write (7,'(a)') '                  direction  .8 1 .5}'
write (7,'(a)') 'DirectionalLight {ambientIntensity  0.2'
write (7,'(a)') '                  color      1 1 1'
write (7,'(a)') '                  direction  -.8 -1 -.5}'
write (7,'(a)') 'Transform { children Viewpoint {'
write (7,'(a)') '      description "Starting"'
write (7,'(a)') '      fieldOfView 1'
write (7,'(a,3f12.8,a)') '      position    ',-.41885125637054443, -.8311104774475098, 1.5406757593154907
write (7,'(a,4f12.8,a)') '      orientation ', .7352051138877869, -.10698327422142029, -.669348955154419, 1.3260198831558228,'}}'
write (7,'(a,a)') 'DEF Node0 Shape{geometry Sphere{radius 0.0075', &
                      ' }appearance Appearance{material Material{diffuseColor 1 0 0}}}'

do ie=1,nelem
write (7,'(a,24f10.3,a,a,a)') &
     'Shape { geometry IndexedLineSet { coord Coordinate { point [', &
      (x(icon(kkk,ie)),y(icon(kkk,ie)),z(icon(kkk,ie)),kkk=1,8), &
     ']} coordIndex [0 1 3 2 0 -1 4 5 7 6 4 -1 0 4 -1 1 5 -1 2 6 -1 3 7 -1', &
     ']}appearance Appearance { material Material { emissiveColor 0 0 0}}}'
enddo

do i=1,nnode
write (7,'(a,3f10.6,a)') 'Transform{translation',x(i),y(i),z(i), &
                         ' children [USE Node0]}'
enddo

write (7,'(a)') ']}'
close (7)

return
end

!=================================!
!=====[OCTREE_FIND_BAD_FACES]=====!
!=================================!

subroutine octree_find_bad_faces (octree,noctree,iface,nface,icon,nelem)

! returns the bad faces as an array (iface) of 9 nodes per face
! numbering used is different from earlier version of octreebit
! here it is:

! 4--7--3
! |  |  |
! 8--9--6
! |  |  |
! 1--5--2

! iface is the resulting bad face information iface(9,nface)
! nface is the number of bad faces found
! icon is the connectivity array
! of dimension nelem

implicit none

integer noctree,octree(noctree)
integer nface,iface(9,nface),nelem,icon(8,nelem)

integer loc,ix,iy,iz,mface

loc=4
ix=0
iy=0
iz=0
mface=nface
nface=0
call badface (octree,noctree,loc,ix,iy,iz,iface,nface,mface,icon,nelem)

return
end

!===================!
!=====[BADFACE]=====!
!===================!

recursive subroutine badface (octree,noctree,loc,ix,iy,iz,iface,nface,mface,icon,nelem)

! DO NOT USE
! internal routine used by octree_find_bad_faces

implicit none

integer noctree,octree(noctree),loc,ix,iy,iz
integer mface,iface(9,mface),nelem,icon(8,nelem),nface

integer level,levelmax,ipower,ixn,iyn,izn,locn,ipowerne
integer k,idx,idy,idz,ip,ixpp,iypp,izpp
integer ipmax,iddx,iddy,iddz,ixp,iyp,izp,levelin,locp,locne,levelne,leaf
double precision xp,yp,zp,x0,y0,z0,dxyz

level=octree(loc)
levelmax=octree(1)

  do idz=0,1
  do idy=0,1
  do idx=0,1
  k=idx+idy*2+idz*4
  locn=octree(loc+2+k)
  ipower=2**(levelmax-level)
  ixn=ix+idx*ipower
  iyn=iy+idy*ipower
  izn=iz+idz*ipower
    if (locn.lt.0) then
! here i am going through the leaves one by one and i know
! the binary coordinate of their bottom corner (ixn,iyn,izn)
! their address (loc+2+k)
! their leaf number (-locn)
! their level (level)
! the address of their parent (loc)

! first check that 'right' neighbours are of a higher level
    ipmax=2**levelmax
    ip=2**level
    ixp=ixn+ipower
    iyp=iyn+ipower
    izp=izn+ipower
      if (izp.lt.ipmax) then
      zp=dble(izp)/ipmax
      yp=dble(iyn)/ipmax
      xp=dble(ixn)/ipmax
      levelin=0
      locp=4
      call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        if (levelne.gt.level) then
        ipowerne=2**(levelmax-levelne)
        nface=nface+1
        if (nface.gt.mface) stop 'nface needs to grow'
        iface(1,nface)=icon(5,-locn)
        iface(2,nface)=icon(6,-locn)
        iface(3,nface)=icon(8,-locn)
        iface(4,nface)=icon(7,-locn)
        iface(5,nface)=icon(2,leaf)
        iface(8,nface)=icon(3,leaf)
        iface(9,nface)=icon(4,leaf)
        ixpp=ixn+ipowerne
        xp=dble(ixpp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(6,nface)=icon(4,leaf)
        iypp=iyn+ipowerne
        yp=dble(iypp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(7,nface)=icon(3,leaf)
        endif
      endif
      if (iyp.lt.ipmax) then
      zp=dble(izn)/ipmax
      yp=dble(iyp)/ipmax
      xp=dble(ixn)/ipmax
      levelin=0
      locp=4
      call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        if (levelne.gt.level) then
        ipowerne=2**(levelmax-levelne)
        nface=nface+1
        if (nface.gt.mface) stop 'nface needs to grow'
        iface(1,nface)=icon(3,-locn)
        iface(2,nface)=icon(7,-locn)
        iface(3,nface)=icon(8,-locn)
        iface(4,nface)=icon(4,-locn)
        iface(5,nface)=icon(5,leaf)
        iface(8,nface)=icon(2,leaf)
        iface(9,nface)=icon(6,leaf)
        izpp=izn+ipowerne
        zp=dble(izpp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(6,nface)=icon(6,leaf)
        ixpp=ixn+ipowerne
        xp=dble(ixpp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(7,nface)=icon(2,leaf)
        endif
      endif
      if (ixp.lt.ipmax) then
      zp=dble(izn)/ipmax
      yp=dble(iyn)/ipmax
      xp=dble(ixp)/ipmax
      levelin=0
      locp=4
      call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        if (levelne.gt.level) then
        ipowerne=2**(levelmax-levelne)
        nface=nface+1
        if (nface.gt.mface) stop 'nface needs to grow'
        iface(1,nface)=icon(2,-locn)
        iface(2,nface)=icon(4,-locn)
        iface(3,nface)=icon(8,-locn)
        iface(4,nface)=icon(6,-locn)
        iface(5,nface)=icon(3,leaf)
        iface(8,nface)=icon(5,leaf)
        iface(9,nface)=icon(7,leaf)
        iypp=iyn+ipowerne
        yp=dble(iypp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(6,nface)=icon(7,leaf)
        izpp=izn+ipowerne
        zp=dble(izpp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(7,nface)=icon(5,leaf)
        endif
      endif

! second check if the 'left' neighbours are of a higher level
    ixp=ixn-1
    iyp=iyn-1
    izp=izn-1
      if (izp.ge.0) then
      zp=dble(izp)/ipmax
      yp=dble(iyn)/ipmax
      xp=dble(ixn)/ipmax
      levelin=0
      locp=4
      call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        if (levelne.gt.level) then
        ipowerne=2**(levelmax-levelne)
        nface=nface+1
        if (nface.gt.mface) stop 'nface needs to grow'
        iface(1,nface)=icon(1,-locn)
        iface(2,nface)=icon(2,-locn)
        iface(3,nface)=icon(4,-locn)
        iface(4,nface)=icon(3,-locn)
        iface(5,nface)=icon(6,leaf)
        iface(8,nface)=icon(7,leaf)
        iface(9,nface)=icon(8,leaf)
        ixpp=ixn+ipowerne
        xp=dble(ixpp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(6,nface)=icon(8,leaf)
        iypp=iyn+ipowerne
        yp=dble(iypp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(7,nface)=icon(7,leaf)
        endif
      endif
      if (iyp.ge.0) then
      zp=dble(izn)/ipmax
      yp=dble(iyp)/ipmax
      xp=dble(ixn)/ipmax
      levelin=0
      locp=4
      call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        if (levelne.gt.level) then
        ipowerne=2**(levelmax-levelne)
        nface=nface+1
        if (nface.gt.mface) stop 'nface needs to grow'
        iface(1,nface)=icon(1,-locn)
        iface(2,nface)=icon(2,-locn)
        iface(3,nface)=icon(6,-locn)
        iface(4,nface)=icon(5,-locn)
        iface(5,nface)=icon(4,leaf)
        iface(8,nface)=icon(7,leaf)
        iface(9,nface)=icon(8,leaf)
        izpp=izn+ipowerne
        zp=dble(izpp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(7,nface)=icon(8,leaf)
        ixpp=ixn+ipowerne
        xp=dble(ixpp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(6,nface)=icon(4,leaf)
        endif
      endif
      if (ixp.ge.0) then
      zp=dble(izn)/ipmax
      yp=dble(iyn)/ipmax
      xp=dble(ixp)/ipmax
      levelin=0
      locp=4
      call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        if (levelne.gt.level) then
        ipowerne=2**(levelmax-levelne)
        nface=nface+1
        if (nface.gt.mface) stop 'nface needs to grow'
        iface(1,nface)=icon(1,-locn)
        iface(2,nface)=icon(5,-locn)
        iface(3,nface)=icon(7,-locn)
        iface(4,nface)=icon(3,-locn)
        iface(5,nface)=icon(6,leaf)
        iface(8,nface)=icon(4,leaf)
        iface(9,nface)=icon(8,leaf)
        iypp=iyn+ipowerne
        yp=dble(iypp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(7,nface)=icon(8,leaf)
        izpp=izn+ipowerne
        zp=dble(izpp)/ipmax
        call octree_find_leaf (octree,noctree,xp,yp,zp,leaf,levelne,locne,x0,y0,z0,dxyz)
        iface(6,nface)=icon(6,leaf)
        endif
      endif

    else
    call badface (octree,noctree,locn,ixn,iyn,izn,iface,nface,mface,icon,nelem)
    endif
  enddo
  enddo
  enddo

return
end

!=================================!
!=====[OCTREE_SHOW_BAD_FACES]=====!
!=================================!

subroutine octree_show_bad_faces (octree,noctree,iface,nface,x,y,z,nnode)

! this routine creates a VRML file called bad_faces.wrl
! that shows the octree as produced by show_octree but it
! adds color to the bad faces to locate them

implicit none

integer noctree,octree(noctree)
integer nface,nnode,iface(9,nface)
double precision x(nnode),y(nnode),z(nnode)
integer loc,ix,iy,iz,i,k

open (7,file='bad_faces.wrl',status='unknown')
write (7,'(a)') '#VRML V2.0 utf8'
write (7,'(a)') 'Transform { children ['
write (7,'(a)') 'NavigationInfo { '
write (7,'(a)') 'type ["EXAMINE"]'
write (7,'(a)') 'headlight FALSE}'
write (7,'(a)') 'Background{groundColor 1 1 1 skyColor 1 1 1}'
write (7,'(a)') 'DirectionalLight {ambientIntensity  0.2'
write (7,'(a)') '                  color      1 1 1'
write (7,'(a)') '                  direction  .8 1 .5}'
write (7,'(a)') 'DirectionalLight {ambientIntensity  0.2'
write (7,'(a)') '                  color      1 1 1'
write (7,'(a)') '                  direction  -.8 -1 -.5}'
write (7,'(a)') 'Transform { children Viewpoint {'
write (7,'(a)') '      description "Starting"'
write (7,'(a)') '      fieldOfView 1'
write (7,'(a,3f12.8,a)') '      position    ',-.41885125637054443, -.8311104774475098, 1.5406757593154907
write (7,'(a,4f12.8,a)') '      orientation ', .7352051138877869, -.10698327422142029, -.669348955154419, 1.3260198831558228,'}}'

loc=4
ix=0
iy=0
iz=0
call show (octree,noctree,loc,ix,iy,iz)

do i=1,nface
write (7,'(a,27f10.3,a,a,a)') &
     'Shape { geometry IndexedLineSet { coord Coordinate { point [', &
     (x(iface(k,i)),y(iface(k,i)),z(iface(k,i)),k=1,9), &
     ']} coordIndex [8 1 -1 8 2 -1 8 3 -1 8 4 -1 8 5 -1 8 6 -1 8 7 -1 8 0 -1', &
     ']}appearance Appearance { material Material { emissiveColor 0 1 0}}}'
enddo

write (7,'(a)') ']}'
close (7)

return
end

!========================!
!=====[OCTREE_UNION]=====!
!========================!

subroutine octree_union (octree,noctree,octree1,noctree1,octree2,noctree2)

! NOTE: JEAN FOUND A BUG IN THIS ROUTINE ON JULY 4th 2006; HAS NOT BEEN FIXED!!!!
! IT SHOULD NOT BE USED.
! DOUAR HAS BEEN MODIFIED TO PERFORM THIS OPERATION FROM OTHER LOWER LEVEL ROUTINES
! IN OCTREEBITPLUS

! subroutine to calculate the union of two octrees (octree1,octree2)
! and store the result in a third octree (octree)
! it is recommended that the largest of the two octrees be octree1
! (as defined by their size ioctree_size (octree,noctree))
! on exit the two original octrees are left unchanged

implicit none

integer noctree,octree(noctree)
integer noctree1,octree1(noctree1)
integer noctree2,octree2(noctree2)

integer loc,ix,iy,iz

octree=octree1
noctree=noctree1

loc=4
ix=0
iy=0
iz=0
call unite (octree2,noctree2,loc,ix,iy,iz,octree,noctree)

return
end

!=================!
!=====[UNITE]=====!
!=================!

recursive subroutine unite (octree2,noctree2,loc,ix,iy,iz,octree,noctree)

! DO NOT USE
! internal routine
! called by octree_union

implicit none

integer noctree,octree(noctree),loc,ix,iy,iz
integer noctree2,octree2(noctree2)

integer level,levelmax,ipower,ixn,iyn,izn,locn
integer k,idx,idy,idz,ip
integer ipmax,iddx,iddy,iddz,ixp,iyp,izp,levelin,locp
double precision xp,yp,zp

level=octree2(loc)
levelmax=octree2(1)

  do idz=0,1
  do idy=0,1
  do idx=0,1
  k=idx+idy*2+idz*4
  locn=octree2(loc+2+k)
  ipower=2**(levelmax-level)
  ixn=ix+idx*ipower
  iyn=iy+idy*ipower
  izn=iz+idz*ipower
    if (locn.lt.0) then
! here i am going through the leaves one by one and i know
! the binary coordinate of their bottom corner (ixn,iyn,izn)
! their address (loc+2+k)
! their leaf number (-locn)
! their level (level)
! the address of their parent (loc)
    ip=2**level
    zp=dfloat(izn)/ip
    yp=dfloat(iyn)/ip
    xp=dfloat(ixn)/ip
    levelin=0
    locp=4
    call update (octree,noctree,xp,yp,zp,level,levelin,locp)
    else
    call unite (octree2,noctree2,locn,ixn,iyn,izn,octree,noctree)
    endif
  enddo
  enddo
  enddo

return
end




!==============================!
!=====[OCTREE_INTERPOLATE]=====!
!==============================!

subroutine octree_interpolate (octree,noctree,icon,nleaves,field,nfield,x,y,z,f)

! This function returns the value of a field (field) known at the nodes
! of an octree by trilinear interpolation

! icon is the connectivity matrix
! nleaves is the number of leaves in the octree
! field is the array of dimension nfield containing the field 
!     known at the nodes of the octree and to be interpolated
! x,y,z are the location of the point where the field is to be interpolated
! f is the resulting interpolated field

implicit none

integer noctree,octree(noctree),nleaves,icon(8,nleaves)
integer nfield
double precision field(nfield),x,y,z,x0,y0,z0,dxyz,r,s,t,h(8),phi,xt,yt,zt,f
integer leaf,level,loc,k,iii,jjj,kkk

! function modified by JEAN BRAUN on September 26 2005
! to correct for an error in the logics that led to an interpolation
! from an octree to another identical octree with differences in the
! interpolated function. The reason for this problem was related to
! bad faces or hanging nodes. Indeed, for a hanging node it was very likely
! that the leaf that was detected as the loeaf in which the node resides
! was in fact a leave where the node was a hanging node (ie not one of the
! 4 corner nodes). This meant that the interpolated value was not equal
! to the "constrained" value imposed by the linear constraint at the 
! hanging node. To correct for this we first check if the node can
! be interpolated with r,s,t values that are equal to 1 or -1. If this is
! true than this value is chosen as this would correspond to a nodal value

xt=x
yt=y
zt=z

if (xt.lt.-1.e-11 .or. xt.gt.1.d0+1.d-11) return
if (yt.lt.-1.e-11 .or. yt.gt.1.d0+1.d-11) return
if (zt.lt.-1.e-11 .or. zt.gt.1.d0+1.d-11) return

if (x.lt.1.e-11) xt=1.e-11
if (x.gt.1.d0-1.d-11) xt=1.d0-1.d-11
if (y.lt.1.e-11) yt=1.e-11
if (y.gt.1.d0-1.d-11) yt=1.d0-1.d-11
if (z.lt.1.e-11) zt=1.e-11
if (z.gt.1.d0-1.d-11) zt=1.d0-1.d-11

do kkk=-1,1,2
do jjj=-1,1,2
do iii=-1,1,2

xt=x+iii*1.d-10
yt=y+jjj*1.d-10
zt=z+kkk*1.d-10

if (xt*(xt-1.d0).ge.0d0 .or. yt*(yt-1.d0).ge.0d0 .or. zt*(zt-1.d0).ge.0d0) goto 111

call octree_find_leaf (octree,noctree,xt,yt,zt,leaf,level,loc,x0,y0,z0,dxyz)

r=(x-x0)/dxyz*2.d0-1.d0
s=(y-y0)/dxyz*2.d0-1.d0
t=(z-z0)/dxyz*2.d0-1.d0
h(1)=(1.d0-r)*(1.d0-s)*(1.d0-t)/8.d0
h(2)=(1.d0+r)*(1.d0-s)*(1.d0-t)/8.d0
h(3)=(1.d0-r)*(1.d0+s)*(1.d0-t)/8.d0
h(4)=(1.d0+r)*(1.d0+s)*(1.d0-t)/8.d0
h(5)=(1.d0-r)*(1.d0-s)*(1.d0+t)/8.d0
h(6)=(1.d0+r)*(1.d0-s)*(1.d0+t)/8.d0
h(7)=(1.d0-r)*(1.d0+s)*(1.d0+t)/8.d0
h(8)=(1.d0+r)*(1.d0+s)*(1.d0+t)/8.d0
phi=0.d0
  do k=1,8
  phi=phi+h(k)*field(icon(k,leaf))
  enddo
f=phi
if (abs(abs(r)-1.d0).lt.1.d-10 .and. abs(abs(s)-1.d0).lt.1.d-10 .and. abs(abs(t)-1.d0).lt.1.d-10) return

111 continue

enddo
enddo
enddo

return
end

!==============================!
!=====[OCTREE_INTERPOLATE]=====!
!==============================!

subroutine octree_interpolate3 (octree,noctree,icon,nleaves,field,nodex,nodey,nodez,nnode,x,y,z,f)

! This function returns the value of a field (field) known at the nodes
! of an octree by trilinear interpolation

! icon is the connectivity matrix
! nleaves is the number of leaves in the octree
! field is the array of dimension nfield containing the field 
!     known at the nodes of the octree and to be interpolated
! x,y,z are the location of the point where the field is to be interpolated
! f is the resulting interpolated field

implicit none

integer noctree,octree(noctree),nleaves,icon(8,nleaves)
integer nnode,inode
double precision nodex(nnode),nodey(nnode),nodez(nnode)
double precision field(nnode),x,y,z,x0,y0,z0,dxyz,r,s,t,h(8),phi,xt,yt,zt,f
integer leaf,level,loc,k,iii,jjj,kkk

! function modified by JEAN BRAUN on September 26 2005
! to correct for an error in the logics that led to an interpolation
! from an octree to another identical octree with differences in the
! interpolated function. The reason for this problem was related to
! bad faces or hanging nodes. Indeed, for a hanging node it was very likely
! that the leaf that was detected as the loeaf in which the node resides
! was in fact a leave where the node was a hanging node (ie not one of the
! 4 corner nodes). This meant that the interpolated value was not equal
! to the "constrained" value imposed by the linear constraint at the 
! hanging node. To correct for this we first check if the node can
! be interpolated with r,s,t values that are equal to 1 or -1. If this is
! true than this value is chosen as this would correspond to a nodal value

! function modified by Cedric Thieulot on November 21st 2007
! to correct for an apparent little bug in the case of the interpolation of 
! a given field from an octree onto the same one.
! this modification simply checks whether the point on which we want to interpolate
! already exists in the octree structure. The first three do-loops explore the 8
! points distant from point (x,y,z) by a tiny distance in all three dimensions.
! After having checked that the predicted point falls within the unit cube, 
! the leaf in which the predicted point falls in is found, and we check whether
! any of the nodes of the leaf has the same coordinates as point (x,y,z).
! This slows down a bit the function but also insures and exact interpolation on 
! common nodes of both octrees which are many since they usually at least 
! share the nodes of a level 5 uniform octree, i.e. 35937 nodes.


!=====[CT]=====
do kkk=-1,1,2
   zt=z+kkk*1.d-8
   do jjj=-1,1,2
      yt=y+jjj*1.d-8
      do iii=-1,1,2
         xt=x+iii*1.d-8
         if (xt>0.d0 .and. xt<1.d0 .and. &
             yt>0.d0 .and. yt<1.d0 .and. &
             zt>0.d0 .and. zt<1.d0 ) then
            call octree_find_leaf (octree,noctree,xt,yt,zt,leaf,level,loc,x0,y0,z0,dxyz)
            do k=1,8
               inode=icon(k,leaf)
               if (abs(nodex(inode)-x)<1.d-10 .and. &
                   abs(nodey(inode)-y)<1.d-10 .and. &
                   abs(nodez(inode)-z)<1.d-10 ) then
                  f=field(inode)
                  return
               end if
            end do
         end if
      end do
   end do
end do
!==============


xt=x
yt=y
zt=z

do kkk=-1,1,2
   zt=z+kkk*1.d-10
   do jjj=-1,1,2
      yt=y+jjj*1.d-10
      do iii=-1,1,2
         xt=x+iii*1.d-10
         if (xt*(xt-1.d0).ge.0d0 .or. &
             yt*(yt-1.d0).ge.0d0 .or. &
             zt*(zt-1.d0).ge.0d0) goto 111
         call octree_find_leaf (octree,noctree,xt,yt,zt,leaf,level,loc,x0,y0,z0,dxyz)
         r=(x-x0)/dxyz*2.d0-1.d0
         s=(y-y0)/dxyz*2.d0-1.d0
         t=(z-z0)/dxyz*2.d0-1.d0
         h(1)=(1.d0-r)*(1.d0-s)*(1.d0-t)/8.d0
         h(2)=(1.d0+r)*(1.d0-s)*(1.d0-t)/8.d0
         h(3)=(1.d0-r)*(1.d0+s)*(1.d0-t)/8.d0
         h(4)=(1.d0+r)*(1.d0+s)*(1.d0-t)/8.d0
         h(5)=(1.d0-r)*(1.d0-s)*(1.d0+t)/8.d0
         h(6)=(1.d0+r)*(1.d0-s)*(1.d0+t)/8.d0
         h(7)=(1.d0-r)*(1.d0+s)*(1.d0+t)/8.d0
         h(8)=(1.d0+r)*(1.d0+s)*(1.d0+t)/8.d0
         phi=0.d0
         do k=1,8
            phi=phi+h(k)*field(icon(k,leaf))
         enddo
         f=phi
         if (abs(r)-1.d0+abs(s)-1.d0+abs(t)-1.d0.lt.1.d-10) return
         111 continue
      enddo
   enddo
enddo

return
end














!===================================!
!=====[OCTREE_INTERPOLATE_MANY]=====!
!===================================!

subroutine octree_interpolate_many (nf,octree,noctree,icon,nleaves,nfield,x,y,z, &
                                    field1,f1,field2,f2,field3,f3, &
                                    field4,f4,field5,f5,field6,f6, &
                                    field7,f7,field8,f8,field9,f9, &
                                    field10,f10,field11,f11,field12,f12, &
                                    field13,f13,field14,f14,field15,f15)
! This function returns the value of several fields (fieldi) known at the nodes
! of an octree by trilinear interpolation

! nf is the number of fields being interpolate (must be comprised between 1 and 15)
! icon is the connectivity matrix
! nleaves is the number of leaves in the octree
! fieldi are the arrays of dimension nfield containing the fields
!     known at the nodes of the octree and to be interpolated
! x,y,z are the location of the point where the fields are to be interpolated
! fi are the resulting interpolated fields

! Note that the number of arguments to this routine depends on the number of
! fields to be interpolated (nf). This is why some of the arguments are declared
! as optional

implicit none

optional :: field2,f2,field3,f3,field4,f4,field5,f5,field6,f6
optional :: field7,f7,field8,f8,field9,f9,field10,f10
optional :: field11,f11,field12,f12,field13,f13,field14,f14,field15,f15

integer noctree,octree(noctree),nleaves,icon(8,nleaves)
integer nfield,nf
double precision field1(nfield),field2(nfield),field3(nfield),field4(nfield), &
                 field5(nfield),field6(nfield),field7(nfield),field8(nfield)
double precision field9(nfield),field10(nfield),field11(nfield),field12(nfield), &
                 field13(nfield),field14(nfield),field15(nfield)
double precision f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15
double precision x,y,z,x0,y0,z0,dxyz,r,s,t,h(8),phi,xt,yt,zt
integer leaf,level,loc,k,iii,jjj,kkk,ii

! function modified by JEAN BRAUN on September 26 2005
! to correct for an error in the logics that led to an interpolation
! from an octree to another identical octree with differences in the
! interpolated function. The reason for this problem was related to
! bad faces or hanging nodes. Indeed, for a hanging node it was very likely
! that the leaf that was detected as the loeaf in which the node resides
! was in fact a leave where the node was a hanging node (ie not one of the
! 4 corner nodes). This meant that the interpolated value was not equal
! to the "constrained" value imposed by the linear constraint at the
! hanging node. To correct for this we first check if the node can
! be interpolated with r,s,t values that are equal to 1 or -1. If this is
! true than this value is chosen as this would correspond to a nodal value

xt=x
yt=y
zt=z

if (xt.lt.-1.e-11 .or. xt.gt.1.d0+1.d-11) return
if (yt.lt.-1.e-11 .or. yt.gt.1.d0+1.d-11) return
if (zt.lt.-1.e-11 .or. zt.gt.1.d0+1.d-11) return

if (x.lt.1.e-11) xt=1.e-11
if (x.gt.1.d0-1.d-11) xt=1.d0-1.d-11
if (y.lt.1.e-11) yt=1.e-11
if (y.gt.1.d0-1.d-11) yt=1.d0-1.d-11
if (z.lt.1.e-11) zt=1.e-11
if (z.gt.1.d0-1.d-11) zt=1.d0-1.d-11

do kkk=-1,1,2
do jjj=-1,1,2
do iii=-1,1,2

xt=x+iii*1.d-10
yt=y+jjj*1.d-10
zt=z+kkk*1.d-10

if (xt*(xt-1.d0).ge.0d0 .or. yt*(yt-1.d0).ge.0d0 .or. zt*(zt-1.d0).ge.0d0) goto 111

call octree_find_leaf (octree,noctree,xt,yt,zt,leaf,level,loc,x0,y0,z0,dxyz)

r=(x-x0)/dxyz*2.d0-1.d0
s=(y-y0)/dxyz*2.d0-1.d0
t=(z-z0)/dxyz*2.d0-1.d0
h(1)=(1.d0-r)*(1.d0-s)*(1.d0-t)/8.d0
h(2)=(1.d0+r)*(1.d0-s)*(1.d0-t)/8.d0
h(3)=(1.d0-r)*(1.d0+s)*(1.d0-t)/8.d0
h(4)=(1.d0+r)*(1.d0+s)*(1.d0-t)/8.d0
h(5)=(1.d0-r)*(1.d0-s)*(1.d0+t)/8.d0
h(6)=(1.d0+r)*(1.d0-s)*(1.d0+t)/8.d0
h(7)=(1.d0-r)*(1.d0+s)*(1.d0+t)/8.d0
h(8)=(1.d0+r)*(1.d0+s)*(1.d0+t)/8.d0
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field1(icon(k,leaf))
    enddo
  f1=phi
if (nf.eq.1) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field2(icon(k,leaf))
    enddo
  f2=phi
if (nf.eq.2) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field3(icon(k,leaf))
    enddo
  f3=phi
if (nf.eq.3) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field4(icon(k,leaf))
    enddo
  f4=phi
if (nf.eq.4) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field5(icon(k,leaf))
    enddo
  f5=phi
if (nf.eq.5) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field6(icon(k,leaf))
    enddo
  f6=phi
if (nf.eq.6) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field7(icon(k,leaf))
    enddo
  f7=phi
if (nf.eq.7) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field8(icon(k,leaf))
    enddo
  f8=phi
if (nf.eq.8) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field9(icon(k,leaf))
    enddo
  f9=phi
if (nf.eq.9) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field10(icon(k,leaf))
    enddo
  f10=phi
if (nf.eq.10) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field11(icon(k,leaf))
    enddo
  f11=phi
if (nf.eq.11) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field12(icon(k,leaf))
    enddo
  f12=phi
if (nf.eq.12) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field13(icon(k,leaf))
    enddo
  f13=phi
if (nf.eq.13) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field14(icon(k,leaf))
    enddo
  f14=phi
if (nf.eq.14) goto 222
  phi=0.d0
    do k=1,8
    phi=phi+h(k)*field15(icon(k,leaf))
    enddo
  f15=phi
if (nf.gt.15) stop 'too many field'

222 continue
!if (abs(r)-1.d0+abs(s)-1.d0+abs(t)-1.d0.lt.1.d-10) return
if (abs(abs(r)-1.d0).lt.1.d-10 .and. abs(abs(s)-1.d0).lt.1.d-10 .and. abs(abs(t)-1.d0).lt.1.d-10) return

111 continue

enddo
enddo
enddo

return
end

!==============================================!
!=====[OCTREE_INTERPOLATE_MANY_DERIVATIVE]=====!
!==============================================!

subroutine octree_interpolate_many_derivative &
                                    (nf,octree,noctree,icon,nleaves,nfield,x,y,z, &
                                    field1,f1,df1x,df1y,df1z, &
                                    field2,f2,df2x,df2y,df2z, &
                                    field3,f3,df3x,df3y,df3z, &
                                    field4,f4,df4x,df4y,df4z, &
                                    field5,f5,df5x,df5y,df5z, &
                                    field6,f6,df6x,df6y,df6z, &
                                    field7,f7,df7x,df7y,df7z, &
                                    field8,f8,df8x,df8y,df8z, &
                                    field9,f9,df9x,df9y,df9z, &
                                    field10,f10,df10x,df10y,df10z, &
                                    field11,f11,df11x,df11y,df11z, &
                                    field12,f12,df12x,df12y,df12z, &
                                    field13,f13,df13x,df13y,df13z, &
                                    field14,f14,df14x,df14y,df14z, &
                                    field15,f15,df15x,df15y,df15z)
! This function returns the value of several fields (fieldi) known at the nodes
! of an octree by trilinear interpolation as well as their 3 spatial derivatives

! nf is the number of fields being interpolate (must be comprised between 1 and 15)
! icon is the connectivity matrix
! nleaves is the number of leaves in the octree
! fieldi are the arrays of dimension nfield containing the fields
!     known at the nodes of the octree and to be interpolated
! x,y,z are the location of the point where the fields are to be interpolated
! fi are the resulting interpolated fields

! Note that the number of oarguments to this routine depends on the number of
! fields to be interpolated (nf). This is why some of the arguments are declared
! as optional

implicit none

optional :: field2,f2,field3,f3,field4,f4,field5,f5,field6,f6
optional :: field7,f7,field8,f8,field9,f9,field10,f10
optional :: field11,f11,field12,f12,field13,f13,field14,f14,field15,f15
optional :: df2x,df3x,df4x,df5x,df6x,df7x,df8x,df9x
optional :: df10x,df11x,df12x,df13x,df14x,df15x
optional :: df2y,df3y,df4y,df5y,df6y,df7y,df8y,df9y
optional :: df10y,df11y,df12y,df13y,df14y,df15y
optional :: df2z,df3z,df4z,df5z,df6z,df7z,df8z,df9z
optional :: df10z,df11z,df12z,df13z,df14z,df15z


integer noctree,octree(noctree),nleaves,icon(8,nleaves)
integer nfield,nf
double precision field1(nfield),field2(nfield),field3(nfield),field4(nfield), &
                 field5(nfield),field6(nfield),field7(nfield),field8(nfield)
double precision field9(nfield),field10(nfield),field11(nfield),field12(nfield), &
                 field13(nfield),field14(nfield),field15(nfield)
double precision f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14,f15
double precision df1x,df2x,df3x,df4x,df5x,df6x,df7x,df8x,df9x
double precision df10x,df11x,df12x,df13x,df14x,df15x
double precision df1y,df2y,df3y,df4y,df5y,df6y,df7y,df8y,df9y
double precision df10y,df11y,df12y,df13y,df14y,df15y
double precision df1z,df2z,df3z,df4z,df5z,df6z,df7z,df8z,df9z
double precision df10z,df11z,df12z,df13z,df14z,df15z
double precision x,y,z,x0,y0,z0,dxyz,r,s,t,h(8),phi,xt,yt,zt
double precision phix,phiy,phiz
integer leaf,level,loc,k,iii,jjj,kkk,ii,jj,kk,ic,ijk
double precision dhdr(8),dhds(8),dhdt(8),xg(8),yg(8),zg(8),jcb(3,3),jcbi(3,3),volume
double precision dhdx(8),dhdy(8),dhdz(8)

! function modified by JEAN BRAUN on September 26 2005
! to correct for an error in the logics that led to an interpolation
! from an octree to another identical octree with differences in the
! interpolated function. The reason for this problem was related to
! bad faces or hanging nodes. Indeed, for a hanging node it was very likely
! that the leaf that was detected as the loeaf in which the node resides
! was in fact a leave where the node was a hanging node (ie not one of the
! 4 corner nodes). This meant that the interpolated value was not equal
! to the "constrained" value imposed by the linear constraint at the
! hanging node. To correct for this we first check if the node can
! be interpolated with r,s,t values that are equal to 1 or -1. If this is
! true than this value is chosen as this would correspond to a nodal value

xt=x
yt=y
zt=z

if (xt.lt.-1.e-11 .or. xt.gt.1.d0+1.d-11) return
if (yt.lt.-1.e-11 .or. yt.gt.1.d0+1.d-11) return
if (zt.lt.-1.e-11 .or. zt.gt.1.d0+1.d-11) return

if (x.lt.1.e-11) xt=1.e-11
if (x.gt.1.d0-1.d-11) xt=1.d0-1.d-11
if (y.lt.1.e-11) yt=1.e-11
if (y.gt.1.d0-1.d-11) yt=1.d0-1.d-11
if (z.lt.1.e-11) zt=1.e-11
if (z.gt.1.d0-1.d-11) zt=1.d0-1.d-11

do kkk=-1,1,2
do jjj=-1,1,2
do iii=-1,1,2

xt=x+iii*1.d-10
yt=y+jjj*1.d-10
zt=z+kkk*1.d-10

if (xt*(xt-1.d0).ge.0d0 .or. yt*(yt-1.d0).ge.0d0 .or. zt*(zt-1.d0).ge.0d0) goto 111

call octree_find_leaf (octree,noctree,xt,yt,zt,leaf,level,loc,x0,y0,z0,dxyz)

r=(x-x0)/dxyz*2.d0-1.d0
s=(y-y0)/dxyz*2.d0-1.d0
t=(z-z0)/dxyz*2.d0-1.d0
h(1)=(1.d0-r)*(1.d0-s)*(1.d0-t)/8.d0
h(2)=(1.d0+r)*(1.d0-s)*(1.d0-t)/8.d0
h(3)=(1.d0-r)*(1.d0+s)*(1.d0-t)/8.d0
h(4)=(1.d0+r)*(1.d0+s)*(1.d0-t)/8.d0
h(5)=(1.d0-r)*(1.d0-s)*(1.d0+t)/8.d0
h(6)=(1.d0+r)*(1.d0-s)*(1.d0+t)/8.d0
h(7)=(1.d0-r)*(1.d0+s)*(1.d0+t)/8.d0
h(8)=(1.d0+r)*(1.d0+s)*(1.d0+t)/8.d0
dhdr(1)=-(1.d0-s)*(1.d0-t)/8.d0
dhdr(2)=(1.d0-s)*(1.d0-t)/8.d0
dhdr(3)=-(1.d0+s)*(1.d0-t)/8.d0
dhdr(4)=(1.d0+s)*(1.d0-t)/8.d0
dhdr(5)=-(1.d0-s)*(1.d0+t)/8.d0
dhdr(6)=(1.d0-s)*(1.d0+t)/8.d0
dhdr(7)=-(1.d0+s)*(1.d0+t)/8.d0
dhdr(8)=(1.d0+s)*(1.d0+t)/8.d0
dhds(1)=-(1.d0-r)*(1.d0-t)/8.d0
dhds(2)=-(1.d0+r)*(1.d0-t)/8.d0
dhds(3)=(1.d0-r)*(1.d0-t)/8.d0
dhds(4)=(1.d0+r)*(1.d0-t)/8.d0
dhds(5)=-(1.d0-r)*(1.d0+t)/8.d0
dhds(6)=-(1.d0+r)*(1.d0+t)/8.d0
dhds(7)=(1.d0-r)*(1.d0+t)/8.d0
dhds(8)=(1.d0+r)*(1.d0+t)/8.d0
dhdt(1)=-(1.d0-r)*(1.d0-s)/8.d0
dhdt(2)=-(1.d0+r)*(1.d0-s)/8.d0
dhdt(3)=-(1.d0-r)*(1.d0+s)/8.d0
dhdt(4)=-(1.d0+r)*(1.d0+s)/8.d0
dhdt(5)=(1.d0-r)*(1.d0-s)/8.d0
dhdt(6)=(1.d0+r)*(1.d0-s)/8.d0
dhdt(7)=(1.d0-r)*(1.d0+s)/8.d0
dhdt(8)=(1.d0+r)*(1.d0+s)/8.d0
ijk=0
do kk=0,1
do jj=0,1
do ii=0,1
ijk=ijk+1
xg(ijk)=x0+ii*dxyz
yg(ijk)=y0+jj*dxyz
zg(ijk)=z0+kk*dxyz
enddo
enddo
enddo
jcb=0.
do k=1,8
jcb(1,1)=jcb(1,1)+dhdr(k)*xg(k)
jcb(1,2)=jcb(1,2)+dhdr(k)*yg(k)
jcb(1,3)=jcb(1,3)+dhdr(k)*zg(k)
jcb(2,1)=jcb(2,1)+dhds(k)*xg(k)
jcb(2,2)=jcb(2,2)+dhds(k)*yg(k)
jcb(2,3)=jcb(2,3)+dhds(k)*zg(k)
jcb(3,1)=jcb(3,1)+dhdt(k)*xg(k)
jcb(3,2)=jcb(3,2)+dhdt(k)*yg(k)
jcb(3,3)=jcb(3,3)+dhdt(k)*zg(k)
enddo

volume=jcb(1,1)*jcb(2,2)*jcb(3,3)+jcb(1,2)*jcb(2,3)*jcb(3,1) &
+jcb(2,1)*jcb(3,2)*jcb(1,3) &
-jcb(1,3)*jcb(2,2)*jcb(3,1)-jcb(1,2)*jcb(2,1)*jcb(3,3) &
-jcb(2,3)*jcb(3,2)*jcb(1,1)

jcbi(1,1)=(jcb(2,2)*jcb(3,3)-jcb(2,3)*jcb(3,2))/volume
jcbi(2,1)=(jcb(2,3)*jcb(3,1)-jcb(2,1)*jcb(3,3))/volume
jcbi(3,1)=(jcb(2,1)*jcb(3,2)-jcb(2,2)*jcb(3,1))/volume
jcbi(1,2)=(jcb(1,3)*jcb(3,2)-jcb(1,2)*jcb(3,3))/volume
jcbi(2,2)=(jcb(1,1)*jcb(3,3)-jcb(1,3)*jcb(3,1))/volume
jcbi(3,2)=(jcb(1,2)*jcb(3,1)-jcb(1,1)*jcb(3,2))/volume
jcbi(1,3)=(jcb(1,2)*jcb(2,3)-jcb(1,3)*jcb(2,2))/volume
jcbi(2,3)=(jcb(1,3)*jcb(2,1)-jcb(1,1)*jcb(2,3))/volume
jcbi(3,3)=(jcb(1,1)*jcb(2,2)-jcb(1,2)*jcb(2,1))/volume

do k=1,8
dhdx(k)=jcbi(1,1)*dhdr(k)+jcbi(1,2)*dhds(k)+jcbi(1,3)*dhdt(k)
dhdy(k)=jcbi(2,1)*dhdr(k)+jcbi(2,2)*dhds(k)+jcbi(2,3)*dhdt(k)
dhdz(k)=jcbi(3,1)*dhdr(k)+jcbi(3,2)*dhds(k)+jcbi(3,3)*dhdt(k)
enddo

  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field1(ic)
    phix=phix+dhdx(k)*field1(ic)
    phiy=phiy+dhdy(k)*field1(ic)
    phiz=phiz+dhdz(k)*field1(ic)
    enddo
  f1=phi
  df1x=phix
  df1y=phiy
  df1z=phiz
if (nf.eq.1) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field2(ic)
    phix=phix+dhdx(k)*field2(ic)
    phiy=phiy+dhdy(k)*field2(ic)
    phiz=phiz+dhdz(k)*field2(ic)
    enddo
  f2=phi
  df2x=phix
  df2y=phiy
  df2z=phiz
if (nf.eq.2) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field3(ic)
    phix=phix+dhdx(k)*field3(ic)
    phiy=phiy+dhdy(k)*field3(ic)
    phiz=phiz+dhdz(k)*field3(ic)
    enddo
  f3=phi
  df3x=phix
  df3y=phiy
  df3z=phiz
if (nf.eq.3) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field4(ic)
    phix=phix+dhdx(k)*field4(ic)
    phiy=phiy+dhdy(k)*field4(ic)
    phiz=phiz+dhdz(k)*field4(ic)
    enddo
  f4=phi
  df4x=phix
  df4y=phiy
  df4z=phiz
if (nf.eq.4) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field5(ic)
    phix=phix+dhdx(k)*field5(ic)
    phiy=phiy+dhdy(k)*field5(ic)
    phiz=phiz+dhdz(k)*field5(ic)
    enddo
  f5=phi
  df5x=phix
  df5y=phiy
  df5z=phiz
if (nf.eq.5) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field6(ic)
    phix=phix+dhdx(k)*field6(ic)
    phiy=phiy+dhdy(k)*field6(ic)
    phiz=phiz+dhdz(k)*field6(ic)
    enddo
  f6=phi
  df6x=phix
  df6y=phiy
  df6z=phiz
if (nf.eq.6) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field7(ic)
    phix=phix+dhdx(k)*field7(ic)
    phiy=phiy+dhdy(k)*field7(ic)
    phiz=phiz+dhdz(k)*field7(ic)
    enddo
  f7=phi
  df7x=phix
  df7y=phiy
  df7z=phiz
if (nf.eq.7) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field8(ic)
    phix=phix+dhdx(k)*field8(ic)
    phiy=phiy+dhdy(k)*field8(ic)
    phiz=phiz+dhdz(k)*field8(ic)
    enddo
  f8=phi
  df8x=phix
  df8y=phiy
  df8z=phiz
if (nf.eq.8) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field9(ic)
    phix=phix+dhdx(k)*field9(ic)
    phiy=phiy+dhdy(k)*field9(ic)
    phiz=phiz+dhdz(k)*field9(ic)
    enddo
  f9=phi
  df9x=phix
  df9y=phiy
  df9z=phiz
if (nf.eq.9) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field10(ic)
    phix=phix+dhdx(k)*field10(ic)
    phiy=phiy+dhdy(k)*field10(ic)
    phiz=phiz+dhdz(k)*field10(ic)
    enddo
  f10=phi
  df10x=phix
  df10y=phiy
  df10z=phiz
if (nf.eq.10) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field11(ic)
    phix=phix+dhdx(k)*field11(ic)
    phiy=phiy+dhdy(k)*field11(ic)
    phiz=phiz+dhdz(k)*field11(ic)
    enddo
  f11=phi
  df11x=phix
  df11y=phiy
  df11z=phiz
if (nf.eq.11) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field12(ic)
    phix=phix+dhdx(k)*field12(ic)
    phiy=phiy+dhdy(k)*field12(ic)
    phiz=phiz+dhdz(k)*field12(ic)
    enddo
  f12=phi
  df12x=phix
  df12y=phiy
  df12z=phiz
if (nf.eq.12) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field13(ic)
    phix=phix+dhdx(k)*field13(ic)
    phiy=phiy+dhdy(k)*field13(ic)
    phiz=phiz+dhdz(k)*field13(ic)
    enddo
  f13=phi
  df13x=phix
  df13y=phiy
  df13z=phiz
if (nf.eq.13) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field14(ic)
    phix=phix+dhdx(k)*field14(ic)
    phiy=phiy+dhdy(k)*field14(ic)
    phiz=phiz+dhdz(k)*field14(ic)
    enddo
  f14=phi
  df14x=phix
  df14y=phiy
  df14z=phiz
if (nf.eq.14) goto 222
  phi=0.d0
  phix=0.d0
  phiy=0.d0
  phiz=0.d0
    do k=1,8
    ic=icon(k,leaf)
    phi=phi+h(k)*field15(ic)
    phix=phix+dhdx(k)*field15(ic)
    phiy=phiy+dhdy(k)*field15(ic)
    phiz=phiz+dhdz(k)*field15(ic)
    enddo
  f15=phi
  df15x=phix
  df15y=phiy
  df15z=phiz
if (nf.gt.15) stop 'too many field'

222 continue
!if (abs(r)-1.d0+abs(s)-1.d0+abs(t)-1.d0.lt.1.d-10) return
if (abs(abs(r)-1.d0).lt.1.d-10 .and. abs(abs(s)-1.d0).lt.1.d-10 .and. abs(abs(t)-1.d0).lt.1.d-10) return

111 continue

enddo
enddo
enddo

return
end


!-------------------------------------

Subroutine mrgrnk (XDONT, IRNGT, np)
!Subroutine mrgrnk (XDONT, IRNGT)
! __________________________________________________________
!   MRGRNK = Merge-sort ranking of an array
!   For performance reasons, the first 2 passes are taken
!   out of the standard loop, and use dedicated coding.
! __________________________________________________________
! __________________________________________________________
      Integer XDONT(np),IRNGT(np)
!      Integer, Dimension (:), Intent (In)  :: XDONT
!      Integer, Dimension (:), Intent (Out) :: IRNGT
! __________________________________________________________
      Integer :: XVALA, XVALB
!
      Integer, Dimension(:),allocatable :: JWRKT
!      Integer, Dimension (SIZE(IRNGT)) :: JWRKT
      Integer :: LMTNA, LMTNC, IRNG1, IRNG2
      Integer :: NVAL, IIND, IWRKD, IWRK, IWRKF, JINDA, IINDA, IINDB
!
      NVAL = Min (SIZE(XDONT), SIZE(IRNGT))
      Select Case (NVAL)
      Case (:0)
         Return
      Case (1)
         IRNGT (1) = 1
         Return
      Case Default
         Continue
      End Select

allocate (JWRKT(np))

!
!  Fill-in the index array, creating ordered couples
!
      Do IIND = 2, NVAL, 2
         If (XDONT(IIND-1) <= XDONT(IIND)) Then
            IRNGT (IIND-1) = IIND - 1
            IRNGT (IIND) = IIND
         Else
            IRNGT (IIND-1) = IIND
            IRNGT (IIND) = IIND - 1
         End If
      End Do
      If (Modulo(NVAL, 2) /= 0) Then
         IRNGT (NVAL) = NVAL
      End If
!
!  We will now have ordered subsets A - B - A - B - ...
!  and merge A and B couples into     C   -   C   - ...
!
      LMTNA = 2
      LMTNC = 4
!
!  First iteration. The length of the ordered subsets goes from 2 to 4
!
      Do
         If (NVAL <= 2) Exit
!
!   Loop on merges of A and B into C
!
         Do IWRKD = 0, NVAL - 1, 4
            If ((IWRKD+4) > NVAL) Then
               If ((IWRKD+2) >= NVAL) Exit
!
!   1 2 3
!
               If (XDONT(IRNGT(IWRKD+2)) <= XDONT(IRNGT(IWRKD+3))) Exit
!
!   1 3 2
!
               If (XDONT(IRNGT(IWRKD+1)) <= XDONT(IRNGT(IWRKD+3))) Then
                  IRNG2 = IRNGT (IWRKD+2)
                  IRNGT (IWRKD+2) = IRNGT (IWRKD+3)
                  IRNGT (IWRKD+3) = IRNG2
!
!   3 1 2
!
               Else
                  IRNG1 = IRNGT (IWRKD+1)
                  IRNGT (IWRKD+1) = IRNGT (IWRKD+3)
                  IRNGT (IWRKD+3) = IRNGT (IWRKD+2)
                  IRNGT (IWRKD+2) = IRNG1
               End If
               Exit
            End If
!
!   1 2 3 4
!
            If (XDONT(IRNGT(IWRKD+2)) <= XDONT(IRNGT(IWRKD+3))) Cycle
!
!   1 3 x x
!
            If (XDONT(IRNGT(IWRKD+1)) <= XDONT(IRNGT(IWRKD+3))) Then
               IRNG2 = IRNGT (IWRKD+2)
               IRNGT (IWRKD+2) = IRNGT (IWRKD+3)
               If (XDONT(IRNG2) <= XDONT(IRNGT(IWRKD+4))) Then
!   1 3 2 4
                  IRNGT (IWRKD+3) = IRNG2
               Else
!   1 3 4 2
                  IRNGT (IWRKD+3) = IRNGT (IWRKD+4)
                  IRNGT (IWRKD+4) = IRNG2
               End If
!
!   3 x x x
!
            Else
               IRNG1 = IRNGT (IWRKD+1)
               IRNG2 = IRNGT (IWRKD+2)
               IRNGT (IWRKD+1) = IRNGT (IWRKD+3)
               If (XDONT(IRNG1) <= XDONT(IRNGT(IWRKD+4))) Then
                  IRNGT (IWRKD+2) = IRNG1
                  If (XDONT(IRNG2) <= XDONT(IRNGT(IWRKD+4))) Then
!   3 1 2 4
                     IRNGT (IWRKD+3) = IRNG2
                  Else
!   3 1 4 2
                     IRNGT (IWRKD+3) = IRNGT (IWRKD+4)
                     IRNGT (IWRKD+4) = IRNG2
                  End If
               Else
!   3 4 1 2
                  IRNGT (IWRKD+2) = IRNGT (IWRKD+4)
                  IRNGT (IWRKD+3) = IRNG1
                  IRNGT (IWRKD+4) = IRNG2
               End If
            End If
         End Do
!
!  The Cs become As and Bs
!
         LMTNA = 4
         Exit
      End Do
!
!  Iteration loop. Each time, the length of the ordered subsets
!  is doubled.
!
      Do
         If (LMTNA >= NVAL) Exit
         IWRKF = 0
         LMTNC = 2 * LMTNC
!
!   Loop on merges of A and B into C
!
         Do
            IWRK = IWRKF
            IWRKD = IWRKF + 1
            JINDA = IWRKF + LMTNA
            IWRKF = IWRKF + LMTNC
            If (IWRKF >= NVAL) Then
               If (JINDA >= NVAL) Exit
               IWRKF = NVAL
            End If
            IINDA = 1
            IINDB = JINDA + 1
!
!   Shortcut for the case when the max of A is smaller
!   than the min of B. This line may be activated when the
!   initial set is already close to sorted.
!
!          IF (XDONT(IRNGT(JINDA)) <= XDONT(IRNGT(IINDB))) CYCLE
!
!  One steps in the C subset, that we build in the final rank array
!
!  Make a copy of the rank array for the merge iteration
!
            JWRKT (1:LMTNA) = IRNGT (IWRKD:JINDA)
!
            XVALA = XDONT (JWRKT(IINDA))
            XVALB = XDONT (IRNGT(IINDB))
!
            Do
               IWRK = IWRK + 1
!
!  We still have unprocessed values in both A and B
!
               If (XVALA > XVALB) Then
                  IRNGT (IWRK) = IRNGT (IINDB)
                  IINDB = IINDB + 1
                  If (IINDB > IWRKF) Then
!  Only A still with unprocessed values
                     IRNGT (IWRK+1:IWRKF) = JWRKT (IINDA:LMTNA)
                     Exit
                  End If
                  XVALB = XDONT (IRNGT(IINDB))
               Else
                  IRNGT (IWRK) = JWRKT (IINDA)
                  IINDA = IINDA + 1
                  If (IINDA > LMTNA) Exit! Only B still with unprocessed values
                  XVALA = XDONT (JWRKT(IINDA))
               End If
!
            End Do
         End Do
!
!  The Cs become As and Bs
!
         LMTNA = 2 * LMTNA
      End Do
!

deallocate (JWRKT)

      Return
!
End Subroutine mrgrnk