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