Merge, but I have to recover the outflow boundary condition

This commit is contained in:
Jorge Gonzalez 2026-03-12 19:40:48 +01:00
commit 6828f1ef96
554 changed files with 1714358 additions and 41895 deletions

View file

@ -8,7 +8,6 @@ MODULE moduleInput
SUBROUTINE readConfig(inputFile)
USE json_module
USE moduleErrors
USE moduleBoundary
USE moduleOutput
USE moduleMesh
IMPLICIT NONE
@ -39,10 +38,20 @@ MODULE moduleInput
CALL readSpecies(config)
CALL checkStatus(config, "readSpecies")
!Read boundaries
CALL verboseError('Reading boundary conditions...')
CALL readBoundary(config)
CALL checkStatus(config, "readBoundary")
!Read particle boundaries
CALL verboseError('Reading particle boundary conditions...')
CALL readBoundaryParticle(config)
CALL checkStatus(config, "readBoundaryParticle")
! read EM boundaries
CALL verboseError('Reading EM boundary conditions...')
CALL readBoundaryEM(config)
CALL checkStatus(config, "readBoundaryEM")
! Read Physical Surfaces
call verboseError('Reading Physical Surfaces...')
call readPhysicalSurfaces(config)
call checkStatus(config, 'readPhysicalSurfaces')
!Read Geometry
CALL verboseError('Reading Geometry...')
@ -255,17 +264,7 @@ MODULE moduleInput
IF (found) THEN
CALL solver%initEM(EMType)
SELECT CASE(EMType)
CASE("Electrostatic")
!Read BC
CALL readEMBoundary(config)
CASE("ElectrostaticBoltzmann")
!Read BC
CALL readEMBoundary(config)
CASE("ConstantB")
!Read BC
CALL readEMBoundary(config)
!Read constant magnetic field
DO i = 1, 3
WRITE(iString, '(i2)') i
@ -288,9 +287,6 @@ MODULE moduleInput
END DO
CASE DEFAULT
CALL criticalError('EM Solver ' // EMType // ' not found', 'readSolver')
END SELECT
END IF
@ -495,7 +491,8 @@ MODULE moduleInput
CALL config%get(object // '.cpuTime', timeOutput, found)
CALL config%get(object // '.numColl', collOutput, found)
CALL config%get(object // '.EMField', emOutput, found)
CALL config%get(object // '.EMField', emOutput, found)
call config%get(object // '.boundariesParticle', boundaryParticleOutput, found)
CALL config%get(object // '.triggerCPUTime', triggerCPUTime, found)
IF (.NOT. found) THEN
@ -513,6 +510,7 @@ MODULE moduleInput
USE moduleRefParam
USE moduleList
USE json_module
use moduleMesh, only: qSpecies
IMPLICIT NONE
TYPE(json_file), INTENT(inout):: config
@ -521,7 +519,7 @@ MODULE moduleInput
CHARACTER(:), ALLOCATABLE:: speciesType
REAL(8):: mass, charge
LOGICAL:: found
INTEGER:: i
INTEGER:: s
CHARACTER(:), ALLOCATABLE:: linkName
INTEGER:: linkID
@ -533,8 +531,8 @@ MODULE moduleInput
ALLOCATE(species(1:nSpecies))
!Reads information of individual species
DO i = 1, nSpecies
WRITE(iString, '(I2)') i
DO s = 1, nSpecies
WRITE(iString, '(I2)') s
object = 'species(' // TRIM(iString) // ')'
CALL config%get(object // '.type', speciesType, found)
CALL config%get(object // '.mass', mass, found)
@ -543,12 +541,12 @@ MODULE moduleInput
!Allocate species depending on type and assign specific parameters
SELECT CASE(speciesType)
CASE ("neutral")
ALLOCATE(species(i)%obj, source=speciesNeutral())
ALLOCATE(species(s)%obj, source=speciesNeutral())
CASE ("charged")
CALL config%get(object // '.charge', charge, found)
IF (.NOT. found) CALL criticalError("Required parameter charge not found for species " // object, 'readSpecies')
ALLOCATE(species(i)%obj, source=speciesCharged(q = charge, &
ALLOCATE(species(s)%obj, source=speciesCharged(q = charge, &
qm = charge/mass))
CASE DEFAULT
@ -556,18 +554,32 @@ MODULE moduleInput
END SELECT
!Assign shared parameters for all species
CALL config%get(object // '.name', species(i)%obj%name, found)
CALL config%get(object // '.weight', species(i)%obj%weight, found)
species(i)%obj%n = i
species(i)%obj%m = mass
CALL config%get(object // '.name', species(s)%obj%name, found)
CALL config%get(object // '.weight', species(s)%obj%weight, found)
species(s)%obj%n = s
species(s)%obj%m = mass
END DO
! Allocate the vector with the species charges for calculating the EM field
ALLOCATE(qSpecies(1:nSpecies))
DO s = 1, nSpecies
SELECT TYPE(sp => species(s)%obj)
TYPE IS (speciesCharged)
qSpecies(s) = sp%q
CLASS DEFAULT
qSpecies(s) = 0.D0
END SELECT
END DO
!Read relations between species
DO i = 1, nSpecies
WRITE(iString, '(I2)') i
DO s = 1, nSpecies
WRITE(iString, '(I2)') s
object = 'species(' // TRIM(iString) // ')'
SELECT TYPE(sp => species(i)%obj)
SELECT TYPE(sp => species(s)%obj)
TYPE IS (speciesNeutral)
!Get species linked ion
CALL config%get(object // '.ion', linkName, found)
@ -792,48 +804,48 @@ MODULE moduleInput
END SUBROUTINE readInteractions
!Reads boundary conditions for the mesh
SUBROUTINE readBoundary(config)
USE moduleBoundary
SUBROUTINE readBoundaryParticle(config)
use moduleMesh
USE moduleErrors
USE moduleSpecies
USE moduleRefParam
USE moduleList, ONLY: partSurfaces
use moduleRefParam, only: m_ref
use moduleConstParam, only: me
USE json_module
IMPLICIT NONE
TYPE(json_file), INTENT(inout):: config
INTEGER:: i, s
CHARACTER(2):: iString, sString
CHARACTER(:), ALLOCATABLE:: object, bType
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
integer:: b
character(2):: bString
character(:), allocatable:: object
LOGICAL:: found
INTEGER:: nTypes
character(:), allocatable:: bType
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
integer:: s_incident
CALL config%info('boundary', found, n_children = nBoundary)
ALLOCATE(boundary(1:nBoundary))
DO i = 1, nBoundary
WRITE(iString, '(i2)') i
object = 'boundary(' // TRIM(iString) // ')'
! Read models of particles
object = 'boundaries.particles'
CALL config%info(object, found, n_children = nBoundariesParticle)
allocate(boundariesParticle(1:nBoundariesParticle))
DO b = 1, nBoundariesParticle
WRITE(bString, '(i2)') b
object = 'boundaries.particles(' // trim(bString) // ')'
boundary(i)%n = i
CALL config%get(object // '.name', boundary(i)%name, found)
CALL config%get(object // '.physicalSurface', boundary(i)%physicalSurface, found)
CALL config%info(object // '.bTypes', found, n_children = nTypes)
IF (nTypes /= nSpecies) CALL criticalError('Not enough boundary types defined in ' // object, 'readBoundary')
ALLOCATE(boundary(i)%bTypes(1:nSpecies))
DO s = 1, nSpecies
associate(bound => boundary(i)%bTypes(s)%obj)
WRITE(sString,'(i2)') s
object = 'boundary(' // TRIM(iString) // ').bTypes(' // TRIM(sString) // ')'
CALL config%get(object // '.type', bType, found)
SELECT CASE(bType)
CALL config%get(object // '.type', bType, found)
if (.not. found) then
call criticalError('Required parameter "type" for particle boundary condition not found', &
'initBoundaryParticle')
end if
associate(bound => boundariesParticle(b)%obj)
SELECT CASE(bType)
CASE('reflection')
ALLOCATE(boundaryReflection:: bound)
@ -860,55 +872,186 @@ MODULE moduleInput
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) THEN
m0 = species(s)%obj%m*m_ref
END IF
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 // '.electronSecondary', electronSecondary, found)
IF (found) THEN
electronSecondaryID = speciesName2Index(electronSecondary)
CALL initIonization(bound, me/m_ref, m0, n0, v0, T0, &
speciesID, effTime, crossSection, eThreshold, electronSecondaryID)
ELSE
CALL initIonization(bound, me/m_ref, m0, n0, v0, T0, &
speciesID, effTime, crossSection, eThreshold)
CALL config%get(object // '.effectiveTime', effTime, found)
IF (.NOT. found) CALL criticalError("missing parameter 'effectiveTime' for ionization", 'readBoundary')
END IF
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')
case('quasiNeutrality')
call config%get(object // '.incident', speciesName, found)
if (.not. found) call criticalError("Incident species name not found for quasiNeutrality boundary model", 'readBoundary')
CALL config%get(object // '.electronSecondary', electronSecondary, found)
electronSecondaryID = speciesName2Index(electronSecondary)
IF (found) THEN
CALL initIonization(bound, species(s)%obj%m, m0, n0, v0, T0, &
speciesID, effTime, crossSection, eThreshold,electronSecondaryID)
s_incident = speciesName2Index(speciesName)
ELSE
CALL initIonization(bound, species(s)%obj%m, m0, n0, v0, T0, &
speciesID, effTime, crossSection, eThreshold)
END IF
case('outflowAdaptive')
allocate(boundaryOutflowAdaptive:: bound)
call initQuasiNeutrality(bound, s_incident)
CASE DEFAULT
CALL criticalError('Boundary type ' // bType // ' undefined', 'readBoundary')
END SELECT
END SELECT
end associate
bound%n = b
END DO
CALL config%get(object // '.name', bound%name, found)
if (.not. found) then
call criticalError('Required parameter "name" for particle boundary condition not found', &
'initBoundaryParticle')
end if
end associate
END DO
!Init the list of particles from surfaces
CALL OMP_INIT_LOCK(partSurfaces%lock)
END SUBROUTINE readBoundary
END SUBROUTINE readBoundaryParticle
SUBROUTINE readBoundaryEM(config)
USE moduleMesh
USE moduleOutput
USE moduleErrors
USE moduleEM
USE json_module
IMPLICIT NONE
TYPE(json_file), INTENT(inout):: config
CHARACTER(:), ALLOCATABLE:: object
LOGICAL:: found
INTEGER:: b
CHARACTER(2):: bString
character(:), allocatable:: bType
CALL config%info('boundaries.EM', found, n_children = nBoundariesEM)
IF (found) THEN
ALLOCATE(boundariesEM(1:nBoundariesEM))
END IF
do b = 1, nBoundariesEM
write(bString, '(I2)') b
object = 'boundaries.EM(' // TRIM(bString) // ')'
associate(self => boundariesEM(b)%obj)
call config%get(object // '.type', bType, found)
if (.not. found) then
call criticalError('Required parameter "type" for EM boundary condition not found', &
'initBoundaryEM')
end if
select case(bType)
case ("dirichlet")
! Allocate boundary edge
allocate(boundaryEMDirichlet:: self)
CALL initDirichlet(self, config, object)
case ("dirichlettime")
! Allocate boundary edge
allocate(boundaryEMDirichletTime:: self)
call initDirichletTime(self, config, object)
case default
call criticalError('Boundary type ' // bType // ' not supported', 'readBoundaryEM')
end select
self%n = b
allocate(self%nodes(0))
call config%get(object // '.name', self%name, found)
if (.not. found) then
call criticalError('Required parameter "name" for EM boundary condition not found', &
'initBoundaryEM')
end if
end associate
end do
END SUBROUTINE readBoundaryEM
subroutine readPhysicalSurfaces(config)
use json_module
use moduleMesh
use moduleErrors
implicit none
type(json_file), intent(inout):: config
character(:), allocatable:: object
logical:: found
integer:: ps
character(2):: psString, sSTring
integer:: nParticleModels
character(:), allocatable:: particleModel
character(:), allocatable:: EMModel
integer:: s, boundaryIndex
call config%info('physicalSurfaces', found, n_children = nPhysicalSurfaces)
if (found) then
allocate(physicalSurfaces(1:nPhysicalSurfaces))
end if
do ps = 1, nPhysicalSurfaces
write(psString, '(I2)') ps
object = 'physicalSurfaces(' // trim(psString) // ')'
allocate(physicalSurfaces(ps)%nodes(0))
allocate(physicalSurfaces(ps)%edges(0))
call config%get(object // '.index', physicalSurfaces(ps)%index, found)
if (.not. found) then
call criticalError('Physical surface index not found', 'readPhysicalSurfaces')
end if
! Link models for particles
call config%info(object // '.particles', found, n_children = nParticleModels)
if ((.not. found) .or. &
(nParticleModels /= nSpecies)) then
call criticalError('Not enough models for particles provided', 'readPhysicalSurfaces')
end if
allocate(physicalSurfaces(ps)%particles(1:nSpecies))
do s = 1, nSpecies
write(sString, '(I2)') s
call config%get(object // '.particles(' // trim(sSTring) // ')', particleModel, found)
boundaryIndex = boundaryParticleName_to_Index(particleModel)
physicalSurfaces(ps)%particles(s)%obj => boundariesParticle(boundaryIndex)%obj
end do
! Link electromagnetic boundary condition
call config%get(object // '.EM', EMModel, found)
if (found) then
boundaryIndex = boundaryEMName_to_Index(EMModel)
physicalSurfaces(ps)%EM => boundariesEM(boundaryIndex)%obj
end if
end do
end subroutine readPhysicalSurfaces
!Read the geometry (mesh) for the case
SUBROUTINE readGeometry(config)
@ -934,6 +1077,9 @@ MODULE moduleInput
LOGICAL:: found
CHARACTER(:), ALLOCATABLE:: meshFormat, meshFile
REAL(8):: volume
integer:: b, ps, s
integer:: e
integer:: nVolColl
object = 'geometry'
@ -1102,18 +1248,101 @@ MODULE moduleInput
END SELECT
!Builds the K matrix for the Particles mesh
CALL mesh%constructGlobalK()
!Assign the procedure to find a volume for meshColl
!Assign the procedure to find a cell for meshColl
IF (doubleMesh) THEN
findCellColl => findCellCollMesh
! Link edges with cells in meshColl
DO e=1, mesh%numEdges
nVolColl = findCellBrute(meshColl, mesh%edges(e)%obj%center())
IF (nVolColl > 0) THEN
mesh%edges(e)%obj%eColl => meshColl%cells(nVolColl)%obj
ELSE
CALL criticalError("No connection between edge and meshColl", "readGeometry")
END IF
end do
ELSE
findCellColl => findCellSameMesh
! Link edges with cells in meshColl
DO e=1, mesh%numEdges
IF (ASSOCIATED(mesh%edges(e)%obj%e1)) THEN
mesh%edges(e)%obj%eColl => mesh%edges(e)%obj%e1
ELSE
mesh%edges(e)%obj%eColl => mesh%edges(e)%obj%e2
END IF
end do
END IF
! If needed, add nodes and edges to boundary models
! Particle boundaries
do b = 1, nBoundariesParticle
select type(bound => boundariesParticle(b)%obj)
type is(boundaryQuasiNeutrality)
! Loop over all physical surfaces
do ps = 1, nPhysicalSurfaces
! Loop over all species
do s = 1, nSpecies
! If the boundary for the species is linked to the one analysing, add the edges
if (associated(physicalSurfaces(ps)%particles(s)%obj, bound)) then
bound%edges = [bound%edges, physicalSurfaces(ps)%edges]
end if
end do
end do
allocate(bound%alpha(1:mesh%numEdges)) ! TODO: Change this so only the edges associated to the boundary are here
bound%alpha = 0.d0
end select
end do
! EM Boundaries
do b = 1, nBoundariesEM
associate(bound => boundariesEM(b)%obj)
select type(bound)
type is(boundaryEMDirichlet)
! Loop over all physical surfaces
do ps = 1, nPhysicalSurfaces
! If the boundary for the species is linked to the one analysing, add the edges
if (associated(physicalSurfaces(ps)%EM, bound)) then
bound%nodes = [bound%nodes, physicalSurfaces(ps)%nodes]
end if
end do
type is(boundaryEMDirichletTime)
! Loop over all physical surfaces
do ps = 1, nPhysicalSurfaces
! If the boundary for the species is linked to the one analysing, add the edges
if (associated(physicalSurfaces(ps)%EM, bound)) then
bound%nodes = [bound%nodes, physicalSurfaces(ps)%nodes]
end if
end do
end select
bound%nNodes = size(bound%nodes)
end associate
end do
if (mesh%dimen > 0) then
! Builds the K matrix for the Particles mesh
call mesh%constructGlobalK()
end if
END SUBROUTINE readGeometry
SUBROUTINE readProbes(config)
@ -1157,134 +1386,6 @@ MODULE moduleInput
END SUBROUTINE readProbes
SUBROUTINE readEMBoundary(config)
USE moduleMesh
USE moduleOutput
USE moduleErrors
USE moduleEM
USE moduleSpecies
USE json_module
IMPLICIT NONE
TYPE(json_file), INTENT(inout):: config
CHARACTER(:), ALLOCATABLE:: object
LOGICAL:: found
CHARACTER(:), ALLOCATABLE:: typeEM
REAL(8):: potential
INTEGER:: physicalSurface
CHARACTER(:), ALLOCATABLE:: temporalProfile, temporalProfilePath
INTEGER:: b, s, n, ni
CHARACTER(2):: bString
INTEGER:: info
EXTERNAL:: dgetrf
CALL config%info('boundaryEM', found, n_children = nBoundaryEM)
IF (found) THEN
ALLOCATE(boundaryEM(1:nBoundaryEM))
END IF
DO b = 1, nBoundaryEM
WRITE(bString, '(I2)') b
object = 'boundaryEM(' // TRIM(bString) // ')'
CALL config%get(object // '.type', typeEM, found)
SELECT CASE(typeEM)
CASE ("dirichlet")
CALL config%get(object // '.potential', potential, found)
IF (.NOT. found) THEN
CALL criticalError('Required parameter "potential" for Dirichlet boundary condition not found', 'readEMBoundary')
END IF
CALL config%get(object // '.physicalSurface', physicalSurface, found)
IF (.NOT. found) THEN
CALL criticalError('Required parameter "physicalSurface" for Dirichlet boundary condition not found', &
'readEMBoundary')
END IF
CALL initDirichlet(boundaryEM(b)%obj, physicalSurface, potential)
CASE ("dirichletTime")
CALL config%get(object // '.potential', potential, found)
IF (.NOT. found) THEN
CALL criticalError('Required parameter "potential" for Dirichlet Time boundary condition not found', &
'readEMBoundary')
END IF
CALL config%get(object // '.temporalProfile', temporalProfile, found)
IF (.NOT. found) THEN
CALL criticalError('Required parameter "temporalProfile" for Dirichlet Time boundary condition not found', &
'readEMBoundary')
END IF
temporalProfilePath = path // temporalProfile
CALL config%get(object // '.physicalSurface', physicalSurface, found)
IF (.NOT. found) THEN
CALL criticalError('Required parameter "physicalSurface" for Dirichlet Time boundary condition not found', &
'readEMBoundary')
END IF
CALL initDirichletTime(boundaryEM(b)%obj, physicalSurface, potential, temporalProfilePath)
CASE DEFAULT
CALL criticalError('Boundary type ' // typeEM // ' not yet supported', 'readEMBoundary')
END SELECT
END DO
ALLOCATE(qSpecies(1:nSpecies))
DO s = 1, nSpecies
SELECT TYPE(sp => species(s)%obj)
TYPE IS (speciesCharged)
qSpecies(s) = sp%q
CLASS DEFAULT
qSpecies(s) = 0.D0
END SELECT
END DO
! Modify K matrix due to boundary conditions
DO b = 1, nBoundaryEM
SELECT TYPE(boundary => boundaryEM(b)%obj)
TYPE IS(boundaryEMDirichlet)
DO n = 1, boundary%nNodes
ni = boundary%nodes(n)%obj%n
mesh%K(ni, :) = 0.D0
mesh%K(ni, ni) = 1.D0
END DO
TYPE IS(boundaryEMDirichletTime)
DO n = 1, boundary%nNodes
ni = boundary%nodes(n)%obj%n
mesh%K(ni, :) = 0.D0
mesh%K(ni, ni) = 1.D0
END DO
END SELECT
END DO
!Compute the PLU factorization of K once boundary conditions have been read
CALL dgetrf(mesh%numNodes, mesh%numNodes, mesh%K, mesh%numNodes, mesh%IPIV, info)
IF (info /= 0) THEN
CALL criticalError('Factorization of K matrix failed', 'readEMBoundary')
END IF
END SUBROUTINE readEMBoundary
!Reads the injection of particles from the boundaries
SUBROUTINE readInject(config)
USE moduleSpecies
@ -1460,35 +1561,29 @@ MODULE moduleInput
END SUBROUTINE readParallel
SUBROUTINE initOutput(inputFile)
USE moduleRefParam
USE moduleMesh, ONLY: mesh, doubleMesh, pathMeshParticle, pathMeshColl
USE moduleOutput, ONLY: path, folder
USE moduleOutput, ONLY: createOutputFolder, writeReference, copyFileToOutput, writeCommit
IMPLICIT NONE
CHARACTER(:), ALLOCATABLE, INTENT(in):: inputFile
INTEGER:: fileReference = 30
!If everything is correct, creates the output folder
CALL EXECUTE_COMMAND_LINE('mkdir ' // path // folder )
call createOutputFolder()
!Copies input file to output folder
CALL EXECUTE_COMMAND_LINE('cp ' // inputFile // ' ' // path // folder)
call copyFileToOutput(inputFile)
!Copies particle mesh
IF (mesh%dimen > 0) THEN
CALL EXECUTE_COMMAND_LINE('cp ' // pathMeshParticle // ' ' // path // folder)
call copyFileToOutput(pathMeshParticle)
IF (doubleMesh) THEN
CALL EXECUTE_COMMAND_LINE('cp ' // pathMeshColl // ' ' // path // folder)
call copyFileToOutput(pathMeshColl)
END IF
END IF
! Write commit of fpakc
CALL SYSTEM('git rev-parse HEAD > ' // path // folder // '/' // 'fpakc_commit.txt')
call writeCommit()
! Write file with reference values
OPEN (fileReference, file=path // folder // '/' // 'reference.txt')
WRITE(fileReference, "(7(1X,A20))") 'L_ref', 'v_ref', 'ti_ref', 'Vol_ref', 'EF_ref', 'Volt_ref', 'B_ref'
WRITE(fileReference, "(7(1X,ES20.6E3))") L_ref, v_ref, ti_ref, Vol_ref, EF_ref, Volt_ref, B_ref
CLOSE(fileReference)
call writeReference()
END SUBROUTINE initOutput