!------------------------------------------------------------------------------|
!------------------------------------------------------------------------------|
!                                                                              |
!              ||===\\                                                         | 
!              ||    \\                                                        |
!              ||     ||   //==\\   ||  ||   //==||  ||/==\\                   |
!              ||     ||  ||    ||  ||  ||  ||   ||  ||    ||                  |
!              ||    //   ||    ||  ||  ||  ||   ||  ||                        |
!              ||===//     \\==//    \\==\\  \\==\\  ||                        |
!                                                                              |
!------------------------------------------------------------------------------|
!------------------------------------------------------------------------------|
!                                                                              |
!              EROSION    Nov. 2006                                            |
!                                                                              |
!------------------------------------------------------------------------------|
!------------------------------------------------------------------------------|

subroutine erosion (surface,olsf,is,params)

!------------------------------------------------------------------------------|
!(((((((((((((((( Purpose of the routine  ))))))))))))))))))))))))))))))))))))))
!------------------------------------------------------------------------------|
! this routine will need major improvement in the near future
! it is used to erode the surface "surface" according to a user supplied algorithm
! at the moment it simply "shaves" the top of the model at a given height

! surface are the surface/sheet object
! (the top free surface)
! olsf is a octreelsf object containig the geometry of the current velocity octree/object
! is is the number of the surface (0 playing a special role)
! zerosion is the level of erosion (between 0 and 1)

! Note that all surfaces are required because they can be affected by the erosion process

! for the top surface, we apply the erosion and on the octree
! we define an lsf that is simply the height of the eroded surface
! This allows us, for the other surfaces to find the height of the
! free/eroded surface at any point in space and check whether the
! vertical position of the point is above or below the free surface
! We then simply "erode" the following surfaces using this informatoin

! length_scale is the length scale in km (corresponding to the unit length of the box)
! velocity_scale is the scale for the velocity in km/Myr
! dt is the length of the dimensionless time step
! fluvial_erosion is the fluvial erosion constant
! diffusion_erosion is the diffusion erosion constant

!------------------------------------------------------------------------------|
!((((((((((((((((  declaration of the subroutine arguments  ))))))))))))))))))))
!------------------------------------------------------------------------------|

use definitions
!use mpi

implicit none

include 'mpif.h'
     
type (sheet) surface
type (octreelsf) olsf
type (parameters) params
integer is

!------------------------------------------------------------------------------|
!(((((((((((((((( declaration of the subroutine internal variables )))))))))))))
!------------------------------------------------------------------------------|
integer ierr,iproc,nproc,i,nt,nh,nohalt_hull,loc,nnn,nnlist,ntrilist,k,kp,ic,icp
integer ntmax,nhmax,npmax,nmax,nmode,nvmax,nnpnmax,int_method
double precision,dimension(:),allocatable::field,wpoints
double precision,dimension(:,:),allocatable::points,centres,pointsmin,pointsmax,ppoints
integer,dimension(:,:),allocatable::vertices,neighbour
integer,dimension(:),allocatable::hulltriangles,ln_work
logical*1,dimension(:),allocatable::lt_work_l,ln_work_l
integer,dimension(:),allocatable::vis_tlist,vis_elist,add_tlist
logical clockwise,out,nnext
double precision eps
integer dmode,err
double precision x,y,f,df(2),ddf(3),v
double precision,dimension(:,:),allocatable::work_d1,work_d2,work_d3
double precision,dimension(:,:),allocatable::work_d4,work_d5,work_d6,work_d7
integer,dimension(:),allocatable::work_i1,work_i3
integer,dimension(:,:),allocatable::work_i2
real,dimension(:),allocatable::work_r2
real,dimension(:,:),allocatable::work_r1
double precision,dimension(:),allocatable::lsf
double precision,dimension(:),allocatable::xdouar,ydouar,hdouar,hdouarp
integer nnodedouar
double precision dtdouar,href
double precision dhminfluv,dhmaxfluv,dhmindiff,dhmaxdiff
double precision zerosion
double precision length_scale,velocity_scale,dt
double precision fluvial_erosion,diffusion_erosion
integer baselevelx0,baselevelx1,baselevely0,baselevely1

!------------------------------------------------------------------------------|
!------------------------------------------------------------------------------|

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

zerosion=params%zerosion
length_scale=params%length_scale
velocity_scale=params%velocity_scale
dt=params%dt
fluvial_erosion=params%fluvial_erosion
diffusion_erosion=params%diffusion_erosion
baselevelx0=params%baselevelx0
baselevelx1=params%baselevelx1
baselevely0=params%baselevely0
baselevely1=params%baselevely1

if (is.ne.1) then

   if (surface%spread_surface_points.gt.0) then

      allocate (points(2,surface%nsurface),stat=err); if (err.ne.0) call stop_run ('Error alloc points in erode$')
      allocate (pointsmin(2,surface%nsurface),stat=err) ; if (err.ne.0) call stop_run ('Error alloc pointsmin in erode$')
      allocate (pointsmax(2,surface%nsurface),stat=err) ; if (err.ne.0) call stop_run ('Error alloc pointsmax in erode$')
      allocate (ln_work(surface%nsurface),stat=err) ; if (err.ne.0) call stop_run ('Error alloc ln_work in erode$')

      points=0.d0
      pointsmin=2.d0
      pointsmax=-1.d0
      ln_work=0
      do i=1,surface%nt
         do k=1,3
            kp=mod(k,3)+1
            ic=surface%icon(k,i)
            icp=surface%icon(kp,i)
            points(1,ic)=points(1,ic)+surface%x(icp)
            points(2,ic)=points(2,ic)+surface%y(icp)
            pointsmin(1,ic)=min(pointsmin(1,ic),surface%x(icp))
            pointsmax(1,ic)=max(pointsmax(1,ic),surface%x(icp))
            pointsmin(2,ic)=min(pointsmin(2,ic),surface%y(icp))
            pointsmax(2,ic)=max(pointsmax(2,ic),surface%y(icp))
            ln_work(ic)=ln_work(ic)+1
         enddo
      enddo

      do i=1,surface%nsurface
         if (surface%x(i).lt.1.d-10) ln_work(i)=0
         if (surface%x(i).gt.1.d0-1.d-10) ln_work(i)=0
         if (surface%y(i).lt.1.d-10) ln_work(i)=0
         if (surface%y(i).gt.1.d0-1.d-10) ln_work(i)=0
      enddo

      do i=1,surface%nsurface
         if (ln_work(i).ne.0) then
            if (surface%spread_surface_points.eq.1) then
               surface%x(i)=points(1,i)/ln_work(i)
               surface%y(i)=points(2,i)/ln_work(i)
            elseif (surface%spread_surface_points.eq.2) then
               surface%x(i)=(pointsmin(1,i)+pointsmax(1,i))/2.d0
               surface%y(i)=(pointsmin(2,i)+pointsmax(2,i))/2.d0
            elseif (surface%spread_surface_points.eq.3) then
               points(1,i)=points(1,i)/ln_work(i)
               points(2,i)=points(2,i)/ln_work(i)
            endif
         endif
      enddo

      if (surface%spread_surface_points.eq.3) then
      allocate (ppoints(2,surface%nsurface),stat=err) ; if (err.ne.0) call stop_run ('Error alloc ppoints in erode$')
      allocate (wpoints(surface%nsurface),stat=err) ; if (err.ne.0) call stop_run ('Error alloc wpoints in erode$')
      wpoints=0.d0
      do i=1,surface%nt
         do k=1,3
            kp=mod(k,3)+1
            ic=surface%icon(k,i)
            icp=surface%icon(kp,i)
            wpoints(ic)=wpoints(ic)+sqrt((surface%x(icp)-points(1,ic))**2+(surface%y(icp)-points(2,ic))**2)*0.1d0
         enddo
      enddo
      ppoints=0.d0
      do i=1,surface%nt
         do k=1,3
            kp=mod(k,3)+1
            ic=surface%icon(k,i)
            icp=surface%icon(kp,i)
            ppoints(1,ic)=ppoints(1,ic)+sqrt((surface%x(icp)-points(1,ic))**2+(surface%y(icp)-points(2,ic))**2)*0.1d0*surface%x(icp)
            ppoints(2,ic)=ppoints(2,ic)+sqrt((surface%x(icp)-points(1,ic))**2+(surface%y(icp)-points(2,ic))**2)*0.1d0*surface%y(icp)
         enddo
      enddo
      do i=1,surface%nsurface
         if (ln_work(i).ne.0) then
             surface%x(i)=ppoints(1,i)/wpoints(i)
             surface%y(i)=ppoints(2,i)/wpoints(i)
         endif
      enddo
      deallocate (ppoints,wpoints)
      endif

      deallocate (points)
      deallocate (pointsmin)
      deallocate (pointsmax)
      deallocate (ln_work)

   endif

   do i=1,surface%nsurface
      call octree_interpolate (olsf%octree,olsf%noctree,olsf%icon, &
                               olsf%nleaves,olsf%lsf,olsf%nnode, &
                               surface%x(i),surface%y(i),surface%z(i),f)
      surface%z(i)=min(f,surface%z(i))
   enddo

   return

endif

! first apply erosion to top surface

if (length_scale.gt.0.d0) then

   nnodedouar=surface%nsurface

   allocate (xdouar(nnodedouar))
   allocate (ydouar(nnodedouar))
   allocate (hdouar(nnodedouar))
   allocate (hdouarp(nnodedouar))

   xdouar=surface%x*length_scale*1.d3
   ydouar=surface%y*length_scale*1.d3

   if (iproc.eq.0) call random_number (hdouar)
   call mpi_bcast (hdouar,surface%nsurface,mpi_double_precision,0,mpi_comm_world,ierr)
   hdouar=(hdouar+1.0)/2.d0
   hdouar=hdouar+(surface%z-zerosion)*length_scale*1.d3*params%vex
   href=minval(hdouar)
   hdouar=hdouar-href
   dtdouar=dt*length_scale/velocity_scale*1.d6

   if (iproc.eq.0) write (8,*) 'Cascade time scale ',dtdouar
   if (iproc.eq.0) write (8,*) 'Topo in ',minval(hdouar),maxval(hdouar)
   if (iproc.eq.0) write (8,*) 'X in ',minval(xdouar),maxval(xdouar)
   if (iproc.eq.0) write (8,*) 'Y in ',minval(ydouar),maxval(ydouar)

   hdouarp=hdouar
   call cascade (xdouar,ydouar,hdouar,nnodedouar,dtdouar, &
                 dhminfluv,dhmaxfluv, &
                 dhmindiff,dhmaxdiff, &
                 fluvial_erosion,diffusion_erosion, &
                 baselevelx0,baselevelx1,baselevely0,baselevely1)

   if (iproc.eq.0) write (8,*) 'Topo out ',minval(hdouar),maxval(hdouar)
   if (iproc.eq.0) write (8,*) 'Delta Topo ',minval(hdouar-hdouarp),maxval(hdouar-hdouarp)

   do i=1,surface%nsurface
      surface%z(i)=(hdouar(i)+href)/1.d3/length_scale/params%vex+zerosion
      surface%r(i)=(hdouar(i)-hdouarp(i))/1.d3/length_scale/dt
   enddo

   deallocate (xdouar)
   deallocate (ydouar)
   deallocate (hdouar)
   deallocate (hdouarp)

else ! perfect erosion

   do i=1,surface%nsurface
      surface%z(i)=zerosion
   enddo

endif

! nn_setup (calculates delaunay triangles and other info)

ntmax=surface%nsurface*3
nhmax=surface%nsurface
npmax=surface%nsurface
nnpnmax=100
nmax=3*ntmax+npmax
nvmax=ntmax

allocate (points(2,surface%nsurface),stat=err); if (err.ne.0) call stop_run ('Error alloc points in erode$')
allocate (field(surface%nsurface),stat=err)   ; if (err.ne.0) call stop_run ('Error alloc field in erode$')
allocate (vertices(3,ntmax),stat=err)         ; if (err.ne.0) call stop_run ('Error alloc vertices in erode$')
allocate (centres(3,ntmax),stat=err)          ; if (err.ne.0) call stop_run ('Error alloc centress in erode$')
allocate (neighbour(3,ntmax),stat=err)        ; if (err.ne.0) call stop_run ('Error alloc neighbour in erode$')
allocate (hulltriangles(nhmax),stat=err)      ; if (err.ne.0) call stop_run ('Error alloc hulltriangles in erode$')
allocate (vis_tlist(nvmax),stat=err)          ; if (err.ne.0) call stop_run ('Error alloc vis_tlist in erode$')
allocate (vis_elist(nvmax),stat=err)          ; if (err.ne.0) call stop_run ('Error alloc vis_elist in erode$')
allocate (add_tlist(nvmax),stat=err)          ; if (err.ne.0) call stop_run ('Error alloc add_tlist in erode$')
allocate (ln_work(surface%nsurface),stat=err) ; if (err.ne.0) call stop_run ('Error alloc ln_work in erode$')
allocate (lt_work_l(ntmax),stat=err)            ; if (err.ne.0) call stop_run ('Error alloc lt_work_l in erode$')
allocate (ln_work_l(surface%nsurface),stat=err) ; if (err.ne.0) call stop_run ('Error alloc ln_work_l in erode$')

points(1,:)=surface%x
points(2,:)=surface%y
field(:)=surface%z
dmode=-2
nmode=0
clockwise=.true.
eps=1.d-20
eps=0.d0
      
call nn2d_setup (surface%nsurface,ntmax,nhmax,npmax,nnpnmax,nmax, &
                 points,dmode,nmode,clockwise,field,nt,vertices, &
                 centres,neighbour,nh,hulltriangles,nohalt_hull, &
                 loc,nnn,nnlist,ntrilist, &
                 eps,nvmax,vis_tlist,vis_elist,add_tlist, &
                 lt_work_l,ln_work_l)

allocate (work_d1(2,nnpnmax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc work_d1 in erode$')
allocate (work_d2(2,nnpnmax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc work_d2 in erode$')
allocate (work_d3(2,nnpnmax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc work_d3 in erode$')
allocate (work_d4(2,nnpnmax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc work_d4 in erode$')
allocate (work_d5(2,nnpnmax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc work_d5 in erode$')
allocate (work_d6(2,nnpnmax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc work_d6 in erode$')
allocate (work_d7(2,nnpnmax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc work_d7 in erode$')
allocate (work_i1(nnpnmax),stat=err)   ; if (err.ne.0) call stop_run ('Error alloc work_i1 in erode$')
allocate (work_i2(2,nnpnmax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc work_i2 in erode$')
allocate (work_i3(nnpnmax),stat=err)   ; if (err.ne.0) call stop_run ('Error alloc work_i3 in erode$')
allocate (work_r1(nnpnmax,2),stat=err) ; if (err.ne.0) call stop_run ('Error alloc work_r1 in erode$')
allocate (work_r2(nnpnmax),stat=err)   ; if (err.ne.0) call stop_run ('Error alloc work_r2 in erode$')
allocate (lsf(olsf%nnode),stat=err)    ; if (err.ne.0) call stop_run ('Error alloc lsf in erode$')

nnext=.true.
int_method=3
loc=1

lsf=0.d0

do i=iproc+1,olsf%nnode,nproc
   x=olsf%x(i)
   y=olsf%y(i)
   call nn2D (x,y,points,vertices,neighbour,nnext,int_method,centres, &
              hulltriangles,nh,loc,field,nnpnmax,work_r1,work_r2, &
              work_d1,work_i1,work_i2,work_i3,work_d2,work_d3,work_d4, &
              work_d5,work_d6,work_d7,lt_work_l,ln_work_l,out,f,df,ddf,v)
   lsf(i)=f
enddo

olsf%lsf(1:olsf%nnode)=0.d0
call mpi_allreduce (lsf,olsf%lsf,olsf%nnode, &
                    mpi_double_precision,mpi_sum,mpi_comm_world,ierr)

! here spreads the points on the top surface to avoid collapse of the surface due to erosion
! for that it uses a barrycenter algorithm and a reinterpolation of the surface to avoid undesired diffusion effects

if (surface%spread_surface_points.gt.0) then

allocate (pointsmin(2,surface%nsurface),stat=err) ; if (err.ne.0) call stop_run ('Error alloc pointsmin in erode$')
allocate (pointsmax(2,surface%nsurface),stat=err) ; if (err.ne.0) call stop_run ('Error alloc pointsmax in erode$')

points=0.d0
pointsmin=2.d0
pointsmax=-1.d0
ln_work=0
do i=1,surface%nt
  do k=1,3
  kp=mod(k,3)+1
  ic=surface%icon(k,i)
  icp=surface%icon(kp,i)
  points(1,ic)=points(1,ic)+surface%x(icp)
  points(2,ic)=points(2,ic)+surface%y(icp)
  pointsmin(1,ic)=min(pointsmin(1,ic),surface%x(icp))
  pointsmax(1,ic)=max(pointsmax(1,ic),surface%x(icp))
  pointsmin(2,ic)=min(pointsmin(2,ic),surface%y(icp))
  pointsmax(2,ic)=max(pointsmax(2,ic),surface%y(icp))
  ln_work(ic)=ln_work(ic)+1
  enddo
enddo
do i=1,surface%nsurface
if (surface%x(i).lt.1.d-10) ln_work(i)=0
if (surface%x(i).gt.1.d0-1.d-10) ln_work(i)=0
if (surface%y(i).lt.1.d-10) ln_work(i)=0
if (surface%y(i).gt.1.d0-1.d-10) ln_work(i)=0
enddo
do i=1,surface%nsurface
if (ln_work(i).ne.0) then
  if (surface%spread_surface_points.eq.1) then
  surface%x(i)=points(1,i)/ln_work(i)
  surface%y(i)=points(2,i)/ln_work(i)
  else
  surface%x(i)=(pointsmin(1,i)+pointsmax(1,i))/2.d0
  surface%y(i)=(pointsmin(2,i)+pointsmax(2,i))/2.d0
  endif
endif
enddo

deallocate (pointsmin,pointsmax)

endif

deallocate (points)
deallocate (field)
deallocate (vertices)
deallocate (centres)
deallocate (hulltriangles)
deallocate (vis_tlist)
deallocate (vis_elist)
deallocate (add_tlist)
deallocate (lt_work_l)
deallocate (ln_work)
deallocate (ln_work_l)
deallocate (work_d1)
deallocate (work_d2)
deallocate (work_d3)
deallocate (work_d4)
deallocate (work_d5)
deallocate (work_d6)
deallocate (work_d7)
deallocate (work_r1)
deallocate (work_r2)
deallocate (work_i1)
deallocate (work_i2)
deallocate (work_i3)
deallocate (lsf)

return
end
!------------------------------------------------------------------------------|
!------------------------------------------------------------------------------|
