Files and types with 'Cyl' have been changed to '2DCyl' to better
differentiate between the two types of 2D geometry.
Solvers for charged and neutral particles in 2D Cartesian space.
Added solveds for 1D neutral particles (this branch is not the place to
do it, but it was a minor change).
User Manual updated with the new accepted options.
782 lines
20 KiB
Fortran
782 lines
20 KiB
Fortran
MODULE moduleSolver
|
|
|
|
!Generic type for pusher of particles
|
|
TYPE, PUBLIC:: pusherGeneric
|
|
PROCEDURE(push_interafece), POINTER, NOPASS:: pushParticle => NULL()
|
|
!Boolean to indicate if the species is moved in the iteration
|
|
LOGICAL:: pushSpecies
|
|
!How many interations between advancing the species
|
|
INTEGER:: every
|
|
CONTAINS
|
|
PROCEDURE, PASS:: init => initPusher
|
|
|
|
END TYPE pusherGeneric
|
|
|
|
!Generic type for solver
|
|
TYPE, PUBLIC:: solverGeneric
|
|
TYPE(pusherGeneric), ALLOCATABLE:: pusher(:)
|
|
PROCEDURE(solveEM_interface), POINTER, NOPASS:: solveEM => NULL()
|
|
PROCEDURE(weightingScheme_interface), POINTER, NOPASS:: weightingScheme => NULL()
|
|
CONTAINS
|
|
PROCEDURE, PASS:: initEM
|
|
PROCEDURE, PASS:: initWS
|
|
PROCEDURE, PASS:: updateParticleCell
|
|
PROCEDURE, PASS:: updatePushSpecies
|
|
|
|
END TYPE solverGeneric
|
|
|
|
INTERFACE
|
|
!Push a particle
|
|
PURE SUBROUTINE push_interafece(part, tauIn)
|
|
USE moduleSpecies
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
REAL(8), INTENT(in):: tauIn
|
|
|
|
END SUBROUTINE push_interafece
|
|
|
|
!Solve the electromagnetic field
|
|
SUBROUTINE solveEM_interface()
|
|
|
|
END SUBROUTINE solveEM_interface
|
|
|
|
!Apply nonAnalogue scheme to a particle
|
|
SUBROUTINE weightingScheme_interface(part, volOld, volNew)
|
|
USE moduleSpecies
|
|
USE moduleMesh
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
CLASS(meshVol), POINTER, INTENT(in):: volOld
|
|
CLASS(meshVol), POINTER, INTENT(inout):: volNew
|
|
|
|
END SUBROUTINE weightingScheme_interface
|
|
|
|
END INTERFACE
|
|
|
|
TYPE(solverGeneric):: solver
|
|
|
|
CONTAINS
|
|
!Init Pusher
|
|
SUBROUTINE initPusher(self, pusherType, tau, tauSp)
|
|
USE moduleErrors
|
|
IMPLICIT NONE
|
|
|
|
CLASS(pusherGeneric), INTENT(out):: self
|
|
CHARACTER(:), ALLOCATABLE:: pusherType
|
|
REAL(8):: tau, tauSp
|
|
|
|
SELECT CASE(pusherType)
|
|
!2D Cylindrical
|
|
CASE('2DCylNeutral')
|
|
self%pushParticle => push2DCylNeutral
|
|
|
|
CASE('2DCylCharged')
|
|
self%pushParticle => push2DCylCharged
|
|
|
|
!2D Cartesian
|
|
CASE('2DCartNeutral')
|
|
self%pushParticle => push2DCartNeutral
|
|
|
|
CASE('2DCartCharged')
|
|
self%pushParticle => push2DCartCharged
|
|
|
|
!1D Cartesian
|
|
CASE('1DCartNeutral')
|
|
self%pushParticle => push1DCartNeutral
|
|
|
|
CASE('1DCartCharged')
|
|
self%pushParticle => push1DCartCharged
|
|
|
|
!1D Radial
|
|
CASE('1DRadNeutral')
|
|
self%pushParticle => push1DRadNeutral
|
|
|
|
CASE('1DRadCharged')
|
|
self%pushParticle => push1DRadCharged
|
|
|
|
CASE DEFAULT
|
|
CALL criticalError('Pusher ' // pusherType // ' not found','initPusher')
|
|
|
|
END SELECT
|
|
|
|
self%pushSpecies = .FALSE.
|
|
self%every = INT(tauSp/tau)
|
|
|
|
END SUBROUTINE initPusher
|
|
|
|
SUBROUTINE initEM(self, EMType)
|
|
IMPLICIT NONE
|
|
|
|
CLASS(solverGeneric), INTENT(inout):: self
|
|
CHARACTER(:), ALLOCATABLE:: EMType
|
|
|
|
SELECT CASE(EMType)
|
|
CASE('Electrostatic')
|
|
self%solveEM => solveElecField
|
|
|
|
END SELECT
|
|
|
|
END SUBROUTINE initEM
|
|
|
|
!Initialize the non-analogue scheme
|
|
SUBROUTINE initWS(self, NAType)
|
|
USE moduleMesh
|
|
IMPLICIT NONE
|
|
|
|
CLASS(solverGeneric), INTENT(inout):: self
|
|
CHARACTER(:), ALLOCATABLE:: NAType
|
|
|
|
SELECT CASE(NAType)
|
|
CASE ('Volume')
|
|
self%weightingScheme => volumeWScheme
|
|
|
|
END SELECT
|
|
|
|
END SUBROUTINE initWS
|
|
|
|
!Do all pushes for particles
|
|
SUBROUTINE doPushes()
|
|
USE moduleSpecies
|
|
USE moduleMesh
|
|
IMPLICIT NONE
|
|
|
|
INTEGER:: n
|
|
INTEGER:: sp
|
|
|
|
!$OMP DO
|
|
DO n=1, nPartOld
|
|
!Select species type
|
|
sp = partOld(n)%sp
|
|
!Checks if the species sp is update this iteration
|
|
IF (solver%pusher(sp)%pushSpecies) THEN
|
|
!Push particle
|
|
CALL solver%pusher(sp)%pushParticle(partOld(n), tau(sp))
|
|
!Find cell in wich particle reside
|
|
CALL solver%updateParticleCell(partOld(n))
|
|
|
|
END IF
|
|
|
|
END DO
|
|
!$OMP END DO
|
|
|
|
END SUBROUTINE doPushes
|
|
|
|
!Push one particle. Boris pusher for 2D Cyl Neutral particle
|
|
PURE SUBROUTINE push2DCylNeutral(part, tauIn)
|
|
USE moduleSpecies
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
REAL(8), INTENT(in):: tauIn
|
|
TYPE(particle):: part_temp
|
|
REAL(8):: x_new, y_new, r, sin_alpha, cos_alpha
|
|
REAL(8):: v_p_oh_star(2:3)
|
|
|
|
part_temp = part
|
|
!z
|
|
part_temp%v(1) = part%v(1)
|
|
part_temp%r(1) = part%r(1) + part_temp%v(1)*tauIn
|
|
!r,theta
|
|
v_p_oh_star(2) = part%v(2)
|
|
x_new = part%r(2) + v_p_oh_star(2)*tauIn
|
|
v_p_oh_star(3) = part%v(3)
|
|
y_new = v_p_oh_star(3)*tauIn
|
|
r = DSQRT(x_new**2+y_new**2)
|
|
part_temp%r(2) = r
|
|
IF (r > 0.D0) THEN
|
|
sin_alpha = y_new/r
|
|
cos_alpha = x_new/r
|
|
ELSE
|
|
sin_alpha = 0.D0
|
|
cos_alpha = 1.D0
|
|
END IF
|
|
part_temp%v(2) = cos_alpha*v_p_oh_star(2)+sin_alpha*v_p_oh_star(3)
|
|
part_temp%v(3) = -sin_alpha*v_p_oh_star(2)+cos_alpha*v_p_oh_star(3)
|
|
part_temp%n_in = .FALSE. !Assume particle is outside until cell is found
|
|
!Copy temporal particle to particle
|
|
part=part_temp
|
|
|
|
END SUBROUTINE push2DCylNeutral
|
|
|
|
!Push one particle. Boris pusher for 2D Cyl Charged particle
|
|
PURE SUBROUTINE push2DCylCharged(part, tauIn)
|
|
USE moduleSpecies
|
|
USE moduleEM
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
REAL(8), INTENT(in):: tauIn
|
|
REAL(8):: v_p_oh_star(2:3)
|
|
TYPE(particle):: part_temp
|
|
REAL(8):: x_new, y_new, r, sin_alpha, cos_alpha
|
|
REAL(8):: qmEFt(1:3)!charge*tauIn*EF/mass
|
|
|
|
part_temp = part
|
|
!Get electric field at particle position
|
|
qmEFt = part_temp%qm*gatherElecField(part_temp)*tauIn
|
|
!z
|
|
part_temp%v(1) = part%v(1) + qmEFt(1)
|
|
part_temp%r(1) = part%r(1) + part_temp%v(1)*tauIn
|
|
!r,theta
|
|
v_p_oh_star(2) = part%v(2) + qmEFt(2)
|
|
x_new = part%r(2) + v_p_oh_star(2)*tauIn
|
|
v_p_oh_star(3) = part%v(3) + qmEFt(3)
|
|
y_new = v_p_oh_star(3)*tauIn
|
|
r = DSQRT(x_new**2+y_new**2)
|
|
part_temp%r(2) = r
|
|
IF (r > 0.D0) THEN
|
|
sin_alpha = y_new/r
|
|
cos_alpha = x_new/r
|
|
ELSE
|
|
sin_alpha = 0.D0
|
|
cos_alpha = 1.D0
|
|
END IF
|
|
part_temp%v(2) = cos_alpha*v_p_oh_star(2)+sin_alpha*v_p_oh_star(3)
|
|
part_temp%v(3) = -sin_alpha*v_p_oh_star(2)+cos_alpha*v_p_oh_star(3)
|
|
part_temp%n_in = .FALSE. !Assume particle is outside until cell is found
|
|
!Copy temporal particle to particle
|
|
part=part_temp
|
|
|
|
END SUBROUTINE push2DCylCharged
|
|
|
|
!Push neutral particles in 2D cartesian coordinates
|
|
PURE SUBROUTINE push2DCartNeutral(part, tauIn)
|
|
USE moduleSPecies
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
REAL(8), INTENT(in):: tauIn
|
|
TYPE(particle):: part_temp
|
|
|
|
part_temp = part
|
|
|
|
!x
|
|
part_temp%v(1) = part%v(1)
|
|
part_temp%r(1) = part%r(1) + part_temp%v(1)*tauIn
|
|
|
|
!y
|
|
part_temp%v(2) = part%v(2)
|
|
part_temp%r(2) = part%r(2) + part_temp%v(2)*tauIn
|
|
|
|
part_temp%n_in = .FALSE.
|
|
|
|
part = part_temp
|
|
|
|
END SUBROUTINE push2DCartNeutral
|
|
|
|
!Push charged particles in 2D cartesian coordinates
|
|
PURE SUBROUTINE push2DCartCharged(part, tauIn)
|
|
USE moduleSPecies
|
|
USE moduleEM
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
REAL(8), INTENT(in):: tauIn
|
|
TYPE(particle):: part_temp
|
|
REAL(8):: qmEFt(1:3)
|
|
|
|
part_temp = part
|
|
!Get the electric field at particle position
|
|
qmEFt = part_temp%qm*gatherElecField(part_temp)*tauIn
|
|
|
|
!x
|
|
part_temp%v(1) = part%v(1) + qmEFt(1)
|
|
part_temp%r(1) = part%r(1) + part_temp%v(1)*tauIn
|
|
|
|
!y
|
|
part_temp%v(2) = part%v(2) + qmEFt(2)
|
|
part_temp%r(2) = part%r(2) + part_temp%v(2)*tauIn
|
|
|
|
part_temp%n_in = .FALSE.
|
|
|
|
part = part_temp
|
|
|
|
END SUBROUTINE push2DCartCharged
|
|
|
|
!Push neutral particles in 1D cartesian coordinates
|
|
PURE SUBROUTINE push1DCartNeutral(part, tauIn)
|
|
USE moduleSPecies
|
|
USE moduleEM
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
REAL(8), INTENT(in):: tauIn
|
|
TYPE(particle):: part_temp
|
|
|
|
part_temp = part
|
|
|
|
!x
|
|
part_temp%v(1) = part%v(1)
|
|
part_temp%r(1) = part%r(1) + part_temp%v(1)*tauIn
|
|
|
|
part_temp%n_in = .FALSE.
|
|
|
|
part = part_temp
|
|
|
|
END SUBROUTINE push1DCartNeutral
|
|
|
|
!Push charged particles in 1D cartesian coordinates
|
|
PURE SUBROUTINE push1DCartCharged(part, tauIn)
|
|
USE moduleSPecies
|
|
USE moduleEM
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
REAL(8), INTENT(in):: tauIn
|
|
TYPE(particle):: part_temp
|
|
REAL(8):: qmEFt(1:3)
|
|
|
|
part_temp = part
|
|
!Get the electric field at particle position
|
|
qmEFt = part_temp%qm*gatherElecField(part_temp)*tauIn
|
|
|
|
!x
|
|
part_temp%v(1) = part%v(1) + qmEFt(1)
|
|
part_temp%r(1) = part%r(1) + part_temp%v(1)*tauIn
|
|
|
|
part_temp%n_in = .FALSE.
|
|
|
|
part = part_temp
|
|
|
|
END SUBROUTINE push1DCartCharged
|
|
|
|
!Push one particle. Boris pusher for 1D Radial Neutral particle
|
|
PURE SUBROUTINE push1DRadNeutral(part, tauIn)
|
|
USE moduleSpecies
|
|
USE moduleEM
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
REAL(8), INTENT(in):: tauIn
|
|
REAL(8):: v_p_oh_star(1:2)
|
|
TYPE(particle):: part_temp
|
|
REAL(8):: x_new, y_new, r, sin_alpha, cos_alpha
|
|
|
|
part_temp = part
|
|
!r,theta
|
|
v_p_oh_star(1) = part%v(1)
|
|
x_new = part%r(1) + v_p_oh_star(1)*tauIn
|
|
v_p_oh_star(2) = part%v(2)
|
|
y_new = v_p_oh_star(2)*tauIn
|
|
r = DSQRT(x_new**2+y_new**2)
|
|
part_temp%r(1) = r
|
|
IF (r > 0.D0) THEN
|
|
sin_alpha = y_new/r
|
|
cos_alpha = x_new/r
|
|
ELSE
|
|
sin_alpha = 0.D0
|
|
cos_alpha = 1.D0
|
|
END IF
|
|
part_temp%v(1) = cos_alpha*v_p_oh_star(1)+sin_alpha*v_p_oh_star(2)
|
|
part_temp%v(2) = -sin_alpha*v_p_oh_star(1)+cos_alpha*v_p_oh_star(2)
|
|
part_temp%n_in = .FALSE. !Assume particle is outside until cell is found
|
|
!Copy temporal particle to particle
|
|
part=part_temp
|
|
|
|
END SUBROUTINE push1DRadNeutral
|
|
|
|
!Push one particle. Boris pusher for 1D Radial Charged particle
|
|
PURE SUBROUTINE push1DRadCharged(part, tauIn)
|
|
USE moduleSpecies
|
|
USE moduleEM
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
REAL(8), INTENT(in):: tauIn
|
|
REAL(8):: v_p_oh_star(1:2)
|
|
TYPE(particle):: part_temp
|
|
REAL(8):: x_new, y_new, r, sin_alpha, cos_alpha
|
|
REAL(8):: qmEFt(1:3)!charge*tauIn*EF/mass
|
|
|
|
part_temp = part
|
|
!Get electric field at particle position
|
|
qmEFt = part_temp%qm*gatherElecField(part_temp)*tauMin
|
|
!r,theta
|
|
v_p_oh_star(1) = part%v(1) + qmEFt(1)
|
|
x_new = part%r(1) + v_p_oh_star(1)*tauIn
|
|
v_p_oh_star(2) = part%v(2) + qmEFt(2)
|
|
y_new = v_p_oh_star(2)*tauIn
|
|
r = DSQRT(x_new**2+y_new**2)
|
|
part_temp%r(1) = r
|
|
IF (r > 0.D0) THEN
|
|
sin_alpha = y_new/r
|
|
cos_alpha = x_new/r
|
|
ELSE
|
|
sin_alpha = 0.D0
|
|
cos_alpha = 1.D0
|
|
END IF
|
|
part_temp%v(1) = cos_alpha*v_p_oh_star(1)+sin_alpha*v_p_oh_star(2)
|
|
part_temp%v(2) = -sin_alpha*v_p_oh_star(1)+cos_alpha*v_p_oh_star(2)
|
|
part_temp%n_in = .FALSE. !Assume particle is outside until cell is found
|
|
!Copy temporal particle to particle
|
|
part=part_temp
|
|
|
|
END SUBROUTINE push1DRadCharged
|
|
|
|
!Do the collisions in all the cells
|
|
SUBROUTINE doCollisions()
|
|
USE moduleMesh
|
|
IMPLICIT NONE
|
|
|
|
INTEGER:: e
|
|
|
|
!$OMP DO SCHEDULE(DYNAMIC)
|
|
DO e=1, mesh%numVols
|
|
CALL mesh%vols(e)%obj%collision()
|
|
END DO
|
|
!$OMP END DO
|
|
|
|
END SUBROUTINE doCollisions
|
|
|
|
SUBROUTINE doReset()
|
|
USE moduleSpecies
|
|
USE moduleMesh
|
|
USE moduleList
|
|
IMPLICIT NONE
|
|
|
|
INTEGER:: nn, n, e
|
|
INTEGER, SAVE:: nPartNew
|
|
INTEGER, SAVE:: nInjIn, nOldIn, nWScheme, nCollisions
|
|
TYPE(particle), ALLOCATABLE, SAVE:: partTemp(:)
|
|
TYPE(lNode), POINTER:: partCurr, partNext
|
|
|
|
!$OMP SECTIONS
|
|
!$OMP SECTION
|
|
nInjIn = 0
|
|
IF (ALLOCATED(partInj)) THEN
|
|
nInjIn = COUNT(partInj%n_in)
|
|
|
|
END IF
|
|
!$OMP SECTION
|
|
nOldIn = 0
|
|
IF (ALLOCATED(partOld)) THEN
|
|
nOldIn = COUNT(partOld%n_in)
|
|
|
|
END IF
|
|
!$OMP SECTION
|
|
nWScheme = partWScheme%amount
|
|
!$OMP SECTION
|
|
nCollisions = partCollisions%amount
|
|
!$OMP END SECTIONS
|
|
|
|
!$OMP BARRIER
|
|
|
|
!$OMP SINGLE
|
|
CALL MOVE_ALLOC(partOld, partTemp)
|
|
nPartNew = nInjIn + nOldIn + nWScheme + nCollisions
|
|
ALLOCATE(partOld(1:nPartNew))
|
|
!$OMP END SINGLE
|
|
|
|
!$OMP SECTIONS
|
|
!$OMP SECTION
|
|
!Reset particles from injection
|
|
nn = 0
|
|
DO n = 1, nPartInj
|
|
IF (partInj(n)%n_in) THEN
|
|
nn = nn + 1
|
|
partOld(nn) = partInj(n)
|
|
|
|
END IF
|
|
|
|
END DO
|
|
|
|
!$OMP SECTION
|
|
!Reset particles from previous iteration
|
|
nn = nInjIn
|
|
DO n = 1, nPartOld
|
|
IF (partTemp(n)%n_in) THEN
|
|
nn = nn + 1
|
|
partOld(nn) = partTemp(n)
|
|
|
|
END IF
|
|
|
|
END DO
|
|
!$OMP SECTION
|
|
!Reset particles from weighting scheme
|
|
nn = nInjIn + nOldIn
|
|
partCurr => partWScheme%head
|
|
DO n = 1, nWScheme
|
|
partNext => partCurr%next
|
|
partOld(nn+n) = partCurr%part
|
|
DEALLOCATE(partCurr)
|
|
partCurr => partNext
|
|
|
|
END DO
|
|
IF (ASSOCIATED(partWScheme%head)) NULLIFY(partWScheme%head)
|
|
IF (ASSOCIATED(partWScheme%tail)) NULLIFY(partWScheme%tail)
|
|
partWScheme%amount = 0
|
|
|
|
!$OMP SECTION
|
|
!Reset particles from collisional process
|
|
nn = nInjIn + nOldIn + nWScheme
|
|
partCurr => partCollisions%head
|
|
DO n = 1, nCollisions
|
|
partNext => partCurr%next
|
|
partOld(nn+n) = partCurr%part
|
|
DEALLOCATE(partCurr)
|
|
partCurr => partNext
|
|
|
|
END DO
|
|
IF (ASSOCIATED(partCollisions%head)) NULLIFY(partCollisions%head)
|
|
IF (ASSOCIATED(partCollisions%tail)) NULLIFY(partCollisions%tail)
|
|
partCollisions%amount = 0
|
|
|
|
!$OMP SECTION
|
|
!Reset output in nodes
|
|
DO e = 1, mesh%numNodes
|
|
CALL mesh%nodes(e)%obj%resetOutput()
|
|
|
|
END DO
|
|
|
|
!$OMP SECTION
|
|
!Erase the list of particles inside the cell
|
|
DO e = 1, mesh%numVols
|
|
mesh%vols(e)%obj%totalWeight = 0.D0
|
|
CALL mesh%vols(e)%obj%listPart_in%erase()
|
|
|
|
END DO
|
|
|
|
!$OMP END SECTIONS
|
|
|
|
!$OMP SINGLE
|
|
nPartOld = nPartNew
|
|
!$OMP END SINGLE
|
|
|
|
END SUBROUTINE doReset
|
|
|
|
!Scatter particles in the grid
|
|
SUBROUTINE doScatter
|
|
USE moduleSpecies
|
|
USE moduleMesh
|
|
IMPLICIT NONE
|
|
|
|
INTEGER:: n
|
|
|
|
!Loops over the particles to scatter them
|
|
!$OMP DO
|
|
DO n=1, nPartOld
|
|
CALL mesh%vols(partOld(n)%vol)%obj%scatter(partOld(n))
|
|
|
|
END DO
|
|
!$OMP END DO
|
|
|
|
END SUBROUTINE doScatter
|
|
|
|
SUBROUTINE doEMField()
|
|
IMPLICIT NONE
|
|
|
|
IF (ASSOCIATED(solver%solveEM)) THEN
|
|
CALL solver%solveEM()
|
|
|
|
END IF
|
|
|
|
END SUBROUTINE doEMField
|
|
|
|
!Solving the Poisson equation for electrostatic potential
|
|
SUBROUTINE solveElecField()
|
|
USE moduleEM
|
|
USE moduleMesh
|
|
USE moduleErrors
|
|
IMPLICIT NONE
|
|
|
|
INTEGER, SAVE:: INFO
|
|
INTEGER:: n
|
|
REAL(8), ALLOCATABLE, SAVE:: tempF(:)
|
|
EXTERNAL:: dgetrs
|
|
|
|
!$OMP SINGLE
|
|
ALLOCATE(tempF(1:mesh%numNodes))
|
|
!$OMP END SINGLE
|
|
|
|
CALL assembleSourceVector(tempF)
|
|
|
|
!$OMP SINGLE
|
|
CALL dgetrs('N', mesh%numNodes, 1, mesh%K, mesh%numNodes, &
|
|
mesh%IPIV, tempF, mesh%numNodes, info)
|
|
!$OMP END SINGLE
|
|
|
|
IF (info == 0) THEN
|
|
!Suscessful resolution of Poission equation
|
|
!$OMP DO
|
|
DO n = 1, mesh%numNodes
|
|
mesh%nodes(n)%obj%emData%phi = tempF(n)
|
|
|
|
END DO
|
|
!$OMP END DO
|
|
|
|
ELSE
|
|
!$OMP SINGLE
|
|
CALL criticalError('Poisson equation failed', 'solveElecField')
|
|
!$OMP END SINGLE
|
|
|
|
END IF
|
|
|
|
!$OMP SINGLE
|
|
DEALLOCATE(tempF)
|
|
!$OMP END SINGLE
|
|
|
|
END SUBROUTINE solveElecField
|
|
|
|
!Modify particle weight as a function of cell volume and splits particle
|
|
SUBROUTINE volumeWScheme(part, volOld, volNew)
|
|
USE moduleSpecies
|
|
USE moduleMesh
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
CLASS(meshVol), POINTER, INTENT(in):: volOld
|
|
CLASS(meshVol), POINTER, INTENT(inout):: volNew
|
|
REAL(8):: fractionVolume, fractionWeight
|
|
INTEGER:: nSplit
|
|
|
|
!If particle has change cell, call Weighting scheme
|
|
IF (volOld%n /= volNew%n) THEN
|
|
fractionVolume = volOld%volume/volNew%volume
|
|
|
|
part%weight = part%weight * fractionVolume
|
|
|
|
fractionWeight = part%weight / species(part%sp)%obj%weight
|
|
|
|
IF (fractionWeight >= 2.D0) THEN
|
|
nSplit = FLOOR(fractionWeight)
|
|
CALL splitParticle(part, nSplit, volNew)
|
|
|
|
ELSEIF (part%weight < 1.D0) THEN
|
|
!Particle has lost statistical meaning and will be terminated
|
|
part%n_in = .FALSE.
|
|
|
|
END IF
|
|
|
|
END IF
|
|
|
|
END SUBROUTINE volumeWScheme
|
|
|
|
!Subroutine to split the particle 'part' into a number 'nSplit' of particles.
|
|
!'nSplit-1' particles are added to the partNAScheme list
|
|
SUBROUTINE splitParticle(part, nSplit, vol)
|
|
USE moduleSpecies
|
|
USE moduleList
|
|
USE moduleMesh
|
|
USE OMP_LIB
|
|
IMPLICIT NONE
|
|
|
|
TYPE(particle), INTENT(inout):: part
|
|
INTEGER, INTENT(in):: nSplit
|
|
CLASS(meshVol), INTENT(inout):: vol
|
|
REAL(8):: newWeight
|
|
TYPE(particle), POINTER:: newPart
|
|
INTEGER:: p
|
|
|
|
newWeight = part%weight / nSplit
|
|
|
|
!Assign new weight to original particle
|
|
part%weight = newWeight
|
|
|
|
!Add new particles to list of NA particles
|
|
DO p = 2, nSplit
|
|
!Allocate the pointer for the new particles
|
|
ALLOCATE(newPart)
|
|
!Copy data from original particle
|
|
newPart = part
|
|
CALL OMP_SET_LOCK(lockWScheme)
|
|
CALL partWScheme%add(newPart)
|
|
CALL OMP_UNSET_LOCK(lockWScheme)
|
|
!Add particle to cell list
|
|
CALL OMP_SET_lock(vol%lock)
|
|
CALL vol%listPart_in%add(newPart)
|
|
CALL OMP_UNSET_lock(vol%lock)
|
|
|
|
END DO
|
|
|
|
END SUBROUTINE splitParticle
|
|
|
|
SUBROUTINE updateParticleCell(self, part)
|
|
USE moduleSpecies
|
|
USE moduleMesh
|
|
IMPLICIT NONE
|
|
|
|
CLASS(solverGeneric), INTENT(in):: self
|
|
TYPE(particle), INTENT(inout):: part
|
|
CLASS(meshVol), POINTER:: volOld, volNew
|
|
|
|
volOld => mesh%vols(part%vol)%obj
|
|
CALL volOld%findCell(part)
|
|
volNew => mesh%vols(part%vol)%obj
|
|
!Call the NA shcme
|
|
IF (ASSOCIATED(self%weightingScheme)) THEN
|
|
CALL self%weightingScheme(part, volOld, volNew)
|
|
|
|
END IF
|
|
|
|
END SUBROUTINE updateParticleCell
|
|
|
|
!Update the information about if a species needs to be moved this iteration
|
|
SUBROUTINE updatePushSpecies(self, t)
|
|
USE moduleSpecies
|
|
IMPLICIT NONE
|
|
|
|
CLASS(solverGeneric), INTENT(inout):: self
|
|
INTEGER, INTENT(in):: t
|
|
INTEGER:: s
|
|
|
|
DO s=1, nSpecies
|
|
self%pusher(s)%pushSpecies = MOD(t, self%pusher(s)%every) == 0
|
|
|
|
END DO
|
|
|
|
END SUBROUTINE updatePushSpecies
|
|
|
|
!Output the different data and information
|
|
SUBROUTINE doOutput(t)
|
|
USE moduleMesh
|
|
USE moduleOutput
|
|
USE moduleSpecies
|
|
USE moduleCompTime
|
|
IMPLICIT NONE
|
|
|
|
INTEGER, INTENT(in):: t
|
|
|
|
counterOutput = counterOutput + 1
|
|
IF (counterOutput >= triggerOutput .OR. &
|
|
t == tmax .OR. t == 0) THEN
|
|
|
|
!Resets output counter
|
|
counterOutput=0
|
|
|
|
CALL mesh%printOutput(t)
|
|
CALL mesh%printColl(t)
|
|
CALL mesh%printEM(t)
|
|
WRITE(*, "(5X,A21,I10,A1,I10)") "t/tmax: ", t, "/", tmax
|
|
WRITE(*, "(5X,A21,I10)") "Particles: ", nPartOld
|
|
IF (t == 0) THEN
|
|
WRITE(*, "(5X,A21,F8.1,A2)") " init time: ", 1.D3*tStep, "ms"
|
|
|
|
ELSE
|
|
WRITE(*, "(5X,A21,F8.1,A2)") "iteration time: ", 1.D3*tStep, "ms"
|
|
|
|
END IF
|
|
|
|
IF (nPartOld > 0) THEN
|
|
WRITE(*, "(5X,A21,F8.1,A2)") "avg t/particle: ", 1.D9*tStep/DBLE(nPartOld), "ns"
|
|
|
|
END IF
|
|
WRITE(*,*)
|
|
|
|
END IF
|
|
|
|
counterCPUTime = counterCPUTime + 1
|
|
IF (counterCPUTime >= triggerCPUTime .OR. &
|
|
t == tmax .OR. t == 0) THEN
|
|
|
|
!Reset CPU Time counter
|
|
counterCPUTime = 0
|
|
|
|
CALL printTime(t, t == 0)
|
|
|
|
END IF
|
|
|
|
END SUBROUTINE doOutput
|
|
|
|
END MODULE moduleSolver
|
|
|