subroutine isostasy (params,weightel,ov,surface,mat,zi)

! first go at imposing isostasy
! this routine assumes that the original surface are flat
! it takes the zref height of each surface and the density of the corresponding material
!   to compute the reference isostatic column
! note also that this will not work when the courant condition is used

use definitions

implicit none

type(parameters) params
type(octreev) ov
double precision weightel(ov%nleaves)
type(sheet) surface(params%ns)
type(material) mat(0:params%nmat)
type(ziso) zi
!double precision zisodisp(2**params%levelmax_oct+1,2**params%levelmax_oct+1)

double precision xc,yc,zc,eps
integer i,j,ii,jj,kk,ie,dlev,is,iproc,nproc,ierr,ic
double precision x0,y0,z0,dxyz,dd
integer leaf,level,loc
double precision x0b,y0b,z0b,dxyzb
integer leafb,levelb,locb
double precision,dimension(:),allocatable::diso
double precision weightref,disoel,w
integer,dimension(:),allocatable::np
character shift*72
double precision,dimension(:,:),allocatable::weight,flex,work
double precision pi,hx,dh,pihx,xk,fi,fj,tij,d
integer nflex
integer im,jm,ip,jp,nb

integer iunit

INCLUDE 'mpif.h'

call mpi_comm_size (mpi_comm_world,nproc,ierr)
call mpi_comm_rank (mpi_comm_world,iproc,ierr)

eps=1.d-8
shift=' '
nb=2**params%levelmax_oct

allocate (weight(nb,nb))

! first calculates the weight of each column, based on the weight of each elements
! this is performed on a 2**levelmax X 2**levelmax grid at the base of the model
! the result is stored in weight(i,j)

dd=1.d0/2.d0**(params%levelmax_oct+1)
weight=0.d0

  do ie=1,ov%nleaves
  xc=0.d0;yc=0.d0;zc=0.d0
    do i=1,8
    xc=xc+ov%x(ov%icon(i,ie))
    yc=yc+ov%y(ov%icon(i,ie))
    zc=zc+ov%z(ov%icon(i,ie))
    enddo
  xc=xc/8.d0
  yc=yc/8.d0
  zc=zc/8.d0
  call octree_find_leaf (ov%octree,ov%noctree,xc,yc,zc,leaf,level,loc,x0,y0,z0,dxyz)
  dlev=params%levelmax_oct-level
  call find_integer_coordinates (x0+dd,y0+dd,z0+dd,ii,jj,kk,params%levelmax_oct)
  ii=min(ii,nb-2**dlev)
  jj=min(jj,nb-2**dlev)
    do i=1,2**dlev
      do j=1,2**dlev
      weight(ii+i,jj+j)=weight(ii+i,jj+j)+weightel(leaf)/(4.d0**dlev)*(4.d0**params%levelmax_oct)
      enddo
    enddo
!  call octree_find_leaf (ov%octree,ov%noctree,xc,yc,eps,leafb,levelb,locb,x0b,y0b,z0b,dxyzb)
!  dlev=max(0,levelb-level)
!  w(leafb)=w(leafb)+weightel(leaf)/(4.d0**dlev)*(4.d0**levelb)
  enddo

! calculates the reference weight from the initial surface geometry and the density of the material
! near the base

weightref=0.d0
  do is=1,params%ns-1
  weightref=weightref+mat(surface(is)%material)%density*(surface(is)%sp01-surface(is+1)%sp01)
  enddo
weightref=weightref+mat(surface(params%ns)%material)%density*surface(params%ns)%sp01

if (.not.params%flexure) then

! apply local isostasy
! the deflection is stored in array weight

  do j=1,nb
    do i=1,nb
    weight(i,j)=(weight(i,j)-weightref)/mat(surface(params%ns)%material)%density
    enddo
  enddo

else

! flexural isostasy

nflex=8*nb
allocate(work(nflex,nflex),flex(nflex,nflex))
hx=params%length_scale*8.d3
xk=-mat(surface(params%ns)%material)%density*params%density_difference*9.81d0
d=1.d11/12.d0/(1.d0-0.25d0**2)*params%elastic_plate_thickness**3
dh=hx/(nflex-1)

!if (iproc.eq.0) print*,'hx,dh,xk,d',hx,dh,xk,d

ii=nflex/2-nb/2-1
jj=ii
flex=0.d0
  do j=1,nb
    do i=1,nb
    flex(ii+i,jj+j)=(weight(i,j)-weightref)*dh*dh*params%density_difference*9.81d0*params%length_scale*1.d3
    enddo
  enddo

!if (iproc.eq.0) print*,'poids',minval(flex),maxval(flex)

  do j=1,nflex
  call dsinft (flex(1,j),nflex)
  enddo

  do j=1,nflex
    do i=1,nflex
    work(j,i)=flex(i,j)
    enddo
  enddo

  do i=1,nflex
  call dsinft (work(1,i),nflex)
  enddo

  do j=1,nflex
    do i=1,nflex
    work(j,i)=work(j,i)*4.d0/hx/hx
    enddo
  enddo

pi=atan(1.d0)*4.d0
pihx=pi/hx
  do j=1,nflex
  fj=(j*pihx)**2
    do i=1,nflex
    fi=(i*pihx)**2
    tij=d/xk*(fi**2+2.d0*fi*fj+fj**2)+1.d0
    work(j,i)=work(j,i)/xk/tij
    enddo
  enddo

  do i=1,nflex
  call dsinft (work(1,i),nflex)
  enddo

  do j=1,nflex
    do i=1,nflex
    flex(i,j)=work(j,i)
    enddo
  enddo

  do j=1,nflex
  call dsinft (flex(1,j),nflex)
  enddo

!if (iproc.eq.0) print*,'def in m',minval(flex),maxval(flex)

ii=nflex/2-nb/2-1
jj=ii
  do j=1,nb
    do i=1,nb
    weight(i,j)=-flex(ii+i,jj+j)/params%length_scale/1.d3
    enddo
  enddo

deallocate (flex,work)

!if (iproc.eq.0) print*,'dimensionless def ',minval(weight),maxval(weight)

endif

! redistributes the isostatic response onto the elements
! and distributes it onto the nodes into array diso(nnode)

allocate (np(ov%nnode))
allocate (diso(ov%nnode),work(nb+1,nb+1))

do j=1,nb+1
do i=1,nb+1
im=max(i-1,1)
jm=max(j-1,1)
ip=min(i,nb)
jp=min(j,nb)
work(i,j)=(weight(ip,jp)+weight(im,jp)+weight(ip,jm)+weight(im,jm))/4.d0
enddo
enddo

do i=1,ov%nnode
call find_integer_coordinates(ov%x(i)+1.d-20,ov%y(i)+1.d-20,ov%z(i)+1.d-20,ii,jj,kk,params%levelmax_oct)
ii=ii+1
jj=jj+1
kk=kk+1
if ((ii-1)*(ii-nb-1).gt.0 .or. (jj-1)*(jj-nb-1).gt.0) stop 'error in isostasy'
diso(i)=work(ii,jj)
enddo

!goto 3333

! old version : to be neglected new is much faster

!diso=0.d0
!np=0
!  do ie=1,ov%nleaves
!  xc=0.d0;yc=0.d0;zc=0.d0
!    do i=1,8
!    xc=xc+ov%x(ov%icon(i,ie))
!    yc=yc+ov%y(ov%icon(i,ie))
!    zc=zc+ov%z(ov%icon(i,ie))
!    enddo
!  xc=xc/8.d0
!  yc=yc/8.d0
!  zc=zc/8.d0
!  call octree_find_leaf (ov%octree,ov%noctree,xc,yc,zc,leaf,level,loc,x0,y0,z0,dxyz)
!!  call octree_find_leaf (ov%octree,ov%noctree,xc,yc,eps,leafb,levelb,locb,x0b,y0b,z0b,dxyzb)
!!  disoel=(w(leafb)-weightref)/(mat(surface(params%ns)%material)%density)
!  w=0.d0
!  dlev=params%levelmax_oct-level
!  call find_integer_coordinates (x0+dd,y0+dd,z0+dd,ii,jj,kk,params%levelmax_oct)
!  ii=min(ii,2**(params%levelmax_oct)-2**dlev)
!  jj=min(jj,2**(params%levelmax_oct)-2**dlev)
!    do i=1,2**dlev
!      do j=1,2**dlev 
!      w=w+weight(ii+i,jj+j)
!      enddo
!    enddo
!  disoel=w/(2**dlev*2.d0**dlev)
!!  disoel=(w-weightref)/(mat(surface(params%ns)%material)%density)
!    do i=1,8
!    ic=ov%icon(i,ie)
!    np(ic)=np(ic)+1
!    diso(ic)=diso(ic)+disoel
!    enddo
!  enddo
!
!  do i=1,ov%nnode
!    if (np(i).ne.0) then
!    diso(i)=diso(i)/np(i)
!    else
!    diso(i)=0
!    print*,'error np',i,ov%x(i),ov%y(i),ov%z(i)
!    endif
!  enddo
!
!3333 continue

  if (params%debug>=1.and.iproc.eq.0) then
  write(*,'(a,f10.7,1x,f10.7)') shift//'Isostatic velocity range = ',minval(diso)/params%dt,maxval(diso)/params%dt
  endif

! Store the total displacement of the basal node plane 
if (params%isobc) zi%zisodisp=zi%zisodisp-work

! transforms the displacements into velocities and add this contribution to the velocity
! solution of the Stokes equation before interpolation onto the surfaces and their advection

  do i=1,ov%nnode
  ov%wpreiso(i)=ov%wnode(i)
  ov%wnode(i)=ov%wnode(i)-diso(i)/params%dt
  enddo

!goto 1111
!
!! display results into a vtk file for debugging
!
!iunit=30
!open(unit=iunit,file='isostasy.vtk')
!write(iunit,'(a)')'# vtk DataFile Version 3.0'
!write(iunit,'(a)')'velocities'
!write(iunit,'(a)')'ASCII'
!write(iunit,'(a)')'DATASET UNSTRUCTURED_GRID'
!write(iunit,'(a7,i10,a6)')'POINTS ',ov%nnode,' float'
!
!do i=1,ov%nnode
!write(iunit,'(3f16.11)')ov%x(i),ov%y(i),ov%z(i)
!enddo
!
!write(iunit,'(A6, 2I10)') 'CELLS ',ov%nleaves,(1+8)*ov%nleaves
!do ie=1,ov%nleaves
!write(iunit,'(9I10)')8,ov%icon(1:8,ie)-1
!enddo
!
!write(iunit,'(A11, I10)') 'CELL_TYPES ',ov%nleaves
!do ie=1,ov%nleaves
!   write(iunit,'(I2)')11 ! octree  (8 nodes)
!end do
!
!write(iunit,'(a11,i10)')'POINT_DATA ',ov%nnode
!
!write(iunit,'(a)')'VECTORS iso float'
!do i=1,ov%nnode
!   write(iunit,'(3e11.4)')0.,0.,-diso(i)/params%dt
!enddo
!
!write(iunit,'(a)')'VECTORS velo float'
!do i=1,ov%nnode
!   write(iunit,'(3e11.4)')0.,0.,ov%wnode(i)
!enddo
!
!close (iunit)
!
!1111 continue

deallocate (np)
deallocate (diso)
deallocate (weight)
deallocate (work)

return
end subroutine isostasy


! SINFT

! taken from numerical recipes (see book for further information)

! subroutines called:
! NONE

      SUBROUTINE DSINFT(Y,N)
      common /vocal/ ivocal
      REAL*8 WR,WI,WPR,WPI,WTEMP,THETA
      double precision Y(N)
      THETA=3.14159265358979D0/DBLE(N)
      WR=1.0D0
      WI=0.0D0
      WPR=-2.0D0*DSIN(0.5D0*THETA)**2
      WPI=DSIN(THETA)
      Y(1)=0.0
      M=N/2
      DO 11 J=1,M
        WTEMP=WR
        WR=WR*WPR-WI*WPI+WR
        WI=WI*WPR+WTEMP*WPI+WI
        Y1=WI*(Y(J+1)+Y(N-J+1))
        Y2=0.5*(Y(J+1)-Y(N-J+1))
        Y(J+1)=Y1+Y2
        Y(N-J+1)=Y1-Y2
11    CONTINUE
      CALL DREALFT(Y,M,+1)
      SUM=0.0
      Y(1)=0.5*Y(1)
      Y(2)=0.0
      DO 12 J=1,N-1,2
        SUM=SUM+Y(J)
        Y(J)=Y(J+1)
        Y(J+1)=SUM
12    CONTINUE
      RETURN
      END

! REALFT

! taken from numerical recipes (see book for further information)

! subroutines called:
! NONE

      SUBROUTINE DREALFT(DATA,N,ISIGN)
      common /vocal/ ivocal
      REAL*8 WR,WI,WPR,WPI,WTEMP,THETA
      double precision DATA(*)
      THETA=6.28318530717959D0/2.0D0/DBLE(N)
      C1=0.5
      IF (ISIGN.EQ.1) THEN
        C2=-0.5
        CALL DFOUR1(DATA,N,+1)
      ELSE
        C2=0.5
        THETA=-THETA
      ENDIF
      WPR=-2.0D0*DSIN(0.5D0*THETA)**2
      WPI=DSIN(THETA)
      WR=1.0D0+WPR
      WI=WPI
      N2P3=2*N+3
      DO 11 I=2,N/2+1
        I1=2*I-1
        I2=I1+1
        I3=N2P3-I2
        I4=I3+1
        WRS=SNGL(WR)
        WIS=SNGL(WI)
        H1R=C1*(DATA(I1)+DATA(I3))
        H1I=C1*(DATA(I2)-DATA(I4))
        H2R=-C2*(DATA(I2)+DATA(I4))
        H2I=C2*(DATA(I1)-DATA(I3))
        DATA(I1)=H1R+WRS*H2R-WIS*H2I
        DATA(I2)=H1I+WRS*H2I+WIS*H2R
        DATA(I3)=H1R-WRS*H2R+WIS*H2I
        DATA(I4)=-H1I+WRS*H2I+WIS*H2R
        WTEMP=WR
        WR=WR*WPR-WI*WPI+WR
        WI=WI*WPR+WTEMP*WPI+WI
11    CONTINUE
      IF (ISIGN.EQ.1) THEN
        H1R=DATA(1)
        DATA(1)=H1R+DATA(2)
        DATA(2)=H1R-DATA(2)
      ELSE
        H1R=DATA(1)
        DATA(1)=C1*(H1R+DATA(2))
        DATA(2)=C1*(H1R-DATA(2))
        CALL DFOUR1(DATA,N,-1)
      ENDIF
      RETURN
      END

! FOUR1

! this routine is taken directly out of numerical recipes
! see the book for further information

! subroutines called:
! NONE

      SUBROUTINE DFOUR1(DATA,NN,ISIGN)
      common /vocal/ ivocal
      REAL*8 WR,WI,WPR,WPI,WTEMP,THETA
      double precision DATA(*)
      N=2*NN
      J=1
      DO 11 I=1,N,2
        IF(J.GT.I)THEN
          TEMPR=DATA(J)
          TEMPI=DATA(J+1)
          DATA(J)=DATA(I)
          DATA(J+1)=DATA(I+1)
          DATA(I)=TEMPR
          DATA(I+1)=TEMPI
        ENDIF
        M=N/2
1       IF ((M.GE.2).AND.(J.GT.M)) THEN
          J=J-M
          M=M/2
        GO TO 1
        ENDIF
        J=J+M
11    CONTINUE
      MMAX=2
2     IF (N.GT.MMAX) THEN
        ISTEP=2*MMAX
        THETA=6.28318530717959D0/(ISIGN*MMAX)
        WPR=-2.D0*DSIN(0.5D0*THETA)**2
        WPI=DSIN(THETA)
        WR=1.D0
        WI=0.D0
        DO 13 M=1,MMAX,2
          DO 12 I=M,N,ISTEP
            J=I+MMAX
            TEMPR=SNGL(WR)*DATA(J)-SNGL(WI)*DATA(J+1)
            TEMPI=SNGL(WR)*DATA(J+1)+SNGL(WI)*DATA(J)
            DATA(J)=DATA(I)-TEMPR
            DATA(J+1)=DATA(I+1)-TEMPI
            DATA(I)=DATA(I)+TEMPR
            DATA(I+1)=DATA(I+1)+TEMPI
12        CONTINUE
          WTEMP=WR
          WR=WR*WPR-WI*WPI+WR
          WI=WI*WPR+WTEMP*WPI+WI
13      CONTINUE
        MMAX=ISTEP
      GO TO 2
      ENDIF
      RETURN
      END

