fpakc/src/modules/moduleCollisions.f90
2026-03-06 09:30:33 +01:00

583 lines
18 KiB
Fortran

MODULE moduleCollisions
USE moduleSpecies
USE moduleTable
!Integer for when collisions are computed
INTEGER:: everyColl
!Abstract type for collision between two particles
TYPE, ABSTRACT:: collisionBinary
TYPE(table1D):: crossSec !cross section of collision
CONTAINS
PROCEDURE(collideBinary_interface), PASS, DEFERRED:: collide
END TYPE collisionBinary
ABSTRACT INTERFACE
SUBROUTINE collideBinary_interface(self, part_i, part_j, vRel)
USE moduleSpecies
IMPORT:: collisionBinary
CLASS(collisionBinary), INTENT(in):: self
TYPE(particle), INTENT(inout), TARGET:: part_i, part_j
REAL(8), INTENT(in):: vRel
END SUBROUTINE
END INTERFACE
!Container for binary collisions
TYPE:: collisionCont
CLASS(collisionBinary), ALLOCATABLE:: obj
END TYPE collisionCont
!Binary elastic interaction
TYPE, EXTENDS(collisionBinary):: collisionBinaryElastic
CONTAINS
PROCEDURE, PASS:: collide => collideBinaryElastic
END TYPE collisionBinaryElastic
!Ionization binary interaction
TYPE, EXTENDS(collisionBinary):: collisionBinaryIonization
REAL(8):: eThreshold !Minimum energy (non-dimensional units) required for ionization
REAL(8):: deltaV !Change in velocity due to exchange of eThreshold
CLASS(speciesCharged), POINTER:: electron !Pointer to species considerer as electrons
CLASS(speciesCharged), POINTER:: electronSecondary !Pointer to species considerer as secondary electron
CONTAINS
PROCEDURE, PASS:: collide => collideBinaryIonization
END TYPE collisionBinaryIonization
TYPE, EXTENDS(collisionBinary):: collisionBinaryRecombination
REAL(8):: eBinding !binding energy of free electron in recombining ion
REAL(8):: deltaV !Change in velocity due to energy exchange
CLASS(speciesCharged), POINTER:: electron !Pointer to species considerer as electrons
CONTAINS
PROCEDURE, PASS:: collide => collideBinaryRecombination
END TYPE collisionBinaryRecombination
!Resonant charge-exchange
TYPE, EXTENDS(collisionBinary):: collisionBinaryChargeExchange
CONTAINS
PROCEDURE, PASS:: collide => collideBinaryChargeExchange
END TYPE collisionBinaryChargeExchange
!Type for interaction matrix
TYPE:: interactionsBinary
CLASS(speciesGeneric), POINTER:: sp_i
CLASS(speciesGeneric), POINTER:: sp_j
INTEGER:: amount
TYPE(collisionCont), ALLOCATABLE:: collisions(:)
CONTAINS
PROCEDURE, PASS:: init => initInteractionBinary
PROCEDURE, PASS:: getSigmaVrel => getSigmaVrelBinary
END TYPE interactionsBinary
!Type to count number of collisions
TYPE:: tallyCollisions
INTEGER, ALLOCATABLE:: tally(:)
END TYPE
!Number of collision pairs (nSpecies*(nSpecies+1)/2)
INTEGER:: nCollPairs = 0
!Collision 'Matrix'. A symmetric 2D matrix put into a 1D array to save memory
TYPE(interactionsBinary), ALLOCATABLE, TARGET:: interactionMatrix(:)
!Folder for collision cross section tables
CHARACTER(:), ALLOCATABLE:: pathCollisions
CONTAINS
!Velocity of center of mass of two particles
PURE FUNCTION velocityCM(m_i, v_i, m_j, v_j) RESULT(vCM)
IMPLICIT NONE
REAL(8), INTENT(in):: m_i, m_j
REAL(8), INTENT(in), DIMENSION(1:3):: v_i, v_j
REAL(8):: vCM(1:3)
vCM = (m_i*v_i + m_j*v_j) / (m_i + m_j)
END FUNCTION velocityCM
!Random direction for hard sphere collisions
FUNCTION randomDirectionVHS() RESULT(n)
USE moduleConstParam
USE moduleRandom
IMPLICIT NONE
REAL(8):: n(1:3)
REAL(8):: cosXi, sinXi, eps
cosXi = random(-1.D0, 1.D0)
sinXi = DSQRT(1.D0 - cosXi**2)
eps = random(0.D0, PI2)
n = (/ cosXi, sinXi*DCOS(eps), sinXi*DSIN(eps) /)
END FUNCTION randomDirectionVHS
!Inits the interaction matrix
SUBROUTINE initInteractionMatrix(matrix)
USE moduleSpecies
IMPLICIT NONE
TYPE(interactionsBinary), INTENT(inout), ALLOCATABLE:: matrix(:)
nCollPairs = (nSpecies*(nSpecies+1))/2
ALLOCATE(matrix(1:nCollPairs))
matrix(:)%amount = 0
END SUBROUTINE initInteractionMatrix
!Gets the interaction index from the collision matrix from index i,j
FUNCTION interactionIndex(i,j) RESULT(k)
INTEGER:: i, j
INTEGER:: p
INTEGER:: k
k = i + j
p = (k + ABS(i - j))/2
k = k + (p*(p-3))/2
END FUNCTION interactionIndex
!Inits the binary interaction
SUBROUTINE initInteractionBinary(self, amount, i, j)
USE moduleMath
USE moduleSpecies
IMPLICIT NONE
CLASS(interactionsBinary), INTENT(inout):: self
INTEGER, INTENT(in):: amount
INTEGER, INTENT(in):: i, j
REAL(8):: mass_i, mass_j
self%sp_i => species(i)%obj
self%sp_j => species(j)%obj
self%amount = amount
mass_i = species(i)%obj%m
mass_j = species(j)%obj%m
ALLOCATE(self%collisions(1:self%amount))
END SUBROUTINE initInteractionBinary
SUBROUTINE getSigmaVrelBinary (self, vRel, eRel, sigmaVrelTotal, sigmaVrel)
IMPLICIT NONE
CLASS(interactionsBinary), INTENT(in):: self
REAL(8), INTENT(in):: vRel, eRel
REAL(8), INTENT(out):: sigmaVrelTotal
REAL(8), INTENT(out), ALLOCATABLE:: sigmaVrel(:)
INTEGER:: c
sigmaVrelTotal = 0.D0
ALLOCATE(sigmaVrel(1:self%amount))
DO c = 1, self%amount
sigmaVrel(c) = self%collisions(c)%obj%crossSec%get(eRel)*vRel
END DO
sigmaVrelTotal = SUM(sigmaVrel)
END SUBROUTINE getSigmaVrelBinary
!ELASTIC COLLISIONS
!Inits binary elastic collision
SUBROUTINE initBinaryElastic(collision, crossSectionFilename)
USE moduleTable
USE moduleRefParam
USE moduleConstParam
IMPLICIT NONE
CLASS(collisionBinary), INTENT(out), ALLOCATABLE:: collision
CHARACTER(:), ALLOCATABLE, INTENT(in):: crossSectionFilename
ALLOCATE(collisionBinaryElastic:: collision)
!Reads data from file
CALL collision%crossSec%init(crossSectionFilename)
!Convert to no-dimensional units
CALL collision%crossSec%convert(eV2J/(m_ref*v_ref**2), 1.D0/L_ref**2)
END SUBROUTINE initBinaryElastic
!Binary elastic process
SUBROUTINE collideBinaryElastic(self, part_i, part_j, vRel)
USE moduleSpecies
USE moduleConstParam
USE moduleRandom
USE moduleMath
IMPLICIT NONE
CLASS(collisionBinaryElastic), INTENT(in):: self
TYPE(particle), INTENT(inout), TARGET:: part_i, part_j
REAL(8), INTENT(in):: vRel
REAL(8):: m_i, m_j
REAL(8), DIMENSION(1:3):: vCM, vp
m_i = part_i%species%m
m_j = part_j%species%m
!Applies the collision
vCM = velocityCM(part_i%weight*m_i, part_i%v, part_j%weight*m_j, part_j%v)
vp = vRel*randomDirectionVHS()
!Assign velocities to particles
part_i%v = vCM + m_j*vp / (m_i + m_j)
part_j%v = vCM - m_i*vp / (m_i + m_j)
END SUBROUTINE collideBinaryElastic
!ELECTRON IMPACT IONIZATION
!Inits electron impact ionization
SUBROUTINE initBinaryIonization(collision, crossSectionFilename, energyThreshold, electron, electronSecondary)
USE moduleTable
USE moduleRefParam
USE moduleConstParam
USE moduleSpecies
USE moduleErrors
IMPLICIT NONE
CLASS(collisionBinary), INTENT(out), ALLOCATABLE:: collision
CHARACTER(:), ALLOCATABLE, INTENT(in):: crossSectionFilename
REAL(8), INTENT(in):: energyThreshold
CHARACTER(:), ALLOCATABLE, INTENT(in):: electron
CHARACTER(:), ALLOCATABLE, OPTIONAL, INTENT(in):: electronSecondary
INTEGER:: electronIndex, electronSecondaryIndex
ALLOCATE(collisionBinaryIonization:: collision)
!Reads data from file
CALL collision%crossSec%init(crossSectionFilename)
!Convert to no-dimensional units
CALL collision%crossSec%convert(eV2J/(m_ref*v_ref**2), 1.D0/L_ref**2)
!Specific parameters for ionization collision
SELECT TYPE(collision)
TYPE IS(collisionBinaryIonization)
!Assign the energy threshold
!Input energy is in eV. Convert to J with ev2J and then to
!non-dimensional units.
collision%eThreshold = energyThreshold*eV2J/(m_ref*v_ref**2)
!species for impacting electron
electronIndex = speciesName2Index(electron)
SELECT TYPE(sp => species(electronIndex)%obj)
TYPE IS(speciesCharged)
collision%electron => sp
CLASS DEFAULT
CALL criticalError("Species " // sp%name // " chosen for " // &
"impacting electron is not a charged species", 'initBinaryIonization')
END SELECT
IF (PRESENT(electronSecondary)) THEN
electronSecondaryIndex = speciesName2Index(electronSecondary)
SELECT TYPE(sp => species(electronSecondaryIndex)%obj)
TYPE IS(speciesCharged)
collision%electronSecondary => sp
CLASS DEFAULT
CALL criticalError("Species " // sp%name // " chosen for " // &
"secondary electron is not a charged species", 'initBinaryIonization')
END SELECT
ELSE
collision%electronSecondary => NULL()
END IF
!momentum change per ionization process
collision%deltaV = sqrt(collision%eThreshold / collision%electron%m)
END SELECT
END SUBROUTINE initBinaryIonization
!Binary electron impact ionization process
SUBROUTINE collideBinaryIonization(self, part_i, part_j, vRel)
USE moduleSpecies
USE moduleErrors
USE moduleList
USE moduleRandom
USE moduleMath
USE OMP_LIB
IMPLICIT NONE
CLASS(collisionBinaryIonization), INTENT(in):: self
TYPE(particle), INTENT(inout), TARGET:: part_i, part_j
REAL(8), INTENT(in):: vRel
REAL(8):: rMass, eRel
TYPE(particle), POINTER:: electron => NULL(), neutral => NULL()
REAL(8), DIMENSION(1:3):: vChange
TYPE(particle), POINTER:: newElectron => NULL(), remainingNeutral => NULL()
rMass = reducedMass(part_i%weight*part_i%species%m, part_j%weight*part_j%species%m)
eRel = rMass*vRel**2
!Relative energy must be higher than threshold
IF (eRel > self%eThreshold) THEN
IF (ASSOCIATED(part_i%species, self%electron)) THEN
electron => part_i
neutral => part_j
ELSEIF(ASSOCIATED(part_j%species, self%electron)) THEN
electron => part_j
neutral => part_i
ELSE
CALL criticalError("No matching between input particles and ionizing species", 'collideBinaryIonization')
END IF
!Exchange of velocity between particles
vChange = self%deltaV*randomDirectionVHS()
!Energy is loss by the primary electron
electron%v = electron%v - vChange
!Creates a new electron from ionization
ALLOCATE(newElectron)
!Copy basic information from primary electron
newElectron = electron
!If secondary electron species indicates, convert
IF (ASSOCIATED(self%electronSecondary)) THEN
newElectron%species => self%electronSecondary
END IF
!Secondary electorn gains energy from ionization
newElectron%v = vChange
!Correct the weight of the particles
IF (electron%weight >= neutral%weight) THEN
!If primary electron is hevier than neutral, reduce weight to secondary electron
newElectron%weight = neutral%weight
ELSEIF (electron%weight < neutral%weight) THEN
!If primary electron is ligther than neutral, change weight of neutral and create new neutral
ALLOCATE(remainingNeutral)
remainingNeutral = neutral
remainingNeutral%weight = neutral%weight - electron%weight
neutral%weight = electron%weight
END IF
!Ionize neutral particle
SELECT TYPE(sp => neutral%species)
TYPE IS(speciesNeutral)
CALL sp%ionize(neutral)
CLASS DEFAULT
CALL criticalError(sp%name // " is not a neutral", 'collideBinaryIonization')
RETURN
END SELECT
!Adds new particles to the list
CALL partCollisions%setLock()
CALL partCollisions%add(newElectron)
IF (ASSOCIATED(remainingNeutral)) THEN
CALL partCollisions%add(remainingNeutral)
END IF
CALL partCollisions%unsetLock()
END IF
END SUBROUTINE collideBinaryIonization
!ELECTRON ION RESONANT RECOMBINATION
!Inits electron ion recombination
SUBROUTINE initBinaryRecombination(collision, crossSectionFilename, energyBinding, electron)
USE moduleTable
USE moduleRefParam
USE moduleConstParam
USE moduleSpecies
USE moduleErrors
IMPLICIT NONE
CLASS(collisionBinary), INTENT(out), ALLOCATABLE:: collision
CHARACTER(:), ALLOCATABLE, INTENT(in):: crossSectionFilename
REAL(8), INTENT(in):: energyBinding
CHARACTER(:), ALLOCATABLE, INTENT(in):: electron
INTEGER:: electronIndex
ALLOCATE(collisionBinaryRecombination:: collision)
!Reads data from file
CALL collision%crossSec%init(crossSectionFilename)
!Convert to no-dimensional units
CALL collision%crossSec%convert(eV2J/(m_ref*v_ref**2), 1.D0/L_ref**2)
!Specific parameters for ionization collision
SELECT TYPE(collision)
TYPE IS(collisionBinaryRecombination)
!Assign the energy threshold
!Input energy is in eV. Convert to J with ev2J and then to
!non-dimensional units.
collision%eBinding = energyBinding*eV2J/(m_ref*v_ref**2)
electronIndex = speciesName2Index(electron)
SELECT TYPE(sp => species(electronIndex)%obj)
TYPE IS(speciesCharged)
collision%electron => sp
CLASS DEFAULT
CALL criticalError("Species " // sp%name // " chosen for recombination is not a charged species", &
'initBinaryRecombination')
END SELECT
END SELECT
END SUBROUTINE initBinaryRecombination
!Binary recombination
SUBROUTINE collideBinaryRecombination(self, part_i, part_j, vRel)
USE moduleSpecies
USE moduleErrors
USE moduleList
USE moduleRandom
USE moduleMath
IMPLICIT NONE
CLASS(collisionBinaryRecombination), INTENT(in):: self
REAL(8), INTENT(in):: vRel
TYPE(particle), INTENT(inout), TARGET:: part_i, part_j
TYPE(particle), POINTER:: electron => NULL(), ion => NULL()
REAL(8), DIMENSION(1:3):: vp_i
TYPE(particle), POINTER:: remainingIon => NULL()
IF (ASSOCIATED(part_i%species, self%electron)) THEN
electron => part_i
ion => part_j
ELSEIF(ASSOCIATED(part_j%species, self%electron)) THEN
electron => part_j
ion => part_i
ELSE
CALL criticalError("No matching between input particles and ionizing species", 'collideBinaryIonization')
END IF
!Excess energy
!TODO: This energy should be transformed into photons
vp_i = ion%v* (1.D0 - (vRel + self%deltaV)/NORM2(ion%v))
IF (electron%weight > ion%weight) THEN
!Reduce weight of primary electron but particle continues
electron%weight = electron%weight - ion%weight
ELSE
!Remove electron from simulation
electron%n_in = .FALSE.
!There is some ion remaining
IF (electron%weight < ion%weight) THEN
ALLOCATE(remainingIon)
remainingIon = ion
remainingIon%weight = ion%weight - electron%weight
ion%weight = electron%weight
END IF
END IF
!Neutralize ion particle
SELECT TYPE(sp => ion%species)
TYPE IS(speciesCharged)
CALL sp%neutralize(ion)
CLASS DEFAULT
CALL criticalError(sp%name // " is not a charge", 'collideBinaryRecombination')
END SELECT
!Adds new particles to the list
IF (ASSOCIATED(remainingIon)) THEN
CALL partCollisions%setLock()
CALL partCollisions%add(remainingIon)
CALL partCollisions%unsetLock()
END IF
END SUBROUTINE collideBinaryRecombination
!RESONANT CHARGE EXCHANGE
!Inits resonant charge exchange
SUBROUTINE initBinaryChargeExchange(collision, crossSectionFilename)
USE moduleTable
USE moduleRefParam
USE moduleConstParam
IMPLICIT NONE
CLASS(collisionBinary), INTENT(out), ALLOCATABLE:: collision
CHARACTER(:), ALLOCATABLE, INTENT(in):: crossSectionFilename
ALLOCATE(collisionBinaryChargeExchange:: collision)
!Reads data from file
CALL collision%crossSec%init(crossSectionFilename)
!Convert to no-dimensional units
CALL collision%crossSec%convert(eV2J/(m_ref*v_ref**2), 1.D0/L_ref**2)
END SUBROUTINE initBinaryChargeExchange
SUBROUTINE collideBinaryChargeExchange(self, part_i, part_j, vRel)
USE moduleSpecies
USE moduleRandom
USE moduleMath
IMPLICIT NONE
CLASS(collisionBinaryChargeExchange), INTENT(in):: self
REAL(8), INTENT(in):: vRel ! NOTE: Required by itnerface but unused
TYPE(particle), INTENT(inout), TARGET:: part_i, part_j
SELECT TYPE(sp => part_i%species)
TYPE IS (speciesNeutral)
!Species i is neutral, ionize particle i
CALL sp%ionize(part_i)
TYPE IS (speciesCharged)
!Species i is charged, neutralize particle i
CALL sp%neutralize(part_i)
END SELECT
SELECT TYPE(sp => part_j%species)
TYPE IS (speciesNeutral)
!Species j is neutral, ionize particle j
CALL sp%ionize(part_j)
TYPE IS (speciesCharged)
!Species j is charged, neutralize particle j
CALL sp%neutralize(part_j)
END SELECT
END SUBROUTINE collideBinaryChargeExchange
END MODULE moduleCollisions