!------------------------------------------------------------------------------|
!------------------------------------------------------------------------------|
!                                                                              |
!              ||===\\                                                         | 
!              ||    \\                                                        |
!              ||     ||   //==\\   ||  ||   //==||  ||/==\\                   |
!              ||     ||  ||    ||  ||  ||  ||   ||  ||    ||                  |
!              ||    //   ||    ||  ||  ||  ||   ||  ||                        |
!              ||===//     \\==//    \\==\\  \\==\\  ||                        |
!                                                                              |
!------------------------------------------------------------------------------|
!------------------------------------------------------------------------------|
!                                                                              |
!              REFINE_SURFACE    May 2008                                      |
!                                                                              |
!------------------------------------------------------------------------------|
!------------------------------------------------------------------------------|
      
subroutine refine_surface(params,surface,surface0,threadinfo,nadd,nrem,is,&
           istep,ref_count) 

!------------------------------------------------------------------------------|
!(((((((((((((((( Purpose of the routine  ))))))))))))))))))))))))))))))))))))))
!------------------------------------------------------------------------------|
! This routine builds an edge array between a set of particles on a surface.
! It uses the delaunay triangulation and then steps through the triangles
! to build the edge information.
! ed is the computed edge array
! nedge is the number of edges
! nedgepernode,nodenodenumber and nodenodenumber contain the list of edges 
! that start from each node; for a given node i
! their number is nedgepernode(i), the edge number in the list of edges is
! nodeedgenumber(j,i) and the node at the end of the edge is
! nodenodenumber(j,i) for j=1,nedgepernode(i)

! this routine builds the boolean 'refine' array of length nedge (number of edges 
! in the triangulation connecting the particles) that contains the list of 
! edges to be refined.  

! surface is the sheet/surface to be refined
! it will contain the new number of particles
! ed is the computed edge array
! refine is the integer array determining the edges to be refined
! nedge is the number of edges
! nadd is the number of edges to be changed/split
! nedgepernode, nodenodenumber,nodeedgenumber are arrays containing edge info
! nnmax is the maximum number of nn

!------------------------------------------------------------------------------|
!((((((((((((((((  declaration of the subroutine arguments  ))))))))))))))))))))
!------------------------------------------------------------------------------|
      
use definitions
!use mpi
use threads

implicit none

include 'mpif.h'

type (parameters) params
type (sheet) surface 
type (sheet) surface0 
type (thread) threadinfo
integer nadd,nrem
integer is,istep
integer ref_count

!------------------------------------------------------------------------------|
!(((((((((((((((( declaration of the subroutine internal variables )))))))))))))
!------------------------------------------------------------------------------|

integer naddp,nremp
integer nnmax
type (edge), dimension(:), allocatable :: ed
type (edge), dimension(:), allocatable :: edswap
integer,dimension(:)  ,allocatable :: nedgepernode
integer,dimension(:)  ,allocatable :: refine_list,remove_list
integer,dimension(:,:),allocatable :: nodenodenumber,nodeedgenumber
logical,dimension(:),allocatable::refine,refinep,remove,removep
character*72  :: shift
integer ie,j,jedge,kp,kpp,k,inode,inodep,inodepp,iadd,irem
integer ntriangle
integer nedge
integer nedgen,nsurfacen
double precision dist1,distmax,prod,distmin,eps
double precision, external :: dist


integer err,ierr,iproc,nproc,i,iedge,i1,i2
integer nnodeint,nelemint,nedgeint,nelemmax,nnodemax,nedgemax
double precision,dimension(:,:),allocatable::memswap
integer,dimension(:,:),allocatable::iconswap
double precision xm,ym,zm,xyzn

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

call mpi_comm_size (mpi_comm_world,nproc,ierr)
call mpi_comm_rank (mpi_comm_world,iproc,ierr)
         
shift=' '
eps=1.d-8

!------------------------
! build edge information 
!------------------------

nsurfacen=surface%nsurface
ntriangle=surface%nt
nedge=nsurfacen+ntriangle-1
if (surface%closed.eq.1) nedge=nedge-1
nnmax=12 ! nnmax is maximum number of neighbours in triangulation (should be around 6 on average)

allocate (ed(nedge),stat=threadinfo%err)
if (params%debug.gt.1) call heap (threadinfo,'ed','refine_surface',size(ed),'int',+1)
allocate (nedgepernode(nsurfacen),stat=threadinfo%err)
if (params%debug.gt.1) call heap (threadinfo,'nedgepernode','refine_surface',size(nedgepernode),'int',+1)
allocate (nodenodenumber(nnmax,nsurfacen),stat=threadinfo%err)
if (params%debug.gt.1) call heap (threadinfo,'nodenodenumber','refine_surface',size(nodenodenumber),'int',+1)
allocate (nodeedgenumber(nnmax,nsurfacen),stat=threadinfo%err)
if (params%debug.gt.1) call heap (threadinfo,'nodeedgenumber','refine_surface',size(nodeedgenumber),'int',+1)

ed%t1=0
ed%t2=0
nedgepernode=0
iedge=0

do ie=1,ntriangle
  do k=1,3
    inode=surface%icon(k,ie)
    kp=mod(k,3)+1
    kpp=mod(k+1,3)+1
    inodep=surface%icon(kp,ie)
    inodepp=surface%icon(kpp,ie)
    do j=1,nedgepernode(inodep)
      if (nodenodenumber(j,inodep).eq.inode) then
        jedge=nodeedgenumber(j,inodep)
        ed(jedge)%m2=inodepp
        ed(jedge)%t2=ie
        goto 111
      endif
    enddo
    iedge=iedge+1
    if (iedge.gt.nedge) call stop_run ('too many edges in delaunay2$')
    nedgepernode(inode)=nedgepernode(inode)+1
    if (nedgepernode(inode).gt.nnmax) then
      if (iproc.eq.0) write (8,*) 'node',inode,'has',nedgepernode(inode),'neighbours'
      call stop_run ('too many neighbours$')
    endif
    nodenodenumber(nedgepernode(inode),inode)=inodep
    nodeedgenumber(nedgepernode(inode),inode)=iedge
    ed(iedge)%n1=inode
    ed(iedge)%n2=inodep
    ed(iedge)%m1=inodepp
    ed(iedge)%t1=ie
    111 continue
  enddo
enddo

if (iedge/=nedge) call stop_run ('pb_b in refine_surface$')

!------------------------------------
! find edges that need to be refined
!------------------------------------

!distmax=1.d0/2.d0**surface%levelt*sqrt(2.d0)*surface%stretch
distmax=1.d0/(2.d0**surface%levelt+1)*sqrt(2.d0)*surface%stretch   !opla
distmin=1.d0/(2.d0**(surface%levelt+4)+1)*sqrt(2.d0)

allocate (refine(nedge),stat=err)
allocate (refinep(nedge),stat=err)
refine=.false.
refinep=.false.

if (params%remove_surf_pts) then
  allocate (remove(nedge),stat=err)
  allocate (removep(nedge),stat=err)
  remove=.false.
  removep=.false.
endif

do iedge=1+iproc,nedge,nproc
   i1=ed(iedge)%n1
   i2=ed(iedge)%n2
   dist1=dist(surface%x(i1),surface%x(i2),surface%y(i1),surface%y(i2),         &
             surface%z(i1),surface%z(i2))
   !dist1=dist(surface%x(i1),surface%x(i2),surface%y(i1),surface%y(i2),         &
   !          surface%z(i1),surface%z(i2),params%distance_exponent)
   prod=surface%xn(i1)*surface%xn(i2)+surface%yn(i1)*surface%yn(i2)+surface%zn(i1)*surface%zn(i2)
   !if (dist1.gt.distmax .or. prod.lt.prodmin) refinep(iedge)=1
   refinep(iedge)=(dist1.gt.distmax) 
   if (params%remove_surf_pts) removep(iedge)=(dist1.gt.eps .and. dist1.le.distmin)
   !if (dist1.le.distmin) print *,'dist1 (',dist1,') less than distmin'
enddo

call mpi_allreduce (refinep,refine,nedge,mpi_logical,mpi_lor,mpi_comm_world,ierr)
if (params%remove_surf_pts) call mpi_allreduce (removep,remove,nedge,mpi_logical,mpi_lor,mpi_comm_world,ierr)

!-----------------------
! compute nadd and naddp
!-----------------------

nadd=count(refine)
allocate(refine_list(nadd))
iadd=0
naddp=0

if (params%remove_surf_pts) then
  nrem=count(remove)
  allocate(remove_list(nrem))
  irem=0
  nremp=0
else
  nrem=0
endif

do iedge=1,nedge
  if (refine(iedge)) then
     iadd=iadd+1
     if (ed(iedge)%t1.eq.0 .or. ed(iedge)%t2.eq.0) naddp=naddp+1
     refine_list(iadd)=iedge
  endif
  if (params%remove_surf_pts) then
    if (remove(iedge)) then
      irem=irem+1
      if (ed(iedge)%t1.eq.0 .or. ed(iedge)%t2.eq.0) nremp=nremp+1
      !remove_list(irem)=iedge
      remove_list(irem)=ed(iedge)%n1
    endif
  endif
enddo

! Modify remove_list to account for removal of nodes from surface
if (params%remove_surf_pts) then
  if (nrem.gt.0) then
    do i=1,nrem
      do j=i,nrem
        if (remove_list(i).lt.remove_list(j)) remove_list(j)=remove_list(j)-1
      enddo
    enddo
  endif
  if (irem/=nrem) call stop_run ('pb_b in refine_surface$')
  deallocate (remove)
  deallocate (removep)
endif

if (iadd/=nadd) call stop_run ('pb_b in refine_surface$')

deallocate (refine)
deallocate (refinep)

if (iproc.eq.0 .and. params%debug>=1) write(*,'(a,i2,a,i8,a)') shift//'S.',is,':', nadd,' added ptcls in refine_surface'

!-----------------------------------
! resizing ed (allocate/deallocate)
!-----------------------------------

nedgen=(surface%nsurface+nadd)+(surface%nt+(nadd-naddp)*2+naddp)-1
if (surface%closed.eq.1) nedgen=nedgen-1

! Not sure this is correct...
!nedgen=(surface%nsurface+(nadd-nrem))+(surface%nt+(((nadd-naddp)*2+naddp)-((nrem-nremp)*2+nremp))-1

allocate (edswap(nedgen),stat=err) ; if (err.ne.0) call stop_run ('Error alloc edswap in main$')
edswap(1:nedge)=ed(1:nedge)
deallocate (ed)
allocate (ed(nedgen),stat=err) ; if (err.ne.0) call stop_run ('Error alloc edswap in main$')
ed(1:nedge)=edswap(1:nedge)
deallocate (edswap)

!----------------------------------
! prepare memory for the refinment
!----------------------------------

nnodemax=surface%nsurface+nadd
nelemmax=surface%nt+(nadd-naddp)*2+naddp
nedgemax=nnodemax+nelemmax-1
if (surface%closed.eq.1) nedgemax=nedgemax-1

if (nedgemax.ne.nedgen) call stop_run ('error in counting number of edges in refine_surface$')

allocate (memswap(surface%nsurface,8),stat=err) ; if (err.ne.0) call stop_run('Error alloc memswap in refine_surface$')
allocate (iconswap(3,surface%nt),stat=err) ; if (err.ne.0) call stop_run('Error alloc iconswap in refine_surface$')
      
memswap(:,1)=surface%x
memswap(:,2)=surface%y
memswap(:,3)=surface%z
memswap(:,4)=surface%xn
memswap(:,5)=surface%yn
memswap(:,6)=surface%zn
memswap(:,7)=surface%r
memswap(:,8)=surface%s
iconswap(1:3,1:surface%nt)=surface%icon(1:3,1:surface%nt)

deallocate (surface%x)
deallocate (surface%y)
deallocate (surface%z)
deallocate (surface%xn)
deallocate (surface%yn)
deallocate (surface%zn)
deallocate (surface%r)
deallocate (surface%s)
deallocate (surface%icon)

allocate (surface%x(nnodemax), stat=err) ; if (err.ne.0) call stop_run ('Error alloc x in refine_surface$')
allocate (surface%y(nnodemax), stat=err) ; if (err.ne.0) call stop_run ('Error alloc y in refine_surface$')
allocate (surface%z(nnodemax), stat=err) ; if (err.ne.0) call stop_run ('Error alloc z in refine_surface$')
allocate (surface%xn(nnodemax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc xn in refine_surface$')
allocate (surface%yn(nnodemax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc yn in refine_surface$')
allocate (surface%zn(nnodemax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc zn in refine_surface$')
allocate (surface%r(nnodemax), stat=err) ; if (err.ne.0) call stop_run ('Error alloc r in refine_surface$')
allocate (surface%s(nnodemax), stat=err) ; if (err.ne.0) call stop_run ('Error alloc s in refine_surface$')
allocate (surface%icon(3,nelemmax),stat=err) ; if (err.ne.0) call stop_run ('Error alloc icon in refine_surface$')
      
surface%x(1:surface%nsurface)=memswap(:,1)
surface%y(1:surface%nsurface)=memswap(:,2)
surface%z(1:surface%nsurface)=memswap(:,3)
surface%xn(1:surface%nsurface)=memswap(:,4)
surface%yn(1:surface%nsurface)=memswap(:,5)
surface%zn(1:surface%nsurface)=memswap(:,6)
surface%r(1:surface%nsurface)=memswap(:,7)
surface%s(1:surface%nsurface)=memswap(:,8)
surface%icon(1:3,1:surface%nt)=iconswap(1:3,1:surface%nt)

deallocate (memswap)
deallocate (iconswap)

!---------------------
! updates the surface
!---------------------

nnodeint=surface%nsurface
nelemint=surface%nt
nedgeint=nedge

do i=1,nadd
   iedge=refine_list(i)
   i1=ed(iedge)%n1
   i2=ed(iedge)%n2
   call middle (xm,ym,zm,i1,i2,surface%x,surface%y,surface%z, &
                surface%xn,surface%yn,surface%zn,nnodeint)
   nnodeint=nnodeint+1
   surface%x(nnodeint)=xm
   surface%y(nnodeint)=ym
   surface%z(nnodeint)=zm
   surface%r(nnodeint)=(surface%r(i1)+surface%r(i2))/2.d0
   surface%s(nnodeint)=(surface%s(i1)+surface%s(i2))/2.d0
   xm=(surface%xn(i1)+surface%xn(i2))/2.d0
   ym=(surface%yn(i1)+surface%yn(i2))/2.d0
   zm=(surface%zn(i1)+surface%zn(i2))/2.d0
   xyzn=sqrt(xm**2+ym**2+zm**2)
   surface%xn(nnodeint)=xm/xyzn
   surface%yn(nnodeint)=ym/xyzn
   surface%zn(nnodeint)=zm/xyzn
   call update_icon (surface%icon,nelemmax,ed,nedgemax,iedge,nnodeint,nelemint,nedgeint)
enddo

if (iproc.eq.0 .and. params%debug>=1 .and. params%remove_surf_pts) write(*,'(a,i2,a,i8,a)') shift//'S.',is,':', nrem,' removed ptcls in refine_surface'

do i=1,size(ed%n1)
   i1=ed(i)%n1
   i2=ed(i)%n2
   if (i1 == i2 .and. iproc == 0) then
     print *,'*******************************************************************'
     print *,'Nodes for edge ',i,' have the same spatial coordinates'
     print *,'The edges nodes are are node i1: ',i1,' and i2: ',i2
     print *,'x1:',surface%x(i1),' y1: ',surface%y(i1),' z1: ',surface%z(i1)
     print *,'x2:',surface%x(i2),' y2: ',surface%y(i2),' z2: ',surface%z(i2)
     print *,'*******************************************************************'
   endif
enddo

surface%nsurface=nnodemax
surface%nt=nelemmax

if (params%remove_surf_pts) then
  do i=1,nrem
    i1=remove_list(i)
    call remove_point(i1,surface,0,surface0)
  end do
endif

if (.not.params%normaladvect) then
   call compute_normals (surface%nsurface,surface%x,surface%y,surface%z, &
                         surface%nt,surface%icon,surface%xn,surface%yn,surface%zn)
end if

if (nnodeint.ne.nnodemax) call stop_run ('Error counting nodes in refine_surface$')
if (nelemint.ne.nelemmax) call stop_run ('Error counting elements in refine_surface$')

if (nrem.gt.0) then
  nnodemax=surface%nsurface
  nelemmax=surface%nt
  nedgemax=nnodemax+nelemmax-1
endif

surface%nsurface=nnodemax
surface%nt=nelemmax
nedge=nedgemax

deallocate(refine_list)
if (params%remove_surf_pts) deallocate(remove_list)

if (params%debug.gt.1) call heap (threadinfo,'ed','refine_surface',size(ed),'int',-1)
deallocate (ed)         
if (params%debug.gt.1) call heap (threadinfo,'nedgepernode','refine_surface',size(nedgepernode),'int',-1)
deallocate (nedgepernode)
if (params%debug.gt.1) call heap (threadinfo,'nodenodenumber','refine_surface',size(nodenodenumber),'int',-1)
deallocate (nodenodenumber)
if (params%debug.gt.1) call heap (threadinfo,'nodeedgenumber','refine_surface',size(nodeedgenumber),'int',-1)
deallocate (nodeedgenumber)

if (params%debug .ge.2) call output_surf(surface,is,'after_refine',istep,ref_count) 

end subroutine  

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

subroutine middle (xm,ym,zm,i1,i2,xx,yy,zz,xxn,yyn,zzn,nnode)
  
implicit none

double precision xx(nnode),yy(nnode),zz(nnode)
double precision xxn(nnode),yyn(nnode),zzn(nnode)
double precision xm,ym,zm,xnm,ynm,znm,alpha,dist12,prod,xyzn
integer i1,i2,nnode
  
xm=(xx(i2)+xx(i1))/2.d0
ym=(yy(i2)+yy(i1))/2.d0
zm=(zz(i2)+zz(i1))/2.d0
  
return
  
! what follows is an attempt at defining the middle of the segment
! outside of the euclidian line
!xnm=(xxn(i2)+xxn(i1))/2.d0
!ynm=(yyn(i2)+yyn(i1))/2.d0
!znm=(zzn(i2)+zzn(i1))/2.d0
!xyzn=sqrt(xnm**2+ynm**2+znm**2)
!xnm=xnm/xyzn
!ynm=ynm/xyzn
!znm=znm/xyzn
!dist12=sqrt((xx(i2)-xx(i1))**2+(yy(i2)-yy(i1))**2+(zz(i2)-zz(i1))**2)
!prod=xxn(i1)*xxn(i2)+yyn(i1)*yyn(i2)+zzn(i1)*zzn(i2)
!alpha=dist12*(1./prod**2-1.)/4.d0
!xm=xm+xnm*alpha
!ym=ym+ynm*alpha
!zm=zm+ynm*alpha
  
end subroutine

!------------------------------------------------------------------------------|
!------------------------------------------------------------------------------|
  
subroutine update_icon (icon,nelemmax,ed,nedgemax,iedge,nnodeint,nelemint,nedgeint)
  
use definitions
  
implicit none
  
integer icon(3,nelemmax),nelemmax,nedge,iedge,nnodeint,nelemint
integer jedge,nedgemax,nedgeint
type(edge) ed(nedgemax)
integer k,k12,k21
integer n1,n2,m1,m2,t1,t2,t1n,t2n
  
nedge=nedgeint

n1=ed(iedge)%n1
n2=ed(iedge)%n2
m1=ed(iedge)%m1
m2=ed(iedge)%m2
t1=ed(iedge)%t1
t2=ed(iedge)%t2

if (t1.ne.0) then
   do k=1,3
      if (icon(k,t1).eq.n1) k12=k
   enddo
   icon(k12,t1)=nnodeint
   nelemint=nelemint+1
   t1n=nelemint
   icon(1,nelemint)=m1
   icon(2,nelemint)=n1
   icon(3,nelemint)=nnodeint
endif

if (t2.ne.0) then
   do k=1,3
      if (icon(k,t2).eq.n2) k21=k
   enddo
   icon(k21,t2)=nnodeint
   nelemint=nelemint+1
   t2n=nelemint
   icon(1,nelemint)=m2
   icon(2,nelemint)=n2
   icon(3,nelemint)=nnodeint
endif

ed(iedge)%n2=nnodeint
if (t1.ne.0) then
   ed(iedge)%t1=t1n
else
   ed(iedge)%t1=0
endif

if (t1.ne.0) then
   nedgeint=nedgeint+1
   ed(nedgeint)%n1=m1
   ed(nedgeint)%n2=nnodeint
   ed(nedgeint)%m1=n2
   ed(nedgeint)%m2=n1
   ed(nedgeint)%t1=t1
   ed(nedgeint)%t2=t1n
endif

nedgeint=nedgeint+1
ed(nedgeint)%n1=n2
ed(nedgeint)%n2=nnodeint

if (t1.ne.0) then
   ed(nedgeint)%m2=m1
   ed(nedgeint)%t2=t1
else
   ed(nedgeint)%t2=0
endif

if (t2.ne.0) then
   ed(nedgeint)%m1=m2
   ed(nedgeint)%t1=t2n
else
   ed(nedgeint)%t1=0
endif

if (t2.ne.0) then
   nedgeint=nedgeint+1
   ed(nedgeint)%n1=m2
   ed(nedgeint)%n2=nnodeint
   ed(nedgeint)%m1=n1
   ed(nedgeint)%m2=n2
   ed(nedgeint)%t1=t2
   ed(nedgeint)%t2=t2n
endif

do jedge=1,nedge
   if (t1.ne.0) then
      if (ed(jedge)%t1.eq.t1) then
         if (ed(jedge)%n1.eq.m1) then
            ed(jedge)%m1=nnodeint
            ed(jedge)%t1=t1n
         endif
         if (ed(jedge)%n1.eq.n2) ed(jedge)%m1=nnodeint
      endif
      if (ed(jedge)%t2.eq.t1) then
         if (ed(jedge)%n1.eq.n1) then
            ed(jedge)%m2=nnodeint
            ed(jedge)%t2=t1n
         endif
         if (ed(jedge)%n1.eq.m1) ed(jedge)%m2=nnodeint
      endif
   endif
   if (t2.ne.0) then
      if (ed(jedge)%t1.eq.t2) then
         if (ed(jedge)%n1.eq.m2) then
            ed(jedge)%m1=nnodeint
            ed(jedge)%t1=t2n
         endif
         if (ed(jedge)%n1.eq.n1) ed(jedge)%m1=nnodeint
      endif
      if (ed(jedge)%t2.eq.t2) then
         if (ed(jedge)%n1.eq.n2) then
            ed(jedge)%m2=nnodeint
            ed(jedge)%t2=t2n
         endif
         if (ed(jedge)%n1.eq.m2) ed(jedge)%m2=nnodeint
      endif
   endif
enddo

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