KDClass.f90 Source File


Source Code

module KDClass

   use SMConstants
   use Utilities
   use TessellationTypes
   use OrientedBoundingBox

   implicit none

!~          5-----------------8
!~         /|                /|
!~        / |               / |
!~       /  |              /  |
!~      6-----------------7   |
!~      |   |             |   |
!~      |   |             |   |
!~      |   |             |   |
!~      |   |             |   |
!~      |   1-------------|---4                  
!~      |  /              |  /                   | z
!~      | /               | /                    |_______  y
!~      |/                |/                    /
!~      2-----------------3                    / x
       
  public :: LastLevel, BoxIndex, POINTS_KDTREE, TRIANGLES_KDTREE_SAH, TRIANGLES_KDTREE_MEDIAN

  integer :: BoxIndex, LastLevel = -1, depth, NumOfObjsIndx, NumOfKDtreePoints
  
  integer, parameter :: side_L = 4, side_R = 0, ON_PLANE = 0, POINTS_KDTREE = 0, TRIANGLES_KDTREE_SAH = 1, TRIANGLES_KDTREE_MEDIAN = 2
  integer, parameter :: START_ = 2, END_ = 0, PLANAR_ = 1, ONLYLEFT = 0, ONLYRIGHT = 1, BOTH = 2, ODD = 1, EVEN = 2
  integer, parameter :: BREADTHFIRST = 1, DEPTHFIRST = 0
  
  real(kind=rp), parameter :: C_TRANSVERSE = 1.0_RP, C_INTERSECT = 1.5_RP, C_1 = 1.2_RP, C_2 = 2.0_RP
  
  integer,  parameter, dimension(8) :: vertices_x = (/ 1,4,5,8,  2,3,6,7 /), &
                                       vertices_y = (/ 1,2,6,5,  3,4,7,8 /), &
                                       vertices_z = (/ 1,2,3,4,  5,6,7,8 /)  
                                       
  type Event
  
     real(kind=rp) :: plane, median
     integer       :: eType, index, axis
  
  end type
                            
!
!  **************************************************
!  Main type for a kd-tree
!  **************************************************
   type KDtree

      class(KDtree), pointer                         :: child_L, child_R, parent
      type(Object_type), dimension(:),   allocatable :: ObjectsList 
      real(kind=rp),     dimension(3,8)              :: vertices   !local
      integer                                        :: NumOfObjs, level, axis, &
                                                        index, Min_n_of_Objs,   &
                                                        which_KDtree, MaxAxis,  &
                                                        SIDE, N_L, N_R, N_B,    &
                                                        HalfEvents, NumThreads, &
                                                        STLNum
      logical                                        :: isLast, Split
      logical                                        :: built_R = .false., built_L = .false.
      integer,           dimension(NDIM)             :: NumOfEvents
      real(kind=rp)                                  :: S, SplitCost, SplittingPlane
      type(Event),       dimension(:,:), allocatable :: Events
      integer,           dimension(:),   allocatable :: ObjsIndeces
      
      contains
         procedure :: construct           => KDtree_construct
         procedure :: SetUpRoot           => KDtree_SetUpRoot
         procedure :: FindLeaf            => KDtree_FindLeaf 
         procedure :: plot                => KDtree_plot
         procedure :: plotBlock           => KDtree_plotBlock
         procedure :: Destruct            => KD_treeDestruct
         procedure :: GetArea             => KD_treeGetArea
         procedure :: BuildChild          => KDtree_BuildChild
         procedure :: EvaluateCostSAH     => KDtree_EvaluateCostSAH
         procedure :: EvaluateCostMEDIAN  => KDtree_EvaluateCostMEDIAN
         procedure :: SaveObjsIndeces     => KDtree_SaveObjsIndeces
         procedure :: SavePointsIndeces   => KDtree_SavePointsIndeces

   end type

   type DepthFirst_type
   
      type(KDtree), pointer                     :: taskTree
      type(Event),  dimension(:,:), allocatable :: taskEvents
      logical                                   :: active = .false.
   
   end type 
   
   type taskPart_dim_type
   
      real(kind=RP) :: splittingPlane
      integer       :: N_L, N_R, N_P, ChunkDim, Starting_index, Values2Check
      
   end type 
   
   type taskSAH_type
    
      real(kind=RP) :: SplitCost, splittingPlane
      integer       :: axis, SIDE
      logical       :: split = .false.
   
   end type 
   
   type taskPart_type
   
      type(taskPart_dim_type), dimension(NDIM) :: taskPartDim

   end type 

   contains 
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  --------------------------------------------------------------
! This function computes the index corresponding to the integer i. 
!  --------------------------------------------------------------
 
   integer function AxisIndex( i ) result( axis )
 
      implicit none
      !-arguments-------------
      integer, intent(in) :: i
       
      if( i .eq. 1 ) axis = 2
      if( i .eq. 2 ) axis = 3
      if( i .eq. 3 ) axis = 1
 
   end function AxisIndex
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
! This function computes the longest axis of the OBB. 
!  -------------------------------------------------
   integer function FindAxis( this ) result( FirstAxis )
   
      implicit none
      !-arguments----------------------
      type(KDtree), intent(in) :: this
      !-local-variables----------------
      integer,  dimension(1) :: maxvec
      
      maxvec = maxloc( (/ abs(this% vertices(1,7) - this% vertices(1,1)),   &
                          abs(this% vertices(2,7) - this% vertices(2,1)),   &
                          abs(this% vertices(3,7) - this% vertices(3,1)) /) )
       
      FirstAxis = maxvec(1)
 
   end function FindAxis
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  ----------------------------------------------------------
! This subroutine saves and plots the leaves of the KD tree. 
!  ----------------------------------------------------------
   
   recursive subroutine Plot_leaves( tree, STLNum, funit, which_KDtree )
      use PhysicsStorage
      implicit none
      !-arguments--------------------------------------
      type(KDtree), intent(inout) :: tree
      integer,      intent(in)    :: STLNum, funit, &
                                    which_KDtree
      !-local-variables--------------------------------
      real(kind=rp) :: x_g(NDIM)
      integer       :: i
      
      if( tree% isLast ) then 
         write(funit,"(a69)") 'ZONE NODES=8, ELEMENTS = 6, DATAPACKING=POINT, ZONETYPE=FETETRAHEDRON'

         do i = 1, 8
            call OBB(STLNum)% ChangeRefFrame( tree% vertices(:,i), GLOBAL, x_g )
            write(funit,'(3E13.5)') Lref*x_g(1),Lref*x_g(2), Lref*x_g(3)
         end do 
         
         write(funit,'(4i2)') 1, 2, 3, 4
         write(funit,'(4i2)') 1, 5, 8, 4
         write(funit,'(4i2)') 5, 6, 7, 8
         write(funit,'(4i2)') 2, 3, 7, 6
         write(funit,'(4i2)') 4, 8, 7, 3
         write(funit,'(4i2)') 1, 2, 6, 5
         
      else
         call Plot_leaves( tree% child_L, STLNum, funit, which_KDtree )
         call Plot_leaves( tree% child_R, STLNum, funit, which_KDtree )
      end if
        
   end subroutine Plot_leaves
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
! This subroutine creates a new file call KDtree in which the leaves will be stored. 
!  ------------------------------------------------  
   subroutine KDtree_plot( this, lvl )
      use MPI_Process_Info
      implicit none
      !-arguemnts----------------------------------------
      class(KDtree), target,   intent(inout) :: this
      integer,       optional, intent(in)    :: lvl
      !-local-variables----------------------------------
      real(kind=rp)              :: x_g(NDIM)
      character(len=LINE_LENGTH) :: filename, myString
      integer                    :: i, funit

      select case( this% which_KDtree )
         case( POINTS_KDTREE )
          
            if( .not. MPI_Process% isRoot ) return
                        
            if( present(lvl) ) then
               if( lvl .gt. 0 ) then
                  write(myString,'(i100)') lvl
                  filename = 'KDtreeBandPoints_MGlevel'//trim(adjustl(myString))
               else 
                  filename = 'KDTreeBandPoints'
               end if
            else
               filename = 'KDTreeBandPoints'
            end if

         case( TRIANGLES_KDTREE_SAH )
         
            write(myString,'(i100)') MPI_Process% rank
      
            if( MPI_Process% nProcs .eq. 1 ) then
               filename = 'KDTree_'//OBB(this% STLNum)% filename
            else
               filename = 'KDTree_Partition'//trim(adjustl(myString))//'_'//OBB(this% STLNum)% filename
               filename = trim(filename)
            end if
            
      end select
          
      funit = UnusedUnit()
     
      open(funit,file='IBM/'//trim(filename)//'.tec', status='unknown')
         
      write(funit,"(a28)") 'TITLE = "KD-tree"'
      write(funit,"(a25)") 'VARIABLES = "x", "y", "z"'

      call Plot_leaves( this, this% STLNum, funit, this% which_KDtree )

      close(funit)
   
   end subroutine KDtree_plot
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
! This subroutine plot a single block whose index is BlockIndex. if PlotObjs is true
! also the objects belonging to the box are saved and plot 
!  ------------------------------------------------
   subroutine KDtree_plotBlock( this, ObjectsList, STLNum, PlotObjs )
   
      implicit none
      !-arguments---------------------------------------------
      class(KDtree),     intent(inout) :: this
      type(object_type), intent(in)    :: ObjectsList(:)
      integer,           intent(in)    :: STLNum
      logical,           intent(in)    :: PlotObjs
      !-local-variables---------------------------------------
      real(kind=rp)              :: x_g(NDIM)
      character(len=LINE_LENGTH) :: filename, myString
      integer                    :: i,j, funit, index
      
      funit = UnusedUnit()

      write(myString,'(i100)') this% index
      filename = 'block'//trim(adjustl(myString))//'_'//OBB(STLNum)% filename
      filename = trim(filename)
      
      open(funit,file='IBM/'//trim(filename)//'.tec', status='unknown')
 
      write(funit,"(a28)") 'TITLE = "BLOCK"'
      write(funit,"(a25)") 'VARIABLES = "x", "y", "z"'
      
      write(funit,"(a69)") 'ZONE NODES=8, ELEMENTS = 6, DATAPACKING=POINT, ZONETYPE=FETETRAHEDRON'
      do i = 1, 8
         call OBB(STLNum)% ChangeRefFrame( this% vertices(:,i), GLOBAL, x_g )
         write(funit,'(3E13.5)') x_g(1),x_g(2), x_g(3)
      end do 

      write(funit,'(4i2)') 1, 2, 3, 4
      write(funit,'(4i2)') 1, 5, 8, 4
      write(funit,'(4i2)') 5, 6, 7, 8
      write(funit,'(4i2)') 2, 3, 7, 6
      write(funit,'(4i2)') 4, 8, 7, 3
      write(funit,'(4i2)') 1, 2, 6, 5
      
      close(funit)
      
      if( PlotObjs .and. this% NumOfObjs .gt. 0 ) then

        filename = 'Objects'//trim(adjustl(myString))//'_'//OBB(STLNum)% filename
        filename = trim(filename)
      
         open(funit,file='IBM/'//trim(filename)//'.tec', status='unknown')
 
         write(funit,"(a28)") 'TITLE = "ObjsIndx"'
         write(funit,"(a25)") 'VARIABLES = "x", "y", "z"'

         do i = 1, this% NumOfObjs
            write(funit,"(a66)") 'ZONE NODES=3, ELEMENTS = 1, DATAPACKING=POINT, ZONETYPE=FETRIANGLE'
            index = this% ObjsIndeces(i)
            do j = 1, 3
               call OBB(STLNum)% ChangeRefFrame( ObjectsList(index)% vertices(j)% coords, GLOBAL, x_g )
               write(funit,'(3E13.5)') x_g(1),x_g(2), x_g(3)
            end do

            write(funit,'(3i2)') 1, 2, 3 
         end do

         close(funit)
      end if
      
   end subroutine KDtree_plotBlock
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
! This subroutine sets up the starting box of the tree which is called root. 
! It coincides whit the OBB if PointList not present. 
!  ------------------------------------------------   
   subroutine KDtree_SetUpRoot( this, stl, Vertices, PointList )
   
      implicit none
      !-arguments--------------------------------------------
      class(KDtree),              intent(inout) :: this
      type(STLfile),              intent(in)    :: stl
      real(kind=RP),              intent(in)    :: Vertices(:,:)
      type(Point_type), optional, intent(in)    :: PointList(:)
      !-local-variables--------------------------------------
      type(Object_type) :: Obj 
      integer           :: i
      
      this% NumOfObjs = 0
      
      do i = 1, 4
         this% vertices(:,i)   = Vertices(:,i)
         this% vertices(:,i+4) = Vertices(:,i+4)
      end do
      
      select case( this% which_KDtree )
      
         case( POINTS_KDTREE )
         
            allocate( this% ObjectsList(size(PointList)) )
            this% NumOfObjs = size(PointList)
         
         case( TRIANGLES_KDTREE_MEDIAN,TRIANGLES_KDTREE_SAH )
         
            allocate( this% ObjectsList(stl% NumOfObjs) )
         
            associate( Objs => stl% ObjectsList )

            do i = 1, stl% NumOfObjs
               this% ObjectsList(i) = Objs(i)            
            end do

            end associate

            this% NumOfObjs = stl% NumOfObjs
         
      end select
   
   end subroutine KDtree_SetUpRoot
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
! This subroutine builds the KD tree. 
!  ------------------------------------------------   
   subroutine KDtree_construct( this, stl, Vertices, isPlot, Min_n_of_Objs, PointList, lvl )
      use omp_lib
      implicit none
      !-arguments-------------------------------------------------
      class(KDtree),              intent(inout) :: this
      type(STLfile),              intent(in)    :: stl
      real(kind=RP),              intent(in)    :: Vertices(:,:)
      logical,                    intent(in)    :: isPlot
      integer,                    intent(in)    :: Min_n_of_Objs
      integer,          optional, intent(in)    :: lvl
      type(point_type), optional, intent(in)    :: PointList(:)
      !-local-varables--------------------------------------------
      real(kind=rp)                      :: NumOfObjs,        &
                                            NumThreads_RP
      integer,               allocatable :: ObjsIndx(:)
      type(Event),           allocatable :: Events(:,:)
      type(DepthFirst_type), allocatable :: Depth_First(:)
      integer                            :: i, k, NumThreads, &
                                            DepthFirstLevel,  &
                                            NumDepthFirst
      
      if( this% which_KDtree .eq. POINTS_KDTREE ) then
         if( .not. present(PointList) ) then
            print *, "KDtree_construct:: PointList needed for this type of KDtree"
            error stop
         end if
         call this% SetUpRoot( stl, vertices, PointList )   
      else
         call this% SetUpRoot( stl, vertices )
      end if
      
      this% level         = 0
      NumOfObjs           = this% NumOfObjs
      this% Min_n_of_Objs = Min_n_of_Objs
      this% isLast        = .false.
      BoxIndex            = 1
      this% index         = BoxIndex
      
      select case( this% which_KDtree )
         case( POINTS_KDTREE )

            this% axis = FindAxis( this )

            allocate(Events(NDIM,this% NumOfObjs))

            call GetPointsEvents( this, Events, PointList, NumThreads )
      
            depth             = huge(1)
            this% NumThreads  = NumThreads
            NumThreads_RP     = NumThreads 
            DepthFirstLevel   = floor(log(NumThreads_RP)/log(2.0_RP)) 
            NumDepthFirst     = 2.0_RP**(DepthFirstLevel)
            
            allocate(Depth_First(NumThreads),ObjsIndx(this% NumOfObjs))

            call KDtree_buildPoints_BreadthFirst( this, Events, ObjsIndx, DepthFirstLevel, Depth_First )

            deallocate(ObjsIndx)

!$omp parallel shared(this,Depth_First,k) 
!$omp single 
            do k = 1, count( Depth_First(:)% active )
!$omp task firstprivate(k) private(ObjsIndx) 
               allocate(ObjsIndx(this% NumOfObjs))
               call KDtree_buildPoints_DepthFirst( Depth_First(k)% taskTree, Depth_First(k)% taskEvents, ObjsIndx )
               nullify( Depth_First(k)% taskTree )
               deallocate(ObjsIndx)
!$omp end task
            end do
!$omp end single
!$omp end parallel
            deallocate(Depth_First)

         case( TRIANGLES_KDTREE_SAH )

            allocate(Events(NDIM,2*this% NumOfObjs))      

            call GetEvents( this, Events, NumThreads )
          
            depth             = C_1*log(NumOfObjs)/log(2.0_RP) + C_2!1.2 * log2(N) + 2 --> on improving kd tree for ray shooting Havran
            this% NumThreads  = NumThreads
            NumThreads_RP     = NumThreads 
            DepthFirstLevel   = floor(log(NumThreads_RP)/log(2.0_RP)) 
            NumDepthFirst     = 2.0_RP**(DepthFirstLevel)
            
            allocate(Depth_First(NumThreads),ObjsIndx(this% NumOfObjs))

            call KDtree_buildTRIANGLES_BreadthFirst( this, Events, ObjsIndx, DepthFirstLevel, Depth_First )

            deallocate(ObjsIndx)
!$omp parallel shared(this,Depth_First,k) 
!$omp single 
            do k = 1, count( Depth_First(:)% active )
!$omp task firstprivate(k) private(ObjsIndx) 
               allocate(ObjsIndx(this% NumOfObjs))
               call KDtree_buildTRIANGLES_DepthFirst( Depth_First(k)% taskTree, Depth_First(k)% taskEvents, ObjsIndx )
               nullify( Depth_First(k)% taskTree )
               deallocate(ObjsIndx)
!$omp end task
            end do
!$omp end single
!$omp end parallel
            deallocate(Depth_First)

         case( TRIANGLES_KDTREE_MEDIAN )

            this% axis = FindAxis(this)

            allocate(Events(NDIM,2*this% NumOfObjs))      

            call GetEvents( this, Events, NumThreads )
          
            depth             = log(NumOfObjs)/log(2.0_RP) !--> on improving kd tree for ray shooting Havran
            this% NumThreads  = NumThreads
            NumThreads_RP     = NumThreads 
            DepthFirstLevel   = floor(log(NumThreads_RP)/log(2.0_RP)) 
            NumDepthFirst     = 2.0_RP**(DepthFirstLevel)
            
            allocate(Depth_First(NumThreads),ObjsIndx(this% NumOfObjs))

            call KDtree_buildTRIANGLES_BreadthFirst( this, Events, ObjsIndx, DepthFirstLevel, Depth_First )

            deallocate(ObjsIndx)

!$omp parallel shared(this,Depth_First,k) 
!$omp single 
            do k = 1, count( Depth_First(:)% active )
!$omp task firstprivate(k) private(ObjsIndx) 
               allocate(ObjsIndx(this% NumOfObjs))
               call KDtree_buildTRIANGLES_DepthFirst( Depth_First(k)% taskTree, Depth_First(k)% taskEvents, ObjsIndx )
               nullify( Depth_First(k)% taskTree )
               deallocate(ObjsIndx)
!$omp end task
            end do
!$omp end single
!$omp end parallel
            deallocate(Depth_First)
         case default
            print *, 'KD tree type not recognized'
            error stop
      end select  

      if( present(lvl) ) then
         if( isPlot ) call this% plot(lvl)
      else
         if( isPlot ) call this% plot()
      end if

   end subroutine KDtree_construct
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
! This subroutine finds the leaf where Point lies
!  ------------------------------------------------   
   subroutine KDtree_FindLeaf( this, Point, tree, RIGHTCHILD ) 
   
      implicit none
      !-arguments----------------------------------------------
      class(KDtree), target, intent(inout) :: this
      real(kind=rp),         intent(in)    :: Point(:)
      type(KDtree), pointer, intent(inout) :: tree
      logical,               intent(in)    :: RIGHTCHILD
      !-local-variables----------------------------------------
      real(kind=rp) :: vertices(NDIM)
      integer       :: level
   
      tree => this
      
      if( tree% isLast ) return
      
      level = 0
      
      do
         if( RIGHTCHILD ) then
            if(  Point(tree% axis) .lt. tree% SplittingPlane ) then !lt
               tree => tree% child_L            
            elseif( Point(tree% axis) .ge. tree% SplittingPlane ) then
               tree => tree% child_R
            end if
         else
            if(  Point(tree% axis) .le. tree% SplittingPlane ) then !lt
               tree => tree% child_L            
            elseif( Point(tree% axis) .gt. tree% SplittingPlane ) then
               tree => tree% child_R
            end if      
         endif       
         if( tree% isLast ) return
      end do
      
   end subroutine KDtree_FindLeaf

   recursive subroutine KD_treeDestruct( this, isChild ) 
   
      implicit none
      !-arguments-----------------------------
      class(KDtree), intent(inout) :: this
      logical,       intent(in)    :: isChild
      !-local-variables-----------------------
      integer :: i, stat       
         
      if( allocated(this% ObjectsList) ) then
         if( this% which_KDtree .ne.  POINTS_KDTREE ) then 
            do i = 1, this% NumOfObjs
               deallocate(this% ObjectsList(i)% vertices)
            end do
         end if
         deallocate(this% ObjectsList)      
      end if

      if( allocated(this% ObjsIndeces) ) deallocate(this% ObjsIndeces)
      if( allocated(this% Events) )      deallocate(this% Events)
      
      if( this% built_L .and. .not. this% isLast ) call this% child_L% destruct( isChild )     
      if( this% built_R .and. .not. this% isLast ) call this% child_R% destruct( isChild ) 

      if( this% built_L ) deallocate(this% child_L) 
      if( this% built_R ) deallocate(this% child_R) 
      nullify( this% parent )

   end subroutine KD_treeDestruct
   
   subroutine KD_treeGetArea( tree )
   
      implicit none
      !-arguments---------------------------
      class(KDtree), intent(inout) :: tree
      !-local-variables---------------------
      real(kind=rp) :: L_x, L_y, L_z
      
      L_x = abs(tree% vertices(1,7)-tree% vertices(1,1))
      L_y = abs(tree% vertices(2,7)-tree% vertices(2,1))
      L_z = abs(tree% vertices(3,7)-tree% vertices(3,1))
      
      tree% S = 2.0_RP*(L_x*L_y + L_x*L_z + L_y*L_z)
      
   end subroutine KD_treeGetArea
   
   subroutine GetEvents( tree, Events, NumThreads )
      use omp_lib
      implicit none
      !-arguments---------------------------------
      type(KDtree), intent(inout) :: tree
      type(Event),  intent(inout) :: Events(:,:)
      integer,      intent(out)   :: NumThreads
      !-local-variables---------------------------
      integer :: k
!$omp parallel shared(tree,Events,k)
!$omp single
#ifdef _OPENMP
            NumThreads = omp_get_num_threads()
#else
            NumThreads = 1
#endif
            do k = 1, NDIM
!$omp task firstprivate(k)
               call Event_Construct( Events(k,:), tree% ObjectsList, tree, k ) 
               call QsortEvents( Events(k,:), 1, tree% NumOfEvents(k) )  
!$omp end task 
            end do           
!$omp end single
!$omp end parallel
   end subroutine GetEvents
   
   subroutine GetPointsEvents( tree, Events, PointList, NumThreads )
      use omp_lib
      implicit none
      !-arguments-------------------------------------
      type(KDtree),     intent(inout) :: tree
      type(Event),      intent(inout) :: Events(:,:)
      type(point_type), intent(in)    :: PointList(:)
      integer,          intent(out)   :: NumThreads
      !-local-variables-------------------------------
      integer :: k
!$omp parallel shared(tree,Events,k,PointList)
!$omp single
#ifdef _OPENMP
           NumThreads = omp_get_num_threads()
#else
           NumThreads = 1
#endif
            do k = 1, NDIM
!$omp task firstprivate(k)
               call Points_Event_Construct( Events(k,:), tree, k, PointList )
               call QsortEvents( Events(k,:), 1, tree% NumOfEvents(k) )
!$omp end task
            end do        
!$omp end single
!$omp end parallel
   end subroutine GetPointsEvents
   
   recursive subroutine KDtree_buildTRIANGLES_BreadthFirst( this, Events, ObjsIndx, level, Depth_First )
   
      implicit none
      !-arguments------------------------------------------------
      type(KDtree), target,      intent(inout) :: this
      type(Event),  allocatable, intent(inout) :: Events(:,:)
      integer,                   intent(in)    :: level
      integer,                   intent(inout) :: ObjsIndx(:) 
      type(DepthFirst_type),     intent(inout) :: Depth_First(:)
      !-local-variables------------------------------------------
      type(KDtree), pointer     :: child_L, child_R 
      type(Event),  allocatable :: Events_L(:,:), Events_R(:,:)
      integer                   :: j

      call this% GetArea()

      BoxIndex = BoxIndex + 1

      this% index  = BoxIndex
 
      if( this% level .lt. level .and. this% NumOfObjs .gt. 0 ) then
 
         if( this% which_KDtree .eq. TRIANGLES_KDTREE_SAH ) then
            call this% EvaluateCostSAH( Events )
         else
            call this% EvaluateCostMEDIAN( Events )
         end if

         if( this% split  ) then 

            allocate(this% child_L,this% child_R)
                    
            child_L => this% child_L
            child_R => this% child_R
            
            child_L% parent => this
            child_R% parent => this
           
            child_L% level = this% level + 1
            child_R% level = this% level + 1
            
            this% built_L = .true.
            this% built_R = .true.

            this% isLast = .false.   

            if( this% which_KDtree .eq. TRIANGLES_KDTREE_MEDIAN ) then
               child_L% axis = AxisIndex( this% axis )
               child_R% axis = AxisIndex( this% axis )
            endif

            call this% BuildChild( child_L, side_L )
            call this% BuildChild( child_R, side_R )
         
            child_L% Min_n_of_Objs = this% Min_n_of_Objs
            child_R% Min_n_of_Objs = this% Min_n_of_Objs
            child_L% NumThreads    = this% NumThreads
            child_R% NumThreads    = this% NumThreads
            child_L% which_KDtree  = this% which_KDtree
            child_R% which_KDtree  = this% which_KDtree

            call Event_ClassifyObjs( Events(this% axis,:), this, child_L, child_R, ObjsIndx )

            allocate(Events_L(NDIM,2*child_L% NumOfObjs))
            allocate(Events_R(NDIM,2*child_R% NumOfObjs))

            call Event_BuildLists( Events, this, child_L, child_R, ObjsIndx, Events_L, Events_R )

            deallocate(Events)
            
            call KDtree_buildTRIANGLES_BreadthFirst( child_L, Events_L, ObjsIndx, level, Depth_First )
            call KDtree_buildTRIANGLES_BreadthFirst( child_R, Events_R, ObjsIndx, level, Depth_First )
            
            nullify(child_L,child_R)

         else
            this% isLast = .false.
            if( this% level .lt. level ) this% isLast = .true.
            do j = 1, size(Depth_First)
               if( .not. Depth_First(j)% active ) then 
                  Depth_First(j)% taskTree => this
                  allocate(Depth_First(j)% taskEvents(NDIM,2*this% NumOfObjs+1))
                  Depth_First(j)% taskEvents = Events
                  Depth_First(j)% active = .true.
                  deallocate(Events)
                  exit
               end if 
            end do  
         end if
      else
         this% isLast = .false.
         if( this% NumOfObjs .eq. 0 ) this% isLast = .true.
         do j = 1, size(Depth_First)
            if( .not. Depth_First(j)% active ) then 
               Depth_First(j)% taskTree => this
               allocate(Depth_First(j)% taskEvents(NDIM,2*this% NumOfObjs))               
               Depth_First(j)% taskEvents = Events
               Depth_First(j)% active = .true.
               deallocate(Events)
               exit
            end if
         end do
      end if
     
      if( allocated(Events) ) deallocate( Events )

   end subroutine KDtree_buildTRIANGLES_BreadthFirst
   
   
   recursive subroutine KDtree_buildTRIANGLES_DepthFirst( this, Events, ObjsIndx )
      use omp_lib
      implicit none
      !-arguments----------------------------------------------------------
      type(KDtree), target,      intent(inout) :: this
      type(Event),  allocatable, intent(inout) :: Events(:,:)
      integer,                   intent(inout) :: ObjsIndx(:)
      !-local-variables---------------------------------------------------
      type(KDtree), pointer     :: child_L, child_R 
      type(Event),  allocatable :: Events_L(:,:), Events_R(:,:)

      call this% GetArea()
!$omp critical
      BoxIndex = BoxIndex + 1
!$omp end critical
      this% index  = BoxIndex

      if( this% level .lt. depth .and. this% NumOfObjs .ge. this% Min_n_of_Objs ) then
         
         if( this% which_KDtree .eq. TRIANGLES_KDTREE_SAH ) then
            call this% EvaluateCostSAH( Events )
         else
            call this% EvaluateCostMEDIAN( Events )
         end if

         if( this% split ) then 

            allocate(this% child_L,this% child_R)
        
            child_L => this% child_L
            child_R => this% child_R
            
            child_L% parent => this
            child_R% parent => this
           
            child_L% level = this% level + 1
            child_R% level = this% level + 1

            this% built_L = .true.
            this% built_R = .true.

            this% isLast = .false.   

            if( this% which_KDtree .eq. TRIANGLES_KDTREE_MEDIAN ) then
               child_L% axis = AxisIndex( this% axis )
               child_R% axis = AxisIndex( this% axis )
            endif

            call this% BuildChild( child_L, side_L )
            call this% BuildChild( child_R, side_R )
         
            child_L% Min_n_of_Objs = this% Min_n_of_Objs
            child_R% Min_n_of_Objs = this% Min_n_of_Objs
            child_L% NumThreads    = this% NumThreads
            child_R% NumThreads    = this% NumThreads
            child_L% which_KDtree  = this% which_KDtree
            child_R% which_KDtree  = this% which_KDtree

            call Event_ClassifyObjs( Events(this% axis,:), this, child_L, child_R, ObjsIndx )

            allocate(Events_L(NDIM,2*child_L% NumOfObjs))
            allocate(Events_R(NDIM,2*child_R% NumOfObjs))

            call Event_BuildLists( Events, this, child_L, child_R, ObjsIndx, Events_L, Events_R )

            deallocate(Events)
  
            call KDtree_buildTRIANGLES_DepthFirst( child_L, Events_L, ObjsIndx )
            call KDtree_buildTRIANGLES_DepthFirst( child_R, Events_R, ObjsIndx )
 
            nullify(child_L,child_R)

         else
            this% isLast = .true.
            call this% SaveObjsIndeces( Events )          
            deallocate(Events)
         end if
      else
         this% isLast = .true.
         if( this% NumOfObjs .gt. 0 ) then
            call this% SaveObjsIndeces( Events )     
            deallocate(Events)
         end if

      end if

      if( allocated(Events) ) deallocate(Events)

   end subroutine KDtree_buildTRIANGLES_DepthFirst
   
   subroutine ComputeSplitSurface_L(tree, SplittingPlane, axis, S_L)
   
      implicit none
      !-arguments-------------------------------------
      type(KDtree),  intent(in)  :: tree
      real(kind=rp), intent(in)  :: SplittingPlane
      integer,       intent(in)  :: axis
      real(kind=rp), intent(out) :: S_L
      !-local-variables-------------------------------
      real(kind=rp) :: L_x, L_y, L_z
   
      select case( axis )
         case(IX)
            L_x = abs(SplittingPlane-tree% vertices(1,1))
            L_y = abs(tree% vertices(2,7)-tree% vertices(2,1))
            L_z = abs(tree% vertices(3,7)-tree% vertices(3,1))    
         case(IY)
            L_x = abs(tree% vertices(1,7)-tree% vertices(1,1))
            L_y = abs(SplittingPlane-tree% vertices(2,1))
            L_z = abs(tree% vertices(3,7)-tree% vertices(3,1))
         case(IZ)
            L_x = abs(tree% vertices(1,7)-tree% vertices(1,1))
            L_y = abs(tree% vertices(2,7)-tree% vertices(2,1))
            L_z = abs(SplittingPlane-tree% vertices(3,1))
      end select

      S_L = 2.0_RP*(L_x*L_y+L_y*L_z+L_x*L_z)
   
   end subroutine ComputeSplitSurface_L
   
   subroutine ComputeSplitSurface_R(tree, SplittingPlane, axis, S_R)
   
      implicit none
      !-arguments------------------------------------
      type(KDtree),  intent(in)  :: tree
      real(kind=rp), intent(in)  :: SplittingPlane
      integer,       intent(in)  :: axis
      real(kind=rp), intent(out) :: S_R
      !-local-variables------------------------------
      real(kind=rp) :: L_x, L_y, L_z
   
      select case( axis )
         case(IX)
            L_x = abs(tree% vertices(1,7)-SplittingPlane)
            L_y = abs(tree% vertices(2,7)-tree% vertices(2,1))
            L_z = abs(tree% vertices(3,7)-tree% vertices(3,1))    
         case(IY)
            L_x = abs(tree% vertices(1,7)-tree% vertices(1,1))
            L_y = abs(tree% vertices(2,7)-SplittingPlane)
            L_z = abs(tree% vertices(3,7)-tree% vertices(3,1))
         case(IZ)
            L_x = abs(tree% vertices(1,7)-tree% vertices(1,1))
            L_y = abs(tree% vertices(2,7)-tree% vertices(2,1))
            L_z = abs(tree% vertices(3,7)-SplittingPlane)
      end select

      S_R = 2.0_RP*(L_x*L_y+L_y*L_z+L_x*L_z)
   
   end subroutine ComputeSplitSurface_R
   
   real(kind=rp) function computeSplittingCost(S, S_L, S_R, N_L, N_R) result(SplittingCost)
   
      implicit none
      !-arguments------------------------------
      real(kind=rp), intent(in) :: S, S_L, S_R
      integer,       intent(in) :: N_L, N_R
     
      SplittingCost = C_TRANSVERSE + C_INTERSECT*(S_L/S*N_L+S_R/S*N_R)
   
   end function computeSplittingCost
   
   subroutine KDtree_EvaluateCostSAH( this, Events )
      use omp_lib
      implicit none
      !-arguments----------------------------------------------------------------------------
      class(KDtree), intent(inout) :: this
      type(Event),   intent(in)    :: Events(:,:)
      !-local-variables----------------------------------------------------------------------
      integer                          :: i, k, N_L(NDIM), N_R(NDIM), N_P(NDIM), &
                                          BestSide(1), pminus, pplus, pvert, axis
      real(kind=rp)                    :: SplittingCost(2), S_L, S_R, SplittingPlane

      this% split     = .false.
      this% SplitCost = C_INTERSECT * this% NumOfObjs
      
      if( this% NumOfObjs .eq. 0 ) return
      
      N_R = this% NumOfObjs; N_L = 0; N_P = 0
      this% split = .false.
      this% SplitCost = C_INTERSECT * this% NumOfObjs      
      
      do k = 1, NDIM
         i = 1
         do while ( i .le. this% NumOfEvents(k) )
            pplus = 0; pminus = 0; pvert = 0
            SplittingPlane = Events(k,i)% plane

            if( Events(k,i)% eType .lt. 0 ) exit

            if( i .le. this% NumOfEvents(k) ) then
               do while( Events(k,i)% plane .eq. SplittingPlane .and. Events(k,i)% eType .eq. END_ )
                  pminus = pminus + 1
                  i = i + 1
                  if( i .gt. this% NumOfEvents(k) ) exit
               end do
            end if
        
            if( i .le. this% NumOfEvents(k) ) then
               do while( Events(k,i)% plane .eq. SplittingPlane .and. Events(k,i)% eType .eq. PLANAR_ )
                  pvert = pvert + 1
                  i = i + 1
                  if( i .gt. this% NumOfEvents(k) ) exit
               end do
            end if
            
            if( i .le. this% NumOfEvents(k) ) then
               do while( Events(k,i)% plane .eq. SplittingPlane .and. Events(k,i)% eType .eq. START_ )                                    
                  pplus = pplus + 1
                  i = i + 1
                  if( i .gt. this% NumOfEvents(k) ) exit
               end do   
            end if

            N_R(k) = N_R(k) - pminus - pvert
   
            call ComputeSplitSurface_L(this, SplittingPlane, k, S_L)
            call ComputeSplitSurface_R(this, SplittingPlane, k, S_R)

            SplittingCost(1) = computeSplittingCost( this% S, S_L, S_R, N_L(k)+N_P(k), N_R(k) )
            SplittingCost(2) = computeSplittingCost( this% S, S_L, S_R, N_L(k), N_R(k)+N_P(k) )
               
            BestSide = minloc(SplittingCost) 

            if( this% SplitCost .gt. SplittingCost(BestSide(1)) ) then
               this% SplittingPlane = SplittingPlane
               this% SplitCost = SplittingCost(BestSide(1))
               this% split = .true.
               this% axis  = k
               this% SIDE  = BestSide(1) 
            end if
            N_L(k) = N_L(k) + pplus + pvert
            N_P(k) = pvert

         end do
      end do
      
   end subroutine KDtree_EvaluateCostSAH

   subroutine KDtree_EvaluateCostMEDIAN( this, Events )
      use omp_lib
      implicit none
      !-arguments----------------------------------
      class(KDtree), intent(inout) :: this
      type(Event),   intent(in)    :: Events(:,:)
      
      this% SplittingPlane = sum(Events(this% axis,:)% median)/this% NumOfEvents(this% axis)
      this% split = .true.

   end subroutine KDtree_EvaluateCostMEDIAN
   
   subroutine Event_ClassifyObjs( this, tree, child_L, child_R, ObjsIndx )

      implicit none
      !-arguments----------------------------------
      type(Event),  intent(in)    :: this(:)
      type(KDtree), intent(inout) :: tree
      type(KDtree), intent(inout) :: child_L, child_R
      integer,      intent(inout) :: ObjsIndx(:)
      !-local-variables----------------------------------
      integer :: i, N_B, N_L, N_R

      ObjsIndx = BOTH
      
      N_B = tree% NumOfObjs; N_L = 0; N_R = 0
           
      do i = 1, tree% NumOfEvents(tree% axis)
         if( this(i)% eType .eq. END_ .and. &
            (this(i)% plane .lt. tree% SplittingPlane .or. this(i)% plane .eq. tree% SplittingPlane) ) then         
            ObjsIndx(this(i)% index) = ONLYLEFT   
            N_L = N_L + 1
         elseif( this(i)% eType .eq. START_ .and. &
               (this(i)% plane .gt. tree% SplittingPlane .or. this(i)% plane .eq. tree% SplittingPlane) ) then 
            ObjsIndx(this(i)% index) = ONLYRIGHT 
            N_R = N_R + 1
         elseif( this(i)% eType .eq. PLANAR_ ) then
            if( this(i)% plane .lt. tree% SplittingPlane .or. &
                (this(i)% plane .eq. tree% SplittingPlane .and. tree% SIDE .eq. 1) ) then
               ObjsIndx(this(i)% index) = ONLYLEFT
               N_L = N_L + 1
            elseif( this(i)% plane .gt. tree% SplittingPlane .or. &
                    (this(i)% plane .eq. tree% SplittingPlane .and. tree% SIDE .eq. 2) ) then 
               ObjsIndx(this(i)% index) = ONLYRIGHT
               N_R = N_R + 1
            end if   
         end if
      end do
      
      N_B = N_B - N_L - N_R
      
      tree% N_L = N_L; tree% N_R = N_R; tree% N_B = N_B
      child_L% NumOfObjs = N_B+N_L 
      child_R% NumOfObjs = N_B+N_R 
      
   end subroutine Event_ClassifyObjs
   
   subroutine KDtree_BuildChild( this, child, side )
   
      implicit none
      !-arguments----------------------------
      class(KDtree), intent(inout) :: this
      type(KDtree),  intent(inout) :: child
      integer,       intent(in)    :: side
      !-local-variables----------------------
      integer :: i                                        
   
      child% vertices = this% vertices
      child% isLast = .false.

      select case( this% axis ) 
         case( IX ) 
            do i = 1, 4
               child% vertices(1,vertices_x(side+i)) = this% SplittingPlane
            end do
         case( IY ) 
            do i = 1, 4
               child% vertices(2,vertices_y(side+i)) = this% SplittingPlane
            end do
         case( IZ ) 
            do i = 1, 4
               child% vertices(3,vertices_z(side+i)) = this% SplittingPlane
            end do
      end select
   
   end subroutine KDtree_BuildChild

   subroutine Event_BuildLists( this, tree, child_L, child_R, ObjsIndx, Events_L, Events_R )
      use omp_lib
      implicit none
      !-arguments---------------------------------------------------------------------------------
      type(Event),  intent(in)    :: this(:,:)
      type(KDtree), intent(in)    :: tree
      type(KDtree), intent(inout) :: child_L, child_R
      integer,      intent(inout) :: ObjsIndx(:)
      type(Event),  intent(inout) :: Events_L(:,:), Events_R(:,:)
      !-local-variables---------------------------------------------------------------------------
      integer                          :: i, k, B, L, R

      child_L% NumOfEvents = 0
      child_R% NumOfEvents = 0
      
      do k = 1, NDIM
         L = 0; R = 0
         do i = 1, tree% NumOfEvents(k)
            if( ObjsIndx(this(k,i)% index) .eq. ONLYLEFT ) then 
               L = L + 1
               Events_L(k,L) = this(k,i)
               child_L% NumOfEvents(k) = child_L% NumOfEvents(k) + 1
            elseif( ObjsIndx(this(k,i)% index) .eq. ONLYRIGHT ) then
               R = R + 1
               Events_R(k,R) = this(k,i)
               child_R% NumOfEvents(k) = child_R% NumOfEvents(k) + 1
            elseif( ObjsIndx(this(k,i)% index) .eq. BOTH ) then
               L = L + 1
               R = R + 1
               Events_L(k,L) = this(k,i)
               Events_R(k,R) = this(k,i)
               child_L% NumOfEvents(k) = child_L% NumOfEvents(k) + 1
               child_R% NumOfEvents(k) = child_R% NumOfEvents(k) + 1     
            end if
         end do 
      end do

   end subroutine Event_BuildLists
   
   subroutine KDtree_SaveObjsIndeces( this, Events )
   
      implicit none
      !-arguments----------------------------------
      class(KDtree), intent(inout) :: this
      type(Event),   intent(in)    :: Events(:,:)
      !-local-variables----------------------------
      integer :: i, k
      
      allocate(this% ObjsIndeces(this% NumOfObjs))
      
      k = 1
      
      do i = 1, this% NumOfEvents(1)
         if( Events(1,i)% eType .eq. START_ .or. &
             Events(1,i)% eType .eq. PLANAR_      ) then
            this% ObjsIndeces(k) = Events(1,i)% index       
            k = k + 1
            if( k .gt. this% NumOfObjs ) exit
         end if
      end do
   
   end subroutine KDtree_SaveObjsIndeces
   
   subroutine Event_Construct( this, ObjectsList, tree, axis )
    
      implicit none
      !-arguments------------------------------------------
      type(Event),       intent(inout) :: this(:)
      type(object_type), intent(in)    :: ObjectsList(:)
      type(KDtree),      intent(inout) :: tree
      integer,           intent(in)    :: axis
      !-local-variables------------------------------------
      integer :: i, j, k, shift

      tree% NumOfEvents(axis) = 0

      do i =1, tree% NumOfObjs
         this(i)% index = ObjectsList(i)% index 
         this(i+tree% NumOfObjs)% index = ObjectsList(i)% index 
     
         this(i)% plane = minval(ObjectsList(i)% vertices(1:NumOfVertices)% coords(axis)) 
         this(i+tree% NumOfObjs)% plane = maxval(ObjectsList(i)% vertices(1:NumOfVertices)% coords(axis)) 

         if( almostEqual(this(i)% plane,this(i+tree% NumOfObjs)% plane) ) then
            this(i)% eType = PLANAR_
            !pushed to the end
            this(i+tree% NumOfObjs)% plane = huge(1.0_RP)
            this(i+tree% NumOfObjs)% eType = -1
            tree% NumOfEvents(axis) = tree% NumOfEvents(axis) + 2           
         else
            this(i)% eType = START_
            this(i+tree% NumOfObjs)% eType = END_
            tree% NumOfEvents(axis) = tree% NumOfEvents(axis) + 2
         end if
         this(i)% median = sum(ObjectsList(i)% vertices(1:NumOfVertices)% coords(axis))/NumOfVertices
         this(i+tree% NumOfObjs)% median = this(i)% median
      end do
      
   end subroutine Event_Construct
   
!////////////////// SUBROUTINES FOR KD TREE made of points ///////////////////////
   
   recursive subroutine KDtree_buildPoints_BreadthFirst( this, Events, ObjsIndx, level, Depth_First )
   
      implicit none
      !-arguments---------------------------------------------------
      type(KDtree), target,      intent(inout) :: this
      type(Event),  allocatable, intent(inout) :: Events(:,:)
      integer,                   intent(in)    :: level
      integer,                   intent(inout) :: ObjsIndx(:)
      type(DepthFirst_type),     intent(inout) :: Depth_First(:)
      !-local-variables---------------------------------------------
      type(KDtree), pointer     :: child_L, child_R 
      type(Event),  allocatable :: Events_L(:,:), Events_R(:,:)
      integer                   :: j

      BoxIndex = BoxIndex + 1

      this% index  = BoxIndex
      this% NumOfEvents(:)  = this% NumOfObjs

      if( this% level .lt. level .and. this% NumOfObjs .gt. 0 ) then

         if( mod(this% NumOfObjs,2) .eq. 0 ) then
            this% HalfEvents = this% NumOfObjs/2
            this% SIDE = EVEN
         else
            this% HalfEvents = this% NumOfObjs/2
            this% HalfEvents = this% HalfEvents+1
            this% SIDE = ODD
         end if

         this% SplittingPlane = Events(this% axis,this% HalfEvents)% plane
         
         allocate(this% child_L,this% child_R)
        
         child_L => this% child_L
         child_R => this% child_R
           
         child_L% level = this% level + 1
         child_R% level = this% level + 1

         this% built_L = .true.
         this% built_R = .true.

         this% isLast = .false.   

         call this% BuildChild( child_L, side_L )
         call this% BuildChild( child_R, side_R )
         
         child_L% Min_n_of_Objs = this% Min_n_of_Objs
         child_R% Min_n_of_Objs = this% Min_n_of_Objs
         child_L% NumThreads = this% NumThreads
         child_R% NumThreads = this% NumThreads

         child_L% axis = AxisIndex( this% axis )
         child_R% axis = AxisIndex( this% axis )

         call Points_Event_ClassifyObjs( Events(this% axis,:), this, child_L, child_R, ObjsIndx )

         allocate(Events_L(NDIM,child_L% NumOfObjs))
         allocate(Events_R(NDIM,child_R% NumOfObjs))

         call Event_BuildLists( Events, this, child_L, child_R, ObjsIndx, Events_L, Events_R )

         deallocate(Events)
            
         call KDtree_buildPoints_BreadthFirst( child_L, Events_L, ObjsIndx, level, Depth_First )
         call KDtree_buildPoints_BreadthFirst( child_R, Events_R, ObjsIndx, level, Depth_First )

      else
         this% isLast = .false.
         if( this% NumOfObjs .eq. 0  ) this% isLast = .true.
         do j = 1, size(Depth_First)
            if( .not. Depth_First(j)% active ) then 
               Depth_First(j)% taskTree => this
               allocate(Depth_First(j)% taskEvents(NDIM,this% NumOfObjs))
               Depth_First(j)% taskEvents = Events
               Depth_First(j)% active = .true.
               deallocate(Events)
               exit
            end if
         end do
      end if
      
      if( allocated( Events ) ) deallocate( Events )
   
   end subroutine KDtree_buildPoints_BreadthFirst
   
   recursive subroutine KDtree_buildPoints_DepthFirst( this, Events, ObjsIndx )
      use omp_lib
      implicit none
      !-arguments------------------------------------------------
      type(KDtree), target,      intent(inout) :: this
      type(Event),  allocatable, intent(inout) :: Events(:,:)
      integer,                   intent(inout) :: ObjsIndx(:)
      !-local-variables------------------------------------------
      type(KDtree), pointer     :: child_L, child_R 
      type(Event),  allocatable :: Events_L(:,:), Events_R(:,:)
!$omp critical
      BoxIndex = BoxIndex + 1
!$omp end critical
      this% index  = BoxIndex
      this% NumOfEvents(:)  = this% NumOfObjs
 
      if( this% level .lt. depth .and. this% NumOfObjs .ge. this% Min_n_of_Objs ) then

         if( mod(this% NumOfObjs,2) .eq. 0 ) then
            this% HalfEvents = this% NumOfObjs/2
            this% SIDE = EVEN
         else
            this% HalfEvents = this% NumOfObjs/2
            this% HalfEvents = this% HalfEvents+1
            this% SIDE = ODD
         end if

         this% SplittingPlane = Events(this% axis,this% HalfEvents)% plane
         
         allocate(this% child_L,this% child_R)
        
         child_L => this% child_L
         child_R => this% child_R
           
         child_L% level = this% level + 1
         child_R% level = this% level + 1

         this% built_L = .true.
         this% built_R = .true.

         this% isLast = .false.   

         call this% BuildChild( child_L, side_L )
         call this% BuildChild( child_R, side_R )
         
         child_L% Min_n_of_Objs = this% Min_n_of_Objs
         child_R% Min_n_of_Objs = this% Min_n_of_Objs
         child_L% NumThreads = this% NumThreads
         child_R% NumThreads = this% NumThreads
         
         child_L% axis = AxisIndex( this% axis )
         child_R% axis = AxisIndex( this% axis )

         call Points_Event_ClassifyObjs( Events(this% axis,:), this, child_L, child_R, ObjsIndx )

         allocate(Events_L(NDIM,child_L% NumOfObjs))
         allocate(Events_R(NDIM,child_R% NumOfObjs))

         call Event_BuildLists( Events, this, child_L, child_R, ObjsIndx, Events_L, Events_R )

         deallocate(Events)

         call KDtree_buildPoints_DepthFirst( child_L, Events_L, ObjsIndx )
         call KDtree_buildPoints_DepthFirst( child_R, Events_R, ObjsIndx )
            
      else
         this% isLast = .true.
         if( this% NumOfObjs .gt. 0 ) then
            call this% SavePointsIndeces(Events)     
            deallocate(Events)
         end if
      end if

      if( allocated( Events ) ) deallocate( Events )

   end subroutine KDtree_buildPoints_DepthFirst
   
   subroutine KDtree_SavePointsIndeces( this, Events )
   
      implicit none
      !-arguments----------------------------------
      class(KDtree), intent(inout) :: this
      type(Event),   intent(in)    :: Events(:,:)
      !-local-variables----------------------------
      integer :: i, k
      
      allocate(this% ObjsIndeces(this% NumOfObjs))
   
      do i = 1, this% NumOfObjs
         this% ObjsIndeces(i) = Events(1,i)% index
      end do   
   
   end subroutine KDtree_SavePointsIndeces
   
   subroutine Points_Event_ClassifyObjs( this, tree, child_L, child_R, ObjsIndx )
      use omp_lib
      implicit none
      !-arguments----------------------------------------
      type(Event),  intent(in)    :: this(:)
      type(KDtree), intent(inout) :: tree
      type(KDtree), intent(inout) :: child_L, child_R
      integer,      intent(inout) :: ObjsIndx(:)
      !-local-variables----------------------------------
      integer :: i, N_B, N_L, N_R
      
      N_L = 0; N_R = 0; N_B = 0
      
      do i = 1, tree% NumOfObjs
         if( i .lt. tree% HalfEvents ) then        
             ObjsIndx(this(i)% index) = ONLYLEFT   
             N_L = N_L + 1
         elseif( i .gt. tree% HalfEvents ) then 
            ObjsIndx(this(i)% index) = ONLYRIGHT 
            N_R = N_R + 1   
         elseif( i .eq. tree% HalfEvents ) then
            if( tree% SIDE .eq. ODD ) then
               ObjsIndx(this(i)% index) = BOTH 
               N_B = N_B + 1 
            else
               ObjsIndx(this(i)% index) = ONLYLEFT 
               N_L = N_L + 1
            end if
         end if
      end do
            
      tree% N_L = N_L; tree% N_R = N_R; tree% N_B = N_B
      child_L% NumOfObjs = N_L + N_B
      child_R% NumOfObjs = N_R + N_B
      
   end subroutine Points_Event_ClassifyObjs
   
   subroutine Points_Event_construct(this, tree, axis, PointList)
      use MPI_IBMUtilities
      implicit none
      !-arguments--------------------------------------
      type(Event),      intent(inout) :: this(:)
      type(KDtree),     intent(inout) :: tree
      integer,          intent(in)    :: axis
      type(point_type), intent(in)    :: PointList(:)
      !-local-varibales--------------------------------
      integer :: i
      
      tree% NumOfEvents = tree% NumOfObjs
      
      do i = 1, tree% NumOfObjs !no planar evetns thus NumOfObjs = NumOfEvents
         this(i)% index = PointList(i)% index
         this(i)% plane = PointList(i)% coords(axis)
         this(i)% eType = START_
      end  do
   
   end subroutine Points_Event_construct
   
!/////////////////////////////// EVENTS SORTING ///////////////////////

   recursive subroutine QsortEvents( Events, startIdx, endIdx )
  
      implicit none
      !-arguments-------------------------------------
      type(Event), intent(inout) :: Events(:)
      integer,     intent(in)    :: startIdx, endIdx
      !-local-variables-------------------------------
      type(Event) :: pivot
      integer     :: left, right, mid
     
      if( startIdx .gt. endIdx ) return
     
      left = startIdx; right = endIdx
     
      mid = (startIdx + endIdx)/2
     
      pivot = Events(mid)
     
      do while ( left < right )
         do while( EventIsLess(Events(left),pivot) )
            left = left + 1
         end do
         do while( EventIsLess(pivot,Events(right)) )
            right = right - 1
         end do
         if( left .le. right ) then
            call swapEvents(Events(left), Events(right))
            left = left + 1
            right = right - 1
         end if
      end do
     
      if( startIdx .lt. right ) call QsortEvents( Events, startIdx, right )
      if( left .lt. endIdx  ) call QsortEvents( Events, left, endIdx )
 
   end subroutine QsortEvents

   subroutine swapEvents( EventA, EventB )
  
      implicit none
      !-arguments------------------------------------
      type(Event), intent(inout) :: EventA, EventB
      !-local-variables------------------------------
      type(Event) :: temp
  
      temp = EventA
      EventA = EventB
      EventB = temp
  
   end subroutine swapEvents
  
   logical function EventIsLess( EventA, EventB ) result( IsLess )
  
     implicit none
     !-arguments----------------------------------
     type(Event), intent(in) :: EventA, EventB
     
      if( EventA% plane .eq. EventB% plane ) then
         if( EventA% eTYPE .lt. EventB% eTYPE ) then
            IsLess = .true.
         else
            IsLess = .false.
         end if
      elseif( EventA% plane < EventB% plane ) then
         IsLess = .true.
      else
         IsLess = .false.
      end if
  
   end function EventIsLess
   
   logical function TrinagleIntersectBox( vertices, triangle ) result( inside )
      use MappedGeometryClass
      implicit none 

      real(kind=rp),     intent(in) :: vertices(:,:)
      type(object_type), intent(in) :: Triangle

      real(kind=rp) :: center(NDIM), halfL(NDIM), trivert0(NDIM), trivert1(NDIM), trivert2(NDIM), &
                       v0(NDIM), v1(NDIM), v2(NDIM), e0(NDIM), e1(NDIM), e2(NDIM),                &
                       f0(NDIM), f1(NDIM), f2(NDIM), a00(NDIM), a01(NDIM), a02(NDIM),             &
                       a10(NDIM), a11(NDIM), a12(NDIM), a20(NDIM), a21(NDIM), a22(NDIM),          &
                       a23(NDIM), p0, p1, p2, r, plane_normal(NDIM), plane_distance, Bmin(NDIM),  &
                       Bmax(NDIM)
      integer       :: i


      do i = 1, NDIM 
         Bmin(i)   = minval(vertices(1,:))
         Bmax(i)   = maxval(vertices(1,:))
         center(i) = 0.5_RP*(Bmax(i)+Bmin(i))
         halfL(i)  = 0.5_RP*abs(Bmax(i)-Bmin(i))
      end do 

      trivert0 = triangle% vertices(1)% coords
      trivert1 = triangle% vertices(2)% coords
      trivert2 = triangle% vertices(3)% coords

      if( isInsideBox( trivert0, vertices ) .and. &
          isInsideBox( trivert1, vertices ) .and. &
          isInsideBox( trivert2, vertices ) ) then 
         inside = .true.
         return 
      end if 

      v0 = trivert0 - center;
      v1 = trivert1 - center;
      v2 = trivert2 - center;

      e0 = v1 - v0;
      e1 = v2 - v1;
      e2 = v0 - v2;
  
      f0 = trivert1 - trivert0
      f1 = trivert2 - trivert1
      f2 = trivert0 - trivert2
  
      a00 = (/ 0.0_RP, -f0(3), f0(2) /)
      p0 = dot_product(v0, a00)
      p1 = dot_product(v1, a00)
      p2 = dot_product(v2, a00)
      r = halfL(2) * abs(f0(3)) + halfL(3) * abs(f0(2))
      if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r) then 
         inside = .false.
         return 
      end if 
  
      a01 = (/ 0.0_RP, -f1(3), f1(2) /)
      p0 = dot_product(v0, a01)
      p1 = dot_product(v1, a01)
      p2 = dot_product(v2, a01)
      r = halfL(2) * abs(f1(3)) + halfL(3) * abs(f1(2))
      if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r) then 
         inside = .false. 
         return 
      end if 
  
      a02 = (/ 0.0_RP, -f2(3), f2(2) /)
      p0 = dot_product(v0, a02)
      p1 = dot_product(v1, a02)
      p2 = dot_product(v2, a02)
      r = halfL(2) * abs(f2(3)) + halfL(3) * abs(f2(2))
      if ( max(-max(p0, p1, p2), min(p0, p1, p2)) > r ) then 
         inside= .false.
         return 
      end if 
  
      a10 = (/ f0(3), 0.0_RP, -f0(1) /)
      p0 = dot_product(v0, a10)
      p1 = dot_product(v1, a10)
      p2 = dot_product(v2, a10)
      r = halfL(1) * abs(f0(3)) + halfL(3) * abs(f0(1))
      if ( max(-max(p0, p1, p2), min(p0, p1, p2)) > r) then 
         inside = .false.
         return 
      end if 
  
      a11 = (/ f1(3), 0.0_RP, -f1(1) /)
      p0 = dot_product(v0, a11)
      p1 = dot_product(v1, a11)
      p2 = dot_product(v2, a11)
      r = halfL(1) * abs(f1(3)) + halfL(3) * abs(f1(1))
      if (max(-Max(p0, p1, p2), Min(p0, p1, p2)) > r) then 
         inside = .false.
         return 
      end if 
  
      a12 = (/ f2(3), 0.0_RP, -f2(1) /)
      p0 = dot_product(v0, a12)
      p1 = dot_product(v1, a12)
      p2 = dot_product(v2, a12)
      r = halfL(1) * abs(f2(3)) + halfL(3) * abs(f2(1))
      if (max(-Max(p0, p1, p2), Min(p0, p1, p2)) > r) then 
         inside = .false.
         return 
      end if 
  
      a20 = (/ -f0(2), f0(1), 0.0_RP /)
      p0 = dot_product(v0, a20)
      p1 = dot_product(v1, a20)
      p2 = dot_product(v2, a20)
      r = halfL(1) * abs(f0(2)) + halfL(2) * abs(f0(1))
      if (max(-Max(p0, p1, p2), Min(p0, p1, p2)) > r) then 
         inside = .false.
         return 
      end if 
  
      a21 = (/ -f1(2), f1(1), 0.0_RP /)
      p0 = dot_product(v0, a21)
      p1 = dot_product(v1, a21)
      p2 = dot_product(v2, a21)
      r = halfL(1) * abs(f1(2)) + halfL(2) * abs(f1(1))
      if (max(-Max(p0, p1, p2), Min(p0, p1, p2)) > r) then 
         inside = .false.
         return 
      end if 
  
      a22 = (/-f2(2), f2(1), 0.0_RP/)
      p0 = dot_product(v0, a22)
      p1 = dot_product(v1, a22)
      p2 = dot_product(v2, a22)
      r = halfL(1) * abs(f2(2)) + halfL(2) * abs(f2(1))
      if (max(-Max(p0, p1, p2), Min(p0, p1, p2)) > r) then 
         inside = .false.
         return 
      end if 

      if (Max(v0(1), v1(1), v2(1)) < -halfL(1) .or. Min(v0(1), v1(1), v2(1)) > halfL(1)) then 
         inside = .false.
         return 
      end if 

      if (Max(v0(2), v1(2), v2(2)) < -halfL(2) .or. Min(v0(2), v1(2), v2(2)) > halfL(2)) then 
         inside = .false.
         return 
      end if 

      if (Max(v0(3), v1(3), v2(3)) < -halfL(3) .or. Min(v0(3), v1(3), v2(3)) > halfL(3)) then 
         inside = .false.
         return 
      end if 
  
      call vcross(f0, f1, plane_normal)
      plane_distance = abs(dot_product(plane_normal, v0))
  
      r = halfL(1) * abs(plane_normal(1)) + halfL(2) * abs(plane_normal(2)) + halfL(3) * abs(plane_normal(3));
  
      if (plane_distance > r) then 
         inside = .false.
         return 
      end if 
  
      inside = .true.

   end function TrinagleIntersectBox

end module KDClass