Rename of boundary module as I start with EM

This commit is contained in:
Jorge Gonzalez 2026-02-17 09:30:25 +01:00
commit 0ae121e3b1

View file

@ -0,0 +1,505 @@
!moduleMeshBoundary: Boundary functions for the mesh edges
submodule(moduleMesh) boundaryParticle
contains
module function boundaryParticleName_to_Index(boundaryName) result(bp)
use moduleErrors
implicit none
character(:), allocatable:: boundaryName
integer:: bp
integer:: b
bp = 0
do b = 1, nBoundaries
if (boundaryName == boundariesParticle(b)%obj%name) then
bp = boundariesParticle(b)%obj%n
end if
end do
if (bp == 0) then
call criticalError('Boundary ' // boundaryName // ' not found', 'boundaryName2Index')
end if
end function boundaryParticleName_to_Index
module subroutine initBoundary(self, config, object, b)
use json_module
use moduleRefParam, only: m_ref
use moduleConstParam, only: me
implicit none
class(boundaryGeneric), allocatable, intent(out):: self
type(json_file), intent(inout):: config
character(:), allocatable, intent(in):: object
integer, intent(in):: b
character(:), allocatable:: bType
logical:: found
real(8):: Tw, cw !Wall temperature and specific heat
!neutral Properties
real(8):: m0, n0, T0
real(8), dimension(:), allocatable:: v0
real(8):: effTime
real(8):: eThreshold !Energy threshold
integer:: speciesID, electronSecondaryID
character(:), allocatable:: speciesName, crossSection, electronSecondary
self%n = b
CALL config%get(object // '.name', self%name, found)
CALL config%get(object // '.type', bType, found)
SELECT CASE(bType)
CASE('reflection')
ALLOCATE(boundaryReflection:: self)
CASE('absorption')
ALLOCATE(boundaryAbsorption:: self)
CASE('transparent')
ALLOCATE(boundaryTransparent:: self)
CASE('axis')
ALLOCATE(boundaryAxis:: self)
CASE('wallTemperature')
CALL config%get(object // '.temperature', Tw, found)
IF (.NOT. found) CALL criticalError("temperature not found for wallTemperature boundary type", 'readBoundary')
CALL config%get(object // '.specificHeat', cw, found)
IF (.NOT. found) CALL criticalError("specificHeat not found for wallTemperature boundary type", 'readBoundary')
CALL initWallTemperature(self, Tw, cw)
CASE('ionization')
!Neutral parameters
CALL config%get(object // '.neutral.ion', speciesName, found)
IF (.NOT. found) CALL criticalError("missing parameter 'ion' for neutrals in ionization", 'readBoundary')
speciesID = speciesName2Index(speciesName)
CALL config%get(object // '.neutral.mass', m0, found)
IF (.NOT. found) CALL criticalError("missing parameter 'mass' for neutrals in ionization", 'readBoundary')
CALL config%get(object // '.neutral.density', n0, found)
IF (.NOT. found) CALL criticalError("missing parameter 'density' for neutrals in ionization", 'readBoundary')
CALL config%get(object // '.neutral.velocity', v0, found)
IF (.NOT. found) CALL criticalError("missing parameter 'velocity' for neutrals in ionization", 'readBoundary')
CALL config%get(object // '.neutral.temperature', T0, found)
IF (.NOT. found) CALL criticalError("missing parameter 'temperature' for neutrals in ionization", 'readBoundary')
CALL config%get(object // '.effectiveTime', effTime, found)
IF (.NOT. found) CALL criticalError("missing parameter 'effectiveTime' for ionization", 'readBoundary')
CALL config%get(object // '.energyThreshold', eThreshold, found)
IF (.NOT. found) CALL criticalError("missing parameter 'eThreshold' in ionization", 'readBoundary')
CALL config%get(object // '.crossSection', crossSection, found)
IF (.NOT. found) CALL criticalError("missing parameter 'crossSection' for neutrals in ionization", 'readBoundary')
CALL config%get(object // '.electronSecondary', electronSecondary, found)
electronSecondaryID = speciesName2Index(electronSecondary)
IF (found) THEN
CALL initIonization(self, me/m_ref, m0, n0, v0, T0, &
speciesID, effTime, crossSection, eThreshold,electronSecondaryID)
ELSE
CALL initIonization(self, me/m_ref, m0, n0, v0, T0, &
speciesID, effTime, crossSection, eThreshold)
END IF
case('quasiNeutrality')
call initQuasiNeutrality(self)
CASE DEFAULT
CALL criticalError('Boundary type ' // bType // ' undefined', 'readBoundary')
END SELECT
end subroutine initBoundary
SUBROUTINE initWallTemperature(boundary, T, c)
USE moduleRefParam
IMPLICIT NONE
CLASS(boundaryGeneric), ALLOCATABLE, INTENT(out):: boundary
REAL(8), INTENT(in):: T, c !Wall temperature and specific heat
REAL(8):: vTh
vTh = DSQRT(c * T) / v_ref
allocate(boundaryWallTemperature:: boundary)
select type(boundary)
type is(boundaryWallTemperature)
boundary%vTh = vTh
end select
END SUBROUTINE initWallTemperature
SUBROUTINE initIonization(boundary, mImpact, m0, n0, v0, T0, ion, effTime, crossSection, eThreshold, electronSecondary)
USE moduleRefParam
USE moduleSpecies
USE moduleCaseParam
USE moduleConstParam
USE moduleErrors
IMPLICIT NONE
CLASS(boundaryGeneric), ALLOCATABLE, INTENT(out):: boundary
real(8), intent(in):: mImpact
REAL(8), INTENT(in):: m0, n0, v0(1:3), T0 !Neutral properties
INTEGER, INTENT(in):: ion
INTEGER, OPTIONAL, INTENT(in):: electronSecondary
REAL(8):: effTime
CHARACTER(:), ALLOCATABLE, INTENT(in):: crossSection
REAL(8), INTENT(in):: eThreshold
ALLOCATE(boundaryIonization:: boundary)
SELECT TYPE(boundary)
TYPE IS(boundaryIonization)
boundary%m0 = m0 / m_ref
boundary%n0 = n0 * Vol_ref
boundary%v0 = v0 / v_ref
boundary%vTh = DSQRT(kb*T0/m0)/v_ref
boundary%species => species(ion)%obj
IF (PRESENT(electronSecondary)) THEN
SELECT TYPE(sp => species(electronSecondary)%obj)
TYPE IS(speciesCharged)
boundary%electronSecondary => sp
CLASS DEFAULT
CALL criticalError("Species " // sp%name // " chosen for " // &
"secondary electron is not a charged species", 'initIonization')
END SELECT
ELSE
boundary%electronSecondary => NULL()
END IF
boundary%effectiveTime = effTime / ti_ref
CALL boundary%crossSection%init(crossSection)
CALL boundary%crossSection%convert(eV2J/(m_ref*v_ref**2), 1.D0/L_ref**2)
boundary%eThreshold = eThreshold*eV2J/(m_ref*v_ref**2)
boundary%deltaV = DSQRT(boundary%eThreshold/mImpact)
END SELECT
END SUBROUTINE initIonization
subroutine initQuasiNeutrality(boundary)
implicit none
class(boundaryGeneric), allocatable, intent(out):: boundary
integer:: e, et
allocate(boundaryQuasiNeutrality:: boundary)
select type(boundary)
type is(boundaryQuasiNeutrality)
boundary%alpha = 0.d0
end select
end subroutine initQuasiNeutrality
module SUBROUTINE reflection(self, edge, part)
USE moduleCaseParam
USE moduleSpecies
IMPLICIT NONE
class(boundaryReflection), intent(in):: self
CLASS(meshEdge), INTENT(inout):: edge
CLASS(particle), INTENT(inout):: part
!rp = intersection between particle and edge
!rpp = final position of particle
!vpp = final velocity of particle
REAL(8), DIMENSION(1:3):: rp, vpp
!Reflect particle velocity
vpp = part%v - 2.D0*DOT_PRODUCT(part%v, edge%normal)*edge%normal
part%v = vpp
rp = edge%intersection(part%r)
part%r = 2.D0*(rp - part%r) + part%r
!particle is assumed to be inside
part%n_in = .TRUE.
END SUBROUTINE reflection
!Absoption in a surface
SUBROUTINE absorption(self, edge, part)
USE moduleCaseParam
USE moduleSpecies
IMPLICIT NONE
class(boundaryAbsorption), intent(in):: self
CLASS(meshEdge), INTENT(inout):: edge
CLASS(particle), INTENT(inout):: part
REAL(8):: rpp(1:3) !Position of particle projected to the edge
REAL(8):: d !Distance from particle to edge
rpp = edge%intersection(part%r)
d = NORM2(rpp - part%r)
IF (d >= 0.D0) THEN
part%weight = part%weight/d
END IF
!Assign new position to particle
part%r = rpp
!Remove particle from the domain
part%n_in = .FALSE.
!Scatter particle in associated volume
IF (ASSOCIATED(edge%e1)) THEN
CALL edge%e1%scatter(edge%e1%nNodes, part)
ELSE
CALL edge%e2%scatter(edge%e2%nNodes, part)
END IF
END SUBROUTINE absorption
!Transparent boundary condition
SUBROUTINE transparent(self, edge, part)
USE moduleSpecies
IMPLICIT NONE
class(boundaryTransparent), intent(in):: self
CLASS(meshEdge), INTENT(inout):: edge
CLASS(particle), INTENT(inout):: part
!Removes particle from domain
part%n_in = .FALSE.
END SUBROUTINE transparent
!Symmetry axis. Reflects particles.
!Although this function should never be called, it is set as a reflective boundary
!to properly deal with possible particles reaching a corner and selecting this boundary.
SUBROUTINE symmetryAxis(self, edge, part)
USE moduleSpecies
IMPLICIT NONE
class(boundaryAxis), intent(in):: self
CLASS(meshEdge), INTENT(inout):: edge
CLASS(particle), INTENT(inout):: part
CALL genericReflection(edge, part)
END SUBROUTINE symmetryAxis
!Wall with temperature
SUBROUTINE wallTemperature(self, edge, part)
USE moduleSpecies
USE moduleRandom
IMPLICIT NONE
class(boundaryWallTemperature), intent(in):: self
CLASS(meshEdge), INTENT(inout):: edge
CLASS(particle), INTENT(inout):: part
INTEGER:: i
!Modifies particle velocity according to wall temperature
DO i = 1, 3
part%v(i) = part%v(i) + self%vTh*randomMaxwellian()
END DO
CALL genericReflection(edge, part)
END SUBROUTINE wallTemperature
!Ionization surface: an electron will pass through the surface
! and create an ion-electron pair based on a neutral background
SUBROUTINE ionization(self, edge, part)
USE moduleList
USE moduleSpecies
USE moduleMesh
USE moduleRefParam
USE moduleRandom
USE moduleMath
IMPLICIT NONE
class(boundaryIonization), intent(in):: self
CLASS(meshEdge), INTENT(inout):: edge
CLASS(particle), INTENT(inout):: part
REAL(8):: vRel, eRel, mRel !relative velocity, energy and mass
INTEGER:: nIonizations !Number of ionizations based on eRel
REAL(8):: pIonization !Probability of ionization of each event
INTEGER:: p
REAL(8):: v0(1:3) !random velocity of neutral
TYPE(particle), POINTER:: newElectron
TYPE(particle), POINTER:: newIon
mRel = reducedMass(self%m0, part%species%m)
vRel = SUM(DABS(part%v-self%v0))
eRel = mRel*vRel**2*5.D-1
!Maximum number of possible ionizations based on relative energy
nIonizations = FLOOR(eRel/self%eThreshold)
DO p = 1, nIonizations
!Get probability of ionization
pIonization = 1.D0 - DEXP(-self%n0*self%crossSection%get(eRel)*vRel*self%effectiveTime/REAL(nIonizations))
!If a random number is below the probability of ionization, create new pair of ion-electron
IF (random() < pIonization) THEN
!Assign random velocity to the neutral
v0(1) = self%v0(1) + self%vTh*randomMaxwellian()
v0(2) = self%v0(2) + self%vTh*randomMaxwellian()
v0(3) = self%v0(3) + self%vTh*randomMaxwellian()
!Allocates the new particles
ALLOCATE(newElectron)
ALLOCATE(newIon)
IF (ASSOCIATED(self%electronSecondary)) THEN
newElectron%species => self%electronSecondary
ELSE
newElectron%species => part%species
END IF
newIon%species => self%species
newElectron%v = v0 + (1.D0 + self%deltaV*v0/NORM2(v0))
newIon%v = v0
newElectron%r = edge%randPos()
newIon%r = newElectron%r
IF (ASSOCIATED(edge%e1)) THEN
newElectron%cell = edge%e1%n
ELSEIF (ASSOCIATED(edge%e2)) THEN
newElectron%cell = edge%e2%n
END IF
newIon%cell = newElectron%cell
newElectron%Xi = mesh%cells(part%cell)%obj%phy2log(newElectron%r)
newIon%Xi = newElectron%Xi
newElectron%weight = part%weight
newIon%weight = newElectron%weight
newElectron%n_in = .TRUE.
newIon%n_in = .TRUE.
!Add particles to list
CALL partSurfaces%setLock()
CALL partSurfaces%add(newElectron)
CALL partSurfaces%add(newIon)
CALL partSurfaces%unsetLock()
!Electron loses energy due to ionization
eRel = eRel - self%eThreshold
vRel = 2.D0*DSQRT(eRel)/mRel
!Reduce number of possible ionizations
nIonizations = nIonizations - 1
END IF
END DO
!Removes ionizing electron regardless the number of pair created
part%n_in = .FALSE.
END SUBROUTINE ionization
subroutine quasiNeutrality(self, edge, part)
use moduleRandom
implicit none
class(boundaryQuasiNeutrality), intent(in):: self
class(meshEdge), intent(inout):: edge
class(particle), intent(inout):: part
real(8), allocatable:: density(:)
class(meshCell), pointer:: cell
real(8):: EF_dir
real(8):: alpha
if (associated(edge%e1)) then
cell => edge%e1
else
cell => edge%e2
end if
if (random() <= alpha) then
call genericReflection(edge, part)
else
call genericTransparent(edge, part)
end if
end subroutine quasiNeutrality
! Generic boundary conditions for internal use
module subroutine genericReflection(edge, part)
use moduleCaseParam
use moduleSpecies
implicit none
class(meshEdge), intent(inout):: edge
class(particle), intent(inout):: part
!rp = intersection between particle and edge
!rpp = final position of particle
!vpp = final velocity of particle
real(8), dimension(1:3):: rp, vpp
!Reflect particle velocity
vpp = part%v - 2.D0*dot_product(part%v, edge%normal)*edge%normal
part%v = vpp
rp = edge%intersection(part%r)
part%r = 2.D0*(rp - part%r) + part%r
!particle is assumed to be inside
part%n_in = .TRUE.
end subroutine genericReflection
subroutine genericTransparent(edge, part)
use moduleSpecies
implicit none
class(meshEdge), intent(inout):: edge
class(particle), intent(inout):: part
!Removes particle from domain
part%n_in = .FALSE.
end subroutine genericTransparent
function physicalSurface_to_id(physicalSurface) result(b)
implicit none
integer:: physicalSurface
integer:: b
integer:: bt
do bt=1, nPhysicalSurfaces
if (boundariesParticleLinking(bt)%physicalSurface == physicalSurface) then
b = bt
exit
end if
end do
end function physicalSurface_to_id
end submodule boundaryParticle