Particle.f90 Source File


Source Code

#include "Includes.h"
#if  defined(FLOW) || defined(SCALAR)
module ParticleClass
 use SMConstants
 use HexMeshClass
 use ElementClass
 use PhysicsStorage
 use NodalStorageClass, only: NodalStorage
implicit none
!
#include "Includes.h"

private
public  Particle_t
!
!  ******************************
!  Main particle class definition
!  ******************************
!  
type Particle_t
    real(KIND=RP)   :: pos(3)
    real(KIND=RP)   :: pos_old(3)    
    real(KIND=RP)   :: vel(3)
    real(KIND=RP)   :: vel_old(3)
    real(KIND=RP)   :: temp
    real(KIND=RP)   :: temp_old
    real(KIND=RP)   :: fluidVel(3)
    real(KIND=RP)   :: fluidTemp

    logical         :: active
    integer                         :: eID
    integer                         :: eID_old
    real(kind=RP)                   :: x(3)
    real(kind=RP)                   :: xi(3)
    real(kind=RP)                   :: xi_old(3)
    real(kind=RP), allocatable      :: lxi(:), leta(:), lzeta(:)

    contains
        procedure   :: init                => particle_init
        procedure   :: set_pos             => particle_set_pos
        procedure   :: set_vel             => particle_set_vel
        procedure   :: set_temp            => particle_set_temp    
        procedure   :: setGlobalPos        => particle_setGlobalPos
        procedure   :: getFluidVelandTemp  => particle_getFluidVelandTemp
        procedure   :: show                => particle_show
        procedure   :: integrate           => particle_integrate
        procedure   :: source              => particle_source
        procedure   :: updateVelRK3        => particle_updateVelRK3
        procedure   :: updateTempRK3       => particle_updateTempRK3        
end type Particle_t
!
!  ========
contains
!  ========
!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine particle_init(self, mesh)
   class(particle_t)       :: self
   type(HexMesh), target   :: mesh

    self % pos(:)      = 0.0_RP
    self % vel(:)      = 0.0_RP
    self % temp        = 0.0_RP
    self % fluidVel(:) = 0.0_RP
    self % fluidTemp   = 0.0_RP    
    self % active      = .true.
    self % eID         = -1

   end subroutine particle_init
!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine particle_set_pos(self, pos)
    implicit none
    class(particle_t)  :: self
    real(KIND=RP)      :: pos(3)

    self % pos(:) = pos

end subroutine particle_set_pos
!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine particle_set_vel(self, vel)
    implicit none
    class(particle_t)  :: self
    real(KIND=RP)      :: vel(3)

    self % vel(:) = vel

end subroutine particle_set_vel
!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine particle_set_temp(self, temp)
    implicit none
    class(particle_t)  :: self
    real(KIND=RP)      :: temp

    self % temp = temp

end subroutine particle_set_temp
!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine particle_show ( self ) 
    implicit none
    class(Particle_t), intent(inout)  :: self   

    write(*,*) "eID         = ", self % eID          
    write(*,*) "active      = ", self % active       
    write(*,*) "vel(:)      = ", self % vel(:)       
    write(*,*) "temp        = ", self % temp         
    write(*,*) "pos(:)      = ", self % pos(:)       
    write(*,*) "fluidVel(:) = ", self % fluidVel(:)  
    write(*,*) "fluidTemp   = ", self % fluidTemp        

end subroutine     

!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine particle_setGlobalPos ( self, mesh )
    implicit none
    class(Particle_t)       , intent(inout)  :: self    
    class(HexMesh)          , intent(in)     :: mesh    
#if defined(NAVIERSTOKES)
!
!        ---------------
!        Local variables
!        ---------------
!
    integer                 :: i ,j, k 
    integer, dimension(1)   :: elementwas 
    
    elementwas(1)  = self % eID
    self % eID_old = self % eID 
    self % xi_old  = self % xi 
    !
    ! Find the requested point in the mesh
    ! ------------------------------------
    self % active = &
    mesh % FindPointWithCoords(self % pos, & ! physical position of the particle
                               self % eID, & ! element in which the particle is
                               self % xi,  & ! computational position of the particle (rel to eID)
                               elementwas  & ! element in which the particle was
                               )

    if ( .not. self % active) return 
    !
    ! Check whether the probe is located in other partition
    ! -----------------------------------------------------
    !  call self % LookInOtherPartitions
    !
    ! Disable the probe if the point is not found
    ! -------------------------------------------
    !  if ( .not. self % active ) then
    !     if ( MPI_Process % isRoot ) then
    !        write(STD_OUT,'(A,I0,A)') "Probe ", ID, " was not successfully initialized."
    !        print*, "Probe is set to inactive."
    !     end if

    !     return
    !  end if    
    !
    ! Get the Lagrange interpolants
    ! -----------------------------

    associate(e => mesh % elements(self % eID))
    associate(spAxi   => NodalStorage(e % Nxyz(1)), &
              spAeta  => NodalStorage(e % Nxyz(2)), &
              spAzeta => NodalStorage(e % Nxyz(3)) )
        if ( elementwas(1) /= self % eID ) then 
            if (allocated(self % lxi)) deallocate(self % lxi)
            allocate( self % lxi(0 : e % Nxyz(1)) )
            if (allocated(self % leta)) deallocate(self % leta)
            allocate( self % leta(0 : e % Nxyz(2)) )
            if (allocated(self % lzeta)) deallocate(self % lzeta)        
            allocate( self % lzeta(0 : e % Nxyz(3)) )
            self % lxi = spAxi % lj(self % xi(1))
            self % leta = spAeta % lj(self % xi(2))
            self % lzeta = spAzeta % lj(self % xi(3))
        endif 
        ! 
        ! Recover the coordinates from direct projection. These will be the real coordinates
        ! ----------------------------------------------
        self % x = 0.0_RP
        do k = 0, e % Nxyz(3)   ; do j = 0, e % Nxyz(2) ; do i = 0, e % Nxyz(1)
            self % x = self % x + e % geom % x(:,i,j,k) * self % lxi(i) * self % leta(j) * self % lzeta(k)
        end do                  ; end do                ; end do      
    end associate   
    end associate
#endif                      
end subroutine 
!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine particle_getFluidVelandTemp ( self, mesh ) 
    use Physics
    use MPI_Process_Info
    use VariableConversion
    implicit none
    class(Particle_t), intent(inout)  :: self   
    class(HexMesh)                    :: mesh
#if defined(NAVIERSTOKES)
!
!        ---------------
!        Local variables
!        ---------------
!
    integer        :: i, j, k, ierr
    integer        :: dir
    real(kind=RP)  :: value
    real(kind=RP)  :: vel(3,&
                          0:mesh % elements(self % eID) % Nxyz(1),&
                          0:mesh % elements(self % eID) % Nxyz(2),&
                          0:mesh % elements(self % eID) % Nxyz(3)  )
    real(kind=RP)  :: temp(0:mesh % elements(self % eID) % Nxyz(1),&
                           0:mesh % elements(self % eID) % Nxyz(2),&
                           0:mesh % elements(self % eID) % Nxyz(3)  )

    ! if ( MPI_Process % rank .eq. self % rank ) then
!
!           Update the probe
!           ----------------
       associate( e => mesh % elements(self % eID) )
       associate( Q => e % storage % Q )

        do k = 0, e % Nxyz(3) ; do j = 0, e % Nxyz(2)  ; do i = 0, e % Nxyz(1) 
            ! get u velocity
            vel(1,i,j,k) = Q(IRHOU,i,j,k) / Q(IRHO,i,j,k) 
            ! get v velocity
            vel(2,i,j,k) = Q(IRHOV,i,j,k) / Q(IRHO,i,j,k)
            ! get w velocity            
            vel(3,i,j,k) = Q(IRHOW,i,j,k) / Q(IRHO,i,j,k)
            ! get temp
            temp(i,j,k) = Temperature( Q(:,i,j,k) )
        end do            ; end do             ; end do

        self % fluidVel(:) = 0.0_RP
        do k = 0, e % Nxyz(3)    ; do j = 0, e % Nxyz(2)  ; do i = 0, e % Nxyz(1)
            self % fluidVel(:) = self % fluidVel(:) + vel(:,i,j,k) * self % lxi(i) * self % leta(j) * self % lzeta(k)
        end do               ; end do             ; end do

        self % fluidTemp = 0.0_RP
        do k = 0, e % Nxyz(3)    ; do j = 0, e % Nxyz(2)  ; do i = 0, e % Nxyz(1)
            self % fluidTemp = self % fluidTemp + temp(i,j,k) * self % lxi(i) * self % leta(j) * self % lzeta(k)
        end do               ; end do             ; end do        

       end associate
       end associate
! #ifdef _HAS_MPI_            
!        if ( MPI_Process % doMPIAction ) then
! !
! !              Share the result with the rest of the processes
! !              -----------------------------------------------         
!           call mpi_bcast(value, 1, MPI_DOUBLE, self % rank, MPI_COMM_WORLD, ierr)

!        end if
! #endif
!     else
! !
! !           Receive the result from the rank that contains the probe
! !           --------------------------------------------------------
! #ifdef _HAS_MPI_
!        if ( MPI_Process % doMPIAction ) then
!           call mpi_bcast(self % values(bufferPosition), 1, MPI_DOUBLE, self % rank, MPI_COMM_WORLD, ierr)
!        end if
! #endif
!     end if                    
#endif
end subroutine 
!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine particle_integrate ( self, mesh, dt, St, Nu, phim, I0, gammaDiv3cvpdivcvStPr, minbox, maxbox , bcbox ) 
#if defined(NAVIERSTOKES)
    use VariableConversion,   only : sutherlandsLaw
    use FluidData, only : dimensionless
#endif
    implicit none
    
    class(Particle_t)               , intent(inout)  :: self   
    type(HexMesh)                   , intent(in)     :: mesh
    real(KIND=RP)                   , intent(in)     :: dt 
    real(KIND=RP), intent(in) :: St         ! Particle non dimensional number
    real(KIND=RP), intent(in) :: Nu         ! Particle non dimensional number 
    real(KIND=RP), intent(in) :: phim       ! Particle non dimensional number 
    real(KIND=RP), intent(in) :: I0         ! Particle non dimensional number (radiation intensity)
    real(KIND=RP), intent(in) :: gammaDiv3cvpdivcvStPr ! Particle and fluid comb of properties to increase performance
    real(KIND=RP), intent(in)       :: minbox(3)  ! Minimum value of box for particles    
    real(KIND=RP), intent(in)       :: maxbox(3)  ! Maximum value of box for particles
    integer, intent(in)       :: bcbox(3)   ! Boundary conditions of box for particles [0,1,2] [inflow/outflow, wall, periodic]      

#if defined(NAVIERSTOKES)
!
!        ---------------
!        Local variables
!        ---------------
!
    real(KIND=RP) :: mu
    real(KIND=RP) :: invFr2
    real(KIND=RP) :: gravity(3) 

    if ( .not. self % active ) then 
      call compute_bounce_parameters(self, mesh, minbox, maxbox, bcbox)
    endif 

    mu              = SutherlandsLaw(self % fluidTemp)    ! Non dimensional viscosity mu(T)
    invFr2          = dimensionless  % invFr2             ! Fluid non dimensional number
    gravity         = dimensionless  % gravity_dir        ! Direction of gravity vector (normalised)    

!
! RK3 method for now
! --------------------------- 
!TDG: esto no es realmente correcto. Hay dos aproximaciones intrínsecas en este modelo:
!   1.- Si avanza el tiempo, habría que actualizar el flujo (self % fluidVel). 
!   2.- Si avanza el tiempo, cambia la posición de la partícula, que habría que actualizar, y el flujo
!en ese punto (self % fluidVel).
!   Al hacerlo de esta forma, el código es más eficiente. Habría que cuantificar el error cometido.

    self % vel_old  = self % vel 
    self % pos_old  = self % pos 
    self % temp_old = self % temp 

    ! VELOCITY
    self % vel = self % updateVelRK3 ( dt, mu, St, invFr2, gravity  ) 
    ! POSITION
    self % pos = self % pos + dt * self % vel
    ! TEMPERATURE
    self % temp = self % updateTempRK3 ( dt, I0, Nu, gammaDiv3cvpdivcvStPr ) 

#endif
end subroutine 
!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine compute_bounce_parameters(p, mesh, minbox, maxbox, bcbox)
   class(particle_t)   , intent(inout)      :: p
   type(HexMesh), intent(in)       :: mesh
   real(KIND=RP), intent(in)       :: minbox(3)  ! Minimum value of box for particles    
   real(KIND=RP), intent(in)       :: maxbox(3)  ! Maximum value of box for particles
   integer, intent(in)       :: bcbox(3)   ! Boundary conditions of box for particles [0,1,2] [inflow/outflow, wall, periodic] 


   integer              :: eID
   real(kind=RP)        :: pos_out(3), pos_in(3)

   eID         = p%eID_old
   pos_in      = p%pos_old
   pos_out     = p%pos
   
      ! Check exit face of the box [i+-,j+-,k+-]

         ! bcbox[yz,xz,xy] 0 is inflow/outflow, 1 is wall, 2 is periodic  
         if ( p % pos(1) < minbox(1) ) then 
            if     ( bcbox(1) == 0 ) then 
               ! Particle abandoned the domain through inflow or outflow
               ! Reinject at outflow or inflow
               ! call p % set_vel  ( v )
               ! call p % set_temp ( T )
               ! p % pos(1) = p % pos(1) - ( minbox(1) - maxbox(1) )
            elseif ( bcbox(1) == 1 ) then 
               !print*, "Warning. Particle lost through wall."
               !pos_in(1) 
               p % pos(1) = minbox(1) - ( pos_out(1) - minbox(1) )  
               p % vel(1)   = - p % vel(1)
            elseif ( bcbox(1) == 2 ) then  
               p % pos(1) = p % pos(1) - ( minbox(1) - maxbox(1) )
            endif
         endif 

         if ( p % pos(2) < minbox(2) ) then 
            if     ( bcbox(2) == 0 ) then 
               ! Particle abandoned the domain through inflow or outflow
               ! Reinject at outflow or inflow
               ! call p % set_vel  ( v )
               ! call p % set_temp ( T )
               ! p % pos(2) = p % pos(2) - ( minbox(2) - maxbox(2) )
            elseif ( bcbox(2) == 1 ) then 
               !print*, "Warning. Particle lost through wall."   
               p % pos(2) = minbox(2) - ( pos_out(2) - minbox(2) )  
               p % vel(2)   = - p % vel(2)
            elseif ( bcbox(2) == 2 ) then  
               p % pos(2) = p % pos(2) - ( minbox(2) - maxbox(2) )
            endif
         endif 

         if ( p % pos(3) < minbox(3) ) then 
            if     ( bcbox(3) == 0 ) then 
               ! Particle abandoned the domain through inflow or outflow
               ! Reinject at outflow or inflow
               ! call p % set_vel  ( v )
               ! call p % set_temp ( T )
               ! p % pos(3) = p % pos(3) - ( minbox(3) - maxbox(3) )
            elseif ( bcbox(3) == 1 ) then 
               !print*, "Warning. Particle lost through wall."
               p % pos(3) = minbox(3) - ( pos_out(3) - minbox(3) )  
               p % vel(3)   = - p % vel(3)
            elseif ( bcbox(3) == 2 ) then  
               p % pos(3) = p % pos(3) - ( minbox(3) - maxbox(3) )
            endif
         endif 

         if ( p % pos(1) > maxbox(1) ) then 
            if     ( bcbox(1) == 0 ) then 
               ! Particle abandoned the domain through inflow or outflow
               ! Reinject at outflow or inflow
               ! call p % set_vel  ( v )
               ! call p % set_temp ( T )
               ! p % pos(1) = p % pos(1) + ( minbox(1) - maxbox(1) ) 
            elseif ( bcbox(1) == 1 ) then 
               !print*, "Warning. Particle lost through wall."
               p % pos(1) = maxbox(1) - ( pos_out(1) - maxbox(1) )  
               p % vel(1)   = - p % vel(1)
            elseif ( bcbox(1) == 2 ) then  
               p % pos(1) = p % pos(1) + ( minbox(1) - maxbox(1) ) 
            endif
         endif 

         if ( p % pos(2) > maxbox(2) ) then 
            if     ( bcbox(2) == 0 ) then 
               ! Particle abandoned the domain through inflow or outflow
               ! Reinject at outflow or inflow
               ! call p % set_vel  ( v )
               ! call p % set_temp ( T )
               ! p % pos(2) = p % pos(2) + ( minbox(2) - maxbox(2) )
            elseif ( bcbox(2) == 1 ) then 
               !print*, "Warning. Particle lost through wall."
               p % pos(2) = maxbox(2) - ( pos_out(2) - maxbox(2) )  
               p % vel(2)   = - p % vel(2)
            elseif ( bcbox(2) == 2 ) then  
               p % pos(2) = p % pos(2) + ( minbox(2) - maxbox(2) ) 
            endif
         endif 

         if ( p % pos(3) > maxbox(3) ) then 
            if     ( bcbox(3) == 0 ) then 
               ! Particle abandoned the domain through inflow or outflow
               ! Reinject at outflow or inflow
               ! call p % set_vel  ( v )
               ! call p % set_temp ( T )
               ! p % pos(3) = p % pos(3) + ( minbox(3) - maxbox(3) )
            elseif ( bcbox(3) == 1 ) then 
               !print*, "Warning. Particle lost through wall."
               p % pos(3) = maxbox(3) - ( pos_out(3) - maxbox(3) )  
               p % vel(3)   = - p % vel(3)        
            elseif ( bcbox(3) == 2 ) then  
               p % pos(3) = p % pos(3) + ( minbox(3) - maxbox(3) )
            endif
         endif 

         p % eID = p%eID_old 
         call p % setGlobalPos ( mesh )

 end subroutine
!
!///////////////////////////////////////////////////////////////////////////////////////
!
 subroutine  FindPointWithCoords_Neighbours(p, mesh, pos,eID,neighbours, xi,inside)
   class(Particle_t), intent(in)      :: p
   class(HexMesh)   , intent(in)      :: mesh    
   real(kind=RP)  , intent(in)      :: pos(3)
   integer        , intent(inout)   :: eID
   integer        , intent(in)      :: neighbours(6)
   real(kind=RP)  , intent(out)     :: xi(3)
   logical        , intent(out)     :: inside

   integer  :: i
   
   inside = mesh%elements(eID)%FindPointWithCoords(pos, mesh % dir2D, xi)
   if (inside) return

   ! Else check if the point resides inside a neighbour

   do i = 1, 6
      if (neighbours(i) <= 0) cycle
      inside = mesh%elements(neighbours(i))%FindPointWithCoords(pos, mesh % dir2D, xi)
      if (inside) then
         eID = neighbours(i)
         return
      end if
   end do

   inside = .false.


end subroutine
!
!///////////////////////////////////////////////////////////////////////////////////////
!
function particle_updateVelRK3 (self, dt, mu, St, invFr2, gravity) result(Q)
    implicit none 

    class(Particle_t), intent(in)     :: self 
    real(KIND=RP),     intent(in)     :: dt
    real(KIND=RP),     intent(in)     :: mu
    real(KIND=RP),     intent(in)     :: St
    real(KIND=RP),     intent(in)     :: invFr2
    real(KIND=RP),     intent(in)     :: gravity(3)
    REAL(KIND=RP)                     :: Q(3)
#if defined(NAVIERSTOKES)
!
!   ---------------
!   Local variables
!   ---------------
!
    INTEGER        :: i, j, k    
    REAL(KIND=RP), DIMENSION(3) :: G, Qdot
    REAL(KIND=RP), DIMENSION(3) :: a = (/0.0_RP       , -5.0_RP /9.0_RP , -153.0_RP/128.0_RP/)
    REAL(KIND=RP), DIMENSION(3) :: b = (/0.0_RP       ,  1.0_RP /3.0_RP ,    3.0_RP/4.0_RP  /)
    REAL(KIND=RP), DIMENSION(3) :: c = (/1.0_RP/3.0_RP,  15.0_RP/16.0_RP,    8.0_RP/15.0_RP /)   

    G = 0.0_RP
    Q = self % vel
    DO k = 1,3
        Qdot = mu / St * ( self % fluidVel - self % vel) + invFr2 * gravity
        G = a(k) * G  + Qdot
        Q = Q         + c(k) * dt * G
    END DO
#endif
end function 
!
!///////////////////////////////////////////////////////////////////////////////////////
!
function particle_updateTempRK3 (self, dt, I0, Nu, gammaDiv3cvpdivcvStPr ) result(Q)
        implicit none
    
        class(Particle_t), intent(in)     :: self 
        real(KIND=RP),     intent(in)     :: dt
        real(KIND=RP),     intent(in)     :: I0
        real(KIND=RP),     intent(in)     :: Nu
        real(KIND=RP),     intent(in)     :: gammaDiv3cvpdivcvStPr
        real(KIND=RP)                     :: Q
#if defined(NAVIERSTOKES)    
    !
    !   ---------------
    !   Local variables
    !   ---------------
    !
        INTEGER        :: i, j, k    
        REAL(KIND=RP)               :: G, Qdot
        REAL(KIND=RP), DIMENSION(3) :: a = (/0.0_RP       , -5.0_RP /9.0_RP , -153.0_RP/128.0_RP/)
        REAL(KIND=RP), DIMENSION(3) :: b = (/0.0_RP       ,  1.0_RP /3.0_RP ,    3.0_RP/4.0_RP  /)
        REAL(KIND=RP), DIMENSION(3) :: c = (/1.0_RP/3.0_RP,  15.0_RP/16.0_RP,    8.0_RP/15.0_RP /)   

        G    = 0.0_RP
        Q = self % temp
        DO k = 1,3
            Qdot = gammaDiv3cvpdivcvStPr * &
                ( I0 - Nu * ( self % temp - self % fluidTemp ) )
            G = a(k) * G  + Qdot
            Q = Q         + c(k) * dt * G
        END DO
#endif    
    end function 
!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine particle_source ( self, e, source, highordersource )
    implicit none
    class(Particle_t)       , intent(in)    :: self    
    class(element)          , intent(in)    :: e    
    real(kind=RP)           , intent(inout) :: source(:,0:,0:,0:)    
    logical                                 :: highordersource
#if defined(NAVIERSTOKES)
!
!        ---------------
!        Local variables
!        ---------------
!
    integer         :: i ,j, k                     
    real(kind=RP)   :: vol  

    associate(spAxi   => NodalStorage(e % Nxyz(1)), &
      spAeta  => NodalStorage(e % Nxyz(2)), &
      spAzeta => NodalStorage(e % Nxyz(3)) )

    if ( .not. self % active ) return 

    !**************************************
    ! COMPUTE SCALING OF THE ELEMENT (vol) 
    !**************************************

    ! This information could be stored in the element so it does not have to be recomputed
    !vol = 0.0_RP
    !do k = 0, e % Nxyz(3)   ; do j = 0, e % Nxyz(2) ; do i = 0, e % Nxyz(1)
    !  vol = vol + e % spAxi % w(i) * e % spAeta % w(j) * e % spAzeta % w(k) * e % geom % jacobian(i,j,k)
    !end do            ; end do           ; end do
    
    ! This is the same as the lines commented at top. Update this for particle_source_gaussian if I recover it.
    vol = e % geom % volume

    !*****************************
    ! COMPUTATION OF SOURCE TERM 
    !*****************************
    if (highordersource) then 
      do k = 0, e % Nxyz(3)   ; do j = 0, e % Nxyz(2) ; do i = 0, e % Nxyz(1)  
         ! New implementation - Kopriva's idea of dealing with dirac function in weak form 
         ! This can be optimized by precomputing:
         ! self % lxi(i) * self % leta(j) * self % lzeta(k) / ( e % geom % jacobian(i,j,k) * spAxi % w(i) * spAeta % w(j) * spAzeta % w(k) )          
         source(2,i,j,k) = source(2,i,j,k) - ( self % fluidVel(1) - self % Vel(1) ) * self % lxi(i) * self % leta(j) * self % lzeta(k) &
         / ( e % geom % jacobian(i,j,k) * spAxi % w(i) * spAeta % w(j) * spAzeta % w(k) )
         source(3,i,j,k) = source(3,i,j,k) - ( self % fluidVel(2) - self % Vel(2) ) * self % lxi(i) * self % leta(j) * self % lzeta(k) &
         / ( e % geom % jacobian(i,j,k) * spAxi % w(i) * spAeta % w(j) * spAzeta % w(k) ) 
         source(4,i,j,k) = source(4,i,j,k) - ( self % fluidVel(3) - self % Vel(3) ) * self % lxi(i) * self % leta(j) * self % lzeta(k) &
         / ( e % geom % jacobian(i,j,k) * spAxi % w(i) * spAeta % w(j) * spAzeta % w(k) ) 
         source(5,i,j,k) = source(5,i,j,k) - ( self % fluidTemp   - self % temp   ) * self % lxi(i) * self % leta(j) * self % lzeta(k) &
         / ( e % geom % jacobian(i,j,k) * spAxi % w(i) * spAeta % w(j) * spAzeta % w(k) ) 
      enddo ; enddo ; enddo     
    else
      do k = 0, e % Nxyz(3)   ; do j = 0, e % Nxyz(2) ; do i = 0, e % Nxyz(1)  
         ! Old implementation - Low order approximation of the source term inside the element (constant)
         source(2,i,j,k) = source(2,i,j,k) - ( self % fluidVel(1) - self % Vel(1) ) * 1.0_RP / vol 
         source(3,i,j,k) = source(3,i,j,k) - ( self % fluidVel(2) - self % Vel(2) ) * 1.0_RP / vol 
         source(4,i,j,k) = source(4,i,j,k) - ( self % fluidVel(3) - self % Vel(3) ) * 1.0_RP / vol 
         source(5,i,j,k) = source(5,i,j,k) - ( self % fluidTemp   - self % temp   ) * 1.0_RP / vol 
      enddo ; enddo ; enddo        
    endif 
      
    end associate
#endif     
end subroutine 
!
!///////////////////////////////////////////////////////////////////////////////////////
!
subroutine particle_source_gaussian ( self, e, source )
   implicit none
   class(Particle_t)       , intent(in)    :: self    
   class(element)          , intent(in)    :: e    
   real(kind=RP)           , intent(inout) :: source(:,0:,0:,0:)    
   ! This subroutine is not used. It could replace particle_source if I want.
#if defined(NAVIERSTOKES)
!
!        ---------------
!        Local variables
!        ---------------
!
   integer         :: i ,j, k                     
   real(kind=RP)   :: delta(0:e % Nxyz(1), 0:e % Nxyz(2), 0:e % Nxyz(3) ) 
   real(kind=RP)   :: x(3) 
   real(kind=RP)   :: val, vol  
   real(kind=RP)   :: dx 

   if ( .not. self % active ) return 

   !**************************************
   ! COMPUTE SCALING OF THE ELEMENT (dx) 
   !**************************************
   associate(spAxi   => NodalStorage(e % Nxyz(1)), &
              spAeta  => NodalStorage(e % Nxyz(2)), &
              spAzeta => NodalStorage(e % Nxyz(3)) )
              
   ! only once! 
   vol = 0.0_RP
   do k = 0, e % Nxyz(3)   ; do j = 0, e % Nxyz(2) ; do i = 0, e % Nxyz(1)
     vol = vol + spAxi % w(i) * spAeta % w(j) * spAzeta % w(k) * e % geom % jacobian(i,j,k)
   end do            ; end do           ; end do
   dx = vol ** (1.0_RP/3.0_RP)

   delta = 1.0_RP / vol 


   !*********************************
   ! CONSTRUCT DISCRETE DIRAC DELTA
   !*********************************
   do k = 0, e % Nxyz(3)   ; do j = 0, e % Nxyz(2) ; do i = 0, e % Nxyz(1)
     x = e % geom % x(:,i,j,k)
     !Compute value of approximation of delta Diract (exp)
     delta(i,j,k) = deltaDirac( x(1) - self % x(1), &
                         x(2) - self % x(2), &
                         x(3) - self % x(3), &
                         dx )
   enddo ; enddo ; enddo

   !*********************************************************************************
   ! DISCRETE NORMALIZATION OF DIRAC DELTA. DISCRETE INTEGRAL IN ELEMENT EQUAL TO 1 
   !*********************************************************************************
   val = 0.0_RP
   do k = 0, e % Nxyz(3)   ; do j = 0, e % Nxyz(2) ; do i = 0, e % Nxyz(1)
     val = val + spAxi % w(i) * spAeta % w(j) * spAzeta % w(k) * e % geom % jacobian(i,j,k) * delta(i,j,k)
   end do            ; end do           ; end do

   ! CON ESTO DESACTIVADO PIERDO ENERGÍA. TENGO QUE EXTENDER ESTA IDEA A LOS VECINOS. 
   ! HACER SOLO UNA VEZ POR PARTICULA
   delta = delta / val 
 
   !*****************************
   ! COMPUTATION OF SOURCE TERM 
   !*****************************
   do k = 0, e % Nxyz(3)   ; do j = 0, e % Nxyz(2) ; do i = 0, e % Nxyz(1)              
     source(2,i,j,k) = source(2,i,j,k) - ( self % fluidVel(1) - self % Vel(1) ) * delta(i,j,k)   
     source(3,i,j,k) = source(3,i,j,k) - ( self % fluidVel(2) - self % Vel(2) ) * delta(i,j,k)
     source(4,i,j,k) = source(4,i,j,k) - ( self % fluidVel(3) - self % Vel(3) ) * delta(i,j,k)
     source(5,i,j,k) = source(5,i,j,k) - ( self % fluidTemp   - self % temp   ) * delta(i,j,k) 
   enddo ; enddo ; enddo     
   
   end associate
#endif     
end subroutine 
!
!///////////////////////////////////////////////////////////////////////////////////////
!
function deltaDirac(x,y,z,dx)
    !This function creates an approximation of a delta Dirac
    !   centered at x,y,z. 
    implicit none 
    real(kind=RP)            :: deltaDirac
    real(kind=RP)            :: x,y,z
    real(kind=RP)            :: dx

    real(kind=RP)            :: sigma 

    !sigma = 1.5 * D
    sigma = 1.5 * dx
    ! This value has been taken from Maxey, Patel, Chang and Wang 1997
    ! According to them, sigma > 1.5 grid spacing AND sigma > 1.3 D for stability
    ! They use a value of sigma ~ D 

    ! This approximation has the same triple integral as the exact 
    ! Dirac delta

    deltaDirac = ( 2 * pi * POW2( sigma ) ) ** ( -3.0_RP / 2.0_RP ) * &
        exp( - ( POW2(x) + POW2(y) + POW2(z) ) / ( 2 * POW2( sigma ) ) )

end function 
!
!///////////////////////////////////////////////////////////////////////////////////////
!    

end module 
#endif