#include "Includes.h" module ParticlesClass #if defined(FLOW) || defined(SCALAR) use SMConstants use ParticleClass use FluidData use PhysicsStorage use HexMeshClass use FileReadingUtilities , only: getRealArrayFromString, getIntArrayFromString implicit none ! #include "Includes.h" private public Particles_t ! ! ******************************* ! Main particles class definition ! ******************************* ! type DimensionlessParticles_t real(kind=RP) :: St real(kind=RP) :: Nu real(kind=RP) :: phim real(kind=RP) :: cvpdivcv real(kind=RP) :: I0 ! Mixed fluid particles to improve performance real(kind=RP) :: gammaDiv3cvpdivcvStPr real(kind=RP) :: phimDivNo_of_particlesSt real(kind=RP) :: phimDiv3No_of_particlesNuDivgammaminus1PrM2St end type DimensionlessParticles_t ! The implementation of particles is limited to boxes right now. It should be easy ! to couple this with the boundary conditions of the solver. I do not have the time ! to do it properly right now. type pMesh_t real(kind=RP) :: min(3) ! minimum dimension of box real(kind=RP) :: max(3) ! maximum dimension of box integer :: bc(3) ! boundary condition (inflow/outflow [0], wall [1], periodic [2]) end type pMesh_t type injection_t logical :: active integer :: axis(3) ! [ , , ] direction of injection integer :: number ! particles injected per step integer :: period ! period of iterations between injections integer :: injected ! number of particles injected real(KIND=RP) :: v(3) ! Injection velocity real(KIND=RP) :: T ! Injection temperature end type injection_t type Particles_t integer :: no_of_particles integer :: part_per_parcel type(Particle_t), allocatable :: particle(:) type(dimensionlessParticles_t) :: dimensionless logical :: active logical :: highordersource type(pMesh_t) :: pMesh type(injection_t) :: injection contains procedure :: Construct => ConstructParticles procedure :: Integrate => IntegrateParticles procedure :: ExportToVTK => ExportToVTKParticles procedure :: AddSource => AddSourceParticles procedure :: ComputeSourceTerm => ComputeSourceTermParticles procedure :: Inject => InjectParticles end type Particles_t ! ! ======== contains ! ======== ! !/////////////////////////////////////////////////////////////////////////////////////// ! subroutine ConstructParticles( self, mesh, controlVariables, solution_file ) use FTValueDictionaryClass use FluidData, only : dimensionless, thermodynamics #if defined(SPALARTALMARAS) use Physics_NSSAKeywordsModule #elif defined(NAVIERSTOKES) use Physics_NSKeywordsModule #endif use headers implicit none class(Particles_t) , intent(inout) :: self class(HexMesh) , intent(in) :: mesh class(FTValueDictionary) , intent(in) :: controlVariables character(len=LINE_LENGTH), intent(in) :: solution_file #if defined(NAVIERSTOKES) ! ! --------------- ! Local variables ! --------------- ! integer :: i real(KIND=RP) :: pos(3) real(KIND=RP) :: vel(3) real(KIND=RP) :: temp ! real(kind=RP) :: dy, dz, y, z, Ly, Lz character(LEN=LINE_LENGTH) :: partFile character(LEN=1) :: trash integer :: itrash logical :: velAndTempFromFile ! ! Read information related to particles from control file !-------------------------------------------------------- self % no_of_particles = controlVariables % integerValueForKey(numberOfParticlesKey) self % part_per_parcel = controlVariables % integerValueForKey(particlesPerParcelKey) if ( controlVariables % ContainsKey(sourceTermKey) ) then self % highordersource = controlVariables % logicalValueForKey(sourceTermKey) else self % highordersource = .false. end if if (self % no_of_particles == huge(1)) then self % no_of_particles = 0 else write(STD_OUT,'(/)') call Section_Header('Loading particles') write(STD_OUT,'(/)') write(STD_OUT,'(30X,A,A28,I10)') "->" , "Number of particles: " , self % no_of_particles endif allocate( self % particle( self % no_of_particles) ) do i = 1, self % no_of_particles call self%particle(i)%init(mesh) enddo self % dimensionless % St = controlVariables % doublePrecisionValueForKey(STOKES_NUMBER_PART_KEY) self % dimensionless % phim = controlVariables % doublePrecisionValueForKey(PHI_M_PART_KEY) self % dimensionless % cvpdivcv = controlVariables % doublePrecisionValueForKey(GAMMA_PART_KEY) self % dimensionless % Nu = 2.0_RP !Stokeian flow self % dimensionless % I0 = controlVariables % doublePrecisionValueForKey(I0_PART_KEY) ! ! Collapse variables to improve performance !-------------------------------------------------------- ! gamma / (3 * cvpdivcv * St * Pr) self % dimensionless % gammaDiv3cvpdivcvStPr = thermodynamics % gamma / & ( 3 * self % dimensionless % cvpdivcv * & self % dimensionless % St * dimensionless % Pr) ! phim / ( no_of_particles * St) self % dimensionless % phimDivNo_of_particlesSt = self % part_per_parcel * self % dimensionless % phim & / (self % no_of_particles * self % dimensionless % St) ! phim / (3 * no_of_particles) * Nu / ( gammaminus1 * Pr * Mach ** 2 * St ) self % dimensionless % phimDiv3No_of_particlesNuDivgammaminus1PrM2St = & self % part_per_parcel * self % dimensionless % phim / (3 * self % no_of_particles) * self % dimensionless % Nu / & ( thermodynamics % gammaminus1 * dimensionless % Pr * dimensionless % Mach ** 2 * self % dimensionless % St ) partFile = controlVariables % StringValueForKey(key = PART_FILE_KEY, requestedLength = LINE_LENGTH) velAndTempFromFile = controlVariables % logicalValueForKey(PART_LOG_FILE_KEY) self % pMesh % min = getRealArrayFromString( controlVariables % StringValueForKey(key = MIN_BOX_KEY,& requestedLength = 132)) self % pMesh % max = getRealArrayFromString( controlVariables % StringValueForKey(key = MAX_BOX_KEY,& requestedLength = 132)) self % pMesh % bc = getIntArrayFromString( controlVariables % StringValueForKey(key = BC_BOX_KEY,& requestedLength = 132)) self % injection % active = controlVariables % logicalValueForKey(PART_LOG_INJ_KEY) ! ! Set up injection if required !-------------------------------------------------------- if (self % injection % active) then self % injection % axis = getIntArrayFromString( controlVariables % StringValueForKey(key = PART_INJ_KEY,& requestedLength = 132)) self % injection % number = controlVariables % integerValueForKey(key = PART_NUMB_PER_STEP_KEY) self % injection % period = controlVariables % integerValueForKey(key = PART_PERIOD_KEY) self % injection % v = getRealArrayFromString( controlVariables % StringValueForKey(key = INJ_VEL_KEY,& requestedLength = 132)) self % injection % T = controlVariables % doublePrecisionValueForKey(INJ_TEMP_KEY) else self % injection % axis = [0,0,0] self % injection % number = 0 self % injection % period = 0 self % injection % v = 0.0_RP self % injection % T = 0.0_RP endif ! ! Initialize particles !-------------------------------------------------------- if (self % injection % active) then do i = 1, self % no_of_particles self % particle (i) % active = .false. enddo self % injection % injected = 0 else self % injection % injected = self % no_of_particles - 1 open(UNIT=10, FILE=partFile) read(10,*) do i = 1, self % no_of_particles ! Read position of the particles from file read(10,*) itrash, & pos(1), pos(2), pos(3), & vel(1), vel(2), vel(3), temp if (itrash .ne. i ) then write(*,*) "Particles missing in the initialization from file, reducing number of particles." write(*,*) "This functionality is in beta mode. Check code for more details." itrash = itrash + 1 self % injection % injected = self % injection % injected - 1 ! This has not been tested. ! Includes this if + the update of line 238 of self % no_of_particles endif call self % particle(i) % set_pos ( pos ) ! Position the particle in the computational mesh (get element) call self % particle(i) % setGlobalPos( mesh ) if (velAndTempFromFile) then ! Initialise particle velocity and temperature from file call self % particle(i) % set_vel ( vel ) call self % particle(i) % set_temp ( temp ) else ! Get Fluid velocity and temperature for the random positions of each particle call self % particle(i) % getFluidVelandTemp( mesh ) ! Initialise particle velocity and temperature with the fluid values call self % particle(i) % set_vel ( self % particle(i) % fluidVel ) call self % particle(i) % set_temp ( self % particle(i) % fluidTemp ) endif enddo self % no_of_particles = self % injection % injected + 1 close(10) endif call ExportToVTKParticles( self, 0, solution_file ) ! ! Show particles at screen !-------------------------------------------------------- write(STD_OUT,'(30X,A,A28,L)') "->" , "Injection active: " , self % injection % active write(STD_OUT,'(30X,A,A28,L)') "->" , "High order source: " , self % highordersource write(STD_OUT,'(30X,A,A28,A132)') "->" , "Initialization file: " , partFile write(STD_OUT,'(30X,A,A30,E10.3)') "->", "Stokes number: ", self % dimensionless % St write(STD_OUT,'(30X,A,A30,E10.3)') "->", "phim: ", self % dimensionless % phim write(STD_OUT,'(30X,A,A30,E10.3)') "->", "Gamma (cvpdivcv): ", self % dimensionless % cvpdivcv write(STD_OUT,'(30X,A,A30,E10.3)') "->", "Nusselt: ", self % dimensionless % Nu write(STD_OUT,'(30X,A,A30,E10.3)') "->", "I0: ", self % dimensionless % I0 write(STD_OUT,'(30X,A,A20,A,F4.1,A,F4.1,A,F4.1,A)') "->" , "minimum box: ","[", & self % pMesh % min(1), ", ", & self % pMesh % min(2), ", ", & self % pMesh % min(3), "]" write(STD_OUT,'(30X,A,A20,A,F4.1,A,F4.1,A,F4.1,A)') "->" , "maximum box: ","[", & self % pMesh % max(1), ", ", & self % pMesh % max(2), ", ", & self % pMesh % max(3), "]" write(STD_OUT,'(30X,A,A20,A,i4.1,A,i4.1,A,i4.1,A, A61)') "->" , "bc box: ","[", & self % pMesh % bc(1), ", ", & self % pMesh % bc(2), ", ", & self % pMesh % bc(3), "]", " // [i,j,k] 0 is inflow/outflow, 1 is wall, 2 is periodic" if ( self % injection % active ) then write(STD_OUT,'(30X,A,A40,A,i4.1,A,i4.1,A,i4.1,A)') "->" , "Injection axis: ","[", & self % injection % axis(1), ", ", & self % injection % axis(2), ", ", & self % injection % axis(3), "]" write(STD_OUT,'(30X,A,A40,I7)') "->", "Injection particles per step: ", self % injection % number write(STD_OUT,'(30X,A,A40,I7)') "->", "Injection iter period: ", self % injection % period write(STD_OUT,'(30X,A,A40,A,F4.1,A,F4.1,A,F4.1,A)') "->" , "Dimensionless Injection velocity: ","[", & self % injection % v (1), ", ", & self % injection % v (2), ", ", & self % injection % v (3), "]" write(STD_OUT,'(30X,A,A40,E10.3)') "->", "Dimensionless Injection temp: ", self % injection % T endif #endif end subroutine ConstructParticles ! !/////////////////////////////////////////////////////////////////////////////////// ! subroutine IntegrateParticles( self, mesh, dt ) implicit none class(HexMesh) , intent(in) :: mesh class(Particles_t) , intent(inout) :: self real(KIND=RP) , intent(in) :: dt #if defined(NAVIERSTOKES) ! ! --------------- ! Local variables ! --------------- ! integer :: i !GTD: Change to for all particles !GTD: Adapt for MPI compatibility !GTD: Performance can be improved (a lot) !GTD: Fix makefile dependencies (now I have to sometimes do make clean) !$omp parallel do schedule(runtime) do i = 1, self % injection % injected + 1 if (self % particle(i) % active) then ! ! Get particle global position and set up interpolation !------------------------------------------------------ !call self % particle(i) % show call self % particle(i) % setGlobalPos( mesh ) !call self % particle(i) % show ! ! Get fluid velocity and temperature at that position !------------------------------------------------------ ! PREVENTS TRYING TO GET VEL AND TEMP OUTSIDE DOMAIN if ( self % particle(i) % active ) then call self % particle(i) % getFluidVelandTemp( mesh ) endif ! ! Integrate in time to get new particle velocity and temperature !------------------------------------------------------ call self % particle(i) % integrate( mesh, dt, & self % dimensionless % St, & self % dimensionless % Nu, & self % dimensionless % phim, & self % dimensionless % I0, & self % dimensionless % gammaDiv3cvpdivcvStPr, & self % pMesh % min, & self % pMesh % max, & self % pMesh % bc ) ! ! Print particle position (only for debugging) !------------------------------------------------------ ! call self % particle(i) % show() endif enddo !$omp end parallel do ! ! Print particle position (only for debugging) !------------------------------------------------------ #endif end subroutine IntegrateParticles ! !/////////////////////////////////////////////////////////////////////////////////////// ! subroutine AddSourceParticles( self, iP, e, time, thermodynamics_, dimensionless_, refValues_ ) USE ElementClass #if defined(NAVIERSTOKES) use VariableConversion, only : temperature, sutherlandsLaw #endif IMPLICIT NONE class(Particles_t) , intent(in) :: self integer , intent(in) :: iP CLASS(element) , intent(inout) :: e REAL(KIND=RP) , intent(in) :: time type(Thermodynamics_t) , intent(in) :: thermodynamics_ type(Dimensionless_t) , intent(in) :: dimensionless_ type(RefValues_t) , intent(in) :: refValues_ #if defined(NAVIERSTOKES) ! ! --------------- ! Local variables ! --------------- ! integer :: i, j, k, eID real(KIND=RP) :: Source( NCONS, 0:e % Nxyz(1), 0:e % Nxyz(2), 0:e % Nxyz(3) ) real :: t1,t2 associate ( d => self % dimensionless ) call self % computeSourceTerm( e, iP, Source ) ! Compute source term in coordinate i, j, k do k = 0, e % Nxyz(3) ; do j = 0, e % Nxyz(2) ; do i = 0, e % Nxyz(1) Source(1, i, j, k ) = 0.0_RP Source(2:4, i, j, k ) = Source(2:4, i, j, k ) * d % phimDivNo_of_particlesSt * & SutherlandsLaw( Temperature( e % storage % Q(:,i,j,k) ) ) Source(5, i, j, k ) = Source(5, i, j, k ) * d % phimDiv3No_of_particlesNuDivgammaminus1PrM2St enddo ; enddo ; enddo !$omp critical do k = 0, e % Nxyz(3) ; do j = 0, e % Nxyz(2) ; do i = 0, e % Nxyz(1) e % storage % S_NSP(1:5,i,j,k) = e % storage % S_NSP(1:5,i,j,k) + Source(1:5,i,j,k) enddo ; enddo ; enddo !$omp end critical end associate #endif end subroutine AddSourceParticles ! !/////////////////////////////////////////////////////////////////////////////////////// ! subroutine ComputeSourceTermParticles(self, e, iP, Source) USE ElementClass IMPLICIT NONE class(Particles_t) , intent(in) :: self class(element) , intent(in) :: e integer , intent(in) :: iP real(KIND=RP) , intent(out) :: Source(:,0:,0:,0:) #if defined(NAVIERSTOKES) Source = 0.0_RP call self % particle(iP) % Source( e, Source, self % highordersource ) #endif end subroutine ! !/////////////////////////////////////////////////////////////////////////////////////// ! subroutine ExportToVTKParticles( self, iter, solution_file ) implicit none class(Particles_t) , intent(in) :: self character(len=LINE_LENGTH) :: solution_file integer :: iter #if defined(NAVIERSTOKES) ! ! --------------- ! Local variables ! --------------- ! character(len=LINE_LENGTH) :: filename integer :: i integer :: no_of_particles no_of_particles = size( self % particle ) write(fileName,'(A,A,I10.10,A)') trim(solution_file),'.parts.',iter,'.csv' open(FILE = fileName, UNIT=10) write(10,*) "i", ",", "x coord", ",", "y coord", ",", "z coord", ",", "u", ",", "v", ",", "w", ",", "T" do i = 1, no_of_particles if ( self % particle (i) % active ) then write( 10, '(i10,7(A,E12.6))' ) i, ",", & self % particle (i) % pos(1), ",", self % particle (i) % pos(2), ",", self % particle (i) % pos(3), ",",& self % particle (i) % vel(1), ",", self % particle (i) % vel(2), ",", self % particle (i) % vel(3),",", & self % particle (i) % temp endif enddo close(10) #endif end subroutine ExportToVTKParticles ! !/////////////////////////////////////////////////////////////////////////////////////// ! subroutine InjectParticles( self, mesh ) implicit none class(Particles_t) , intent(inout) :: self class(HexMesh) , intent(in) :: mesh #if defined(NAVIERSTOKES) ! ! --------------- ! Local variables ! --------------- ! integer :: i, k real(KIND=RP) :: pos(3) real(KIND=RP) :: v(3), T real(KIND=RP) :: eps ! This is a hardcoded value that makes sure that the particle is inside the domain. ! Injection position is Min + (Max - Min) * eps or Max - (Max - Min) * eps depending if it is in positive or negative directions eps = 1.d-2 if ( self % injection % injected + self % injection % number + 1 > self % no_of_particles ) then return endif ! Injection velocity v = self % injection % v ! Injection temperature T = self % injection % T !!!!$omp do schedule(runtime) private(k, pos) !$omp single do i = self % injection % injected, self % injection % injected + self % injection % number do k = 1,3 call random_number( pos(k) ) pos(k) = ( pos(k) - 0.5_RP ) * ( self % pMesh % max(k) - self % pMesh % min(k) ) + & ( self % pMesh % max(k) + self % pMesh % min(k) ) / 2 enddo do k = 1, 3 if ( self % injection % axis (k) /= 0 ) then if ( self % injection % axis (k) == 1 ) then pos(k) = self % pMesh % min(k) + ( self % pMesh % max(k) - self % pMesh % min(k) ) * eps elseif ( self % injection % axis (i) == - 1 ) then pos(k) = self % pMesh % max(k) - ( self % pMesh % max(k) - self % pMesh % min(k) ) * eps endif endif enddo call self % particle(i+1) % set_pos ( pos ) ! Position the particle in the computational mesh (get element) call self % particle(i+1) % setGlobalPos( mesh ) ! Get Fluid velocity and temperature for the random positions of each particle ! It will be required for the computation of the source term. call self % particle(i+1) % getFluidVelandTemp( mesh ) ! Initialise particle velocity and temperature with the fluid values !call self % particle(i+1) % set_vel ( self % particle(i+1) % fluidVel ) !call self % particle(i+1) % set_temp ( self % particle(i+1) % fluidTemp ) call self % particle(i+1) % set_vel ( v ) call self % particle(i+1) % set_temp ( T ) enddo !$omp end single !!!!!$omp end do self % injection % injected = self % injection % injected + self % injection % number #endif end subroutine InjectParticles ! !/////////////////////////////////////////////////////////////////////////////////////// ! #endif end module ParticlesClass