IBMClass.f90 Source File


Source Code

#include "Includes.h"
module IBMClass

   use SMConstants
   use Utilities
   use FTValueDictionaryClass
   use ElementClass
   use FaceClass
   use TessellationTypes
   use OrientedBoundingBox
   use KDClass
   use IntegerDataLinkedList
   use MPI_IBMUtilities
#ifdef _HAS_MPI_
   use mpi
#endif
   implicit none

   type :: Integral_t

      logical              :: ListComputed = .false., &
                              compute      = .false., &
                              constructed  = .false.
   end type

   type IBM_type

      type(STLfile),              allocatable :: stl(:), stlSurfaceIntegrals(:), stlMove(:)
      type(KDtree),               allocatable :: root(:), rootDistance(:), rootPoints(:)
      type(PointLinkedList)                   :: BandPoints
      type(IBMpoints),            allocatable :: BandRegion(:), BandRegion4Distance(:)
      type(point_type),           allocatable :: ImagePoints(:)
      type(Integral_t),           allocatable :: Integral(:)
      character(len=LINE_LENGTH), allocatable :: STLfilename(:)
      character(len=LINE_LENGTH)              :: filename
      logical                                 :: plotOBB              = .false., &
                                                 plotKDtree           = .false., &
                                                 active               = .false., & 
                                                 TimePenal            = .false., &
                                                 semiImplicit         = .false., &
                                                 ComputeBandRegion    = .false., &
                                                 plotBandPoints       = .false., &
                                                 plotMask             = .false., &
                                                 ComputeInterpolation = .false., &
                                                 Wallfunction         = .false., &
                                                 ComputeDistance      = .false., &
                                                 AAB                  = .false.                                                
      real(kind=rp)                           :: eta, BandRegionCoeff, IP_Distance = 0.0_RP, &
                                                 y_plus_target, minCOORDS, maxCOORDS,        &
                                                 penalCoeff
      real(kind=rp),              allocatable :: penalization(:)
      integer                                 :: KDtree_Min_n_of_Objs, NumOfInterPoints,     &
                                                 n_of_INpoints,  rank, lvl = 0, NumOfSTL,    &
                                                 NumOfForcingPoints, Clipaxis = 0,           &
                                                 Nx, Ny, Nz, LocClipAxis = 0,                &
                                                 InterpolationType
      integer,                    allocatable :: ImagePoint_NearestPoints(:,:)
      
      contains  
         procedure :: read_info                           => IBM_read_info
         procedure :: construct                           => IBM_construct
         procedure :: constructMask                       => IBM_constructMask
         procedure :: constructSTL_KDtree                 => IBM_constructSTL_KDtree
         procedure :: CheckPoint                          => IBM_checkPoint
         procedure :: constructBandRegion                 => IBM_constructBandRegion
         procedure :: constructBandRegion4Distance        => IBM_constructBandRegion4Distance
         procedure :: build                               => IBM_build
         procedure :: SetPolynomialOrder                  => IBM_SetPolynomialOrder
         procedure :: GetMask                             => IBM_GetMask
         procedure :: MPI_sendOBB                         => IBM_MPI_sendOBB
         procedure :: MPI_sendSTLpartitions               => IBM_MPI_sendSTLpartitions
         procedure :: MPI_sendMask2Root                   => IBM_MPI_sendMask2Root
         procedure :: MPI_sendMask2Partitions             => IBM_MPI_sendMask2Partitions
         procedure :: MPI_sendNormals2Root                => IBM_MPI_sendNormals2Root
         procedure :: MPI_sendDistNormals2partitions      => IBM_MPI_sendDistNormals2partitions
         procedure :: BandRegionPoints                    => IBM_bandRegionPoints
         procedure :: GetForcingPointsGeom                => IBM_GetForcingPointsGeom
         procedure :: GetInfo                             => IBM_GetInfo 
         procedure :: SourceTerm                          => IBM_SourceTerm
         procedure :: ComputeIBMWallDistance              => IBM_ComputeIBMWallDistance
         procedure :: GetDistanceInsideBox                => IBM_GetDistanceInsideBox
         procedure :: GetDistanceOutsideBox               => IBM_GetDistanceOutsideBox
         procedure :: SemiImplicitCorrection              => IBM_SemiImplicitCorrection
         procedure :: GetImagePoint_nearest               => IBM_GetImagePoint_nearest
         procedure :: GetBandRegionStates                 => IBM_GetBandRegionStates
         procedure :: GetDomainExtreme                    => IBM_GetDomainExtreme
         procedure :: SourceTermTurbulence                => IBM_SourceTermTurbulence
         procedure :: semiImplicitShiftJacobian           => IBM_semiImplicitShiftJacobian
         procedure :: semiImplicitTurbulenceShiftJacobian => IBM_semiImplicitTurbulenceShiftJacobian
         procedure :: semiImplicitJacobian                => IBM_semiImplicitJacobian
         procedure :: semiImplicitTurbulenceJacobian      => IBM_semiImplicitTurbulenceJacobian  
         procedure :: GetSemiImplicitStep                 => IBM_GetSemiImplicitStep   
         procedure :: GetSemiImplicitStepTurbulence       => IBM_GetSemiImplicitStepTurbulence   
         procedure :: SetIntegration                      => IBM_SetIntegration
         procedure :: copy                                => IBM_copy
         procedure :: MoveBody                            => IBM_MoveBody
         procedure :: CleanMask                           => IBM_CleanMask
         procedure :: BandPoint_state                     => IBM_BandPoint_state
         procedure :: Describe                            => IBM_Describe
         procedure :: plot_Mask                           => IBM_plot_Mask
         procedure :: Destruct                            => IBM_Destruct
         procedure :: DestroyKDtree                       => IBM_DestroyKDtree
         procedure :: constructDistance_KDtree            => IBM_constructDistance_KDtree
         procedure :: MPI_PointsListOperations            => IBM_MPI_PointsListOperations
         procedure :: MaskVelocity                        => IBM_MaskVelocity
   end type

   public :: expCoeff, EXPONENTIAL

   real(kind=RP)      :: expCoeff
   integer, parameter :: EXPONENTIAL = 1, IDW = 2, POLYHARMONIC_SPLINE = 3, POLYNOMIAL = 4, MLS = 5

   contains
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
!  Immersed boundary info
!  -------------------------------------------------   
   subroutine IBM_read_info( this, controlVariables )
      use FileReadingUtilities
      implicit none
      
      class(IBM_type), intent(inout) :: this
      class(FTValueDictionary)       :: controlVariables
 
      call this% GetInfo( controlVariables )

   end subroutine IBM_read_info
   

   subroutine IBM_GetInfo( this, controlVariables )
      use FileReadingUtilities
#if defined(NAVIERSTOKES)
      use WallFunctionDefinitions  
#endif
      implicit none
      !-arguments----------------------------------------------------------------
      class(IBM_type), intent(inout) :: this
      class(FTValueDictionary)       :: controlVariables
      !-local-variables----------------------------------------------------------
      logical,       allocatable :: active_in, plotOBB_in,              &
                                    plotKDtree_in, semiImplicit_in,     &
                                    plotBandPoints_in, plotMask_in,     &
                                    BandRegion_in, Distance_in, AAB_in
      real(kind=rp), allocatable :: penalization_in, y_plus_target_in,  &
                                    BandRegionCoeff_in,                 & 
                                    penalization_coeff_in
      integer,       allocatable :: n_of_Objs_in, n_of_interpoints_in,  &
                                    Nx_in, Ny_in, Nz_in, Clipaxis_in
      character(len=LINE_LENGTH) :: in_label, paramFile, name_in, tmp,  &
                                    InterpolationType_in
      real(kind=rp), allocatable :: coords(:)   
      logical                    :: correct
      
      character(len=LINE_LENGTH), parameter :: NumberOfSTL = "number of stl" 
      
!     Read block
!     **********
      write(in_label , '(A)') "#define ibm"
      call get_command_argument(1, paramFile) 
      call readValueInRegion( trim( paramFile ), "name",                           name_in,               in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "active",                         active_in,             in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "penalization",                   penalization_in,       in_label, "#end" )   
      call readValueInRegion( trim( paramFile ), "penalization coeff",             penalization_coeff_in, in_label, "#end" )   
      call readValueInRegion( trim( paramFile ), "semi implicit",                  semiImplicit_in,       in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "nx",                             Nx_in,                 in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "ny",                             Ny_in,                 in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "nz",                             Nz_in,                 in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "target y plus",                  y_plus_target_in,      in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "number of objects",              n_of_Objs_in,          in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "number of interpolation points", n_of_interpoints_in,   in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "clip axis",                      Clipaxis_in,           in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "band region",                    BandRegion_in,         in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "distance",                       Distance_in,           in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "band region coeff",              BandRegionCoeff_in,    in_label, "#end" )    
      call readValueInRegion( trim( paramFile ), "aab",                            AAB_in,                in_label, "#end" )     
      call readValueInRegion( trim( paramFile ), "intepolation",                   InterpolationType_in,  in_label, "#end" )     
      call readValueInRegion( trim( paramFile ), "plot obb",                       plotOBB_in,            in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "plot kdtree" ,                   plotKDtree_in,         in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "plot mask",                      plotMask_in,           in_label, "#end" )
      call readValueInRegion( trim( paramFile ), "plot band points",               plotBandPoints_in,     in_label, "#end" )

      this% filename = trim(name_in)

      if( allocated(active_in) ) then
         this% active = active_in
      else
         this% active = .FALSE.
      end if
      
      if( .not. this% active) return
      
      if( allocated(semiImplicit_in) ) then
         this% semiImplicit = semiImplicit_in
      else
         this% semiImplicit = .FALSE.
      end if       

      if( allocated(Nx_in) ) then
         this% Nx = Nx_in 
      else
         this% Nx = 0
      end if   
           
      if( allocated(Ny_in) ) then
         this% Ny = Ny_in
      else
         this% Ny = 0
      end if 
             
      if( allocated(Nz_in) ) then
         this% Nz = Nz_in
      else
         this% Nz = 0
      end if           
   
      if( allocated(plotOBB_in) ) then
         this% plotOBB = plotOBB_in
      else
         this% plotOBB = .FALSE.
      end if

      if( allocated(plotKDtree_in) ) then
         this% plotKDtree = plotKDtree_in
      else
         this% plotKDtree = .FALSE.
      end if

      if( allocated(penalization_in) ) then
         this% eta = penalization_in
         this% TimePenal = .false.
      else
         this% eta = 0.1_RP
         this% TimePenal = .true.
      end if

      if( allocated(penalization_coeff_in) ) then
         this% penalCoeff = penalization_coeff_in
      else
         this% penalCoeff = 1.0_RP
      end if

      if( allocated(n_of_Objs_in) ) then
         this% KDtree_Min_n_of_Objs = n_of_Objs_in
      else
         this% KDtree_Min_n_of_Objs = 5
      end if
      
      if( allocated(n_of_interpoints_in) ) then
         this% NumOfInterPoints = n_of_interpoints_in
      else
         this% NumOfInterPoints = 15
      end if 
      
      if( allocated(plotBandPoints_in) ) then
         this% plotBandPoints = plotBandPoints_in
      else
         this% plotBandPoints = .false.
      end if
      
      if( allocated(plotMask_in) ) then
         this% plotMask = plotMask_in
      else
         this% plotMask = .false.
      end if
      
      if( allocated(BandRegion_in) ) then
         this% ComputeBandRegion = BandRegion_in
      else
         this% ComputeBandRegion = .false.
      end if
      
      if( allocated(Distance_in) ) then
         this% ComputeDistance = Distance_in
      else
         this% ComputeDistance = .false.
      end if

      if( allocated(Clipaxis_in) ) then 
         this% ClipAxis = Clipaxis_in
      else
         this% ClipAxis = 0
      endif

     if( controlVariables% containsKey("wall function") ) then
        this% Wallfunction = .true.
#if defined(NAVIERSTOKES)
        call Initialize_Wall_Function(controlVariables, correct)   !TO BE REMOVED
#endif
        if( allocated(y_plus_target_in) ) then
           this% y_plus_target = y_plus_target_in
        else
           this% y_plus_target = 50.0_RP
        end if
        
        this% ComputeBandRegion = .true.
        this% ComputeDistance   = .true.
   
     else
        this% Wallfunction = .false.
     end if

      if( allocated(BandRegionCoeff_in) ) then
         this% BandRegionCoeff = BandRegionCoeff_in 
      else
         this% BandRegionCoeff = 2.0_RP      
      end if
 
      if( controlVariables% containsKey(trim(NumberOfSTL)) ) then
         tmp = controlVariables% StringValueForKey(trim(NumberOfSTL),LINE_LENGTH) 
         this% NumOfSTL = GetIntValue(tmp)
      else
         this% NumOfSTL = 1
      end if          

      if( allocated(AAB_in) ) then 
         this% AAB = AAB_in
      else
         this% AAB = .false.
      end if

      select case(trim(InterpolationType_in))
         case("exp")
            this% InterpolationType =  EXPONENTIAL
         case("idw")
            this% InterpolationType =  IDW
         case("spline")
            this% InterpolationType =  POLYHARMONIC_SPLINE
         case("polynomial")
            this% InterpolationType =  POLYNOMIAL
         case("mls")
            this% InterpolationType =  MLS
         case default 
            this% InterpolationType =  IDW
      end select 

   end subroutine IBM_GetInfo
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
!  KDtree procedures 
!  -------------------------------------------------   
   subroutine IBM_construct( this, controlVariables )   
      use mainKeywordsModule
      use FileReadingUtilities
      use MPI_Process_Info
      implicit none
      !-arguments----------------------------------------
      class(IBM_type), intent(inout) :: this  
      class(FTValueDictionary)       :: controlVariables
      !-local-variables----------------------------------
      character(len=LINE_LENGTH) :: filename, MyString
      real(kind=RP)              :: axis(NDIM)
      integer                    :: STLNum, j, k  

      call this% describe()         

      allocate( this% stl(this% NumOfSTL),                 & 
                OBB(this% NumOfSTL),                       &
                this% root(this% NumOfSTL),                &
                this% integral(this% NumOfSTL),            &
                this% STLfilename(this% NumOfSTL),         &
                this% stlSurfaceIntegrals(this% NumOfSTL), &
                this% stlMove(this% NumOfSTL)              )

      if( this% ComputeBandRegion ) then
         allocate( this% rootPoints(this% NumOfSTL),   & 
                   this% BandRegion(this% NumOfSTL)    )
      end if

      if( this% ComputeDistance ) allocate(this% rootDistance(this% NumOfSTL))
      
      do STLNum = 1, this% NumOfSTL 
         write(MyString, '(i100)') STLNum
         if( STLNum .eq. 1 ) then
            filename = stlFileNameKey
         else
            filename = trim(stlFileNameKey)//trim(adjustl(MyString))
         end if          
         this% STLfilename(STLNum) = controlVariables% stringValueForKey(trim(filename), requestedLength = LINE_LENGTH)
         OBB(STLNum)% filename     = this% STLfilename(STLNum)
         call STLfile_GetMotionInfo( this% stl(STLNum), this% STLfilename(STLNum), this% NumOfSTL ) 
         if( MPI_Process% isRoot ) then
            this% stl(STLNum)% show = .true. 
            call this% stl(STLNum)% ReadTessellation( this% STLfilename(STLNum) ) 
            if( this% ClipAxis .ne. 0 ) then 
               call this% stl(STLNum)% Clip( this% minCOORDS, this% maxCOORDS, this% ClipAxis, .true. ) 
            else
               call this% stl(STLNum)% describe(this% STLfilename(STLNum))
            end if
            if( this% stl(STLNum)% move ) call this% stlMove(STLNum)% copy( this% stl(STLNum) )
            call OBB(STLNum)% construct( this% stl(STLNum), this% plotOBB, this% AAB )
            call OBB(STLNum)% ChangeObjsRefFrame( this% stl(STLNum)% ObjectsList, LOCAL )  
         end if
         call this% MPI_sendOBB(STLNum)  
         call this% constructSTL_KDtree( STLNum )                
      end do

   end subroutine IBM_Construct
   
   subroutine IBM_constructSTL_KDtree( this, STLNum )
      use MPI_Process_Info 
      implicit none
      !-arguments-----------------------------------
      class(IBM_type), intent(inout) :: this
      integer,         intent(in)    :: STLNum
      !-local-variables-----------------------------
      real(kind=RP) :: vertices(NDIM,8)

      this% root(STLNum)% STLNum       = STLNum
      this% root(STLNum)% which_KDtree = TRIANGLES_KDTREE_SAH

      vertices = OBB(STLNum)% LocVertices

      call this% MPI_sendSTLpartitions( STLNum, vertices )

      call this% root(STLNum)% construct( stl           = this% stl(STLNum),         &
                                          vertices      = vertices,                  &
                                          isPlot        = this% plotKDtree,          & 
                                          Min_n_of_Objs = this% KDtree_Min_n_of_Objs )

      if( this% ComputeDistance ) call this% constructDistance_KDtree( STLNum ) 

      call this% stl(STLNum)% destroy() 

   end subroutine IBM_constructSTL_KDtree
   
   subroutine IBM_constructDistance_KDtree( this, STLNum )
      use MPI_Process_Info
      implicit none
      !-arguments----------------------------------------
      class(IBM_type), intent(inout) :: this
      integer,         intent(in)    :: STLNum
      !-local-variables----------------------------------
      real(kind=RP) :: BRvertices(NDIM,8)

      this% rootDistance(STLNum)% STLNum       = STLNum      
      this% rootDistance(STLNum)% which_KDtree = TRIANGLES_KDTREE_MEDIAN      

      call GetBRvertices( this% root(STLNum)% vertices, this% BandRegionCoeff, this% root(STLNum)% MaxAxis, STLNum, BRvertices )

      call this% rootDistance(STLNum)% construct( stl           = this% stl(STLNum),         &
                                                  Vertices      = BRvertices,                &
                                                  isPlot        = .false.,                   & 
                                                  Min_n_of_Objs = this% KDtree_Min_n_of_Objs )

   end subroutine IBM_constructDistance_KDtree
!  
!  Copy a KD tree   
!  ---------------
   subroutine IBM_copy( this, parent, lvl )
   
      implicit none
      !-arguments----------------------------------------------------------------
      class(IBM_type),         intent(inout) :: this
      type(IBM_type),  target, intent(in)    :: parent 
      integer,                 intent(in)    :: lvl
      !-local-variables----------------------------------------------------------
      integer :: STLNum
   
      allocate( this% root(parent% NumOfSTL),       &
                this% STLfilename(parent% NumOfSTL) )

      if( parent% ComputeBandRegion ) then
         allocate( this% rootPoints(parent% NumOfSTL), & 
                   this% BandRegion(parent% NumOfSTL)  )
      end if
      
      if( parent% ComputeDistance ) allocate(this% rootDistance(parent% NumOfSTL))

      do STLNum = 1, parent% NumOfSTL
         this% STLfilename(STLNum) = parent% STLfilename(STLNum)
         this% root(STLNum)        = parent% root(STLNum)
         if( parent% ComputeDistance ) this% rootDistance(STLNum) = parent% rootDistance(STLNum)
      end do
      
      this% ClipAxis = parent% ClipAxis

      this% lvl = lvl

   end subroutine IBM_copy
!  
!  Destroy the KD tree   
!  --------------------
   subroutine IBM_DestroyKDtree( this, isChild, DistanceKDtree )
      use MPI_Process_Info
      implicit none
      !-arguments--------------------------------------------------
      class(IBM_type),           intent(inout) :: this
      logical,                   intent(in)    :: isChild
      logical,         optional, intent(in)    :: DistanceKDtree
      !-local-variables--------------------------------------------
      integer :: STLNum
      
      if(this% ComputeDistance ) then
         do STLNum = 1, this% NumOfSTL
            call this% rootDistance(STLNum)% destruct( isChild )
         end do 
         deallocate(this% rootDistance)
         return      
      end if
        
      do STLNum = 1, this% NumOfSTL
         call this% root(STLNum)% destruct( isChild )
      end do
   
   end subroutine IBM_DestroyKDtree
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
!  Mask procedures 
!  -------------------------------------------------   
  subroutine IBM_GetMask( this, elements, no_of_elements, no_of_DoFs, STLNum, iter )
      use MPI_Process_Info
      implicit none
      !-arguments-----------------------------------------------------------
      class(IBM_type), intent(inout) :: this
      type(element),   intent(inout) :: elements(:)
      integer,         intent(in)    :: no_of_elements, no_of_DoFs, STLNum, iter

      call GetMaskCandidates( elements, no_of_elements, no_of_DoFs, STLNum, this% NumOfSTL )

      call this% MPI_PointsListOperations( Mask )

      call this% constructmask( elements, STLNum )

      if( this% plotMask ) call this% plot_Mask( iter, STLNum )
 
      deallocate( Mask% x )

   end subroutine IBM_GetMask
!  
! 
!  mask construction
!  -----------------------------------   
   subroutine IBM_constructmask( this, elements, STLNum )
      use MPI_Process_Info
      implicit none
      !-arguments---------------------------------------------------- 
      class(IBM_type),  intent(inout) :: this
      integer,          intent(in)    :: STLNum
      type(element),    intent(inout) :: elements(:)
      !-local-variables----------------------------------------------
      real(kind=RP) :: Point(NDIM) 
      integer       :: eID, n, i, j, k
!$omp parallel 
!$omp do schedule(runtime) private(Point)
      do n = 1, Mask% NumOfObjs    
         call OBB(STLNum)% ChangeRefFrame( Mask% x(n)% coords, LOCAL, Point )
         Mask% x(n)% isInsideBody       = .false.
         Mask% x(n)% NumOfIntersections = 0
         if( isInsideBox( Point, this% root(STLNum)% vertices ) ) then
            call this% CheckPoint( Point, STLNum, Mask% x(n)% NumOfIntersections )  
         end if   
      end do
!$omp end do
!$omp end parallel
      call this% MPI_sendMask2Root()       
      call this% MPI_sendMask2Partitions()
!$omp parallel 
!$omp do schedule(runtime) private(eID,i,j,k)
       do n = 1, Mask% NumOfObjs
          if( Mask% x(n)% partition .eq. MPI_Process% rank ) then
             eID = Mask% x(n)% element_index
             i = Mask% x(n)% local_Position(1)
             j = Mask% x(n)% local_Position(2)
             k = Mask% x(n)% local_Position(3)
             elements(eID)% isInsideBody(i,j,k) = Mask% x(n)% isInsideBody
             if( elements(eID)% isInsideBody(i,j,k) ) then 
                 elements(eID)% STL(i,j,k) = STLNum
             end if 
         end if    
      end do
!$omp end do
!$omp end parallel
      if( MPI_Process% isRoot ) then 
         if( Mask% NumOfObjs .eq. 0 .and. this% lvl .gt. 0 ) then
            print *, "The mask for the multigrid level ", this% lvl, " is made of 0 points."
            print *, "Try to increase the polynomial order or to refine the mesh."
            error stop
         elseif( Mask% NumOfObjs .eq. 0 ) then
            print *, "The mask is made of 0 points."
            print *, "Try to increase the polynomial order or to refine the mesh."
            error stop         
         end if
      end if
   end subroutine IBM_constructmask
!  
!  Mask plot
!  -----------------------------------------------------------
   subroutine IBM_plot_Mask(  this, iter, STLNum)
      use MPI_Process_Info
      implicit none
      !-arguments------------------------------------------------------
      class(IBM_type), intent(inout) :: this
      integer,         intent(in)    :: iter, STLNum
      !-local-variables------------------------------------------------
      character(len=LINE_LENGTH) :: filename
      integer                    :: i, funit, NumOfObjs
      logical                    :: add_Point = .false.
      
      if( .not. MPI_Process% isRoot ) return

      NumOfObjs = 0
      do i = 1, Mask% NumOfObjs
         if( Mask% x(i)% isInsideBody ) NumOfObjs = NumOfObjs + 1
      end do

      if( NumOfObjs .eq. 0 ) then 
         print*, "Mask is made of 0 points"
         error stop 
      end if

      if( this% lvl .gt. 0 ) then
         write(filename,'(A,A,I1,A,I10.10)') trim(this% STLfilename(STLNum)),'_MGlevel',this% lvl,'_',iter
      else
         write(filename,'(A,A,I10.10)') trim(this% STLfilename(STLNum)),'_',iter
      end if

      call TecFileHeader( 'IBM/Mask_'//trim(filename), 'Mask Points', NumOfObjs/2+mod(NumOfObjs,2),2,1, funit, 'POINT')
     
      if( mod(NumOfObjs,2) .ne. 0 )  add_Point  = .true.

      do i = 1, Mask% NumOfObjs
         if( Mask% x(i)% isInsideBody ) then
            write(funit,'(3E13.5)')  Mask% x(i)% coords(1), Mask% x(i)% coords(2), Mask% x(i)% coords(3)
            if( add_Point ) then
               write(funit,'(3E13.5)')  Mask% x(i)% coords(1), Mask% x(i)% coords(2), Mask% x(i)% coords(3)
               add_Point = .false.
            end if
         end if 
      end do

      close(funit)
      
   end subroutine IBM_plot_Mask
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -----------------------------------------------------------------------
!  Getting extreme of the domain mesh. These are used to clip the STL file
!  -----------------------------------------------------------------------   
   
   subroutine IBM_GetDomainExtreme( this, elements )
      use MPI_Process_Info
      implicit none
      !-arguments---------------------------------------------
      class(IBM_type), intent(inout) :: this
      type(element),   intent(in)   :: elements(:)
      !-local-variables---------------------------------------
      real(kind=rp) :: ElemMax, ElemMin
      integer       :: eID, i, axis, j, k 
#ifdef _HAS_MPI_
      real(kind=RP) :: localmax, localmin
      integer       :: ierr
#endif
      this% maxCOORDS = -huge(1.0_RP); this% minCOORDS = huge(1.0_RP)
      axis = this% ClipAxis
      if( axis .eq. 0 ) return
      do eID = 1, size(elements)
         do k = 0, elements(eID)% Nxyz(3)   ; do j = 0, elements(eID)% Nxyz(2) ; do i = 0, elements(eID)% Nxyz(1)
            this% maxCOORDS = max(this% maxCOORDS,elements(eID)% geom% x(axis,i,j,k)); this% minCOORDS = min(this% minCOORDS,elements(eID)% geom% x(axis,i,j,k))
         end do; end do; end do
      end do
      this% maxCOORDS = this% maxCOORDS + 1.0e-8
      this% minCOORDS = this% minCOORDS - 1.0e-8
#ifdef _HAS_MPI_
      localmax = this% maxCOORDS; localmin = this% minCOORDS
      call mpi_allreduce(localmax, this% maxCOORDS, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD, ierr)
      call mpi_allreduce(localmin, this% minCOORDS, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD, ierr)
#endif
   end subroutine IBM_GetDomainExtreme
!
!  Allocation and coordinates for the integration points
!  -----------------------------------------------------  
   subroutine IBM_SetIntegration( this, STLNum )
      use PhysicsStorage
      implicit none
      !-arguments------------------------------
      class(IBM_type), intent(inout) :: this
      integer,         intent(in)    :: STLNum
      !-local-variables------------------------
      integer :: i, j 

      if( this% Integral(STLNum)% compute ) return

      allocate(this% BandRegion(STLNum)% U_x(NCONS,this% BandRegion(STLNum)% NumOfObjs))
      allocate(this% BandRegion(STLNum)% U_y(NCONS,this% BandRegion(STLNum)% NumOfObjs))
      allocate(this% BandRegion(STLNum)% U_z(NCONS,this% BandRegion(STLNum)% NumOfObjs))

   end subroutine IBM_SetIntegration
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
!  building the immersed boundary 
!  -------------------------------------------------    
   subroutine IBM_build( this, elements, no_of_elements, no_of_DoFs, isChild, movingSTL, iter )
      use MPI_Process_Info
      use PhysicsStorage
      implicit none
      !-arguments-----------------------------------------------------------------
      class(IBM_type),           intent(inout) :: this
      type(element),             intent(inout) :: elements(:)
      integer,                   intent(in)    :: no_of_elements, no_of_DoFs
      logical,                   intent(in)    :: isChild       
      integer,         optional, intent(in)    :: movingSTL, iter       
      !-local-variables-----------------------------------------------------------
      integer :: MaskPoints, STLNum
#ifdef _HAS_MPI_
      integer :: localVal, ierr
#endif
      do STLNum = 1, this% NumOfSTL
         if( .not. present(movingSTL) )then 
            call this% GetMask( elements, no_of_elements, no_of_DoFs, STLNum, 0 )
         else
            if( STLNum .eq. movingSTL ) call this% GetMask( elements, no_of_elements, no_of_DoFs, STLNum, iter )
         end if
      end do  

      if( .not. present(movingSTL) )then
         if (.not. allocated(this% penalization)) then
            allocate( this% penalization(no_of_elements) )
            this% penalization = this% eta
         end if 
      end if 
#if defined(NAVIERSTOKES)
      if( this% ComputeDistance ) then
         if( .not. present(movingSTL) ) then
            call this% ComputeIBMWallDistance( elements )
         else
            call this% ComputeIBMWallDistance( elements, movingSTL )
         end if
      end if
#endif   
      if( this% ComputeBandRegion ) then
         do STLNum = 1, this% NumOfSTL
            if( .not. present(movingSTL) ) then
               call this% constructBandRegion( elements, no_of_elements, STLNum ) 
            else
               if( STLNum .eq. movingSTL ) then
                  call this% constructBandRegion( elements, no_of_elements, STLNum ) 
               end if
            end if
         end do 
      end if

      if( this% Wallfunction ) then
#if defined(NAVIERSTOKES)
         call this% GetImagePoint_nearest( elements )
#endif
      end if

   end subroutine IBM_build
!  
!  Immersed boundary description
!  -----------------------------
   subroutine IBM_Describe( this )
      use Headers
      use mainKeywordsModule
      use MPI_Process_Info
      implicit none
      !-arguments-----------------------------
      class(IBM_type),  intent(inout) :: this
      
      if ( .not. MPI_Process % isRoot ) return
      write(STD_OUT,'(/)')
      call Section_Header("IBM parameters")
      write(STD_OUT,'(/)')
      
      call SubSection_Header('IBM info')
      write(STD_OUT,'(30X,A,A35,L10)') "->" , "Semi implicit treatment: ", this% semiImplicit
      if( .not. this% TimePenal ) then
         write(STD_OUT,'(30X,A,A35,ES14.2)') "->" , "Penalization term: " , this% eta
      else
         write(STD_OUT,'(30X,A,A35,A10)') "->" , "Penalization term: ", " Dt"
      end if

      write(STD_OUT,'(30X,A,A35,I10)') "->" , "Minimum number of objects: ", this% KDtree_Min_n_of_Objs
      write(STD_OUT,'(30X,A,A35,I10)') "->" , "Number of interpolation points: ", this% NumOfInterPoints

   end subroutine IBM_Describe
!
!  Destroy immersed boundary
!  -------------------------
   subroutine IBM_Destruct( this, isChild )
      use MPI_Process_Info
      implicit none
      !-arguments------------------------------
      class(IBM_type), intent(inout) :: this
      logical,         intent(in)    :: isChild
      !----------------------------------------
      integer :: STLNum, i, j 
      
      do STLNum = 1, this% NumOfSTL
         call this% root(STLNum)% destruct( isChild )
         if( this% ComputeDistance ) call this% rootDistance(STLNum)% destruct( isChild ) 
         if( this% ComputeBandRegion ) then
            call this% rootPoints(STLNum)% Destruct( .false. )
            deallocate( this% BandRegion(STLNum)% x, &
                        this% BandRegion(STLNum)% Q  )
            if( this% Integral(STLNum)% compute ) then
               deallocate( this% BandRegion(STLNum)% U_x, &
                           this% BandRegion(STLNum)% U_y, &
                           this% BandRegion(STLNum)% U_z  )
            end if
         end if
         if( this% Integral(STLNum)% compute ) call this% stlSurfaceIntegrals(STLNum)% destroy()
         if( this% stl(STLNum)% move ) call this% stlMove(STLNum)% destroy()
      end do
      
      if( this% Wallfunction ) then 
         do i = 1, this% NumOfForcingPoints
            deallocate( this% ImagePoints(i)% invPhi, &
                        this% ImagePoints(i)% b       )
         end do
         deallocate( this% ImagePoints ) 
      end if 

      deallocate( this% penalization,        &
                  this% stl,                 &
                  this% stlSurfaceIntegrals, &
                  this% stlMove,             &
                  this% Integral,            &
                  this% STLfilename          )

      if( this% ComputeBandRegion ) deallocate( this% BandRegion )

   end subroutine IBM_Destruct
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  ------------------------------------------------------------
!  Creating the .omesh file for p-adaptation close to the STL  
!  ------------------------------------------------------------
   subroutine IBM_SetPolynomialOrder( this, elements, corners )
      implicit none
      
      !-arguments----------------------------------------------
      class(IBM_type),         intent(inout) :: this
      type(element),           intent(inout) :: elements(:)
      real(kind=RP), optional, intent(in)    :: corners(:,:) 
      !-local-variables----------------------------------------
      real(kind=RP) :: corner(NDIM)
      integer       :: STLNum, eID, k

      if( present(corners) ) then 
         do eID = 1, size(elements)
            do k = 1, 8 
               if( isInsideBox( elements(eID)% SurfInfo% corners(:,k), corners ) ) then
                  elements(eID)% Nxyz(1) = this% Nx 
                  elements(eID)% Nxyz(2) = this% Ny
                  elements(eID)% Nxyz(3) = this% Nz 
                  exit
               end if 
            end do 
         end do  
      else 
         do eID = 1, size(elements)
            !loop over the stl files
            do STLNum = 1, this% NumOfSTL
               loop: do k = 1, 8 
                  call OBB(STLNum)% ChangeRefFrame( elements(eID)% SurfInfo% corners(:,k), LOCAL, corner )
                  if( isInsideBox( corner, this% BandRegionCoeff*OBB(STLNum)% LocVertices ) ) then
                     if( this% Nx .ne. 0 ) elements(eID)% Nxyz(1) = this% Nx
                     if( this% Ny .ne. 0 ) elements(eID)% Nxyz(2) = this% Ny
                     if( this% Nz .ne. 0 ) elements(eID)% Nxyz(3) = this% Nz
                     exit loop
                  end if
               end do loop
            end do
         end do     
      end if 

   end subroutine IBM_SetPolynomialOrder
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -----------------------------------------------------------------------
!  Band region for the computation of the forces and turbulent quantities
!  -----------------------------------------------------------------------

   subroutine IBM_constructBandRegion( this, elements, no_of_elements, STLNum )
      use MPI_Process_Info
      use PhysicsStorage
      implicit none
      !-arguments------------------------------------------------------
      class(IBM_type), intent(inout) :: this
      type(element),   intent(inout) :: elements(:)
      integer,         intent(in)    :: no_of_elements, &
                                        STLNum
      !-local-variables------------------------------------------------
      type(STLfile)             :: stl 
      type(point_type), pointer :: p 
      integer                   :: i
#ifdef _HAS_MPI_
      integer                   :: localVal, ierr
#endif        
      call this% BandRegionPoints( elements, no_of_elements, STLNum )

      this% BandRegion(STLNum)% LocNumOfObjs = this% BandPoints% NumOfPoints
#ifdef _HAS_MPI_
      call mpi_allreduce(this% BandPoints% NumOfPoints, this% BandRegion(STLNum)% NumOfObjs, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD, ierr)
#else
      this% BandRegion(STLNum)% NumOfObjs = this% BandPoints% NumOfPoints 
#endif

      if( this% BandRegion(STLNum)% NumOfObjs .eq. 0 ) then
         print *, "IBM_bandRegionPoints: Number of points in the band region is 0"
         error stop
      end if

      allocate(this% BandRegion(STLNum)% x(this% BandRegion(STLNum)% NumOfObjs))

      p => this% BandPoints% head

      do i = 1, this% BandPoints% NumOfPoints
         this% BandRegion(STLNum)% x(i)% index          = i
         this% BandRegion(STLNum)% x(i)% coords         = p% coords
         this% BandRegion(STLNum)% x(i)% local_Position = p% local_Position
         this% BandRegion(STLNum)% x(i)% element_index  = p% element_index
         this% BandRegion(STLNum)% x(i)% partition      = p% partition
         this% BandRegion(STLNum)% x(i)% STLNum         = STLNum
         this% BandRegion(STLNum)% x(i)% normal         = 0.0_RP
         this% BandRegion(STLNum)% x(i)% Dist           = huge(1.0_RP)
         p => p% next 
      end do

      call this% MPI_PointsListOperations( this% BandRegion(STLNum) )
  
      ! STL not used for rootpoints
      this% rootPoints(STLNum)% STLNum       = STLNum
      this% rootPoints(STLNum)% which_KDtree = POINTS_KDTREE

      call this% rootPoints(STLNum)% construct( stl           = this% stl(STLNum),                              &
                                                Vertices      = this% BandRegionCoeff*OBB(STLNum)% LocVertices, &
                                                isPlot        = this% plotBandPoints,                           &
                                                Min_n_of_Objs = this% NumOfInterPoints,                         &
                                                pointList     = this% BandRegion(STLNum)% x,                    &
                                                lvl           = this% lvl                                       )

      allocate( this% BandRegion(STLNum)% Q(NCONS,this% BandRegion(STLNum)% NumOfObjs) )

      call this% BandPoints% destruct()
 
   end subroutine IBM_constructBandRegion
!
!  Building band region for the computation of the STL-DoFs distance
!  ------------------------------------------------------------------
   subroutine IBM_constructBandRegion4Distance( this, elements, no_of_elements, STLNum )
      use MPI_Process_Info
      implicit none
      !-arguments------------------------------------------
      class(IBM_type), intent(inout) :: this
      type(element),   intent(inout) :: elements(:)
      integer,         intent(in)    :: no_of_elements, &
                                        STLNum
      !-local-variables------------------------------------
      type(point_type), pointer :: p 
      integer                   :: i
#ifdef _HAS_MPI_
      integer                   :: localVal, ierr
#endif
      call this% BandRegionPoints( elements, no_of_elements, STLNum )

      this% BandRegion4Distance(STLNum)% LocNumOfObjs = this% BandPoints% NumOfPoints
#ifdef _HAS_MPI_
      call mpi_allreduce(this% BandPoints% NumOfPoints, this% BandRegion4Distance(STLNum)% NumOfObjs, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD, ierr)
#else
      this% BandRegion4Distance(STLNum)% NumOfObjs  = this% BandPoints% NumOfPoints
#endif
      allocate(this% BandRegion4Distance(STLNum)% x(this% BandRegion4Distance(STLNum)% NumOfObjs))

      p => this% BandPoints% head

      do i = 1, this% BandPoints% NumOfPoints
         this% BandRegion4Distance(STLNum)% x(i)% index          = i
         this% BandRegion4Distance(STLNum)% x(i)% coords         = p% coords
         this% BandRegion4Distance(STLNum)% x(i)% local_Position = p% local_Position
         this% BandRegion4Distance(STLNum)% x(i)% element_index  = p% element_index
         this% BandRegion4Distance(STLNum)% x(i)% partition      = p% partition
         this% BandRegion4Distance(STLNum)% x(i)% STLNum         = STLNum
         this% BandRegion4Distance(STLNum)% x(i)% normal         = 0.0_RP
         this% BandRegion4Distance(STLNum)% x(i)% Dist           = huge(1.0_RP)
         p => p% next 
      end do

      call this% MPI_PointsListOperations( this% BandRegion4Distance(STLNum) )
        
      call this% BandPoints% destruct()

   end subroutine IBM_constructBandRegion4Distance
!
!  Band region points are found and stored
!  ---------------------------------------   
   subroutine IBM_bandRegionPoints( this, elements, n_of_elements, STLNum )
      use ElementClass
      use FaceClass
      use MPI_Process_Info
      implicit none
      !-arguments-----------------------------------------
      class(IBM_type), intent(inout) :: this
      type(element),   intent(inout) :: elements(:)
      integer,         intent(in)    :: n_of_elements, &
                                        STLNum
      !-local-variables-----------------------------------
      type(point_type)  :: p          
      real(kind=RP)     :: Point(NDIM)     
      integer           :: eID, n, i, j, k, NumOfPoints

      this% BandPoints = PointLinkedList()      

      n = 0; NumOfPoints = 0

      do eID = 1, n_of_elements
         associate( e => elements(eID) )
         do i = 0, e% Nxyz(1); do j = 0, e% Nxyz(2); do k = 0, e% Nxyz(3) 
            if( .not. e% isInsideBody(i,j,k) .and. .not. e% isForcingPoint(i,j,k) ) then
               call OBB(STLNum)% ChangeRefFrame(e% geom% x(:,i,j,k),LOCAL,Point)
               if( isInsideBox(Point, this% BandRegionCoeff*OBB(STLNum)% LocVertices, .true. ) ) then
                  p% coords = Point
                  p% element_index = eID; p% local_Position = (/ i,j,k /)
                  n = n + 1; NumOfPoints = NumOfPoints + 1 
                  p% index = n; p% partition = MPI_Process% rank
                  call this% BandPoints% add(p)
               end if
            end if
         end do; end do; end do
         end associate 
      end do

      this% BandPoints% NumOfPoints = NumOfPoints

   end subroutine IBM_bandRegionPoints
!
!  Band region points' state is stored
!  -----------------------------------
   subroutine IBM_BandPoint_state( this, elements, STLNum, gradients )
      use PhysicsStorage
      use MPI_Process_Info
      implicit none
      !-arguments------------------------------------------------
      class(IBM_type),           intent(inout) :: this
      type(element),             intent(in)    :: elements(:)
      integer,                   intent(in)    :: STLnum
      logical,                   intent(in)    :: gradients
      !-local-variables------------------------------------------
      integer                    :: n, i, j, k, eID 
#ifdef _HAS_MPI_
      real(kind=rp), allocatable :: local_sum(:), global_sum(:)
      integer                    :: ierr, NumOfObjs
#endif
      this% BandRegion(STLNum)% Q = 0.0_RP
      if( gradients ) then 
         this% BandRegion(STLNum)% U_x = 0.0_RP
         this% BandRegion(STLNum)% U_y = 0.0_RP
         this% BandRegion(STLNum)% U_z = 0.0_RP
      end if
!$omp parallel
!$omp do schedule(runtime) private(i,j,k,eID)
      do n = 1, this% BandRegion(STLNum)% NumOfObjs
         if( this% BandRegion(STLNum)% x(n)% partition .eq. MPI_Process% rank ) then
            i   = this% BandRegion(STLNum)% x(n)% local_Position(1)
            j   = this% BandRegion(STLNum)% x(n)% local_Position(2)
            k   = this% BandRegion(STLNum)% x(n)% local_Position(3)
            eID = this% BandRegion(STLNum)% x(n)% element_index
            this% BandRegion(STLNum)% Q(:,n) = elements(eID)% storage% Q(:,i,j,k)   
            if( gradients ) then
               this% BandRegion(STLNum)% U_x(:,n) = elements(eID)% storage% U_x(:,i,j,k)
               this% BandRegion(STLNum)% U_y(:,n) = elements(eID)% storage% U_y(:,i,j,k)
               this% BandRegion(STLNum)% U_z(:,n) = elements(eID)% storage% U_z(:,i,j,k)
            end if
         end if
      end do
!$omp end do
!$omp end parallel
#ifdef _HAS_MPI_
      if( MPI_Process% doMPIAction ) then     
         NumOfObjs = this% BandRegion(STLNum)% NumOfObjs
         allocate(local_sum(NumOfObjs),global_sum(NumOfObjs))
         do i = 1, NCONS
            local_sum = this% BandRegion(STLNum)% Q(i,:)
            call mpi_allreduce(local_sum, global_sum, NumOfObjs, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD, ierr) 
            this% BandRegion(STLNum)% Q(i,:) = global_sum  
            if( gradients ) then
               local_sum = this% BandRegion(STLNum)% U_x(i,:)
               call mpi_allreduce(local_sum, global_sum, NumOfObjs, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD, ierr)
               this% BandRegion(STLNum)% U_x(i,:) = global_sum
               local_sum = this% BandRegion(STLNum)% U_y(i,:)
               call mpi_allreduce(local_sum, global_sum, NumOfObjs, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD, ierr)
               this% BandRegion(STLNum)% U_y(i,:) = global_sum
               local_sum = this% BandRegion(STLNum)% U_z(i,:)
               call mpi_allreduce(local_sum, global_sum, NumOfObjs, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD, ierr)
               this% BandRegion(STLNum)% U_z(i,:) = global_sum  
            end if
         end do 
         deallocate(local_sum,global_sum)
      end if
#endif
   end subroutine IBM_BandPoint_state
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  ------------------------------------------------------------
!  MPI routines  
!  ------------------------------------------------------------
   subroutine IBM_MPI_sendOBB( this, STLNum )
      use MPI_Process_Info
      implicit none
      !-arguments------------------------------
      class(IBM_type), intent(inout) :: this
      integer,         intent(in)    :: STLNum
      
      if ( MPI_Process% doMPIAction ) then   
         call recvOBB( STLNum )
      end if

      if( MPI_Process% doMPIRootAction ) then
         call SendOBB( STLNum )
      end if
   
   end subroutine IBM_MPI_sendOBB
  
   subroutine IBM_MPI_sendSTLpartitions( this, STLNum, vertices  )
      use MPI_Process_Info
      implicit none
      !-arguments---------------------------
      class(IBM_type), intent(inout) :: this
      integer,         intent(in)    :: STLNum
      real(kind=RP),   intent(inout) :: vertices(:,:)
      
      if( MPI_Process% doMPIAction ) then 
         call receiveSTLpartitions( this% stl(STLNum), STLNum, vertices, this% root(STLNum)% MaxAxis )
      end if 

      if( MPI_Process% isRoot ) then
         call sendSTL2Partitions( this% stl(STLNum), STLNum, vertices, this% root(STLNum)% MaxAxis )
      end if

      this% stl(STLNum)% construct = .true.
  
   end subroutine IBM_MPI_sendSTLpartitions

   subroutine IBM_MPI_PointsListOperations( this, pointsList )
      use MPI_Process_Info
      implicit none 
      !-arguments----------------------------
      class(IBM_type),           intent(inout) :: this 
      type(IBMpoints),           intent(inout) :: pointsList

      if( MPI_Process% doMPIAction ) then
         call SendPointsList2Root( pointsList )
      end if

      if( MPI_Process% doMPIRootAction ) then 
         call RecvPointsListRoot( pointsList )
      end if

      if( MPI_Process% doMPIRootAction ) then 
         call SendPointsList2partitions( pointsList )
      end if

      if( MPI_Process% doMPIAction ) then
         call RecvPointsListpartitions( pointsList )
      end if

   end subroutine IBM_MPI_PointsListOperations
!
!  MASK: root sends and receives its mask points 
!  ----------------------------------------------
   subroutine IBM_MPI_sendMask2Root( this )
      use MPI_Process_Info
      implicit none
      !-arguments-----------------------------
      class(IBM_type), intent(inout) :: this
   
      if( MPI_Process% isRoot ) then 
         call recvPointsMaskRoot()
      end if 

      if( MPI_Process% doMPIAction ) then 
         call sendPointsMask2Root()
      endif  
      
   end subroutine IBM_MPI_sendMask2Root
!
!  MASK: all points go from root to partitions
!  -------------------------------------------
   subroutine IBM_MPI_sendMask2Partitions( this )
      use MPI_Process_Info
      implicit none
      !-arguments----------------------------
      class(IBM_type), intent(inout) :: this
 
      if( MPI_Process% doMPIAction ) then 
         call recvPointsMaskPartitions()
      end if 

      if( MPI_Process% doMPIRootAction ) then 
         call sendPointsMask2Partitions()
      endif

   end subroutine IBM_MPI_sendMask2Partitions

   subroutine IBM_MPI_sendNormals2Root( this, pointsList, ranks )
      use MPI_Process_Info
      implicit none 
      !-arguments----------------------------
      class(IBM_type), intent(inout) :: this 
      type(IBMpoints), intent(inout) :: pointsList
      real(kind=RP),   intent(in)    :: ranks(:)
      
      if( MPI_Process% doMPIAction ) then
         call sendNormals2Root( pointsList )
      end if
      if( MPI_Process% doMPIRootAction ) then
         call recvNormalsRoot( PointsList, ranks )
     end if
   
   end subroutine IBM_MPI_sendNormals2Root
        
   subroutine IBM_MPI_sendDistNormals2partitions( this, PointsList )
      use MPI_Process_Info
      implicit none
      !-arguments-------------------------------
      class(IBM_type), intent(inout) :: this
      type(IBMpoints), intent(inout) :: pointsList
     
      if( MPI_Process% doMPIRootAction ) then
         call sendDistanceANDNormals2partitions( PointsList )
      end if
      if( MPI_Process% doMPIAction ) then
         call recvDistancesANDNormalspartitions( PointsList )
      end if
   
   end subroutine IBM_MPI_sendDistNormals2partitions
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  ------------------------------------------------------------
!  Moving bodies  
!  ------------------------------------------------------------      
   subroutine IBM_MoveBody( this, elements, no_of_elements, no_of_DoFs, isChild, t, iter, autosave )
      use MPI_Process_Info
      implicit none
      !-arguments-----------------------------------------------------------------
      class(IBM_type),           intent(inout) :: this
      type(element),             intent(inout) :: elements(:)
      integer,                   intent(in)    :: no_of_elements, no_of_DoFs
      logical,                   intent(in)    :: isChild 
      real(kind=RP),             intent(in)    :: t
      integer,         optional, intent(in)    :: iter
      logical,         optional, intent(in)    :: autosave
      !-local-variables-----------------------------------------------------------
      integer :: STLNum
 
      do STLNum = 1, this% NumOfSTL
         if( this% stl(STLNum)% move ) then
            call this% root(STLNum)% Destruct( isChild )
            if( this% ComputeBandRegion ) then
               deallocate( this% BandRegion(STLNum)% x )
               call this% rootPoints(STLNum)% destruct( isChild )
            end if
            if( this% ComputeDistance ) call this% rootDistance(STLNum)% destruct( isChild )
            call this% CleanMask( elements, no_of_elements, STLNum ) 
            if( MPI_Process% isRoot .and. .not. isChild ) then
               call this% stl(STLNum)% ReadTessellation( this% STLfilename(STLNum) )
               if( this% stl(STLNum)% motionType .eq. ROTATION ) then
                  call this% stl(STLNum)% getRotationaMatrix( t )
                  call OBB(STLNum)% STL_rotate( this% stl(STLNum), .false. )
               elseif( this% stl(STLNum)% motionType .eq. LINEAR ) then
                  call this% stl(STLNum)% getDisplacement( t )
                  call OBB(STLNum)% STL_translate( this% stl(STLNum), .false. )
               end if
               call this% stl(STLNum)% updateNormals()
               this% stl(STLNum)% show = .false.
               this% plotMask          = .false.
               if( present(autosave) .and. autosave ) this% plotMask = .true.
               if( present(autosave) .and. autosave ) call this% stl(STLNum)% plot( iter )
               call OBB(STLNum)% construct( this% stl(STLNum), this% plotOBB, this% AAB )
               call OBB(STLNum)% ChangeObjsRefFrame( this% stl(STLNum)% ObjectsList, LOCAL ) 
            end if
            call this% MPI_sendOBB(STLNum) 
            this% plotKDtree = .false.
            call this% constructSTL_KDtree( STLNum ) 
         end if
      end do
      
      do STLNum = 1, this% NumOfSTL
         if( this% stl(STLNum)% move ) call this% build( elements, no_of_elements, no_of_DoFs, isChild, STLNum, iter )
      end do

   end subroutine IBM_MoveBody
!
!  Mask is cleaned
!  ---------------
   subroutine IBM_CleanMask( this, elements, no_of_elements, STLNum )
   
      implicit none
      !-arguments------------------------------------------------
      class(IBM_type), intent(inout) :: this
      type(element),   intent(inout) :: elements(:)
      integer,         intent(in)    :: no_of_elements, STLNum
      !-local-variables------------------------------------------
      integer :: eID, i, j, k
!$omp parallel
!$omp do schedule(runtime) private(i,j,k)
      do eID = 1, no_of_elements
         do i = 0, elements(eID)% Nxyz(1); do j = 0, elements(eID)% Nxyz(2); do k = 0, elements(eID)% Nxyz(3) 
            if( elements(eID)% STL(i,j,k) .eq. STLNum .and. elements(eID)% isInsideBody(i,j,k) ) then
               elements(eID)% STL(i,j,k) = 0
               elements(eID)% isInsideBody(i,j,k) = .false.
            end if
         end do; end do; end do
      end do 
!$omp end do
!$omp end parallel
   end subroutine IBM_CleanMask
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  ----------------------------------------------------
!  Geometrical quantities for the turbulence wall model  
!  ----------------------------------------------------   
   subroutine IBM_GetForcingPointsGeom( this, elements )
      use MPI_Process_Info
      use MeshTypes
      implicit none
      !-arguments----------------------------------------
      class(IBM_type), intent(inout) :: this
      type(element),   intent(inout) :: elements(:)
      !-local-variables----------------------------------
      real(kind=RP) :: dx, dy, dz, d, d_min, Dist
      integer       :: eID, i, j, k, n, Total
#ifdef _HAS_MPI_
      integer       :: ierr
#endif
      this% NumOfForcingPoints = 0
      d = huge(1.0_RP) 

      do eID = 1, size(elements) 
         dx = maxval(elements(eID)% SurfInfo% corners(1,:)) - minval(elements(eID)% SurfInfo% corners(1,:))
         dy = maxval(elements(eID)% SurfInfo% corners(2,:)) - minval(elements(eID)% SurfInfo% corners(2,:))
         dz = maxval(elements(eID)% SurfInfo% corners(3,:)) - minval(elements(eID)% SurfInfo% corners(3,:))
         d  = min(d,min(abs(dx),abs(dy),abs(dz)))   
      end do    
      d_min = sqrt(3.0_RP)*d 
#ifdef _HAS_MPI_
      call mpi_allreduce(d_min, this% IP_Distance, 1, MPI_DOUBLE, MPI_MIN, MPI_COMM_WORLD, ierr)
      Dist = this% IP_Distance
      this% IP_Distance = 1.5_RP*this% IP_Distance
#else
      this% IP_Distance = d_min
      Dist = this% IP_Distance
      this% IP_Distance = 1.5_RP*this% IP_Distance
#endif        
      do eID = 1, size(elements) 
         do k = 0, elements(eID)% Nxyz(3); do j = 0, elements(eID)% Nxyz(2); do i = 0, elements(eID)% Nxyz(1)   
            if( elements(eID)% geom% dWall(i,j,k) .gt. Dist ) cycle
            elements(eID)% isForcingPoint(i,j,k) = .true.
            this% NumOfForcingPoints = this% NumOfForcingPoints + 1
         end do; end do; end do     
      end do  
#ifdef _HAS_MPI_
      call mpi_allreduce(this% NumOfForcingPoints, Total, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD, ierr)
#else
      Total = this% NumOfForcingPoints
#endif
      if( Total .eq. 0 .and. MPI_Process% isRoot ) then 
         print*, "No forcing points found, change y+ target"
         error stop 
      endif

      allocate(this% ImagePoints(this% NumOfForcingPoints))
      n = 0
 
      do eID = 1, size(elements) 
         do k = 0, elements(eID)% Nxyz(3); do j = 0, elements(eID)% Nxyz(2); do i = 0, elements(eID)% Nxyz(1) 
            if( elements(eID)% isForcingPoint(i,j,k) ) then 
               Dist = this% IP_distance - elements(eID)% geom% dWall(i,j,k)
               n = n + 1
               this% ImagePoints(n)% coords = elements(eID)% geom% x(:,i,j,k) + Dist * elements(eID)% geom% normal(:,i,j,k)
               this% ImagePoints(n)% element_index = eID 
               this% ImagePoints(n)% local_Position = (/i,j,k/)
               this% ImagePoints(n)% index = n
            end if 
         end do; end do; end do 
      end do

      call Plot_Forcing_Imagepoints( this, elements )

   end subroutine IBM_GetForcingPointsGeom
      
   subroutine Plot_Forcing_Imagepoints( IBM, elements )
      use MPI_Process_Info
      implicit none
      !-arguments----------------------------------------
      type(IBM_type), intent(in) :: IBM
      type(element),  intent(in) :: elements(:)
      !-local-variables----------------------------------
      real(kind=RP)              :: ImagePoint(NDIM), &
                                    Dist
      character(len=LINE_LENGTH) :: filenameFP,       &
                                    filenameIP,       &
                                    lvl, rank
      integer                    :: eID, i, j, k,     &
                                    funit
      logical                    :: add_Point = .false.
      
      if( IBM% NumOfForcingPoints .eq. 0 ) return
 
      if( MPI_Process% nProcs .gt. 1 ) then 
         write(rank,*) MPI_Process% rank
         if( IBM% lvl .gt. 0 ) then 
            write(lvl,*)  IBM% lvl
            filenameFP = 'ForcingPoints_'//trim(IBM% filename)//'_MGlevel'//trim(adjustl(lvl))//'_Process'//trim(adjustl(rank))
            filenameIP = 'ImagePoints_'//trim(IBM% filename)//'_MGlevel'//trim(adjustl(lvl))//'_Process'//trim(adjustl(rank))
         else
            filenameFP = 'ForcingPoints_'//trim(IBM% filename)//'_Process'//trim(adjustl(rank))
            filenameIP = 'ImagePoints_'//trim(IBM% filename)//'_Process'//trim(adjustl(rank))        
         end if    
      else
         if( IBM% lvl .gt. 0 ) then 
            write(lvl,*)  IBM% lvl
            filenameFP = 'ForcingPoints_'//trim(IBM% filename)//'_MGlevel'//trim(adjustl(lvl))
            filenameIP = 'ImagePoints_'//trim(IBM% filename)//'_MGlevel'//trim(adjustl(lvl))
         else
            filenameFP = 'ForcingPoints_'//trim(IBM% filename)
            filenameIP = 'ImagePoints_'//trim(IBM% filename)       
         end if
      endif

      call TecFileHeader( 'IBM/'//trim(filenameFP), 'Forcing Points', IBM% NumOfForcingPoints/2+mod(IBM% NumOfForcingPoints,2),2,1, funit, 'POINT' )

      if( mod(IBM% NumOfForcingPoints,2) .ne. 0 ) add_Point  = .true.

      do eID = 1, size(elements)
         associate ( e => elements(eID) )
         do k = 0, e% Nxyz(3); do j = 0, e% Nxyz(2) ; do i = 0, e% Nxyz(1)
            if( e% isForcingPoint(i,j,k) ) then
               write(funit,'(3E13.5)')  e% geom% x(1,i,j,k), e% geom% x(2,i,j,k), e% geom% x(3,i,j,k)
               if( add_Point ) then
                  write(funit,'(3E13.5)') e% geom% x(1,i,j,k), e% geom% x(2,i,j,k), e% geom% x(3,i,j,k)
                  add_Point = .false.
               end if
            end if
         end do; end do; end do
         end associate
      end do
         
      close(funit)

      call TecFileHeader( 'IBM/'//trim(filenameIP), 'Image Points', IBM% NumOfForcingPoints/2+mod(IBM% NumOfForcingPoints,2),2,1, funit, 'POINT' )      

      do i = 1, IBM% NumOfForcingPoints
         write(funit,'(3E13.5)')  IBM% ImagePoints(i)% coords(1), IBM% ImagePoints(i)% coords(2), IBM% ImagePoints(i)% coords(3)
         if( add_Point ) then
            write(funit,'(3E13.5)') IBM% ImagePoints(i)% coords(1), IBM% ImagePoints(i)% coords(2), IBM% ImagePoints(i)% coords(3)
            add_Point = .false.
         end if
      end do
         
      close(funit)   

   end subroutine Plot_Forcing_Imagepoints
   
!
!  DoFs closer to each image point
!  -------------------------------
   subroutine IBM_GetImagePoint_nearest( this, elements )
      use MappedGeometryClass
      use PhysicsStorage
      use MPI_Process_Info
      use ElementClass
      implicit none
      !-arguments-------------------------------------------------------------------------------------
      class(IBM_type), intent(inout) :: this
      type(element),   intent(inout) :: elements(:)
      !-local-variables-------------------------------------------------------------------------------
      real(kind=RP) :: iP(NDIM), Dist 
      integer       :: i, n, STLNum  
!$omp parallel
!$omp do schedule(runtime) private(Dist,STLNum,iP,n)
         do i = 1, this% NumOfForcingPoints 
            STLNum = elements(this% ImagePoints(i)% element_index)% STL( this% ImagePoints(i)% local_Position(1), &
                                                                         this% ImagePoints(i)% local_Position(2), &
                                                                         this% ImagePoints(i)% local_Position(3)  )

            call OBB(STLNum)% ChangeRefFrame(this% ImagePoints(i)% coords,LOCAL,iP)

            allocate( this% ImagePoints(i)% nearestPoints(this% NumOfInterPoints),                 &
                      this% ImagePoints(i)% invPhi(this% NumOfInterPoints,this% NumOfInterPoints), &
                      this% ImagePoints(i)% b(this% NumOfInterPoints)                              )

            this% ImagePoints(i)% nearestPoints = 0

            do n = 1, this% NumOfInterPoints 
               call MinimumDistancePoints( iP, this% rootPoints(STLNum), this% BandRegion(STLNum), &
                                           Dist, n, this% ImagePoints(i)% nearestPoints            ) 
            end do
                  
            if(any(this% ImagePoints(i)% nearestPoints .eq. 0) ) then
               print *, "Can't fine nearest point for the image point: ", i
               error stop
            end if

            call GetMatrixInterpolationSystem( iP,                                                               &
                                               this% BandRegion(STLNum)% x(this% ImagePoints(i)% nearestPoints), &
                                               this% ImagePoints(i)% invPhi,                                     &
                                               this% ImagePoints(i)% b, this% InterpolationType                  )
         end do
!$omp end do
!$omp end parallel
   end subroutine IBM_GetImagePoint_nearest
!
!  Distance from the wall needed 
!  -----------------------------
   subroutine IBM_ComputeIBMWallDistance( this, elements, movingSTL )
      use MPI_Process_Info
      implicit none
      !-arguments--------------------------------------
      class(IBM_type),    intent(inout) :: this
      type(element),      intent(inout) :: elements(:)
      integer, optional , intent(in)    :: movingSTL
      !-local-variables--------------------------------
      integer :: STLNum

      allocate(this% BandRegion4Distance(this% NumOfSTL))

      do STLNum = 1, this% NumOfSTL
         if( .not. present(movingSTL) )then
            call this% GetDistanceInsideBox( elements, STLNum )
         else
            if( STLNum .eq. movingSTL) call this% GetDistanceInsideBox( elements, STLNum )
         end if
      end do

      do STLNum = 1, this% NumOfSTL
         if( .not. present(movingSTL) )then
            call this% GetDistanceOutsideBox( STLNum )
         else
            if( STLNum .eq. movingSTL) call this% GetDistanceOutsideBox( STLNum )
         end if
      end do

      do STLNum = 1, this% NumOfSTL
         if( .not. present(movingSTL) )then
            call SetDistances( this% BandRegion4Distance(STLNum), elements )
         else
            if( STLNum .eq. movingSTL) call SetDistances( this% BandRegion4Distance(STLNum), elements )
         endif
         deallocate( this% BandRegion4Distance(STLNum)% x)
      end do

      deallocate(this% BandRegion4Distance)
 
      if( this% Wallfunction ) then
#if defined(NAVIERSTOKES)
         call this% GetForcingPointsGeom( elements )
#endif
      endif

   end subroutine IBM_ComputeIBMWallDistance
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------------------------------------------
!  Computing the distance between a point and the triangles inside the box it lies in 
!  -------------------------------------------------------------------------------------    
   subroutine IBM_GetDistanceInsideBox( this, elements, STLNum )
      use MPI_Process_Info
      implicit none
      !-arguments------------------------------------
      class(IBM_type), intent(inout) :: this
      type(element),   intent(inout) :: elements(:)
      integer,         intent(in)    :: STLNum
      !-local-variables------------------------------
      integer       :: i, no_of_elements
      real(kind=RP) :: xP(NDIM), Dist, normal(NDIM)

      no_of_elements = size(elements)

      call this% constructBandRegion4Distance( elements, no_of_elements, STLNum )
!$omp parallel  
!$omp do schedule(runtime) private(xP,Dist,normal)
      do i = 1, this% BandRegion4Distance(STLNum)% NumOfObjs
         this% BandRegion4Distance(STLNum)% x(i)% Dist = huge(1.0_RP)   
         this% BandRegion4Distance(STLNum)% x(i)% STLNum = STLNum      
         this% BandRegion4Distance(STLNum)% x(i)% normal = 0.0_RP      
         xP = this% BandRegion4Distance(STLNum)% x(i)% coords                            
         if( isInsideBox(xP,this% rootDistance(STLNum)% vertices) ) then                              
            call MinimumDistance( xP, this% rootDistance(STLNum), Dist, normal )   
            if( Dist .lt. this% BandRegion4Distance(STLNum)% x(i)% Dist ) then 
               this% BandRegion4Distance(STLNum)% x(i)% Dist   = Dist
               this% BandRegion4Distance(STLNum)% x(i)% normal = normal  
            end if
         end if      
      end do   
!$omp end do
!$omp end parallel    
   end subroutine IBM_GetDistanceInsideBox
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------------------------------------------------
!  Computing the distance between a point and the triangles inside the box it does not lay in 
!  -------------------------------------------------------------------------------------------       
   subroutine IBM_GetDistanceOutsideBox( this, STLNum )
      use MPI_Process_Info
      implicit none
      !-arguments-----------------------------------
      class(IBM_type), intent(inout) :: this
      integer,         intent(in)    :: STLNum
      !-local-variables-----------------------------
      integer       :: i, no_of_elements
      real(kind=RP) :: xP(NDIM), Dist, normal(NDIM)
#ifdef _HAS_MPI_ 
      real(kind=rp), allocatable :: input(:,:), output(:,:)
      integer                    :: ierr, rank
#endif
#ifdef _HAS_MPI_
      allocate( input(2,this% BandRegion4Distance(STLNum)% NumOfObjs), &
                output(2,this% BandRegion4Distance(STLNum)% NumOfObjs) )
#endif     
!$omp parallel  
!$omp do schedule(runtime) private(xP,Dist,normal)
      do i = 1, this% BandRegion4Distance(STLNum)% NumOfObjs         
         xP = this% BandRegion4Distance(STLNum)% x(i)% coords        
         this% BandRegion4Distance(STLNum)% x(i)% STLNum = STLNum                   
         if( .not. isInsideBox(xP,this% rootDistance(STLNum)% vertices) ) then 
           call MinimumDistanceSphereKDtree( xP, this% rootDistance(STLNum), this% BandRegion4Distance(STLNum)% x(i)% Dist, Dist, normal )        
            if( Dist .lt. this% BandRegion4Distance(STLNum)% x(i)% Dist ) then 
               this% BandRegion4Distance(STLNum)% x(i)% Dist   = Dist
               this% BandRegion4Distance(STLNum)% x(i)% normal = normal      
            end if
         end if        
#ifdef _HAS_MPI_ 
         input(1,i) = this% BandRegion4Distance(STLNum)% x(i)% Dist
         input(2,i) = MPI_Process% rank
#endif    
      end do 
!$omp end do
!$omp end parallel
#ifdef _HAS_MPI_ 
      call mpi_allreduce( input, output, this% BandRegion4Distance(STLNum)% NumOfObjs, &
                          MPI_2DOUBLE_PRECISION, MPI_MINLOC,                           &
                          MPI_COMM_WORLD, ierr                                         )
       
      if( MPI_Process% isRoot ) then
         do i = 1, this% BandRegion4Distance(STLNum)% NumOfObjs
            this% BandRegion4Distance(STLNum)% x(i)% Dist = output(1,i)
         end do
      end if

      call this% MPI_sendNormals2Root( this% BandRegion4Distance(STLNum), output(2,:) )
      call this% MPI_sendDistNormals2partitions( this% BandRegion4Distance(STLNum)  )    
 
      deallocate(input,output)
#endif       
   end subroutine IBM_GetDistanceOutsideBox
   
   subroutine SetDistances( BandRegion4Distance, elements )
      use MPI_Process_Info
      implicit none
      !-arguments---------------------------------------------------
      type(IBMpoints), intent(inout) :: BandRegion4Distance
      type(element),   intent(inout) :: elements(:)
      !-local-variables---------------------------------------------
      integer :: i
!$omp parallel
!$omp do schedule(runtime)
      do i = 1, BandRegion4Distance% NumOfObjs
         BandRegion4Distance% x(i)% Dist = sqrt(BandRegion4Distance% x(i)% Dist)
         if( BandRegion4Distance% x(i)% partition .eq. MPI_Process% rank ) then           
            elements(BandRegion4Distance% x(i)% element_index)% geom%  &
               dWall(BandRegion4Distance% x(i)% local_Position(1),     &
                     BandRegion4Distance% x(i)% local_Position(2),     &
                     BandRegion4Distance% x(i)% local_Position(3)  ) = BandRegion4Distance% x(i)% Dist
            elements(BandRegion4Distance% x(i)% element_index)% geom%        &
                     normal(:, BandRegion4Distance% x(i)% local_Position(1), &
                               BandRegion4Distance% x(i)% local_Position(2), &
                               BandRegion4Distance% x(i)% local_Position(3)  ) = BandRegion4Distance% x(i)% normal
            elements(BandRegion4Distance% x(i)% element_index)%                &
                             STL(BandRegion4Distance% x(i)% local_Position(1), &
                                 BandRegion4Distance% x(i)% local_Position(2), &
                                 BandRegion4Distance% x(i)% local_Position(3)  ) = BandRegion4Distance% x(i)% STLNum
         end if
      end do
!$omp end do
!$omp end parallel
   end subroutine SetDistances
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
!  Source terms for the immersed boundary 
!  ------------------------------------------------   
   subroutine IBM_SourceTerm( this, eID, Q, Q_target, Source, Wallfunction )
      use PhysicsStorage
      implicit none
      !-arguments--------------------------------------------
      class(IBM_type),           intent(inout) :: this
      integer,                   intent(in)    :: eID
      real(kind=rp),             intent(in)    :: Q(:)
      real(kind=rp),   optional, intent(in)    :: Q_target(:)
      real(kind=rp),             intent(inout) :: Source(:)
      logical,                   intent(in)    :: Wallfunction
      !-local-variables--------------------------------------
      real(kind=rp) :: rho, rho_s, u, u_s, v, v_s, w, w_s
#if defined(SPALARTALMARAS)
      real(kind=rp) :: theta, theta_s
#endif
      Source = 0.0_RP
#if defined(NAVIERSTOKES)
      if( present(Q_target) ) then
         rho_s = Q_target(IRHO)
         u_s   = Q_target(IRHOU)/rho_s
         v_s   = Q_target(IRHOV)/rho_s
         w_s   = Q_target(IRHOW)/rho_s   
#if defined(SPALARTALMARAS)
         theta_s = Q_target(IRHOTHETA)/rho_s  
#endif
      else
         rho_s = Q(IRHO)
         u_s   = 0.0_RP
         v_s   = 0.0_RP
         w_s   = 0.0_RP
#if defined(SPALARTALMARAS)
         theta_s = 0.0_RP  
#endif
      end if   
        
      rho = Q(IRHO)
      u   = Q(IRHOU)/rho
      v   = Q(IRHOV)/rho
      w   = Q(IRHOW)/rho
      
      Source(IRHOU) = rho*u-rho_s*u_s  
      Source(IRHOV) = rho*v-rho_s*v_s 
      Source(IRHOW) = rho*w-rho_s*w_s
      Source(IRHOE) = 0.5_RP * rho*( POW2(u) + POW2(v) + POW2(w) )  & 
                     -0.5_RP * rho_s*( POW2(u_s) + POW2(v_s) + POW2(w_s) )
#if defined(SPALARTALMARAS)
      theta = Q(IRHOTHETA)/rho
      Source(IRHOTHETA) = rho*theta - rho_s*theta_s
#endif                     
#endif   
      if( Wallfunction ) then 
         Source = -1.0_RP/(this% penalCoeff * this% penalization(eID)) * Source 
      else 
         Source = -1.0_RP/this% penalization(eID) * Source
      end if 
   end subroutine IBM_SourceTerm

   function IBM_MaskVelocity( this, Q, nEqn, STLNum, x, t ) result( Q_target )
      use PhysicsStorage
      use FluidData
      use TessellationTypes
      use OrientedBoundingBox
      implicit none

      class(IBM_type), intent(inout) :: this 
      integer,         intent(in)    :: nEqn, STLNum
      real(kind=RP),   intent(in)    :: Q(nEqn), x(NDIM), t
      real(kind=RP)                  :: Q_target(nEqn)

      real(kind=RP) :: R, v_plane, time, theta, &
                       rho, u_s, v_s, w_s
      
      Q_target = Q

      u_s = 0.0_RP; v_s = 0.0_RP; w_s = 0.0_RP
#if defined(NAVIERSTOKES)
      if( this% stl(STLNum)% motionType .eq. LINEAR ) then
         select case( this% stl(STLNum)% motionAxis )
         case( IX )
            u_s = this% stl(STLNum)% Velocity/refValues% V
         case( IY )
            v_s = this% stl(STLNum)% Velocity/refValues% V
         case( IZ ) 
            w_s = this% stl(STLNum)% Velocity/refValues% V
         end select 
      elseif( this% stl(STLNum)% motionType .eq. ROTATION ) then
         R = norm2( x - this% stl(STLNum)% rotationCenter ) 

         v_plane = this% stl(STLNum)% angularVelocity * R * Lref
         v_plane = v_plane/refValues% V

         time    = t * Lref/refValues% V
         theta   = this% stl(STLNum)% angularVelocity * time

         select case( this% stl(STLNum)% motionAxis )
         case( IX ) 
            v_s = -sin(theta) * v_plane 
            w_s =  cos(theta) * v_plane 
         case( IY )
            u_s =  cos(theta) * v_plane  
            w_s = -sin(theta) * v_plane 
         case( IZ )
            u_s = -sin(theta) * v_plane
            v_s =  cos(theta) * v_plane 
         end select
      end if 

      rho = Q_target(IRHO)

      Q_target(IRHOU) = rho * u_s      
      Q_target(IRHOV) = rho * v_s      
      Q_target(IRHOW) = rho * w_s      
#endif
   end function IBM_MaskVelocity
!
!  Analytical jacobian: dS/dQ 
!  ---------------------------    
   subroutine IBM_semiImplicitJacobian( this, eID, Q, dS_dQ )
      use PhysicsStorage
      implicit none
      !-arguments----------------------------------------------------
      class(IBM_type), intent(inout) :: this
      integer,         intent(in)    :: eID
      real(kind=rp),   intent(in)    :: Q(:)
      real(kind=rp),   intent(inout) :: dS_dQ(:,:)
      !-local-variables----------------------------------------------
      real(kind=rp) :: rho, u, v, w

      dS_dQ = 0.0_RP       
#if defined(NAVIERSTOKES) 
      rho = Q(IRHO)
      u   = Q(IRHOU)/rho 
      v   = Q(IRHOV)/rho 
      w   = Q(IRHOW)/rho 

      dS_dQ(IRHOU,IRHOU) = 1.0_RP
      dS_dQ(IRHOV,IRHOV) = 1.0_RP
      dS_dQ(IRHOW,IRHOW) = 1.0_RP
      dS_dQ(IRHOE,IRHO)  = -0.5_RP*( POW2(u) + POW2(v) + POW2(w) )
      dS_dQ(IRHOE,IRHOU) = u 
      dS_dQ(IRHOE,IRHOV) = v 
      dS_dQ(IRHOE,IRHOW) = w
#if defined(SPALARTALMARAS)
       dS_dQ(IRHOTHETA,IRHOTHETA) = 1.0_RP
#endif
#endif
       dS_dQ = -1.0_RP/this% penalization(eID) * dS_dQ
 
    end subroutine IBM_semiImplicitJacobian

   subroutine IBM_semiImplicitTurbulenceJacobian( this, ImagePoint, Q, normal, dWall, STLNum, dS_dQ )
      use PhysicsStorage
      implicit none
      !-arguments-----------------------------------------------------------------
      class(IBM_type),  intent(inout) :: this
      type(point_type), intent(in)    :: ImagePoint  
      real(kind=rp),    intent(in)    :: Q(:), dWall, normal(:)
      integer,          intent(in)    :: STLNum
      real(kind=rp),    intent(inout) :: dS_dQ(:,:)
      !-local-variables-----------------------------------------------------------
      real(kind=rp) :: Q_IP(NCONS), Q_FP(NCONS), Qnext(NCONS), &
                       Q_IPnext(NCONS), Q_FPnext(NCONS),       &
                       dS_dQns(NCONS,NCONS), iP(NDIM), eps 
      integer       :: i

      do i = 1, NCONS 
         Q_IP(i) = GetInterpolatedValue( this% BandRegion(STLNum)% Q(i,ImagePoint% nearestPoints), &
                                         ImagePoint% invPhi,                                       &
                                         ImagePoint% b,                                            &
                                         this% InterpolationType                                   ) 
      end do

      Q_FP = Q
#if defined(NAVIERSTOKES) 
      call ForcingPointState( Q_IP, this% IP_Distance, dWall, normal, Q_FP )
#endif
      eps = sqrt(EPSILON(eps))

      do i = 1, NCONS
         Q_FPnext = Q; Q_IPnext = Q_IP       
         Q_IPnext(i) = Q_IP(i) + eps 
#if defined(NAVIERSTOKES)
         call ForcingPointState( Q_IPnext, this% IP_Distance, dWall, normal, Q_FPnext )
#endif
         dS_dQ(:,i) = (Q_FPnext - Q_FP)/eps 
      end do 

      dS_dQ = -1.0_RP/(this% penalCoeff*this% penalization(ImagePoint% element_index)) * dS_dQ 

      call this% semiImplicitJacobian( ImagePoint% element_index, Q, dS_dQns )

      dS_dQ = dS_dQns - dS_dQ

   end subroutine IBM_semiImplicitTurbulenceJacobian
!
!  Analytical inverse jacobian: (1 - dt*dS/dQ)^(-1) 
!  --------------------------------------------------   
   subroutine IBM_semiImplicitShiftJacobian( this, eID, Q, dt, invdS_dQ )
      use PhysicsStorage
      implicit none
         !-arguments----------------------------------------------------------------
         class(IBM_type), intent(inout) :: this
         integer,         intent(in)    :: eID
         real(kind=rp),   intent(in)    :: Q(:)
         real(kind=rp),   intent(in)    :: dt
         real(kind=rp),   intent(inout) :: invdS_dQ(:,:)
         !-local-variables----------------------------------------------------------
         real(kind=rp) :: rho, u, v, w

         invdS_dQ = 0.0_RP  
         
         associate( eta => this% penalization(eID) )     
#if defined(NAVIERSTOKES) 
         rho = Q(IRHO)
         u   = Q(IRHOU)/rho 
         v   = Q(IRHOV)/rho 
         w   = Q(IRHOW)/rho 
         
         invdS_dQ(IRHO,IRHO)   = 1.0_RP
         invdS_dQ(IRHOU,IRHOU) = eta/( dt + eta )
         invdS_dQ(IRHOV,IRHOV) = eta/( dt + eta )
         invdS_dQ(IRHOW,IRHOW) = eta/( dt + eta )
         invdS_dQ(IRHOE,IRHO)  = 0.5_RP*dt/eta * (POW2(u) + POW2(v) + POW2(w))
         invdS_dQ(IRHOE,IRHOU) = -dt*u/( dt + eta )
         invdS_dQ(IRHOE,IRHOV) = -dt*v/( dt + eta )
         invdS_dQ(IRHOE,IRHOW) = -dt*w/( dt + eta )
         invdS_dQ(IRHOE,IRHOE) =  1.0_RP 
#if defined(SPALARTALMARAS)
         invdS_dQ(IRHOTHETA,IRHOTHETA) = eta/( dt + eta )
#endif                                                                           
#endif
       end associate
 
   end subroutine IBM_SemiImplicitShiftJacobian
!
!  (1/dt - dS_dQt)
!
   subroutine IBM_SemiImplicitTurbulenceShiftJacobian( this, dt, invdS_dQ )
      use PhysicsStorage
      implicit none 
      !-arguments-----------------------------------
      class(IBM_type), intent(inout) :: this 
      real(kind=RP),   intent(in)    :: dt 
      real(kind=RP),   intent(inout) :: invdS_dQ(:,:)
      !-local-variables-----------------------------
      integer :: i

      invdS_dQ = dt*invdS_dQ

      do i = 1, NCONS 
         invdS_dQ(i,i) = 1.0_RP + invdS_dQ(i,i) 
      end do

   end subroutine IBM_SemiImplicitTurbulenceShiftJacobian
!
!  Second order Strang splitting correction Q^*. (1/dt - dS/dQ)^(-1)*Q^* = Q + dt*(S - dS/dQ*Q^*)
!  ------------------------------------------------------------------------------------------------
   subroutine IBM_GetSemiImplicitStep( this, eID, dt, Q, Q_target )
      use PhysicsStorage
      implicit none
      !-arguments-----------------------------------------------------
      class(IBM_type),           intent(inout) :: this
      integer,                   intent(in)    :: eID
      real(kind=rp),             intent(in)    :: dt
      real(kind=rp),             intent(inout) :: Q(NCONS)
      real(kind=rp),   optional, intent(in)    :: Q_target(NCONS)
      !-local-variables-----------------------------------------------
      real(kind=rp) :: dS_dQ(NCONS,NCONS), invdS_dQ(NCONS,NCONS), &
                       IBMSource(NCONS)

      call this% semiImplicitJacobian( eID, Q, dS_dQ )

      call this% semiImplicitShiftJacobian( eID, Q, dt, invdS_dQ ) 
      if( present(Q_target) ) then 
         call this% SourceTerm(eID = eID, Q = Q, Q_target = Q_target, Source = IBMSource, Wallfunction  =.false.)
      else
         call this% SourceTerm(eID = eID, Q = Q, Source = IBMSource, Wallfunction  =.false.)          
      end if 

      Q = matmul(invdS_dQ, Q + dt*( IBMSource - matmul(dS_dQ,Q) ))

   end subroutine IBM_GetSemiImplicitStep
!
!  Second order Strang splitting correction Q^*. (1/dt - dS/dQ)^(-1)*Q^* = Q + dt*(S - dS/dQ*Q^*)
!  ------------------------------------------------------------------------------------------------
   subroutine IBM_GetSemiImplicitStepTurbulence( this, ImagePoint, dt, Q, normal, dWall, STLNum )
      use PhysicsStorage
      use DenseMatUtilities
      implicit none
      !-arguments-------------------------------------------------------
      class(IBM_type),  intent(inout) :: this
      type(point_type), intent(in)    :: ImagePoint
      real(kind=rp),    intent(in)    :: dt, normal(:), dWall
      integer,          intent(in)    :: STLNum 
      real(kind=rp),    intent(inout) :: Q(:)
      !-local-variables-------------------------------------------------
      real(kind=rp) :: dS_dQ(NCONS,NCONS), invdS_dQ(NCONS,NCONS), &
                       TurbulenceSource(NCONS)

      call this% semiImplicitTurbulenceJacobian( ImagePoint, Q, normal, dWall, STLNum, dS_dQ )

      invdS_dQ = -dS_dQ

      call this% SemiImplicitTurbulenceShiftJacobian( dt, invdS_dQ )

      invdS_dQ = inverse(invdS_dQ)

      call this% SourceTermTurbulence( ImagePoint, Q, normal, dWall, STLNum, TurbulenceSource )       
 
      Q = matmul(invdS_dQ, Q + dt*( TurbulenceSource - matmul(dS_dQ,Q) ))
  
    end subroutine IBM_GetSemiImplicitStepTurbulence
!    
!   Splitting correction is applied. In the splitting, dt = dt/2
!   ------------------------------------------------------------
   subroutine IBM_SemiImplicitCorrection( this, elements, t, dt )
      use PhysicsStorage
      implicit none
      !-arguments-----------------------------------
      class(IBM_type), intent(inout) :: this
      type(element),   intent(inout) :: elements(:)
      real(kind=RP),   intent(in)    :: t, dt
      !-local-variables-----------------------------
      real(kind=RP) :: Q_target(NCONS)
      integer       :: eID, i, j, k, iP

      if( .not. this% semiImplicit ) return

      if( this% Wallfunction ) call this% GetBandRegionStates( elements )
!$omp parallel
!$omp do schedule(runtime) private(i,j,k,Q_target)
      do eID = 1, SIZE( elements )
         associate(e => elements(eID))
         do i = 0, e% Nxyz(1); do j = 0, e% Nxyz(2); do k = 0, e% Nxyz(3)
            if( e% isInsideBody(i,j,k) ) then 
               if( this% stl(e% STL(i,j,k))% move ) then 
#if defined(NAVIERSTOKES)
                  Q_target = this% MaskVelocity( e% storage% Q(:,i,j,k), NCONS, e% STL(i,j,k), e% geom% x(:,i,j,k), t )
#endif
                  call this% GetSemiImplicitStep( eID, 0.5_RP*dt, e% storage% Q(:,i,j,k), Q_target )
               else
                  call this% GetSemiImplicitStep( eID, 0.5_RP*dt, e% storage% Q(:,i,j,k) ) 
               end if 
            end if 
         end do; end do; end do
         end associate
      end do
!$omp end do 
      if( this% Wallfunction ) then
!$omp do schedule(runtime) private(i,j,k)
         do iP = 1, this% NumOfForcingPoints
            associate( e => elements(this% ImagePoints(iP)% element_index))
            i = this% ImagePoints(iP)% local_position(1)
            j = this% ImagePoints(iP)% local_position(2)
            k = this% ImagePoints(iP)% local_position(3)
            call this% GetSemiImplicitStepTurbulence( this% ImagePoints(iP), 0.5_RP*dt, e% storage% Q(:,i,j,k),      &
                                                      e% geom% normal(:,i,j,k), e% geom% dWall(i,j,k), e% STL(i,j,k) )
            end associate 
         end do
!$omp end do 
      end if 
!$omp end parallel    
    end subroutine IBM_SemiImplicitCorrection
    
   subroutine IBM_GetBandRegionStates( this, elements )
      use PhysicsStorage
      implicit none
      !-arguments-----------------------------------------------
      class(IBM_type),            intent(inout) :: this
      type(element),              intent(in)    :: elements(:)
      !-local-variables-----------------------------------------
      integer :: STLNum

      do STLNum = 1, this% NumOfSTL
         call this% BandPoint_state( elements, STLNum, .false. )
      end do
    
   end subroutine IBM_GetBandRegionStates
!
!   Turbulent source term when wall function is applied
!   ---------------------------------------------------
   subroutine IBM_SourceTermTurbulence( this, ImagePoint, Q, normal, dWall, STLNum, TurbulenceSource )
      use PhysicsStorage
      use NodalStorageClass, only: NodalStorage
      implicit none
      !-arguments-----------------------------------------------------------------
      class(IBM_type),  intent(inout) :: this
      type(point_type), intent(in)    :: ImagePoint  
      real(kind=rp),    intent(in)    :: Q(:), dWall, normal(:)
      integer,          intent(in)    :: STLNum
      real(kind=rp),    intent(inout) :: TurbulenceSource(NCONS)
      !-local-variables-----------------------------------------------------------
      real(kind=rp) :: Q_IP(NCONS), Q_FP(NCONS), iP(NDIM)
      integer       :: i

      do i = 1, NCONS 
         Q_IP(i) = GetInterpolatedValue( this% BandRegion(STLNum)% Q(i,ImagePoint% nearestPoints), &
                                         ImagePoint% invPhi,                                       &
                                         ImagePoint% b,                                            &
                                         this% InterpolationType                                   )
      end do

      Q_FP = Q
#if defined(NAVIERSTOKES) 
      call ForcingPointState( Q_IP, this% IP_Distance, dWall, normal, Q_FP )
#endif
      call this% SourceTerm( ImagePoint% element_index, Q, Q_FP, TurbulenceSource, .true. )

      !TurbulenceSource = -1.0_RP/(1.0_RP*this% penalization(ImagePoint% element_index)) * (Q - Q_FP)

   end subroutine IBM_SourceTermTurbulence
!
!   State to be imposed on the forcing points due to the wall model
!   ---------------------------------------------------------------
#if defined(NAVIERSTOKES)  
   subroutine ForcingPointState( Q_IP, y_IP, y_FP, normal, Q_FP )
      use PhysicsStorage
      use WallFunctionDefinitions
      use WallFunctionBC
      use VariableConversion
      use FluidData   
#if defined(SPALARTALMARAS)
      use SpallartAlmarasTurbulence
#endif
      implicit none
      !-arguments--------------------------------------------------------------
      real(kind=rp), intent(in)    :: Q_IP(:), normal(:)
      real(kind=rp), intent(in)    :: y_IP, y_FP
      real(kind=rp), intent(inout) :: Q_FP(:)
      !-local-variables--------------------------------------------------------
      real(kind=rp)            :: nu_IP, nu_FP, mu_IP, mu_FP, T_IP, T_FP, &
                                  u_tau, u_IP(NDIM), u_IPt, u_FPt,        &
                                  u_FPn, u_FP(NDIM), u_IP_t(NDIM),        &
                                  tangent(NDIM), yplus_FP, nu_tilde,      &
                                  kappa_IP, kappa_FP
#if defined(SPALARTALMARAS)
      real(kind=rp)            :: Dump, chi, fv1, nu_t
#endif     
      call get_laminar_mu_kappa(Q_IP,mu_IP,kappa_IP)      
      nu_IP = mu_IP/Q_IP(IRHO)
            
      u_IP   = Q_IP(IRHOU:IRHOW)/Q_IP(IRHO)
       
      u_IP_t = u_IP - (dot_product(u_IP,normal) * normal)
   
      if( almostEqual(norm2(u_IP_t),0.0_RP) ) return 
      
      tangent = u_IP_t/norm2(u_IP_t)
   
      u_IPt = dot_product(u_IP,tangent)
 
      u_tau = u_tau_f(u_IPt, y_IP, nu_IP, u_tau0=.1_RP)

      call get_laminar_mu_kappa(Q_FP,mu_FP,kappa_FP)
      nu_FP = mu_FP/Q_FP(IRHO)

      u_FPt = u_plus_f(y_plus_f(y_FP, u_tau, nu_FP)) * u_tau

      u_FPn = dot_product(u_IP,normal) * y_FP/y_IP 
         
      u_FP = u_FPt*tangent + u_FPn*normal 

      T_FP = Temperature(Q_IP) + (dimensionless% Pr)**(1._RP/3._RP)/(2.0_RP*thermodynamics% cp) * (POW2(u_IPt) - POW2(u_FPt))      
#if defined(SPALARTALMARAS)      
      !yplus_FP = y_plus_f( y_FP, u_tau, nu_FP) 
      !Dump     = POW2(1.0_RP - exp(-yplus_FP/19.0_RP))      
      
      !chi  = QuarticRealPositiveRoot( 1.0_RP, -kappa*u_tau*y_FP*Dump/nu_FP, 0.0_RP, 0.0_RP, -kappa*u_tau*y_FP*Dump/nu_FP*POW3(SAmodel% cv1) )

      !nu_t = nu_FP * chi
      nu_t = kappa * u_tau * y_FP
#endif
     ! Q_FP(IRHO)  = Pressure(Q_IP)*refvalues% p/(thermodynamics% R * refValues% T * T_FP)
     ! Q_FP(IRHO)  = Q_FP(IRHO)/refvalues% rho   

      Q_FP(IRHOU:IRHOW) = Q_FP(IRHO) * u_FP

     ! Q_FP(IRHOE) = pressure(Q_IP)/( thermodynamics% gamma - 1._RP) + 0.5_RP*Q_FP(IRHO)*(POW2(u_FP(1)) + POW2(u_FP(2)) + POW2(u_FP(3)))
      Q_FP(IRHOE) = 0.5_RP*Q_FP(IRHO)*(POW2(u_FP(1)) + POW2(u_FP(2)) + POW2(u_FP(3)))
#if defined(SPALARTALMARAS)
      Q_FP(IRHOTHETA) = Q_FP(IRHO) * nu_t
#endif              
   end subroutine ForcingPointState
#endif      
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------------------
!  Ray tracing 
!  -------------------------------------------------------------
   subroutine IBM_CheckPoint( this, Point, STLNum, NumOfIntersections )
      use MPI_Process_Info
      implicit none
      !-arguments------------------------------------------------------------------
      class(IBM_type), intent(inout) :: this
      real(kind=rp),   intent(inout) :: Point(:)
      integer,         intent(in)    :: STLNum  
      integer,         intent(inout) :: NumOfIntersections     
      !-local-variables------------------------------------------------------------
      type(KDtree),  pointer     :: tree 
      real(kind=rp)              :: RayDirection(NDIM), vecAxis(NDIM)
      integer                    :: Axis, minAxis(1), &
                                    lastIndex
      logical                    :: Upward, OnSurface, OnTriBound, delete
      type(ObjsDataLinkedList_t) :: Integer_List  

      RayDirection = 0.0_RP

      OnSurface = .false.; delete = .false.

      vecAxis = (/OBB(STLNum)% MBR% Length,OBB(STLNum)% MBR% Width,abs(OBB(STLNum)% nMax) + abs(OBB(STLNum)% nMin)/)
      
      minAxis = minloc(vecAxis)
      axis = minAxis(1)

      call RayAxis( this% root(STLNum)% maxAxis, this% ClipAxis, axis )

      if( Point(axis) .le. 0.0_RP ) then
         RayDirection(axis) = -1.0_RP
         Upward = .true.
            vecAxis(1) = OBB(STLNum)% LocVertices(1,1)
            vecAxis(2) = OBB(STLNum)% LocVertices(2,1)
            vecAxis(3) = OBB(STLNum)% LocVertices(3,1)
      else
         RayDirection(axis) = 1.0_RP
         Upward = .false.
            vecAxis(1) = OBB(STLNum)% LocVertices(1,7)
            vecAxis(2) = OBB(STLNum)% LocVertices(2,7)
            vecAxis(3) = OBB(STLNum)% LocVertices(3,7)
      end if

      Integer_List = ObjsDataLinkedList_t()

      NumOfIntersections = 0; lastIndex = -1

      do 

         call this% root(STLNum)% FindLeaf( Point, tree, .false. )
        
         if( tree% index .eq. lastIndex ) then
            call this% root(STLNum)% FindLeaf( Point, tree, .true. )
         endif

         lastIndex = tree% index

         call isPointInside( Point, RayDirection, this% root(STLNum)% ObjectsList, tree, Integer_List, NumOfIntersections, OnTriBound )

         if( OnTriBound ) delete = .true.
 
         if( Upward ) then
            Point(axis) = tree% vertices(axis,1) 
            if( Point(axis) .le. vecAxis(axis) ) exit
         elseif( .not. Upward ) then
            Point(axis) = tree% vertices(axis,7) 
            if( Point(axis) .ge. vecAxis(axis) ) exit
         end if

      end do

      if( delete ) NumOfIntersections = NumOfIntersections - 1

      call integer_List% Destruct()

   end subroutine IBM_CheckPoint
!
!  Intersection between a ray an a set of triangles 
!  ------------------------------------------------
   subroutine isPointInside( Point, RayDirection, ObjectsList, tree, Integer_List, NumOfIntersections, OnTriBound )
      use RealDataLinkedList
      use omp_lib
      implicit none
      !-arguments----------------------------------------------------------------
      real(kind=rp),              intent(in)    :: Point(:), RayDirection(:)
      type(object_type),          intent(in)    :: ObjectsList(:)
      type(KDtree),               intent(inout) :: tree
      type(ObjsDataLinkedList_t), intent(inout) :: Integer_List
      integer,                    intent(inout) :: NumOfIntersections
      logical,                    intent(inout) :: OnTriBound
      !-local-variables----------------------------------------------------------
      logical                        :: Intersect, found
      integer                        :: i, index

      OnTriBound = .false.

      if( tree% NumOfObjs .eq. 0 ) then
         return
      end if 
      
      do i = 1, tree% NumOfObjs
         index = tree% ObjsIndeces(i)
         found = integer_List% Check( index )
         if( .not. found ) then
            call Integer_List% Add( index )
            
            call PointIntersectTriangle( Point,ObjectsList(index)% vertices(1)% coords, &
                                               ObjectsList(index)% vertices(2)% coords, &
                                               ObjectsList(index)% vertices(3)% coords, &
                                               RayDirection, Intersect, OnTriBound      )  
            if( Intersect ) then
               NumOfIntersections = NumOfIntersections + 1  
            end if
         end if
      end do

   end subroutine isPointInside
!  
! This subroutine checks if a ray (RayDirection) starting from a point (Point) intersects
! a triangle in 3D space. If present, the intersection point Q is Q = P + t*RayDirection.
! If there is more than one intersections, the second one is not counted if it has the same t
! as the one previously found.
! See Fast, Minimum Storage Ray/Trinagle INtersection,  Moller TRumbore
!  --------------------------------------------------------------------------------------------   
   subroutine PointIntersectTriangle( Point, TriangleVertex1, TriangleVertex2, &
                                      TriangleVertex3, RayDirection,           &
                                      Intersect, OnTriBound                    )
      use MappedGeometryClass
      implicit none
      !-arguments---------------------------------------------------------------------------------------
      real(kind=rp), intent(in)    :: Point(NDIM), RayDirection(NDIM)
      real(kind=rp), intent(in)    :: TriangleVertex1(NDIM), TriangleVertex2(NDIM), TriangleVertex3(NDIM)
      logical,       intent(inout) :: Intersect, OnTriBound 
      !-local-variables----------------------------------------------------------------------------------
      real(kind=rp) :: E1vec(NDIM), E2vec(NDIM), Pvec(NDIM), &
                       Qvec(NDIM), Tvec(NDIM), N(NDIM),      &
                       Det, u, v, invDet, t
      logical       :: isInside
      
      Intersect  = .false.
      OnTriBound = .false.
      t          = -1.0_RP
      
      E1vec = TriangleVertex2 - TriangleVertex1 
      E2vec = TriangleVertex3 - TriangleVertex1
      Tvec   = Point - TriangleVertex1
      
      call vcross(RayDirection,E2vec,Pvec)
      Det = dot_product( E1vec, Pvec )

      If( almostEqual(Det,0.0_RP) ) return
      
      call vcross(Tvec,E1vec,Qvec)
      
      invDet = 1.0_RP/Det
      
      u = dot_product( Tvec, Pvec )*invDet

      if( u < 0.0_RP .or. u > 1.0_RP ) return
      
      v = dot_product( RayDirection, Qvec )*invDet

      if( v < 0.0_RP .or. u+v > 1.0_RP ) return

      t  = dot_product( E2vec, Qvec )*invDet

      if( almostequal(t,0.0_RP) ) return 
   
      ! Check if the point lies on the boundaries of the triangle
      !----------------------------------------------------------
      if( almostEqual(u,0.0_RP) .and. ( v .ge. 0.0_RP .and. v .le. 1.0_RP ) ) OnTriBound = .true. 
      if( almostEqual(v,0.0_RP) .and. ( u .ge. 0.0_RP .and. u .le. 1.0_RP ) ) OnTriBound = .true. 
      if( almostEqual(u+v,1.0_RP) )  OnTriBound = .true.

      if( t .gt. 0.0_RP ) Intersect = .true.


   end subroutine PointIntersectTriangle
!  
! Point-triangle intersection (point and triangle are on the same plane)
!  ------------------------------------------------   
   logical function isPointInsideTri( Point, TriangleVertex1, TriangleVertex2, &
                                      TriangleVertex3 ) result( isInside )
      use MappedGeometryClass
      implicit none
      !-arguments-----------------------------------------------------------------
      real(kind=rp), intent(in) :: Point(:), TriangleVertex1(:),          &
                                   TriangleVertex2(:), TriangleVertex3(:)
      !-local-variables-----------------------------------------------------------
      real(kind=rp) :: bb(NDIM), E0(NDIM), E1(NDIM), dd(NDIM), &
                       a, b, c, d, e, f, det, s, t
      integer       :: region
      
      isInside = .false.
      
      bb = TriangleVertex1
      E0 = TriangleVertex1 - TriangleVertex2
      E1 = TriangleVertex1 - TriangleVertex3
      dd = bb - Point
   
      a = dot_product(E0,E0)  
      b = dot_product(E0,E1)
      c = dot_product(E1,E1)
      d = dot_product(E0,dd)
      e = dot_product(E1,dd)
      f = dot_product(dd,dd)
      
      det = abs( a*c - b*b)
      s    = (b*e - c*d)/det
      t    = (b*d - a*e)/det
      
      region = FindRegion( det, s, t )
      
      if( region .eq. 0 ) isInside = .true.
      
   end function isPointInsideTri

   subroutine RayAxis( maxAxis, ClipAxis, axis )
      implicit none 
      !-arguments----------------------------------
      integer, intent(in)    :: maxAxis, ClipAxis
      integer, intent(inout) :: axis
      !-local-variables----------------------------
      integer :: vecAxis(NDIM), i

      vecAxis = (/1,2,3/)

      vecAxis(maxAxis)  = 0
      if( ClipAxis .ne. 0 ) vecAxis(ClipAxis) = 0

      if( axis .eq. maxAxis .or. axis .eq. ClipAxis ) then 
         do i = 1, NDIM
            if(vecAxis(i) .eq. 0) cycle
            axis = i; exit
         end do
      end if

   end subroutine RayAxis
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  -------------------------------------------------
!  Procedures for computing the point-STL distance
!  -------------------------------------------------
! 
! This function computes the minimum distance from a point to a triangle in 3D. 
! for more ditails see https://www.geometrictools.com/Documentation/DistancePoint3Triangle3.pdf
!  ------------------------------------------------   
   subroutine MinimumPointTriDistance( Point, TriangleVertex1, TriangleVertex2, &
                                       TriangleVertex3, dist, IntersectionPoint ) 
      use MappedGeometryClass
      implicit none
      !-arguments--------------------------------------------------------------------
      real(kind=rp), intent(in)  :: Point(:), TriangleVertex1(:), &
                                    TriangleVertex2(:),        &
                                    TriangleVertex3(:)
      real(kind=rp), intent(out) :: IntersectionPoint(NDIM) 
      real(kind=rp), intent(out) :: dist     
      !-local-variables--------------------------------------------------------------
      real(kind=rp) :: bb(NDIM), E0(NDIM), E1(NDIM), dd(NDIM), & 
                       a, b, c, d, e, f, det, s, t,    &
                       tmp1, tmp0, numer, denom
      integer       :: region
      
      bb = TriangleVertex1
      E0 = TriangleVertex2 - bb
      E1 = TriangleVertex3 - bb
      dd = bb - Point
   
      a = dot_product(E0,E0) 
      b = dot_product(E0,E1) 
      c = dot_product(E1,E1) 
      d = dot_product(E0,dd) 
      e = dot_product(E1,dd) 
      f = dot_product(dd,dd)
 
      det = a*c - b*b  
      s   = b*e - c*d      
      t   = b*d - a*e

      if( (s + t) <= det ) then 
         if( s < 0.0_RP ) then 
            if( t < 0.0_RP ) then 
               region = 4 
            else 
               region = 3 
            end if 
         else if ( t < 0.0_RP ) then 
            region = 5 
         else 
            region = 0 
         end if 
      else 
         if( s < 0.0_RP ) then 
            region = 2
         elseif( t < 0.0_RP ) then 
            region = 6
         else 
            region = 1
         end if 
      end if 

      select case( region )
      case( 0 )
         s = s/det 
         t = t/det 
      case( 1 )
         numer = (c + e) - (b + d)
         if( numer <= 0.0_RP ) then 
            s = 0.0_RP 
         else 
            denom = a - 2.0_RP * b + c
            if( numer >= denom ) then 
               s = 1.0_RP 
            else 
               s = numer/denom
            end if 
         end if 
         t = 1.0_RP - s 
      case( 2 ) 
         tmp0 = b + d 
         tmp1 = c + e 
         if( tmp1 > tmp0 ) then 
            numer = tmp1 - tmp0 
            denom = a - 2.0_RP*b + c 
            if( numer >= denom ) then 
               s = 1.0_RP 
            else 
               s = numer/denom 
            end if 
            t = 1.0_RP - s 
         else
            s = 0.0_RP 
            if ( tmp1 <= 0.0_RP ) then 
               t = 1.0_RP 
            elseif( e >= 0.0_RP ) then 
               t = 0.0_RP 
            else 
               t = -e/c 
            end if 
         end if 
      case( 3 )
         s = 0.0_RP 
         if( e >= 0.0_RP ) then 
            t = 0.0_RP
         elseif( -e >= c ) then 
            t = 1.0_RP 
         else 
            t = -e/c 
         end if 
      case( 4 )
         if( d < 0.0_RP ) then 
            t = 0.0_RP 
            if( -d >= a ) then 
               s = 1.0_RP 
            else 
               s = -d/a 
            end if 
         else 
            s = 0.0_RP 
            if( e >= 0.0_RP ) then 
               t = 0.0_RP 
            elseif( -e >= c ) then 
               t = 1.0_RP 
            else 
               t = -e/c 
            end if 
         end if 
      case( 5 )
         t = 0.0_RP 
         if( d >= 0.0_RP ) then 
            s = 0.0_RP 
         elseif( -d >= a ) then 
            s = 1.0_RP 
         else 
            s = -d/a 
         end if 
      case( 6 )
         tmp0 = b + e 
         tmp1 = a + d 
         if( tmp1 > tmp0 ) then 
            numer = tmp1 - tmp0 
            denom = a - 2.0_RP*b + c 
            if( numer >= denom ) then 
               t = 1.0_RP 
            else 
               t = numer/denom 
            end if 
            s = 1.0_RP - t
         else  
            t = 0.0_RP 
            if( tmp1 <= 0.0_RP ) then 
               s = 1.0_RP 
            elseif( d >= 0.0_RP ) then 
               s = 0.0_RP 
            else 
               s = -d/a 
            end if 
         end if 
      end select

      IntersectionPoint = TriangleVertex1 + s*E0 + t*E1    
        
      dist = norm2(Point - IntersectionPoint)

   end subroutine MinimumPointTriDistance
!  
!  Identification of the section, see below.
!  --------------------------------------------------------------------------
!      \ region2 ^t
!       \        |
!        \       |
!         \      | 
!          \     |
!           \    |
!            \   |
!             \  |
!              \ |
!               \|
!                *
!                |\    P2
!                | \
!                |  \
!                |   \
!                |    \
!                |     \
!                |      \
!                |       \
!                |        \
!       region3  |         \ region1
!                |          \
!                | region0   \
!                |            \
!                |             \ P1
! ---------------*--------------*------->s
!                |P0             \
!  region4       |    region5     \ region6
   integer function FindRegion( det, s, t ) result( region )
      implicit none
      !-arguments-----------------------------
      real(kind=rp), intent(in) :: det, s, t
      
      if( s+t <= det ) then
         if( s < 0 ) then
            if( t < 0 ) then
               region = 4
            else
               region = 3
            end if
         elseif( t < 0 ) then
            region = 5
         else
            region = 0
         end if
      else
         if( s < 0 ) then
            region = 2
         elseif ( t < 0 ) then
            region = 6
         else
            region = 1
         end if
      end if 
      
   end function FindRegion
!
! This subroutine computes the minumum distance from x (global ref. frame) to the body. First,
! the box (tree) where x lies is found, then the min distance between the objects inside the tree and
! the point is computed. If the sphere whose radius is minDist, is enclosed in the box, the subroutine stops.
! If the latter condition is not true, all the boxes that intersects the sphere are checked in the same way as
! the initial one, getting new_minDist. If a lower distance is found, minDist is updated.
! minDist is the SQUARE of the actual distance.
!  -------------------------------------------------------------------------------------------------------------   
   subroutine MinimumDistance( Point, root, minDist, normal )
      implicit none
      !-arguments----------------------------------------------------------
      real(kind=rp), intent(in)    :: Point(:)
      type(KDtree),  intent(inout) :: root
      real(kind=rp), intent(inout) :: minDist
      real(kind=rp), intent(out)   :: normal(NDIM)    
      !-local-variables-----------------------------------------------------
      real(kind=rp)         :: IntersPoint(NDIM), new_IntersPoint(NDIM), &
                               dsvec(NDIM), x(NDIM), P(NDIM), IP(NDIM),  &
                               IntersectionPoint(NDIM), Dist,            &
                               New_minDist, Radius, ds
      logical               :: Intersect
      type(KDtree), pointer :: tree, newGuess
      integer               :: i, index, LeafIndex,             &
                               TriangleIndex, New_TriangleIndex
                                        
      minDist = huge(1.0_RP)

      call root% FindLeaf( Point, tree, .false. )

      LeafIndex = tree% index

      do i = 1, tree% NumOfObjs
         index = tree% ObjsIndeces(i)

         call MinimumPointTriDistance( Point, root% ObjectsList(index)% vertices(1)% coords, &
                                       root% ObjectsList(index)% vertices(2)% coords,        &
                                       root% ObjectsList(index)% vertices(3)% coords, Dist,  &
                                       IntersPoint                                           )
          
         if( Dist .lt. minDist ) then
            minDist           = Dist
            IntersectionPoint = IntersPoint   
            TriangleIndex     = index           
         end if
      end do  

      if( tree% NumOfObjs .gt. 0 ) then
      ! Check the sphere
      !-----------------
         Radius    = POW2(minDist)
         Intersect = CheckHypersphere( tree, Point, Radius )
      else
         print *, "IBM:: MinimumDistance: "
         print *, "Can't find triangles in leaf ", LeafIndex
         error stop
      end if

      nullify(tree) 

      if( Intersect ) then
         New_minDist = huge(1.0_RP)
         call MinimumDistOtherBoxes( Point, root, root, Radius, LeafIndex, New_minDist, New_IntersPoint, New_TriangleIndex )
         if( New_minDist .lt. minDist ) then 
            minDist           = New_minDist
            IntersectionPoint = New_IntersPoint
            TriangleIndex     = New_TriangleIndex
         end if
      end if    

      call OBB(root% STLNum)% ChangeRefFrame( Point, GLOBAL, P ) 
      call OBB(root% STLNum)% ChangeRefFrame( IntersectionPoint, GLOBAL, IP ) 
      
      normal = (P - IP)/norm2(P - IP)
              
   end subroutine MinimumDistance
!  
!  Distance between the point and triangles in other boxes, i.e. not the one containing it 
!  -----------------------------------------------------------------------------------------
   recursive subroutine MinimumDistOtherBoxes( Point, root, tree, Radius, LeafIndex,           &
                                               New_minDist, New_IntersPoint, New_TriangleIndex )
                                                                              
      implicit none
      !-arguments-----------------------------------------------
      real(kind=rp), intent(in)    :: Point(:)
      type(KDtree),  intent(in)    :: root
      type(KDtree),  intent(inout) :: tree
      real(kind=rp), intent(in)    :: Radius
      integer,       intent(in)    :: LeafIndex
      real(kind=rp), intent(inout) :: New_minDist
      real(kind=rp), intent(inout) :: New_IntersPoint(:)
      integer,       intent(inout) :: New_TriangleIndex 
      !-local-variables-----------------------------------------
      real(kind=rp)                  :: IntersPoint(NDIM), Dist
      logical                        :: Intersect
      integer                        :: i, index
      
      Intersect = BoxIntersectSphere( Radius, Point, tree% vertices )
      
      if( Intersect ) then
         if( tree% isLast ) then
            if( LeafIndex .ne. tree% index ) then 
               do i = 1, tree% NumOfObjs
                  index = tree% ObjsIndeces(i)
                  call MinimumPointTriDistance( Point, root% ObjectsList(index)% vertices(1)% coords, &
                                                root% ObjectsList(index)% vertices(2)% coords,        &
                                                root% ObjectsList(index)% vertices(3)% coords, Dist,  &
                                                IntersPoint                                           )
                  if( Dist .lt. New_minDist ) then
                     New_minDist       = Dist
                     New_IntersPoint   = IntersPoint
                     New_TriangleIndex = index      
                  end if
               end do    
            end if
         else
            call MinimumDistOtherBoxes( Point, root, tree% child_L,        & 
                                        Radius, LeafIndex, New_minDist,    &
                                        New_IntersPoint, New_TriangleIndex )    
            call MinimumDistOtherBoxes( Point, root, tree% child_R,        &
                                        Radius, LeafIndex, New_minDist,    &
                                        New_IntersPoint, New_TriangleIndex )
         end if
      end if
          
   end subroutine MinimumDistOtherBoxes
   
   subroutine MinimumDistanceSphereKDtree( Point, root, Radius, minDist, normal )
      implicit none
      !-arguments----------------------------------------
      real(kind=rp), intent(in)    :: Point(:), Radius
      type(KDtree),  intent(inout) :: root
      real(kind=rp), intent(inout) :: minDist
      real(kind=rp), intent(out)   :: normal(NDIM)    
      !-local-variables----------------------------------
      real(kind=RP) :: P(NDIM), IP(NDIM),       &
                       IntersectionPoint(NDIM)
      integer       :: TriangleIndex
      
      minDist = huge(1.0_RP)

      if( .not. BoxIntersectSphere( Radius, Point, root% vertices ) ) return

      call MinimumDistOtherBoxes( Point, root, root, Radius, 0, minDist, &
                                  IntersectionPoint, TriangleIndex       )
   
      call OBB(root% STLNum)% ChangeRefFrame( Point, GLOBAL, P ) 
      call OBB(root% STLNum)% ChangeRefFrame( IntersectionPoint, GLOBAL, IP ) 
      
      normal = (P - IP)/norm2(P - IP)      
   
   end subroutine MinimumDistanceSphereKDtree
   
   
!
!  Once the first distance is found, a sphere whose radius is equal to the distance is computed. If 
!  it lies inside the starting box, nothing is done; otherwise the boxes intersecting the sphere are
!  selected
!  ---------------------------------------------------------------------------------------------------
   logical function CheckHypersphere( tree, Point, minDist) result( Intersect )
   
      implicit none
      !-arguments-----------------------------------------------------
      real(kind=rp),         intent(in)    :: Point(:)
      type(KDtree),  target, intent(inout) :: tree
      real(kind=rp),         intent(inout) :: minDist
      
      Intersect = BoxIntersectSphere( minDist, Point, tree% vertices )
   
   end function CheckHypersphere
!  
!  Circle-triangle intersection
!  ---------------------------------------------------------
   logical function CircleRectIntersection( RectLength, RectWidth, RectCenter, Center, Radius ) result( Intersect )

      implicit none
      !-arguments------------------------------------------------
      real(kind=rp), intent(in) :: RectCenter(:), Center(:)
      real(kind=rp), intent(in) :: RectLength, RectWidth, Radius 
      !-local-variables------------------------------------------
      real(kind=rp) :: d(2)

      Intersect = .false.
      
      d = Center - RectCenter

      If( abs(d(1)) .gt. 0.5_RP*RectLength + Radius ) return
      If( abs(d(2)) .gt. 0.5_RP*RectWidth + Radius ) return

      if( abs(d(1)) .le. 0.5_RP*RectLength .and. &
          abs(d(2)) .le. 0.5_RP*RectWidth ) then
         Intersect = .true.
         return
      end if
      
      if( POW2(abs(d(1)) - 0.5_RP*RectLength) + POW2(abs(d(2)) - 0.5_RP*RectWidth) .le. &
          POW2(Radius) ) Intersect = .true.

   end function CircleRectIntersection
!  
!  Point-box distance
!  ------------------------------------------------
   real(kind=rp) function PointBoxDistance( Point, vertices ) result( sqrDist )
   
      implicit none
      !-arguments--------------------------------
      real(kind=rp), intent(in) :: Point(:)
      real(kind=rp), intent(in) :: vertices(:,:)
      !-local-variables--------------------------
      integer :: i
   
      sqrDist = 0.0_RP
   
      ! if the point's x-coordinate (xp) is less than the x-min coord. of the box, then
      ! the minimum distance on the x-dir is the distance between the xp and x-min, or
      ! vertices(i,1) - x-coordinate. The following loop avoids the computation of the point
      ! with the minimum distance from P. If the point P is inside the box, sqrDist = 0.  
      !--------------------------------------------------------------------------------------      
      do i = 1, NDIM
         if( Point(i) .lt. minval(vertices(i,:)) ) sqrDist = sqrDist + POW2(minval(vertices(i,:))-Point(i))
         if( Point(i) .gt. maxval(vertices(i,:)) ) sqrDist = sqrDist + POW2(Point(i)-maxval(vertices(i,:)))
      end do
   
   end function PointBoxDistance
!  
!  Sphere-box intersection, the radius must be SQUARED
!  ------------------------------------------------
   logical function BoxIntersectSphere( Radius, Center, vertices ) result( Intersect )
   
      implicit none
      !-arguments-------------------------------------------      
      real(kind=rp), intent(in) :: vertices(:,:)
      real(kind=rp), intent(in) :: Center(:)
      real(kind=rp), intent(in) :: Radius
      !-local-variables-------------------------------------
      real(kind=rp) :: sqrDist
      
      Intersect = .false.
      sqrDist = PointBoxDistance( Center, vertices )
      
      if( sqrDist .le. Radius ) Intersect = .true.
      
   end function BoxIntersectSphere
!
!  Nearest-neighbor algorithm for selecting the DoFs closer to Point. 
!  --------------------------------------------------------------------
   subroutine MinimumDistancePoints( Point, root, BandRegion, minDist, actualIndex, PointsIndex )
      use ElementClass
      implicit none
      !-arguments-------------------------------------------------------
      real(kind=rp),   intent(in)    :: Point(:)
      type(KDtree),    intent(inout) :: root
      type(IBMpoints), intent(in)    :: BandRegion
      real(kind=rp),   intent(inout) :: minDist
      integer,         intent(in)    :: actualIndex
      integer,         intent(inout) :: PointsIndex(:)
      !-local-variables-------------------------------------------------
      real(kind=rp)         :: BandPoint(NDIM), sqrDist, &
                               New_sqrDist, Radius
      logical               :: Intersect
      type(KDtree), pointer :: tree
      integer               :: i, LeafIndex, new_index, k
           
      minDist = huge(1.0_RP)

      call root% FindLeaf( Point, tree, .false. ) 
      
      LeafIndex = tree% index

      do i = 1, tree% NumOfObjs
               
         if( any(PointsIndex .eq. tree% ObjsIndeces(i)) ) cycle

         BandPoint = BandRegion% x(tree% ObjsIndeces(i))% coords
         
         sqrDist = 0.0_RP
         do k = 1, NDIM
            sqrDist = sqrDist + POW2(Point(k) - BandPoint(k))
         end do

         if( sqrDist .lt. minDist .or. almostEqual(sqrDist,minDist) ) then
               minDist                  = sqrDist
               PointsIndex(actualIndex) = tree% ObjsIndeces(i)
         end if 
         
      end do

      if( tree% NumOfObjs .gt. 0 ) then
      ! Check the sphere
      !-----------------
         if( PointsIndex(actualIndex) .eq. 0 ) then 
            BandPoint = BandRegion% x(PointsIndex(actualIndex-1))% coords
         else
            BandPoint = BandRegion% x(PointsIndex(actualIndex))% coords
         end if

         sqrDist = 0.0_RP
         do k = 1, NDIM
            sqrDist = sqrDist + POW2(Point(k) - BandPoint(k))
         end do
         
         Radius = sqrDist  !minDist
         Intersect = CheckHypersphere( tree, Point, Radius )
      else 
         print *, "IBM:: MinimumDistance: "
         print *, "Can't find triangles in leaf ", LeafIndex
         error stop
      end if
      
      nullify(tree)

      if( Intersect ) then
         New_sqrDist = huge(1.0_RP)
         call MinimumDistOtherBoxesPoints( Point, root, BandRegion, Radius, &
                                           New_sqrDist, PointsIndex,        &
                                           LeafIndex, new_index             )         
         if( New_sqrDist .le. minDist ) then
            minDist = New_sqrDist; PointsIndex(actualIndex) = new_index 
         end if
      end if    

      minDist = sqrt(minDist)
      
   end subroutine MinimumDistancePoints
!  
!  Distance between the point and DoFs in other boxes, i.e. not the one containing it 
!  -----------------------------------------------------------------------------------------
   recursive subroutine MinimumDistOtherBoxesPoints( Point, tree, BandRegion, Radius, &
                                                     New_sqrDist,  PointsIndex,       &
                                                     LeafIndex,  new_index            )
      use elementClass                                                                 
      implicit none
      !-arguments---------------------------------------------------
      real(kind=rp),   intent(in)    :: Point(:) 
      type(KDtree),    intent(inout) :: tree
      type(IBMpoints), intent(in)    :: BandRegion
      real(kind=rp),   intent(in)    :: Radius
      real(kind=rp),   intent(inout) :: New_sqrDist
      integer,         intent(inout) :: new_index
      integer,         intent(in)    :: PointsIndex(:)
      integer,         intent(in)    :: LeafIndex
      !-local-variables---------------------------------------------
      real(kind=rp) :: sqrDist, BandPoint(NDIM)
      logical       :: Intersect
      integer       :: i, k
      
      Intersect = BoxIntersectSphere( Radius, Point, tree% vertices )
      
      if( Intersect ) then
         if( tree% isLast ) then
            if( LeafIndex .ne. tree% index ) then
               do i = 1, tree% NumOfObjs
           
                  if( any(PointsIndex .eq. tree% ObjsIndeces(i)) ) cycle

                  BandPoint = BandRegion% x(tree% ObjsIndeces(i))% coords
                 
                  sqrDist = 0.0_RP
                  do k = 1, NDIM
                     sqrDist = sqrDist + POW2(Point(k) - BandPoint(k))
                  end do
                  
                  if( sqrDist .lt. New_sqrDist .or. almostEqual(sqrDist,New_sqrDist)  )then
                     New_sqrDist = sqrDist
                     new_index   = tree% ObjsIndeces(i)          
                  end if

               end do    
            end if
         else
            call MinimumDistOtherBoxesPoints( Point, tree% child_L, BandRegion, &
                                              Radius, New_sqrDist,PointsIndex,  &
                                              LeafIndex, new_index              )    
            call MinimumDistOtherBoxesPoints( Point, tree% child_R, BandRegion, &
                                              Radius, New_sqrDist, PointsIndex, &
                                              LeafIndex, new_index              )
         end if
      end if
          
   end subroutine MinimumDistOtherBoxesPoints


   subroutine GetMatrixInterpolationSystem( Point, x, invPhi, b, INTERPOLATION )
      use DenseMatUtilities
      implicit none
      !-arguments------------------------------------
      real(kind=RP),     intent(in)    :: Point(:)
      type(point_type),  intent(in)    :: x(:)
      real(kind=RP),     intent(inout) :: invPhi(:,:), b(:)
      integer,           intent(in)    :: INTERPOLATION 
      !-local-variables------------------------------
      real(kind=RP) :: Phi(size(x),size(x)),   &
                       dist(size(x),size(x)),  &
                       L(size(x)), d,          &
                       P(size(x),size(x)),     &
                       P_T(size(x),size(x)),   &
                       V(2*size(x),2*size(x)), &
                       lambda(size(x)),        &
                       pp(size(x)), w(size(b))         
      integer       :: i, j, k

      select case( INTERPOLATION )
         case( EXPONENTIAL )

            do i = 1, size(x)
               do j = i, size(x)
                  dist(i,j) = norm2(x(i)% coords - x(j)% coords)
                  dist(j,i) = dist(i,j)
               end do 
            end do

            expCoeff = 0.001_RP!norm2(Point - x(1)% coords) 

            do i = 1, size(x)
               do j = i, size(x)
                  Phi(i,j) = interpolationfunction(dist(i,j), EXPONENTIAL )
                  Phi(j,i) = Phi(i,j)
               end do
               d    = norm2(Point - x(i)% coords)
               b(i) = interpolationfunction(d, EXPONENTIAL)
            end do

            invPhi = inverse(Phi) 

         case( IDW )

            b = 0.0_RP; invPhi = 0.0_RP
            do i = 1, size(x)
               d           = norm2(Point - x(i)% coords)
               invPhi(i,i) = 1.0_RP/d
               b           = b + invPhi(i,i)
            end do  
            do i = 1, size(x)
               b(i) = 1.0_RP/b(i)
            end do 

         case( POLYHARMONIC_SPLINE )

            b = 0.0_RP; invPhi = 0.0_RP; Phi = 0.0_RP
            
            do i = 1, size(x)
               do j = i, size(x)
                  dist(i,j) = norm2(x(i)% coords - x(j)% coords)
                  dist(j,i) = dist(i,j)
               end do 
            end do
           
            do i = 1, size(x) 
               do j = i, size(x) 
                  Phi(i,j) = interpolationfunction(dist(i,j), POLYHARMONIC_SPLINE )
                  Phi(j,i) = Phi(i,j)
               end do 
               d    = norm2(Point - x(i)% coords)
               b(i) = interpolationfunction(d, POLYHARMONIC_SPLINE)
               call PolynomialVector( x(i)% coords(1), x(i)% coords(2), x(i)% coords(3), P(i,:) ) 
            end do  

            P_T = transpose(P)

            call buildMatrixPolySpline( Phi, P, P_T, V )

            invPhi = inverse(V)

         case( POLYNOMIAL )

            Phi = 0.0_RP; invPhi = 0.0_RP; b = 0.0_RP
            do i = 1, size(x)
               call PolynomialVector( x(i)% coords(1), x(i)% coords(2), x(i)% coords(3), Phi(i,:) )
            end do 

            call PolynomialVector( Point(1), Point(2), Point(3), b )

            invPhi = inverse(Phi)

         case( MLS )

            Phi = 0.0_RP; invPhi = 0.0_RP; b = 0.0_RP
            expCoeff = norm2(Point - x(1)% coords) 
            do i = 1, size(x)
               call PolynomialVector( x(i)% coords(1)-Point(1), x(i)% coords(2)-Point(2), x(i)% coords(3)-Point(3), P(i,:) )
               d    = norm2(Point - x(i)% coords)
               W(i) = interpolationfunction( d, MLS )
            end do 

            call PolynomialVector( 0.0_RP, 0.0_RP, 0.0_RP, pp )

            do j = 1, size(p,2)
               do k = 1, size(p,2)
                  do i = 1, size(x)
                     Phi(j,k) = Phi(j,k) + P(i,j)*P(i,k)*W(i)
                  end do
               end do 
            end do

            lambda = matmul(inverse(Phi),pp)

            do i = 1, size(p,2)
               do j = 1, size(p,2)
                  b(i) = b(i) + lambda(j) * p(i,j)
               end do 
               b(i) = W(i)*b(i)
            end do 

      end select 

   end subroutine GetMatrixInterpolationSystem

   real(kind=RP) function interpolationfunction( x, INTERPOLATION, k ) result(f)
      implicit none 
      real(kind=RP),           intent(in) :: x 
      integer,                 intent(in) :: INTERPOLATION 
      real(kind=rp), optional, intent(in) :: k 

      select case( INTERPOLATION )
         case( EXPONENTIAL )
         
            f = exp(-POW2(x/expCoeff))
         
         case( POLYHARMONIC_SPLINE )
         
            if( abs(x) .lt. 1.0d-12 ) then 
               f = 0.0_RP 
               return 
            end if 
            if( present(k) ) then 
               f = x**k * log(x)
            else 
               f = POW2(x) * log(x)
            end if 
         
         case( MLS )

            f = 1.0_RP/(POW2(x) + 1.0d-12)

      end select 
   end function interpolationfunction

   real(kind=RP) function GetInterpolatedValue( forcing, invPhi, b, INTERPOLATION, x, y, z ) result( value )
   implicit none 
   !-arguments------------------------------------------------------------
   real(kind=RP),           intent(in) :: forcing(:), invPhi(:,:), b(:)
   integer,                 intent(in) :: INTERPOLATION
   real(kind=RP), optional, intent(in) :: x, y, z
   !-local-variables------------------------------------------------------
   real(kind=RP), allocatable :: weights(:), f(:)

   select case( INTERPOLATION )
      case( POLYHARMONIC_SPLINE )
         
         allocate( weights(2*size(b)), &
                   f(2*size(b))        )
         f            = 0.0_RP 
         f(1:size(b)) = forcing 
         weights = matmul(invPhi,f)
         value = dot_product(weights(1:size(b)),b) +                   &
                 EvaluateInterp( weights(size(b)+1:2*size(b)), x, y, z )
         deallocate(weights,f)
      case( MLS )

         value = dot_product(forcing,b)

      case default 
         allocate(weights(size(b)))
         weights = matmul(invPhi,forcing)
         value = dot_product(weights,b)
         deallocate(weights)
   end select 

   end function GetInterpolatedValue
!  
!  Inverse Distance Weighting interpolation
!  ----------------------------------------
   subroutine GetIDW_value( Point, x, Q, value )
      use PhysicsStorage
      use MappedGeometryClass
      implicit none
      !-arguments----------------------------------------------------
      real(kind=RP),    intent(in)  :: Point(:)
      type(point_type), intent(in)  :: x(:)
      real(kind=RP),    intent(in)  :: Q(:,:)
      real(kind=RP),    intent(out) :: value(NCONS)
      !-local-variables----------------------------------------------
      real(kind=RP)            :: DistanceNormal(NDIM), d,  &
                                  distanceSqr, sqrd1,       &
                                  num(NCONS), den, xP(NDIM)
      real(kind=RP), parameter :: eps = 1.0d-10
      integer                  :: i, k
   
      num = 0.0_RP; den = 0.0_RP
   
      do i = 1, size(x)
           
         d = norm2(x(i)% coords - Point)

         num = num + Q(:,i)/(d+eps)
         den = den + 1.0_RP/(d+eps)

      end do 
   
      value = num/den
   
   end subroutine GetIDW_value
   
! estimate the yplus for a flat plate 
   
   real(kind=RP) function InitializeDistance( y_plus ) result( y )
      use FluidData
      implicit none
      !-arguments--------------------------
      real(kind=rp), intent(in) :: y_plus
      !-local-varirables-------------------
      real(kind=RP) :: nu, Cf
      
      y = 0.0_RP
#if defined(NAVIERSTOKES)     
      nu = refValues% mu / refValues% rho
#endif
      Cf = Estimate_Cf()
#if defined(NAVIERSTOKES)      
      y = sqrt(2.0_RP) * y_plus * nu /(refValues% V * sqrt(Cf)) 
#endif  
   end function InitializeDistance
   
   real(kind=RP) function Estimate_Cf() result( Cf )
      use FluidData
      implicit none

      Cf = 0.0_RP      
#if defined(NAVIERSTOKES)   
     ! Schlichting, Hermann (1979), Boundary Layer Theory... ok if Re < 10^9
!~       Cf = (2.0_RP*log10(dimensionless% Re) - 0.65_RP)**(-2.3_RP)     
      Cf = 0.058_RP * dimensionless% Re**(-0.2_RP)     
#endif        
   end function Estimate_Cf
   
   real(kind=RP) function Estimate_u_tau( ) result( u_tau )
      use FluidData
      implicit none
      !-local-variables--------------------------------
      real(kind=RP) :: Cf
      
      u_tau = 0.0_RP
      
      Cf = Estimate_Cf()
#if defined(NAVIERSTOKES) 
      u_tau = sqrt( 0.5_RP * POW2(refValues% V) * Cf ) !sqrt(tau_w/rho)
#endif      
   end function Estimate_u_tau
   
   real(kind=RP) function GetEstimated_y( y_plus, nu, u_tau ) result( y )
      use PhysicsStorage 
      implicit none
      !-arguments--------------------------------------
      real(kind=RP), intent(in) :: y_plus, nu, u_tau
   
      y = y_plus * nu / u_tau
      
      y = y/Lref 
   
   end function GetEstimated_y


   real(kind=RP) function QuarticRealPositiveRoot(a0, b0, c0, d0, e0) result( value )
      !https://math.stackexchange.com/questions/115631/quartic-equation-solution-and-conditions-for-real-roots
      implicit none
      !-arguments--------------------------------------
      real(kind=rp), intent(in) :: a0, b0, c0, d0, e0
      !-local-variables--------------------------------
      real(kind=rp) :: a, b, c, d, x, m2, m, n,  &
                       alpha, beta, psi,         & 
                       solution(2)

      a = b0/a0; b = c0/a0; c = d0/a0; d = e0/a0
      
      x = cubic_poly( 1.0_RP, -b, a*c-4.0_RP*d, d*(4.0_RP*b-POW2(a)) - POW2(c) )

      m2 = 0.25_RP*POW2(a)-b+x
      
      if( m2 .gt. 0.0_RP ) then
         m = sqrt(m2)
         n = (a*x-2.0_RP*c)/(4.0_RP*m)
      elseif( almostEqual(m2,0.0_RP) ) then
         m = 0.0_RP
         n = sqrt(0.25_RP*POW2(x) - d)
      else
         print *, 'no real roots found'
         error stop
      end if
      
      alpha = 0.5_RP*POW2(a) - x - b
      
      beta = 4.0_RP * n - a * m
      
      if( (alpha + beta) .ge. 0.0_RP ) then
         psi = sqrt(alpha+beta)
         solution(1) = -0.25_RP*a + 0.5_RP*m + 0.5_RP*psi
         solution(2) = -0.25_RP*a + 0.5_RP*m - 0.5_RP*psi
      elseif( (alpha - beta) .ge. 0.0_RP ) then
         psi = sqrt(alpha-beta)
         solution(1) = -0.25_RP*a - 0.5_RP*m + 0.5_RP*psi
         solution(2) = -0.25_RP*a - 0.5_RP*m - 0.5_RP*psi        
      end if
      !take positive solution
      value = maxval(solution) 

   end function QuarticRealPositiveRoot
   
   real(KIND=rp) function cubic_poly( a0, b0, c0, d0 ) result( x )
   
      implicit none
      !-arguments----------------------------------------------
      real(kind=RP),      intent(in) :: a0, b0, c0, d0
      !-local-variables----------------------------------------
      real(kind=RP) :: a, b, c, Jac, x0, q, r
      real(kind=RP) :: theta, m, s, txx
      integer :: i
      
      
      a = b0/a0; b = c0/a0; c = d0/a0
            
      x0 = 1.0_RP
      
      do  i = 1, 100
      
         Jac = 3.0_RP*a0*POW2(x0) + 2.0_RP*b0*x0 + c0
   
         x = x0 - eval_cubic(a0, b0, c0, d0, x0)/Jac
   
         if( abs(x-x0) .lt. 1.0E-10_RP ) return
   
         x0 = x
   
      end do
    
      print *, "cubic_poly:: Newtown method doesn't coverge"
      error stop
    
   end function cubic_poly

   real(kind=RP) function eval_cubic( a0, b0, c0, d0, x0 ) result( value )
   
      implicit none
      !-arguments----------------------------------
      real(kind=RP), intent(in) :: a0, b0, c0, d0, x0
      
      value = a0*POW3(x0) + b0*POW2(x0) + c0*x0 + d0
   
   end function eval_cubic
!
!/////////////////////////////////////////////////////////////////////////////////////////////
!  
!  ---------------------------------------------------
!  Subourinte for generating a .tec file with forces 
!  ---------------------------------------------------   
   subroutine STLScalarTEC( x, y, z, scalar, STLNum, FileName, title, variables )
   
      implicit none
      !-arguments--------------------------------------------------
      real(kind=RP),     intent(in) :: x(:), y(:), z(:), scalar(:)
      integer,           intent(in) :: STLNum
      character(len=*),  intent(in) :: FileName, title, variables
      !-local-variables--------------------------------------------
      integer :: NumOfObjs, funit
      
      NumOfObjs = size(x)
  
      call TecHeader( funit, FileName, title, variables ) 
      call WriteScalarQuatity( funit, NumOfObjs, x, y, z, scalar )
      call TECtriangle_3points( funit, NumOfObjs )

      close(funit)

   end subroutine STLScalarTEC
   
   subroutine STLvectorTEC( x, y, z, vector_x, vector_y, vector_z, STLNum, FileName, title, variables )
   
      implicit none
      !-arguments---------------------------------------------------------
      real(kind=RP),     intent(in) :: x(:), y(:), z(:), vector_x(:), &
                                       vector_y(:), vector_z(:)
      integer,           intent(in) :: STLNum
      character(len=*),  intent(in) :: FileName, title, variables
      !-local-variables--------------------------------------------------
      integer                    :: NumOfObjs, funit
      
      NumOfObjs = size(x)
  
      call TecHeader( funit, FileName, title, variables )
      call WriteVectorQuatity( funit, NumOfObjs, x, y, z, vector_x, vector_y, vector_z )
      call TECtriangle_3points( funit, NumOfObjs )

      close(funit)
       
   end subroutine STLvectorTEC
   
   subroutine TecHeader( funit, FileName, title, variables )
   
      implicit none
      !-arguments-------------------
      integer,          intent(inout) :: funit
      character(len=*), intent(in)    :: FileName, title, &
                                         variables
   
      funit = UnusedUnit()
   
      open(funit,file='RESULTS/'//trim(FileName), status='unknown')    
   
      write(funit,'(A)') 'Title="' //trim(title)// '"'
      write(funit,'(A)') 'Variables='//trim(variables)
   
   end subroutine TecHeader
   
   subroutine WriteScalarQuatity( funit, NumOfObjs, x, y, z, scalar )
   
      implicit none
      !-arguments-------------------------------------------------------
      integer,       intent(in) :: funit, NumOfObjs
      real(kind=RP), intent(in) :: x(:), y(:), z(:), scalar(:)
      !-local-variables-------------------------------------------------
      integer :: i, j
     
      write(funit,'(a,i6,a,i6,a)') 'ZONE NODES =', NumOfObjs, ',ELEMENTS =', NumOfObjs/3,', DATAPACKING=POINT, ZONETYPE=FETRIANGLE'
      
      do i = 1, NumOfObjs
         write(funit,'(4g15.6)') x(i), y(i), z(i), scalar(i)
      end do
   
   end subroutine WriteScalarQuatity
   
   subroutine WriteVectorQuatity( funit, NumOfObjs, x, y, z, vector_x, vector_y, vector_z )
   
      implicit none
      !-arguments---------------------------------------------------------
      integer,       intent(in) :: funit, NumOfObjs
      real(kind=RP), intent(in) :: x(:), y(:), z(:), vector_x(:), &
                                   vector_y(:), vector_z(:) 
      !-local-variables---------------------------------------------------
      integer :: i, j
     
      write(funit,'(a,i6,a,i6,a)') 'ZONE NODES=', NumOfObjs, ',ELEMENTS =', NumOfObjs/3,', DATAPACKING=POINT, ZONETYPE=FETRIANGLE'
      
      do i = 1, NumOfObjs
         write (funit,'(6g15.6)') x(i), y(i), z(i), vector_x(i), vector_y(i), vector_z(i)
      end do
   
   end subroutine WriteVectorQuatity
   
   subroutine WriteTimeFile( t, STLNum )
      use MPI_Process_Info
      implicit none
      !-arguments---------------------------------------------------------
      real(kind=RP), intent(in) :: t
      integer ,      intent(in) :: STLNum  
      !-local-variables---------------------------------------------------
      integer :: funit 
     
      if( .not. MPI_Process% isRoot ) return

      funit = UnusedUnit()

      open(funit,file='RESULTS/'//trim(OBB(STLNum)% FileName)//'_time.dat', action = "write" , access = "append" , status = "unknown")

      write(funit,'(1g15.6)') t

      close(funit)
   
   end subroutine WriteTimeFile

   subroutine TECtriangle_3points( funit, NumOfObjs )
      
      implicit none
      !-arguments------------------------------------
      integer, intent(in) :: funit, NumOfObjs
      !-local-variables------------------------------
      integer :: i, index
   
      index = 0

      do i = 1, NumOfObjs/3
         write(funit,'(i0,a,i0,a,i0)') index + 1,' ', index + 2,' ', index + 3
         index = index + 3
      end do
   
   end subroutine TECtriangle_3points

end module IBMClass