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

@ -45,6 +45,9 @@ PROGRAM fpakc
!$OMP SINGLE
CALL verboseError("Calculating initial EM field...")
! Update EM boundary models
call boundariesEM_update()
!$OMP END SINGLE
CALL doEMField()
!$OMP END PARALLEL
@ -62,6 +65,12 @@ PROGRAM fpakc
! Update global time step index
timeStep = t
! Update Particle boundary models
call boundariesParticle_update()
! Update EM boundary models
call boundariesEM_update()
!Checks if a species needs to me moved in this iteration
CALL solver%updatePushSpecies()

View file

@ -1,15 +1,15 @@
OBJECTS = $(OBJDIR)/moduleMesh.o $(OBJDIR)/moduleMeshBoundary.o $(OBJDIR)/moduleCompTime.o \
OBJECTS = $(OBJDIR)/moduleMesh.o $(OBJDIR)/moduleMeshCommon.o $(OBJDIR)/moduleCompTime.o \
$(OBJDIR)/moduleMesh@elements.o $(OBJDIR)/moduleMesh@boundaryEM.o $(OBJDIR)/moduleMesh@boundaryParticle.o $(OBJDIR)/moduleMesh@surfaces.o \
$(OBJDIR)/moduleSpecies.o $(OBJDIR)/moduleInject.o $(OBJDIR)/moduleInput.o \
$(OBJDIR)/moduleErrors.o $(OBJDIR)/moduleList.o $(OBJDIR)/moduleOutput.o \
$(OBJDIR)/moduleBoundary.o $(OBJDIR)/moduleCaseParam.o $(OBJDIR)/moduleRefParam.o \
$(OBJDIR)/moduleCaseParam.o $(OBJDIR)/moduleRefParam.o \
$(OBJDIR)/moduleCollisions.o $(OBJDIR)/moduleTable.o $(OBJDIR)/moduleParallel.o \
$(OBJDIR)/moduleEM.o $(OBJDIR)/moduleRandom.o $(OBJDIR)/moduleMath.o \
$(OBJDIR)/moduleProbe.o $(OBJDIR)/moduleAverage.o $(OBJDIR)/moduleCoulomb.o \
$(OBJDIR)/moduleMeshInoutCommon.o \
$(OBJDIR)/moduleMeshInputVTU.o $(OBJDIR)/moduleMeshOutputVTU.o \
$(OBJDIR)/moduleMeshInputGmsh2.o $(OBJDIR)/moduleMeshOutputGmsh2.o \
$(OBJDIR)/moduleMeshInput0D.o $(OBJDIR)/moduleMeshOutput0D.o \
$(OBJDIR)/moduleMeshInputText.o $(OBJDIR)/moduleMeshOutputText.o \
$(OBJDIR)/moduleMeshInputText.o $(OBJDIR)/moduleMeshOutputText.o \
$(OBJDIR)/moduleMesh3DCart.o \
$(OBJDIR)/moduleMesh2DCyl.o \
$(OBJDIR)/moduleMesh2DCart.o \
@ -17,7 +17,8 @@ OBJECTS = $(OBJDIR)/moduleMesh.o $(OBJDIR)/moduleMeshBoundary.o $(OBJDIR)/module
$(OBJDIR)/moduleMesh1DCart.o \
$(OBJDIR)/moduleMesh0D.o \
$(OBJDIR)/moduleSolver.o \
$(OBJDIR)/modulePusher.o
$(OBJDIR)/modulePusher.o \
$(OBJDIR)/velocityDistribution.o
all: $(OUTPUT)

View file

@ -1,6 +1,7 @@
OBJS = moduleCompTime.o moduleCaseParam.o moduleConstParam.o \
moduleErrors.o moduleMath.o moduleParallel.o \
moduleRandom.o moduleRefParam.o moduleTable.o
moduleRandom.o moduleRefParam.o moduleTable.o \
velocityDistribution.o
all: $(OBJS)

View file

@ -10,6 +10,7 @@ MODULE moduleConstParam
REAL(8), PARAMETER:: PI8 = 8.D0*PI !8*pi
REAL(8), PARAMETER:: sccm2atomPerS = 4.5D17 !sccm to atom s^-1
REAL(8), PARAMETER:: qe = 1.60217662D-19 !Elementary charge
real(8), parameter:: me = 9.1093837d-31 !electron mass
REAL(8), PARAMETER:: kb = 1.38064852D-23 !Boltzmann constants SI
REAL(8), PARAMETER:: eV2J = qe !Electron volt to Joule conversion
REAL(8), PARAMETER:: eps_0 = 8.8542D-12 !Epsilon_0

View file

@ -0,0 +1,88 @@
! Functions that return a random velocity based on the distribution function
module velocityDistribution
use moduleRandom
!Generic type for velocity distribution function
type, abstract:: velDistGeneric
contains
!Returns random velocity from distribution function
procedure(randomVel_interface), deferred, pass:: randomVel
end type velDistGeneric
abstract interface
function randomVel_interface(self) result(v)
import velDistGeneric
class(velDistGeneric), intent(in):: self
real(8):: v
end function randomVel_interface
end interface
!Container for velocity distributions
type:: velDistCont
class(velDistGeneric), allocatable:: obj
end type velDistCont
!Maxwellian distribution function
type, extends(velDistGeneric):: velDistMaxwellian
real(8):: vTh !Thermal Velocity
contains
procedure, pass:: randomVel => randomVelMaxwellian
end type velDistMaxwellian
type, extends(velDistGeneric):: velDistHalfMaxwellian
real(8):: vTh !Thermal Velocity
contains
procedure, pass:: randomVel => randomVelHalfMaxwellian
end type velDistHalfMaxwellian
!Dirac's delta distribution function
type, extends(velDistGeneric):: velDistDelta
contains
procedure, pass:: randomVel => randomVelDelta
end type velDistDelta
contains
!Random velocity from Maxwellian distribution
function randomVelMaxwellian(self) result (v)
use moduleRandom
implicit none
class(velDistMaxwellian), intent(in):: self
real(8):: v
v = 0.D0
v = self%vTh*randomMaxwellian()/sqrt(2.d0)
end function randomVelMaxwellian
!Random velocity from Half Maxwellian distribution
function randomVelHalfMaxwellian(self) result (v)
implicit none
class(velDistHalfMaxwellian), intent(in):: self
real(8):: v
v = 0.D0
v = self%vTh*randomHalfMaxwellian()
end function randomVelHalfMaxwellian
!Random velocity from Dirac's delta distribution
PURE function randomVelDelta(self) result(v)
implicit none
class(velDistDelta), intent(in):: self
real(8):: v
v = 0.D0
end function randomVelDelta
end module velocityDistribution

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

View file

@ -1,5 +1,5 @@
OBJS = common.o output.o mesh.o solver.o init.o \
moduleBoundary.o moduleCollisions.o moduleInject.o \
moduleCollisions.o moduleInject.o \
moduleList.o moduleProbe.o moduleCoulomb.o \
moduleSpecies.o
@ -11,7 +11,7 @@ common.o:
output.o: moduleSpecies.o common.o
$(MAKE) -C output all
mesh.o: moduleCollisions.o moduleCoulomb.o moduleBoundary.o output.o common.o
mesh.o: moduleList.o moduleSpecies.o moduleCollisions.o moduleCoulomb.o output.o common.o
$(MAKE) -C mesh all
solver.o: moduleSpecies.o moduleProbe.o common.o output.o mesh.o
@ -20,9 +20,6 @@ solver.o: moduleSpecies.o moduleProbe.o common.o output.o mesh.o
init.o: common.o solver.o moduleInject.o
$(MAKE) -C init all
moduleBoundary.o: common.o moduleBoundary.f90
$(FC) $(FCFLAGS) -c $(subst .o,.f90,$@) -o $(OBJDIR)/$@
moduleCollisions.o: moduleList.o moduleSpecies.o common.o moduleCollisions.f90
$(FC) $(FCFLAGS) -c $(subst .o,.f90,$@) -o $(OBJDIR)/$@
@ -38,6 +35,9 @@ moduleProbe.o: mesh.o moduleProbe.f90
moduleSpecies.o: common.o moduleSpecies.f90
$(FC) $(FCFLAGS) -c $(subst .o,.f90,$@) -o $(OBJDIR)/$@
moduleInject.o: common.o moduleInject.f90
$(FC) $(FCFLAGS) -c $(subst .o,.f90,$@) -o $(OBJDIR)/$@
%.o: %.f90
$(FC) $(FCFLAGS) -c $< -o $(OBJDIR)/$@

View file

@ -43,7 +43,7 @@ MODULE moduleMesh0D
CLASS(meshNode0D), INTENT(out):: self
INTEGER, INTENT(in):: n
REAL(8), INTENT(in):: r(1:3) !Unused variable
REAL(8), INTENT(in):: r(1:3) ! NOTE: Required by interface but unused
self%n = n
@ -117,7 +117,7 @@ MODULE moduleMesh0D
PURE FUNCTION fPsi0D(Xi, nNodes) RESULT(fPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
REAL(8), INTENT(in):: Xi(1:3) ! NOTE: Required by interface but unused
INTEGER, INTENT(in):: nNodes
REAL(8):: fPsi(1:nNodes)
@ -128,7 +128,7 @@ MODULE moduleMesh0D
PURE FUNCTION dPsi0D(Xi, nNodes) RESULT(dPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
REAL(8), INTENT(in):: Xi(1:3) ! NOTE: Required by interface but unused
INTEGER, INTENT(in):: nNodes
REAL(8):: dPsi(1:3,1:nNodes)
@ -142,7 +142,7 @@ MODULE moduleMesh0D
CLASS(meshCell0D), INTENT(in):: self
INTEGER, INTENT(in):: nNodes
REAL(8), INTENT(in):: dPsi(1:3,1:nNodes)
REAL(8):: pDer(1:3, 1:3)
REAL(8):: pDer(1:3, 1:3) ! NOTE: Required by interface but unused
pDer = 0.D0
@ -205,7 +205,7 @@ MODULE moduleMesh0D
IMPLICIT NONE
CLASS(meshCell0D), INTENT(in):: self
REAL(8), INTENT(in):: r(1:3)
REAL(8), INTENT(in):: r(1:3) ! NOTE: Required by interface but unused
REAL(8):: xN(1:3)
xN = 0.D0
@ -215,7 +215,7 @@ MODULE moduleMesh0D
PURE FUNCTION inside0D(Xi) RESULT(ins)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
REAL(8), INTENT(in):: Xi(1:3) ! NOTE: Required by interface but unused
LOGICAL:: ins
ins = .TRUE.
@ -226,7 +226,7 @@ MODULE moduleMesh0D
IMPLICIT NONE
CLASS(meshCell0D), INTENT(in):: self
REAL(8), INTENT(in):: Xi(1:3)
REAL(8), INTENT(in):: Xi(1:3) ! NOTE: Required by interface but unused
CLASS(meshElement), POINTER, INTENT(out):: neighbourElement
neighbourElement => NULL()
@ -237,7 +237,7 @@ MODULE moduleMesh0D
PURE FUNCTION detJ0D(pDer) RESULT(dJ)
IMPLICIT NONE
REAL(8), INTENT(in):: pDer(1:3, 1:3)
REAL(8), INTENT(in):: pDer(1:3, 1:3) ! NOTE: Required by interface but unused
REAL(8):: dJ
dJ = 0.D0
@ -247,7 +247,7 @@ MODULE moduleMesh0D
PURE FUNCTION invJ0D(pDer) RESULT(invJ)
IMPLICIT NONE
REAL(8), INTENT(in):: pDer(1:3, 1:3)
REAL(8), INTENT(in):: pDer(1:3, 1:3) ! NOTE: Required by interface but unused
REAL(8):: invJ(1:3,1:3)
invJ = 0.D0

View file

@ -4,12 +4,9 @@
! z == unused
MODULE moduleMesh1DCart
USE moduleMesh
USE moduleMeshBoundary
use moduleMeshCommon
IMPLICIT NONE
REAL(8), PARAMETER:: corSeg(1:3) = (/ -DSQRT(3.D0/5.D0), 0.D0, DSQRT(3.D0/5.D0) /)
REAL(8), PARAMETER:: wSeg(1:3) = (/ 5.D0/9.D0 , 8.D0/9.D0, 5.D0/9.D0 /)
TYPE, PUBLIC, EXTENDS(meshNode):: meshNode1DCart
!Element coordinates
REAL(8):: x = 0.D0
@ -27,10 +24,13 @@ MODULE moduleMesh1DCart
CLASS(meshNode), POINTER:: n1 => NULL()
CONTAINS
!meshEdge DEFERRED PROCEDURES
PROCEDURE, PASS:: init => initEdge1DCart
PROCEDURE, PASS:: getNodes => getNodes1DCart
PROCEDURE, PASS:: intersection => intersection1DCart
PROCEDURE, PASS:: randPos => randPosEdge
PROCEDURE, PASS:: init => initEdge1DCart
PROCEDURE, PASS:: getNodes => getNodes1DCart
PROCEDURE, PASS:: intersection => intersection1DCart
PROCEDURE, PASS:: randPos => randPosEdge
procedure, pass:: center => centerEdgePoint
procedure, nopass:: centerXi => centerXiPoint
procedure, nopass:: fPsi => fPsiPoint
END TYPE meshEdge1DCart
@ -100,17 +100,16 @@ MODULE moduleMesh1DCart
!EDGE FUNCTIONS
!Init edge element
SUBROUTINE initEdge1DCart(self, n, p, bt, physicalSurface)
USE moduleSpecies
USE moduleBoundary
SUBROUTINE initEdge1DCart(self, n, p, ps)
USE moduleSpecies, only: nSpecies
USE moduleErrors
IMPLICIT NONE
CLASS(meshEdge1DCart), INTENT(out):: self
INTEGER, INTENT(in):: n
INTEGER, INTENT(in):: p(:)
INTEGER, INTENT(in):: bt
INTEGER, INTENT(in):: physicalSurface
INTEGER, INTENT(in):: ps
integer:: ps_index
REAL(8), DIMENSION(1:3):: r1
INTEGER:: s
@ -126,19 +125,21 @@ MODULE moduleMesh1DCart
self%normal = (/ 1.D0, 0.D0, 0.D0 /)
!Boundary index
self%boundary => boundary(bt)
ALLOCATE(self%fboundary(1:nSpecies))
!Assign functions to boundary
DO s = 1, nSpecies
CALL pointBoundaryFunction(self, s)
! Get Physical Surface index based on input numering
ps_index = physicalSurface_to_index(ps)
END DO
! Add elements to physical surface
call meshEdgePointer_add(physicalSurfaces(ps_index)%edges, self%n)
call meshNodePointer_add(physicalSurfaces(ps_index)%nodes, self%n1%n)
!Physical Surface
self%physicalSurface = physicalSurface
! Link edge to particle boundaries
allocate(self%boundariesParticle(1:nSpecies))
do s = 1, nSpecies
self%boundariesParticle(s)%obj => physicalSurfaces(ps_index)%particles(s)%obj
END SUBROUTINE initEdge1DCart
end do
end subroutine initEdge1DCart
!Get nodes from edge
PURE FUNCTION getNodes1DCart(self, nNodes) RESULT(n)
@ -156,7 +157,7 @@ MODULE moduleMesh1DCart
IMPLICIT NONE
CLASS(meshEdge1DCart), INTENT(in):: self
REAL(8), DIMENSION(1:3), INTENT(in):: r0
REAL(8), DIMENSION(1:3), INTENT(in):: r0 ! NOTE: Required by interface but unused
REAL(8), DIMENSION(1:3):: r
r = (/ self%x, 0.D0, 0.D0 /)
@ -165,6 +166,8 @@ MODULE moduleMesh1DCart
!Calculate a 'random' position in edge
FUNCTION randPosEdge(self) RESULT(r)
implicit none
CLASS(meshEdge1DCart), INTENT(in):: self
REAL(8):: r(1:3)
@ -172,7 +175,17 @@ MODULE moduleMesh1DCart
END FUNCTION randPosEdge
!VOLUME FUNCTIONS
function centerEdgePoint(self) RESULT(r)
implicit none
class(meshEdge1DCart), intent(in):: self
real(8):: r(1:3)
r = (/ self%x, 0.D0, 0.D0 /)
end function centerEdgePoint
!CELL FUNCTIONS
!SEGMENT FUNCTIONS
!Init element
SUBROUTINE initCell1DCartSegm(self, n, p, nodes)
@ -236,35 +249,6 @@ MODULE moduleMesh1DCart
END FUNCTION randPos1DCartSegm
!Compute element functions at point Xi
PURE FUNCTION fPsiSegm(Xi, nNodes) RESULT(fPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: fPsi(1:nNodes)
fPsi = (/ 1.D0 - Xi(1), &
1.D0 + Xi(1) /)
fPsi = fPsi * 0.50D0
END FUNCTION fPsiSegm
!Derivative element function at coordinates Xi
PURE FUNCTION dPsiSegm(Xi, nNodes) RESULT(dPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: dPsi(1:3,1:nNodes)
dPsi = 0.D0
dPsi(1, 1:2) = (/ -5.D-1, 5.D-1 /)
END FUNCTION dPsiSegm
!Partial derivative in global coordinates
PURE FUNCTION partialDerSegm(self, nNodes, dPsi) RESULT(pDer)
IMPLICIT NONE
@ -446,7 +430,7 @@ MODULE moduleMesh1DCart
END SUBROUTINE volumeSegm
!COMMON FUNCTIONS FOR 1D VOLUME ELEMENTS
!COMMON FUNCTIONS FOR 1D CELL ELEMENTS
!Compute element Jacobian determinant
PURE FUNCTION detJ1DCart(pDer) RESULT(dJ)
IMPLICIT NONE

View file

@ -4,12 +4,9 @@
! z == unused
MODULE moduleMesh1DRad
USE moduleMesh
USE moduleMeshBoundary
use moduleMeshCommon
IMPLICIT NONE
REAL(8), PARAMETER:: corSeg(1:3) = (/ -DSQRT(3.D0/5.D0), 0.D0, DSQRT(3.D0/5.D0) /)
REAL(8), PARAMETER:: wSeg(1:3) = (/ 5.D0/9.D0 , 8.D0/9.D0, 5.D0/9.D0 /)
TYPE, PUBLIC, EXTENDS(meshNode):: meshNode1DRad
!Element coordinates
REAL(8):: r = 0.D0
@ -27,10 +24,13 @@ MODULE moduleMesh1DRad
CLASS(meshNode), POINTER:: n1 => NULL()
CONTAINS
!meshEdge DEFERRED PROCEDURES
PROCEDURE, PASS:: init => initEdge1DRad
PROCEDURE, PASS:: getNodes => getNodes1DRad
PROCEDURE, PASS:: intersection => intersection1DRad
PROCEDURE, PASS:: randPos => randPos1DRad
PROCEDURE, PASS:: init => initEdge1DRad
PROCEDURE, PASS:: getNodes => getNodes1DRad
PROCEDURE, PASS:: intersection => intersection1DRad
PROCEDURE, PASS:: randPos => randPos1DRad
procedure, pass:: center => centerEdgePoint
procedure, nopass:: centerXi => centerXiPoint
procedure, nopass:: fPsi => fPsiPoint
END TYPE meshEdge1DRad
@ -100,18 +100,16 @@ MODULE moduleMesh1DRad
!EDGE FUNCTIONS
!Init edge element
SUBROUTINE initEdge1DRad(self, n, p, bt, physicalSurface)
USE moduleSpecies
USE moduleBoundary
SUBROUTINE initEdge1DRad(self, n, p, ps)
USE moduleSpecies, only:nSpecies
USE moduleErrors
USE moduleRefParam, ONLY: L_ref
IMPLICIT NONE
CLASS(meshEdge1DRad), INTENT(out):: self
INTEGER, INTENT(in):: n
INTEGER, INTENT(in):: p(:)
INTEGER, INTENT(in):: bt
INTEGER, INTENT(in):: physicalSurface
INTEGER, INTENT(in):: ps
integer:: ps_index
REAL(8), DIMENSION(1:3):: r1
INTEGER:: s
@ -127,19 +125,21 @@ MODULE moduleMesh1DRad
self%normal = (/ 1.D0, 0.D0, 0.D0 /)
!Boundary index
self%boundary => boundary(bt)
ALLOCATE(self%fboundary(1:nSpecies))
!Assign functions to boundary
DO s = 1, nSpecies
CALL pointBoundaryFunction(self, s)
! Get Physical Surface index based on input numering
ps_index = physicalSurface_to_index(ps)
END DO
! Add elements to physical surface
call meshEdgePointer_add(physicalSurfaces(ps_index)%edges, self%n)
call meshNodePointer_add(physicalSurfaces(ps_index)%nodes, self%n1%n)
!Physical Surface
self%physicalSurface = physicalSurface
! Link edge to particle boundaries
allocate(self%boundariesParticle(1:nSpecies))
do s = 1, nSpecies
self%boundariesParticle(s)%obj => physicalSurfaces(ps_index)%particles(s)%obj
END SUBROUTINE initEdge1DRad
end do
end subroutine initEdge1DRad
!Get nodes from edge
PURE FUNCTION getNodes1DRad(self, nNodes) RESULT(n)
@ -157,7 +157,7 @@ MODULE moduleMesh1DRad
IMPLICIT NONE
CLASS(meshEdge1DRad), INTENT(in):: self
REAL(8), DIMENSION(1:3), INTENT(in):: r0
REAL(8), DIMENSION(1:3), INTENT(in):: r0 ! NOTE: Required by interface but unused
REAL(8), DIMENSION(1:3):: r
r = (/ self%r, 0.D0, 0.D0 /)
@ -173,7 +173,15 @@ MODULE moduleMesh1DRad
END FUNCTION randPos1DRad
!VOLUME FUNCTIONS
function centerEdgePoint(self) RESULT(r)
class(meshEdge1DRad), intent(in):: self
real(8):: r(1:3)
r = (/ self%r, 0.D0, 0.D0 /)
end function centerEdgePoint
!CELL FUNCTIONS
!SEGMENT FUNCTIONS
!Init element
SUBROUTINE initCell1DRadSegm(self, n, p, nodes)
@ -237,35 +245,6 @@ MODULE moduleMesh1DRad
END FUNCTION randPos1DRadSegm
!Compute element functions at point Xi
PURE FUNCTION fPsiSegm(Xi, nNodes) RESULT(fPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: fPsi(1:nNodes)
fPsi = (/ 1.D0 - Xi(1), &
1.D0 + Xi(1) /)
fPsi = fPsi * 0.50D0
END FUNCTION fPsiSegm
!Derivative element function at coordinates Xi
PURE FUNCTION dPsiSegm(Xi, nNodes) RESULT(dPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: dPsi(1:3,1:nNodes)
dPsi = 0.D0
dPsi(1, 1:2) = (/ -5.D-1, 5.D-1 /)
END FUNCTION dPsiSegm
!Partial derivative in global coordinates
PURE FUNCTION partialDerSegm(self, nNodes, dPsi) RESULT(pDer)
IMPLICIT NONE
@ -462,7 +441,7 @@ MODULE moduleMesh1DRad
END SUBROUTINE volumeSegm
!COMMON FUNCTIONS FOR 1D VOLUME ELEMENTS
!COMMON FUNCTIONS FOR 1D CELL ELEMENTS
!Compute element Jacobian determinant
PURE FUNCTION detJ1DRad(pDer) RESULT(dJ)
IMPLICIT NONE

View file

@ -4,17 +4,9 @@
! z == unused
MODULE moduleMesh2DCart
USE moduleMesh
USE moduleMeshBoundary
use moduleMeshCommon
IMPLICIT NONE
!Values for Gauss integral
REAL(8), PARAMETER:: corQuad(1:3) = (/ -DSQRT(3.D0/5.D0), 0.D0, DSQRT(3.D0/5.D0) /)
REAL(8), PARAMETER:: wQuad(1:3) = (/ 5.D0/9.D0, 8.D0/9.D0, 5.D0/9.D0 /)
REAL(8), PARAMETER:: Xi1Tria(1:4) = (/ 1.D0/3.D0, 1.D0/5.D0, 3.D0/5.D0, 1.D0/5.D0 /)
REAL(8), PARAMETER:: Xi2Tria(1:4) = (/ 1.D0/3.D0, 1.D0/5.D0, 1.D0/5.D0, 3.D0/5.D0 /)
REAL(8), PARAMETER:: wTria(1:4) = (/ -27.D0/96.D0, 25.D0/96.D0, 25.D0/96.D0, 25.D0/96.D0 /)
TYPE, PUBLIC, EXTENDS(meshNode):: meshNode2DCart
!Element coordinates
REAL(8):: x = 0.D0, y = 0.D0
@ -32,10 +24,13 @@ MODULE moduleMesh2DCart
CLASS(meshNode), POINTER:: n1 => NULL(), n2 => NULL()
CONTAINS
!meshEdge DEFERRED PROCEDURES
PROCEDURE, PASS:: init => initEdge2DCart
PROCEDURE, PASS:: getNodes => getNodes2DCart
PROCEDURE, PASS:: intersection => intersection2DCartEdge
PROCEDURE, PASS:: randPos => randPosEdge
PROCEDURE, PASS:: init => initEdge2DCart
PROCEDURE, PASS:: getNodes => getNodes2DCart
PROCEDURE, PASS:: intersection => intersection2DCartEdge
PROCEDURE, PASS:: randPos => randPosEdge
procedure, pass:: center => centerEdgeSegm
procedure, nopass:: centerXi => centerXiSegm
procedure, nopass:: fPsi => fPsiSegm
END TYPE meshEdge2DCart
@ -140,17 +135,16 @@ MODULE moduleMesh2DCart
!EDGE FUNCTIONS
!Init edge element
SUBROUTINE initEdge2DCart(self, n, p, bt, physicalSurface)
USE moduleSpecies
USE moduleBoundary
SUBROUTINE initEdge2DCart(self, n, p, ps)
USE moduleSpecies, only:nSpecies
USE moduleErrors
IMPLICIT NONE
CLASS(meshEdge2DCart), INTENT(out):: self
INTEGER, INTENT(in):: n
INTEGER, INTENT(in):: p(:)
INTEGER, INTENT(in):: bt
INTEGER, INTENT(in):: physicalSurface
INTEGER, INTENT(in):: ps
integer:: ps_index
REAL(8), DIMENSION(1:3):: r1, r2
INTEGER:: s
@ -170,17 +164,20 @@ MODULE moduleMesh2DCart
0.D0 /)
self%normal = self%normal/NORM2(self%normal)
!Boundary index
self%boundary => boundary(bt)
ALLOCATE(self%fboundary(1:nSpecies))
!Assign functions to boundary
DO s = 1, nSpecies
CALL pointBoundaryFunction(self, s)
! Get Physical Surface index based on input numering
ps_index = physicalSurface_to_index(ps)
END DO
! Add elements to physical surface
call meshEdgePointer_add(physicalSurfaces(ps_index)%edges, self%n)
call meshNodePointer_add(physicalSurfaces(ps_index)%nodes, self%n1%n)
call meshNodePointer_add(physicalSurfaces(ps_index)%nodes, self%n2%n)
!Physical surface
self%physicalSurface = physicalSurface
! Link edge to particle boundaries
allocate(self%boundariesParticle(1:nSpecies))
do s = 1, nSpecies
self%boundariesParticle(s)%obj => physicalSurfaces(ps_index)%particles(s)%obj
end do
END SUBROUTINE initEdge2DCart
@ -221,19 +218,37 @@ MODULE moduleMesh2DCart
IMPLICIT NONE
CLASS(meshEdge2DCart), INTENT(in):: self
REAL(8):: rnd
REAL(8):: r(1:3)
REAL(8):: p1(1:2), p2(1:2)
real(8):: Xi(1:3)
real(8):: r(1:3)
real(8):: fPsi(1:2)
rnd = random()
Xi = 0.d0
Xi(1) = random()
p1 = (/self%x(1), self%y(1) /)
p2 = (/self%x(2), self%y(2) /)
r(1:2) = (1.D0 - rnd)*p1 + rnd*p2
r(3) = 0.D0
fPsi = self%fPsi(Xi, 2)
r = (/dot_product(fPsi, self%x), &
dot_product(fPsi, self%y), &
0.d0/)
END FUNCTION randPosEdge
function centerEdgeSegm(self) result(r)
use moduleMeshCommon, only: cenSeg
implicit none
class(meshEdge2DCart), intent(in):: self
real(8):: r(1:3)
real(8):: fPsi(1:2)
fPsi = self%fPsi(cenSeg, 2)
r = (/dot_product(fPsi, self%x), &
dot_product(fPsi, self%y), &
0.d0/)
end function centerEdgeSegm
!VOLUME FUNCTIONS
!QUAD FUNCTIONS
!Init element
@ -310,49 +325,6 @@ MODULE moduleMesh2DCart
END FUNCTION randPosCellQuad
!Compute element functions in point Xi
PURE FUNCTION fPsiQuad(Xi, nNodes) RESULT(fPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: fPsi(1:nNodes)
fPsi = 0.D0
fPsi = (/ (1.D0 - Xi(1)) * (1.D0 - Xi(2)), &
(1.D0 + Xi(1)) * (1.D0 - Xi(2)), &
(1.D0 + Xi(1)) * (1.D0 + Xi(2)), &
(1.D0 - Xi(1)) * (1.D0 + Xi(2)) /)
fPsi = fPsi * 0.25D0
END FUNCTION fPsiQuad
!Derivative element function at coordinates Xi
PURE FUNCTION dPsiQuad(Xi, nNodes) RESULT(dPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: dPsi(1:3,1:nNodes)
dPsi = 0.D0
dPsi(1, 1:4) = (/ -(1.D0 - Xi(2)), &
(1.D0 - Xi(2)), &
(1.D0 + Xi(2)), &
-(1.D0 + Xi(2)) /)
dPsi(2, 1:4) = (/ -(1.D0 - Xi(1)), &
-(1.D0 + Xi(1)), &
(1.D0 + Xi(1)), &
(1.D0 - Xi(1)) /)
dPsi = dPsi * 0.25D0
END FUNCTION dPsiQuad
!Partial derivative in global coordinates
PURE FUNCTION partialDerQuad(self, nNodes, dPsi) RESULT(pDer)
IMPLICIT NONE
@ -488,9 +460,13 @@ MODULE moduleMesh2DCart
REAL(8), INTENT(in):: Xi(1:3)
LOGICAL:: ins
real(8):: eps
ins = (Xi(1) >= -1.D0 .AND. Xi(1) <= 1.D0) .AND. &
(Xi(2) >= -1.D0 .AND. Xi(2) <= 1.D0)
eps = 1.0d-10
ins = (Xi(1) >= -1.D0-eps .AND. Xi(1) <= 1.D0+eps) .AND. &
(Xi(2) >= -1.D0-eps .AND. Xi(2) <= 1.D0+eps)
END FUNCTION insideQuad
@ -656,35 +632,6 @@ MODULE moduleMesh2DCart
END FUNCTION randPosCellTria
!Compute element functions in point Xi
PURE FUNCTION fPsiTria(Xi, nNodes) RESULT(fPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: fPsi(1:nNodes)
fPsi(1) = 1.D0 - Xi(1) - Xi(2)
fPsi(2) = Xi(1)
fPsi(3) = Xi(2)
END FUNCTION fPsiTria
!Compute element derivative functions in point Xi
PURE FUNCTION dPsiTria(Xi, nNodes) RESULT(dPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: dPsi(1:3,1:nNodes)
dPsi = 0.D0
dPsi(1,1:3) = (/ -1.D0, 1.D0, 0.D0 /)
dPsi(2,1:3) = (/ -1.D0, 0.D0, 1.D0 /)
END FUNCTION dPsiTria
!Compute the derivatives in global coordinates
PURE FUNCTION partialDerTria(self, nNodes, dPsi) RESULT(pDer)
IMPLICIT NONE

View file

@ -4,17 +4,9 @@
! z == theta (unused)
MODULE moduleMesh2DCyl
USE moduleMesh
USE moduleMeshBoundary
use moduleMeshCommon
IMPLICIT NONE
!Values for Gauss integral
REAL(8), PARAMETER:: corQuad(1:3) = (/ -DSQRT(3.D0/5.D0), 0.D0, DSQRT(3.D0/5.D0) /)
REAL(8), PARAMETER:: wQuad(1:3) = (/ 5.D0/9.D0, 8.D0/9.D0, 5.D0/9.D0 /)
REAL(8), PARAMETER:: Xi1Tria(1:4) = (/ 1.D0/3.D0, 1.D0/5.D0, 3.D0/5.D0, 1.D0/5.D0 /)
REAL(8), PARAMETER:: Xi2Tria(1:4) = (/ 1.D0/3.D0, 1.D0/5.D0, 1.D0/5.D0, 3.D0/5.D0 /)
REAL(8), PARAMETER:: wTria(1:4) = (/ -27.D0/96.D0, 25.D0/96.D0, 25.D0/96.D0, 25.D0/96.D0 /)
TYPE, PUBLIC, EXTENDS(meshNode):: meshNode2DCyl
!Element coordinates
REAL(8):: r = 0.D0, z = 0.D0
@ -32,10 +24,13 @@ MODULE moduleMesh2DCyl
CLASS(meshNode), POINTER:: n1 => NULL(), n2 => NULL()
CONTAINS
!meshEdge DEFERRED PROCEDURES
PROCEDURE, PASS:: init => initEdge2DCyl
PROCEDURE, PASS:: getNodes => getNodes2DCyl
PROCEDURE, PASS:: intersection => intersection2DCylEdge
PROCEDURE, PASS:: randPos => randPosEdge
PROCEDURE, PASS:: init => initEdge2DCyl
PROCEDURE, PASS:: getNodes => getNodes2DCyl
PROCEDURE, PASS:: intersection => intersection2DCylEdge
PROCEDURE, PASS:: randPos => randPosEdge
procedure, pass:: center => centerEdgeSegm
procedure, nopass:: centerXi => centerXiSegm
procedure, nopass:: fPsi => fPsiSegm
END TYPE meshEdge2DCyl
@ -140,9 +135,8 @@ MODULE moduleMesh2DCyl
!EDGE FUNCTIONS
!Init edge element
SUBROUTINE initEdge2DCyl(self, n, p, bt, physicalSurface)
SUBROUTINE initEdge2DCyl(self, n, p, ps)
USE moduleSpecies
USE moduleBoundary
USE moduleErrors
USE moduleConstParam, ONLY: PI
IMPLICIT NONE
@ -150,8 +144,8 @@ MODULE moduleMesh2DCyl
CLASS(meshEdge2DCyl), INTENT(out):: self
INTEGER, INTENT(in):: n
INTEGER, INTENT(in):: p(:)
INTEGER, INTENT(in):: bt
INTEGER, INTENT(in):: physicalSurface
INTEGER, INTENT(in):: ps
integer:: ps_index
REAL(8), DIMENSION(1:3):: r1, r2
INTEGER:: s
@ -179,17 +173,20 @@ MODULE moduleMesh2DCyl
0.D0 /)
self%normal = self%normal/NORM2(self%normal)
!Boundary index
self%boundary => boundary(bt)
ALLOCATE(self%fboundary(1:nSpecies))
!Assign functions to boundary
DO s = 1, nSpecies
CALL pointBoundaryFunction(self, s)
! Get Physical Surface index based on input numering
ps_index = physicalSurface_to_index(ps)
END DO
! Add elements to physical surface
call meshEdgePointer_add(physicalSurfaces(ps_index)%edges, self%n)
call meshNodePointer_add(physicalSurfaces(ps_index)%nodes, self%n1%n)
call meshNodePointer_add(physicalSurfaces(ps_index)%nodes, self%n2%n)
!Physical surface
self%physicalSurface = physicalSurface
! Link edge to particle boundaries
allocate(self%boundariesParticle(1:nSpecies))
do s = 1, nSpecies
self%boundariesParticle(s)%obj => physicalSurfaces(ps_index)%particles(s)%obj
end do
END SUBROUTINE initEdge2DCyl
@ -230,20 +227,38 @@ MODULE moduleMesh2DCyl
IMPLICIT NONE
CLASS(meshEdge2DCyl), INTENT(in):: self
REAL(8):: rnd
REAL(8):: r(1:3)
REAL(8):: p1(1:2), p2(1:2)
real(8):: Xi(1:3)
real(8):: r(1:3)
real(8):: fPsi(1:2)
rnd = random()
Xi = 0.d0
Xi(1) = random()
p1 = (/self%z(1), self%r(1) /)
p2 = (/self%z(2), self%r(2) /)
r(1:2) = (1.D0 - rnd)*p1 + rnd*p2
r(3) = 0.D0
fPsi = self%fPsi(Xi, 2)
r = (/dot_product(fPsi, self%z), &
dot_product(fPsi, self%r), &
0.d0/)
END FUNCTION randPosEdge
!VOLUME FUNCTIONS
function centerEdgeSegm(self) result(r)
use moduleMeshCommon, only: cenSeg
implicit none
class(meshEdge2DCyl), intent(in):: self
real(8):: r(1:3)
real(8):: fPsi(1:2)
fPsi = self%fPsi(cenSeg, 2)
r = (/dot_product(fPsi, self%z), &
dot_product(fPsi, self%r), &
0.d0/)
end function centerEdgeSegm
!CELL FUNCTIONS
!QUAD FUNCTIONS
!Init element
SUBROUTINE initCellQuad2DCyl(self, n, p, nodes)
@ -318,49 +333,6 @@ MODULE moduleMesh2DCyl
END FUNCTION randPosCellQuad
!Computes element functions in point Xi
PURE FUNCTION fPsiQuad(Xi, nNodes) RESULT(fPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: fPsi(1:nNodes)
fPsi = 0.D0
fPsi = (/ (1.D0 - Xi(1)) * (1.D0 - Xi(2)), &
(1.D0 + Xi(1)) * (1.D0 - Xi(2)), &
(1.D0 + Xi(1)) * (1.D0 + Xi(2)), &
(1.D0 - Xi(1)) * (1.D0 + Xi(2)) /)
fPsi = fPsi * 0.25D0
END FUNCTION fPsiQuad
!Derivative element function at coordinates Xi
PURE FUNCTION dPsiQuad(Xi, nNodes) RESULT(dPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: dPsi(1:3,1:nNodes)
dPsi = 0.D0
dPsi(1, 1:4) = (/ -(1.D0 - Xi(2)), &
(1.D0 - Xi(2)), &
(1.D0 + Xi(2)), &
-(1.D0 + Xi(2)) /)
dPsi(2, 1:4) = (/ -(1.D0 - Xi(1)), &
-(1.D0 + Xi(1)), &
(1.D0 + Xi(1)), &
(1.D0 - Xi(1)) /)
dPsi = dPsi * 0.25D0
END FUNCTION dPsiQuad
!Partial derivative in global coordinates
PURE FUNCTION partialDerQuad(self, nNodes, dPsi) RESULT(pDer)
IMPLICIT NONE
@ -504,9 +476,12 @@ MODULE moduleMesh2DCyl
REAL(8), INTENT(in):: Xi(1:3)
LOGICAL:: ins
real(8):: eps
ins = (Xi(1) >= -1.D0 .AND. Xi(1) <= 1.D0) .AND. &
(Xi(2) >= -1.D0 .AND. Xi(2) <= 1.D0)
eps = 1.0d-10
ins = (Xi(1) >= -1.D0-eps .AND. Xi(1) <= 1.D0+eps) .AND. &
(Xi(2) >= -1.D0-eps .AND. Xi(2) <= 1.D0+eps)
END FUNCTION insideQuad
@ -683,35 +658,6 @@ MODULE moduleMesh2DCyl
END FUNCTION randPosCellTria
!Compute element functions in point Xi
PURE FUNCTION fPsiTria(Xi, nNodes) RESULT(fPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: fPsi(1:nNodes)
fPsi(1) = 1.D0 - Xi(1) - Xi(2)
fPsi(2) = Xi(1)
fPsi(3) = Xi(2)
END FUNCTION fPsiTria
!Compute element derivative functions in point Xi
PURE FUNCTION dPsiTria(Xi, nNodes) RESULT(dPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: dPsi(1:3,1:nNodes)
dPsi = 0.D0
dPsi(1,1:3) = (/ -1.D0, 1.D0, 0.D0 /)
dPsi(2,1:3) = (/ -1.D0, 0.D0, 1.D0 /)
END FUNCTION dPsiTria
!Compute the derivatives in global coordinates
PURE FUNCTION partialDerTria(self, nNodes, dPsi) RESULT(pDer)
IMPLICIT NONE
@ -929,7 +875,7 @@ MODULE moduleMesh2DCyl
END SUBROUTINE volumeTria
!COMMON FUNCTIONS FOR CYLINDRICAL VOLUME ELEMENTS
!COMMON FUNCTIONS FOR CYLINDRICAL CELL ELEMENTS
!Compute element Jacobian determinant
PURE FUNCTION detJ2DCyl(pDer) RESULT(dJ)
IMPLICIT NONE

View file

@ -4,7 +4,7 @@
! z == z
MODULE moduleMesh3DCart
USE moduleMesh
USE moduleMeshBoundary
use moduleMeshCommon
IMPLICIT NONE
TYPE, PUBLIC, EXTENDS(meshNode):: meshNode3DCart
@ -25,12 +25,14 @@ MODULE moduleMesh3DCart
CLASS(meshNode), POINTER:: n1 => NULL(), n2 => NULL(), n3 => NULL()
CONTAINS
!meshEdge DEFERRED PROCEDURES
PROCEDURE, PASS:: init => initEdge3DCartTria
PROCEDURE, PASS:: getNodes => getNodes3DCartTria
PROCEDURE, PASS:: intersection => intersection3DCartTria
PROCEDURE, PASS:: randPos => randPosEdgeTria
PROCEDURE, PASS:: init => initEdge3DCartTria
PROCEDURE, PASS:: getNodes => getNodes3DCartTria
PROCEDURE, PASS:: intersection => intersection3DCartTria
PROCEDURE, PASS:: randPos => randPosEdgeTria
procedure, pass:: center => centerEdgeTria
!PARTICULAR PROCEDURES
PROCEDURE, NOPASS, PRIVATE:: fPsi => fPsiEdgeTria
procedure, nopass:: centerXi => centerXiTria
PROCEDURE, NOPASS:: fPsi => fPsiTria
END TYPE meshEdge3DCartTria
@ -104,9 +106,8 @@ MODULE moduleMesh3DCart
!EDGE FUNCTIONS
!Init surface element
SUBROUTINE initEdge3DCartTria(self, n, p, bt, physicalSurface)
USE moduleSpecies
USE moduleBoundary
SUBROUTINE initEdge3DCartTria(self, n, p, ps)
USE moduleSpecies, only: nSpecies
USE moduleErrors
USE moduleMath
USE moduleRefParam, ONLY: L_ref
@ -115,8 +116,8 @@ MODULE moduleMesh3DCart
CLASS(meshEdge3DCartTria), INTENT(out):: self
INTEGER, INTENT(in):: n
INTEGER, INTENT(in):: p(:)
INTEGER, INTENT(in):: bt
INTEGER, INTENT(in):: physicalSurface
INTEGER, INTENT(in):: ps
integer:: ps_index
REAL(8), DIMENSION(1:3):: r1, r2, r3
REAL(8), DIMENSION(1:3):: vec1, vec2
INTEGER:: s
@ -145,17 +146,21 @@ MODULE moduleMesh3DCart
self%surface = 1.D0/L_ref**2 !TODO: FIX THIS WHEN MOVING TO 3D
!Boundary index
self%boundary => boundary(bt)
ALLOCATE(self%fBoundary(1:nSpecies))
!Assign functions to boundary
DO s = 1, nSpecies
CALL pointBoundaryFunction(self, s)
! Get Physical Surface index based on input numering
ps_index = physicalSurface_to_index(ps)
END DO
! Add elements to physical surface
call meshEdgePointer_add(physicalSurfaces(ps_index)%edges, self%n)
call meshNodePointer_add(physicalSurfaces(ps_index)%nodes, self%n1%n)
call meshNodePointer_add(physicalSurfaces(ps_index)%nodes, self%n2%n)
call meshNodePointer_add(physicalSurfaces(ps_index)%nodes, self%n3%n)
!Physical surface
self%physicalSurface = physicalSurface
! Link edge to particle boundaries
allocate(self%boundariesParticle(1:nSpecies))
do s = 1, nSpecies
self%boundariesParticle(s)%obj => physicalSurfaces(ps_index)%particles(s)%obj
end do
END SUBROUTINE initEdge3DCartTria
@ -204,25 +209,28 @@ MODULE moduleMesh3DCart
Xi(2) = random( 0.D0, 1.D0 - Xi(1))
Xi(3) = 0.D0
fPsi = self%fPsi(Xi)
fPsi = self%fPsi(Xi, 3)
r = (/DOT_PRODUCT(fPsi, self%x), &
DOT_PRODUCT(fPsi, self%y), &
DOT_PRODUCT(fPsi, self%z)/)
END FUNCTION randPosEdgeTria
!Shape functions for triangular surface
PURE FUNCTION fPsiEdgeTria(Xi) RESULT(fPsi)
IMPLICIT NONE
function centerEdgeTria(self) result(r)
use moduleMeshCommon, only: cenTria
implicit none
REAL(8), INTENT(in):: Xi(1:3)
REAL(8):: fPsi(1:3)
class(meshEdge3DCartTria), intent(in):: self
real(8):: r(1:3)
real(8):: fPsi(1:3)
fPsi(1) = 1.D0 - Xi(1) - Xi(2)
fPsi(2) = Xi(1)
fPsi(3) = Xi(2)
fPsi = self%fPsi(cenTria, 3)
END FUNCTION fPsiEdgeTria
r = (/dot_product(fPsi, self%x), &
dot_product(fPsi, self%y), &
dot_product(fPsi, self%z)/)
end function centerEdgeTria
!VOLUME FUNCTIONS
!TETRA FUNCTIONS
@ -301,37 +309,6 @@ MODULE moduleMesh3DCart
END FUNCTION randPosCellTetra
!Compute element functions in point Xi
PURE FUNCTION fPsiTetra(Xi, nNodes) RESULT(fPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: fPsi(1:nNodes)
fPsi(1) = 1.D0 - Xi(1) - Xi(2) - Xi(3)
fPsi(2) = Xi(1)
fPsi(3) = Xi(2)
fPsi(4) = Xi(3)
END FUNCTION fPsiTetra
!Compute element derivative functions in point Xi
PURE FUNCTION dPsiTetra(Xi, nNodes) RESULT(dPsi)
IMPLICIT NONE
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8):: dPsi(1:3, 1:nNodes)
dPsi = 0.D0
dPsi(1,1:4) = (/ -1.D0, 1.D0, 0.D0, 0.D0 /)
dPsi(2,1:4) = (/ -1.D0, 0.D0, 1.D0, 0.D0 /)
dPsi(3,1:4) = (/ -1.D0, 0.D0, 0.D0, 1.D0 /)
END FUNCTION dPsiTetra
!Compute the derivatives in global coordinates
PURE FUNCTION partialDerTetra(self, nNodes, dPsi) RESULT(pDer)
IMPLICIT NONE

View file

@ -34,7 +34,7 @@ MODULE moduleMeshInput0D
IMPLICIT NONE
CLASS(meshGeneric), INTENT(inout):: self
CHARACTER(:), ALLOCATABLE, INTENT(in):: filename !Dummy file, not used
CHARACTER(:), ALLOCATABLE, INTENT(in):: filename ! NOTE: Required by interface but unused
REAL(8):: r(1:3)
!Allocates one node

View file

@ -15,22 +15,24 @@ MODULE moduleMeshOutput0D
CHARACTER(:), ALLOCATABLE:: fileName
DO i = 1, nSpecies
fileName='OUTPUT_' // species(i)%obj%name // '.dat'
fileName = formatFileName('Output', species(i)%obj%name, 'csv')
IF (timeStep == 0) THEN
OPEN(20, file = path // folder // '/' // fileName, action = 'write')
WRITE(20, "(A1, 14X, A5, A20, 40X, A20, 2(A20))") "#","t (s)","density (m^-3)", "velocity (m/s)", &
"pressure (Pa)", "temperature (K)"
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN(20, file = generateFilePath(fileName), action = 'write')
WRITE(20, "(*("//fmtColStr//"))")'"t (s)"','"density (m^-3)"', &
'"velocity (m/s):0"', '"velocity (m/s):1"', '"velocity (m/s):2"', &
'"pressure (Pa)"', &
'"temperature (K)"'
call informFileCreation(fileName)
CLOSE(20)
END IF
OPEN(20, file = path // folder // '/' // fileName, position = 'append', action = 'write')
OPEN(20, file = generateFilePath(fileName), position = 'append', action = 'write')
CALL calculateOutput(self%nodes(1)%obj%output(i), output, self%nodes(1)%obj%v, species(i)%obj)
WRITE(20, "(7(ES20.6E3))") REAL(timeStep)*tauMin*ti_ref, output%density, &
output%velocity, &
output%pressure, &
output%temperature
WRITE(20, "(*("//fmtColReal//"))") REAL(timeStep)*tauMin*ti_ref, output%density, &
output%velocity, &
output%pressure, &
output%temperature
CLOSE(20)
END DO
@ -49,17 +51,22 @@ MODULE moduleMeshOutput0D
CHARACTER(:), ALLOCATABLE:: fileName
INTEGER:: k
fileName='OUTPUT_Collisions.dat'
fileName = formatFileName('Output', 'Collisions', 'csv')
IF (timeStep == tInitial) THEN
OPEN(20, file = path // folder // '/' // fileName, action = 'write')
WRITE(20, "(A1, 14X, A5, A20)") "#","t (s)","collisions"
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN(20, file = generateFilePath(fileName), action = 'write')
call informFileCreation(fileName)
WRITE(20, "(A,A)", advance='no') "t (s)", ','
do k = 1, nCollPairs-1
write(20, '(A,A,I3,A,A)', advance='no') '"',"pair", k, '"', ','
end do
write(20, "(A,A,I3,A)", advance='no') '"',"pair",k, '"'
CLOSE(20)
END IF
OPEN(20, file = path // folder // '/' // fileName, position = 'append', action = 'write')
WRITE(20, "(ES20.6E3, 10I20)") REAL(timeStep)*tauMin*ti_ref, (self%cells(1)%obj%tallyColl(k)%tally, k=1,nCollPairs)
OPEN(20, file = generateFilePath(fileName), position = 'append', action = 'write')
WRITE(20, "("//fmtColReal//", *("//fmtColInt//"))") REAL(timeStep)*tauMin*ti_ref, (self%cells(1)%obj%tallyColl(k)%tally, k=1,nCollPairs)
CLOSE(20)
END SUBROUTINE printColl0D

View file

@ -30,14 +30,13 @@ MODULE moduleMeshInputGmsh2
USE moduleMesh2DCart
USE moduleMesh1DRad
USE moduleMesh1DCart
USE moduleBoundary
IMPLICIT NONE
CLASS(meshGeneric), INTENT(inout):: self
CHARACTER(:), ALLOCATABLE, INTENT(in):: filename
REAL(8):: r(1:3) !3 generic coordinates
INTEGER, ALLOCATABLE:: p(:) !Array for nodes
INTEGER:: e = 0, n = 0, eTemp = 0, elemType = 0, bt = 0
INTEGER:: e = 0, n = 0, eTemp = 0, elemType = 0
INTEGER:: totalNumElem
INTEGER:: numEdges
INTEGER:: boundaryType
@ -161,9 +160,6 @@ MODULE moduleMeshInputGmsh2
READ(10, *) n, elemType, eTemp, boundaryType
BACKSPACE(10)
!Associate boundary condition procedure.
bt = getBoundaryID(boundaryType)
SELECT CASE(elemType)
CASE(2)
!Triangular surface
@ -178,8 +174,6 @@ MODULE moduleMeshInputGmsh2
CASE (2)
ALLOCATE(p(1:2))
READ(10,*) n, elemType, eTemp, boundaryType, eTemp, p(1:2)
!Associate boundary condition procedure.
bt = getBoundaryId(boundaryType)
SELECT CASE(self%geometry)
CASE("Cyl")
@ -193,8 +187,6 @@ MODULE moduleMeshInputGmsh2
CASE(1)
ALLOCATE(p(1:1))
READ(10, *) n, elemType, eTemp, boundaryType, eTemp, p(1)
!Associate boundary condition
bt = getBoundaryId(boundaryType)
SELECT CASE(self%geometry)
CASE("Rad")
ALLOCATE(meshEdge1DRad:: self%edges(e)%obj)
@ -206,8 +198,13 @@ MODULE moduleMeshInputGmsh2
END SELECT
CALL self%edges(e)%obj%init(n, p, bt, boundaryType)
DEALLOCATE(p)
! If an edge was found, init it
if (allocated(p)) then
call self%edges(e)%obj%init(n, p, boundaryType)
deallocate(p)
end if
END DO

View file

@ -16,6 +16,7 @@ MODULE moduleMeshOutputGmsh2
!Node data subroutines
!Header
SUBROUTINE writeGmsh2HeaderNodeData(fileID, title, iteration, time, dimensions, nNodes)
use moduleOutput
IMPLICIT NONE
INTEGER, INTENT(in):: fileID
@ -25,14 +26,14 @@ MODULE moduleMeshOutputGmsh2
WRITE(fileID, "(A)") '$NodeData'
WRITE(fileID, "(I10)") 1
WRITE(fileID, "("//fmtInt//")") 1
WRITE(fileID, "(A1, A, A1)") '"' , title , '"'
WRITE(fileID, "(I10)") 1
WRITE(fileID, "(ES20.6E3)") time
WRITE(fileID, "(I10)") 3
WRITE(fileID, "(I10)") iteration
WRITE(fileID, "(I10)") dimensions
WRITE(fileID, "(I10)") nNodes
WRITE(fileID, "("//fmtInt//")") 1
WRITE(fileID, "("//fmtReal//")") time
WRITE(fileID, "("//fmtInt//")") 3
WRITE(fileID, "("//fmtInt//")") iteration
WRITE(fileID, "("//fmtInt//")") dimensions
WRITE(fileID, "("//fmtInt//")") nNodes
END SUBROUTINE writeGmsh2HeaderNodeData
@ -49,6 +50,7 @@ MODULE moduleMeshOutputGmsh2
!Element data subroutines
!Header
SUBROUTINE writeGmsh2HeaderElementData(fileID, title, iteration, time, dimensions, nVols)
use moduleOutput
IMPLICIT NONE
INTEGER, INTENT(in):: fileID
@ -58,14 +60,14 @@ MODULE moduleMeshOutputGmsh2
WRITE(fileID, "(A)") '$ElementData'
WRITE(fileID, "(I10)") 1
WRITE(fileID, "("//fmtInt//")") 1
WRITE(fileID, "(A1, A, A1)") '"' , title , '"'
WRITE(fileID, "(I10)") 1
WRITE(fileID, "(ES20.6E3)") time
WRITE(fileID, "(I10)") 3
WRITE(fileID, "(I10)") iteration
WRITE(fileID, "(I10)") dimensions
WRITE(fileID, "(I10)") nVols
WRITE(fileID, "("//fmtInt//")") 1
WRITE(fileID, "("//fmtReal//")") time
WRITE(fileID, "("//fmtInt//")") 3
WRITE(fileID, "("//fmtInt//")") iteration
WRITE(fileID, "("//fmtInt//")") dimensions
WRITE(fileID, "("//fmtInt//")") nVols
END SUBROUTINE writeGmsh2HeaderElementData
@ -85,7 +87,6 @@ MODULE moduleMeshOutputGmsh2
USE moduleRefParam
USE moduleSpecies
USE moduleOutput
USE moduleMeshInoutCommon
USE moduleCaseParam, ONLY: timeStep
IMPLICIT NONE
@ -99,33 +100,33 @@ MODULE moduleMeshOutputGmsh2
DO i = 1, nSpecies
fileName = formatFileName(prefix, species(i)%obj%name, 'msh', timeStep)
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN (60, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
OPEN (60, file = generateFilePath(fileName))
CALL writeGmsh2HeaderMesh(60)
CALL writeGmsh2HeaderNodeData(60, species(i)%obj%name // ' density (m^-3)', timeStep, time, 1, self%numNodes)
DO n=1, self%numNodes
CALL calculateOutput(self%nodes(n)%obj%output(i), output(n), self%nodes(n)%obj%v, species(i)%obj)
WRITE(60, "(I6,ES20.6E3)") n, output(n)%density
WRITE(60, "(I6,"//fmtReal//")") n, output(n)%density
END DO
CALL writeGmsh2FooterNodeData(60)
CALL writeGmsh2HeaderNodeData(60, species(i)%obj%name // ' velocity (m s^-1)', timeStep, time, 3, self%numNodes)
DO n=1, self%numNodes
WRITE(60, "(I6,3(ES20.6E3))") n, output(n)%velocity
WRITE(60, "(I6,3("//fmtReal//"))") n, output(n)%velocity
END DO
CALL writeGmsh2FooterNodeData(60)
CALL writeGmsh2HeaderNodeData(60, species(i)%obj%name // ' Pressure (Pa)', timeStep, time, 1, self%numNodes)
DO n=1, self%numNodes
WRITE(60, "(I6,3(ES20.6E3))") n, output(n)%pressure
WRITE(60, "(I6,3("//fmtReal//"))") n, output(n)%pressure
END DO
CALL writeGmsh2FooterNodeData(60)
CALL writeGmsh2HeaderNodeData(60, species(i)%obj%name // ' Temperature (K)', timeStep, time, 1, self%numNodes)
DO n=1, self%numNodes
WRITE(60, "(I6,3(ES20.6E3))") n, output(n)%temperature
WRITE(60, "(I6,3("//fmtReal//"))") n, output(n)%temperature
END DO
CALL writeGmsh2FooterNodeData(60)
CLOSE (60)
@ -141,7 +142,6 @@ MODULE moduleMeshOutputGmsh2
USE moduleCaseParam
USE moduleCollisions
USE moduleOutput
USE moduleMeshInoutCommon
IMPLICIT NONE
CLASS(meshGeneric), INTENT(in):: self
@ -169,8 +169,8 @@ MODULE moduleMeshOutputGmsh2
time = DBLE(timeStep)*tauMin*ti_ref
fileName = formatFileName(prefix, 'Collisions', 'msh', timeStep)
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN (60, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
OPEN (60, file = generateFilePath(fileName))
CALL writeGmsh2HeaderMesh(60)
@ -180,7 +180,7 @@ MODULE moduleMeshOutputGmsh2
title = '"Pair ' // interactionMatrix(k)%sp_i%name // '-' // interactionMatrix(k)%sp_j%name // ' collision ' // cString
CALL writeGmsh2HeaderElementData(60, title, timeStep, time, 1, self%numCells)
DO n=1, self%numCells
WRITE(60, "(I6,I10)") n + numEdges, self%cells(n)%obj%tallyColl(k)%tally(c)
WRITE(60, "(I6,"//fmtInt//")") n + numEdges, self%cells(n)%obj%tallyColl(k)%tally(c)
END DO
CALL writeGmsh2FooterElementData(60)
@ -200,7 +200,6 @@ MODULE moduleMeshOutputGmsh2
USE moduleRefParam
USE moduleCaseParam
USE moduleOutput
USE moduleMeshInoutCommon
IMPLICIT NONE
CLASS(meshParticles), INTENT(in):: self
@ -215,8 +214,8 @@ MODULE moduleMeshOutputGmsh2
time = DBLE(timeStep)*tauMin*ti_ref
fileName = formatFileName(prefix, 'EMField', 'msh', timeStep)
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN (20, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
OPEN (20, file = generateFilePath(fileName))
CALL writeGmsh2HeaderMesh(20)
@ -251,7 +250,6 @@ MODULE moduleMeshOutputGmsh2
USE moduleSpecies
USE moduleOutput
USE moduleAverage
USE moduleMeshInoutCommon
IMPLICIT NONE
CLASS(meshParticles), INTENT(in):: self
@ -262,12 +260,12 @@ MODULE moduleMeshOutputGmsh2
DO i = 1, nSpecies
fileName = formatFileName('Average_mean', species(i)%obj%name, 'msh')
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN (fileMean, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
OPEN (fileMean, file = generateFilePath(fileName))
fileName = formatFileName('Average_deviation', species(i)%obj%name, 'msh')
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN (filedeviation, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
OPEN (filedeviation, file = generateFilePath(fileName))
CALL writeGmsh2HeaderMesh(fileMean)
CALL writeGmsh2HeaderMesh(fileDeviation)
@ -276,9 +274,9 @@ MODULE moduleMeshOutputGmsh2
CALL writeGmsh2HeaderNodeData(fileDeviation, species(i)%obj%name // ' density, sd (m^-3)', 0, 0.D0, 1, self%numNodes)
DO n=1, self%numNodes
CALL calculateOutput(averageScheme(n)%mean%output(i), outputMean(n), self%nodes(n)%obj%v, species(i)%obj)
WRITE(fileMean, "(I6,ES20.6E3)") n, outputMean(n)%density
WRITE(fileMean, "(I6,"//fmtReal//")") n, outputMean(n)%density
CALL calculateOutput(averageScheme(n)%deviation%output(i), outputDeviation(n), self%nodes(n)%obj%v, species(i)%obj)
WRITE(fileDeviation, "(I6,ES20.6E3)") n, outputDeviation(n)%density
WRITE(fileDeviation, "(I6,"//fmtReal//")") n, outputDeviation(n)%density
END DO
CALL writeGmsh2FooterNodeData(fileMean)
CALL writeGmsh2FooterNodeData(fileDeviation)
@ -286,8 +284,8 @@ MODULE moduleMeshOutputGmsh2
CALL writeGmsh2HeaderNodeData(fileMean, species(i)%obj%name // ' velocity, mean (m s^-1)', 0, 0.D0, 3, self%numNodes)
CALL writeGmsh2HeaderNodeData(fileDeviation, species(i)%obj%name // ' velocity, sd (m s^-1)', 0, 0.D0, 3, self%numNodes)
DO n=1, self%numNodes
WRITE(fileMean, "(I6,3(ES20.6E3))") n, outputMean(n)%velocity
WRITE(fileDeviation, "(I6,3(ES20.6E3))") n, outputDeviation(n)%velocity
WRITE(fileMean, "(I6,3("//fmtReal//"))") n, outputMean(n)%velocity
WRITE(fileDeviation, "(I6,3("//fmtReal//"))") n, outputDeviation(n)%velocity
END DO
CALL writeGmsh2FooterNodeData(fileMean)
CALL writeGmsh2FooterNodeData(fileDeviation)
@ -295,8 +293,8 @@ MODULE moduleMeshOutputGmsh2
CALL writeGmsh2HeaderNodeData(fileMean, species(i)%obj%name // ' Pressure, mean (Pa)', 0, 0.D0, 1, self%numNodes)
CALL writeGmsh2HeaderNodeData(fileDeviation, species(i)%obj%name // ' Pressure, sd (Pa)', 0, 0.D0, 1, self%numNodes)
DO n=1, self%numNodes
WRITE(fileMean, "(I6,3(ES20.6E3))") n, outputMean(n)%pressure
WRITE(fileDeviation, "(I6,3(ES20.6E3))") n, outputDeviation(n)%pressure
WRITE(fileMean, "(I6,3("//fmtReal//"))") n, outputMean(n)%pressure
WRITE(fileDeviation, "(I6,3("//fmtReal//"))") n, outputDeviation(n)%pressure
END DO
CALL writeGmsh2FooterNodeData(fileMean)
CALL writeGmsh2FooterNodeData(fileDeviation)
@ -304,8 +302,8 @@ MODULE moduleMeshOutputGmsh2
CALL writeGmsh2HeaderNodeData(fileMean, species(i)%obj%name // ' Temperature, mean (K)', 0, 0.D0, 1, self%numNodes)
CALL writeGmsh2HeaderNodeData(fileDeviation, species(i)%obj%name // ' Temperature, sd (K)', 0, 0.D0, 1, self%numNodes)
DO n=1, self%numNodes
WRITE(fileMean, "(I6,3(ES20.6E3))") n, outputMean(n)%temperature
WRITE(fileDeviation, "(I6,3(ES20.6E3))") n, outputDeviation(n)%temperature
WRITE(fileMean, "(I6,3("//fmtReal//"))") n, outputMean(n)%temperature
WRITE(fileDeviation, "(I6,3("//fmtReal//"))") n, outputDeviation(n)%temperature
END DO
CALL writeGmsh2FooterNodeData(fileMean)
CALL writeGmsh2FooterNodeData(fileDeviation)

View file

@ -1,6 +1,6 @@
all: vtu.o gmsh2.o 0D.o text.o
vtu.o: moduleMeshInoutCommon.o
vtu.o:
$(MAKE) -C vtu all
gmsh2.o:

View file

@ -1,27 +0,0 @@
MODULE moduleMeshInoutCommon
CHARACTER(LEN=4):: prefix = 'Step'
CONTAINS
PURE FUNCTION formatFileName(prefix, suffix, extension, timeStep) RESULT(fileName)
USE moduleOutput
IMPLICIT NONE
CHARACTER(*), INTENT(in):: prefix, suffix, extension
INTEGER, INTENT(in), OPTIONAL:: timeStep
CHARACTER (LEN=iterationDigits):: tString
CHARACTER(:), ALLOCATABLE:: fileName
IF (PRESENT(timeStep)) THEN
WRITE(tString, iterationFormat) timeStep
fileName = prefix // '_' // tString // '_' // suffix // '.' // extension
ELSE
fileName = prefix // '_' // suffix // '.' // extension
END IF
END FUNCTION formatFileName
END MODULE moduleMeshInoutCommon

View file

@ -47,7 +47,6 @@ module moduleMeshInputText
integer:: physicalID
integer:: n, c
integer, allocatable:: p(:)
integer:: bt
fileID = 10
@ -141,8 +140,9 @@ module moduleMeshInputText
allocate(p(1))
p(1) = n
bt = getBoundaryId(physicalID)
call self%edges(physicalID)%obj%init(physicalID, p, physicalID, physicalID)
call self%edges(physicalID)%obj%init(physicalID, p, physicalID)
deallocate(p)
end if
@ -197,6 +197,7 @@ module moduleMeshInputText
fileID = 10
open(fileID, file=trim(filename))
nNodes = 0
do
read(fileID, *, iostat=reason) line

View file

@ -27,7 +27,6 @@ module moduleMeshOutputText
subroutine writeCollOutput(self, fileID)
use moduleMesh
use moduleCollisions
use moduleRefParam, only: L_ref
implicit none
class(meshGeneric), intent(in):: self
@ -122,7 +121,6 @@ module moduleMeshOutputText
subroutine printOutputText(self)
use moduleMesh
use moduleSpecies
use moduleMeshInoutCommon
use moduleCaseParam, ONLY: timeStep
implicit none
@ -135,13 +133,13 @@ module moduleMeshOutputText
do s = 1, nSpecies
fileName = formatFileName(prefix, species(s)%obj%name, 'csv', timeStep)
write(*, "(6X,A15,A)") "Creating file: ", fileName
open (fileID, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
open (fileID, file = generateFilePath(fileName))
write(fileID, '(5(A,","),A)') 'Position (m)', &
'Density (m^-3)', &
'Velocity (m s^-1):0', 'Velocity (m s^-1):1', 'Velocity (m s^-1):2', &
'Temperature (K)'
write(fileID, '(5(A,","),A)') '"Position (m)"', &
'"Density (m^-3)"', &
'"Velocity (m s^-1):0"', 'Velocity (m s^-1):1"', 'Velocity (m s^-1):2"', &
'"Temperature (K)"'
call writeSpeciesOutput(self, fileID, s)
@ -154,7 +152,6 @@ module moduleMeshOutputText
subroutine printCollText(self)
use moduleMesh
use moduleOutput
use moduleMeshInoutCommon
use moduleCaseParam, only: timeStep
implicit none
@ -168,8 +165,8 @@ module moduleMeshOutputText
if (collOutput) then
fileName = formatFileName(prefix, 'Collisions', 'csv', timeStep)
write(*, "(6X,A15,A)") "Creating file: ", fileName
open (fileID, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
open (fileID, file = generateFilePath(fileName))
write(fileID, '(A)', advance='no') "Cell"
do k = 1, nCollPairs
@ -192,7 +189,6 @@ module moduleMeshOutputText
subroutine printEMText(self)
use moduleMesh
use moduleMeshInoutCommon
use moduleCaseParam, only: timeStep
implicit none
@ -204,13 +200,13 @@ module moduleMeshOutputText
if (emOutput) then
fileName = formatFileName(prefix, 'EMField', 'csv', timeStep)
write(*, "(6X,A15,A)") "Creating file: ", fileName
open (fileID, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
open (fileID, file = generateFilePath(fileName))
write(fileID, '(8(A,","),A)') 'Position (m)', &
'Potential (V)', &
'Electric Field (V m^-1):0', 'Electric Field (V m^-1):1', 'Electric Field (V m^-1):2', &
'Magnetic Field (T):0', 'Magnetic Field (T):1', 'Magnetic Field (T):2'
write(fileID, '(8(A,","),A)') '"Position (m)"', &
'"Potential (V)"', &
'"Electric Field (V m^-1):0"', 'Electric Field (V m^-1):1"', 'Electric Field (V m^-1):2"', &
'"Magnetic Field (T):0"', 'Magnetic Field (T):1"', 'Magnetic Field (T):2"'
call writeEMOutput(self, fileID)
@ -223,7 +219,6 @@ module moduleMeshOutputText
subroutine printAverageText(self)
use moduleMesh
use moduleSpecies
use moduleMeshInoutCommon
implicit none
class(meshParticles), intent(in):: self
@ -235,23 +230,23 @@ module moduleMeshOutputText
fileIDDeviation = 67
do s = 1, nSpecies
fileNameMean = formatFileName('Average_mean', species(s)%obj%name, 'csv', timeStep)
write(*, "(6X,A15,A)") "Creating file: ", fileNameMean
open (fileIDMean, file = path // folder // '/' // fileNameMean)
fileNameMean = formatFileName('Average_mean', species(s)%obj%name, 'csv')
call informFileCreation(fileNameMean)
open (fileIDMean, file = generateFilePath(fileNameMean))
write(fileIDMean, '(5(A,","),A)') 'Position (m)', &
'Density, mean (m^-3)', &
'Velocity, mean (m s^-1):0', 'Velocity (m s^-1):1', 'Velocity (m s^-1):2', &
'Temperature, mean (K)'
write(fileIDMean, '(5(A,","),A)') '"Position (m)"', &
'"Density, mean (m^-3)"', &
'"Velocity, mean (m s^-1):0"', '"Velocity (m s^-1):1"', '"Velocity (m s^-1):2"', &
'"Temperature, mean (K)"'
fileNameDeviation = formatFileName('Average_deviation', species(s)%obj%name, 'csv', timeStep)
write(*, "(6X,A15,A)") "Creating file: ", fileNameDeviation
open (fileIDDeviation, file = path // folder // '/' // fileNameDeviation)
fileNameDeviation = formatFileName('Average_deviation', species(s)%obj%name, 'csv')
call informFileCreation(fileNameDeviation)
open (fileIDDeviation, file = generateFilePath(fileNameDeviation))
write(fileIDDeviation, '(5(A,","),A)') 'Position (m)', &
'Density, deviation (m^-3)', &
'Velocity, deviation (m s^-1):0', 'Velocity (m s^-1):1', 'Velocity (m s^-1):2', &
'Temperature, deviation (K)'
write(fileIDDeviation, '(5(A,","),A)') '"Position (m)"', &
'"Density, deviation (m^-3)"', &
'"Velocity, deviation (m s^-1):0"', 'Velocity (m s^-1):1"', 'Velocity (m s^-1):2"', &
'"Temperature, deviation (K)"'
call writeAverage(self, fileIDMean, fileIDDeviation, s)

View file

@ -161,7 +161,6 @@ MODULE moduleMeshInputVTU
USE moduleMesh2DCart
USE moduleMesh1DRad
USE moduleMesh1DCart
USE moduleBoundary
IMPLICIT NONE
CLASS(meshGeneric), INTENT(inout):: self
@ -174,7 +173,6 @@ MODULE moduleMeshInputVTU
REAL(8), ALLOCATABLE, DIMENSION(:):: coordinates
INTEGER:: n, e, c
INTEGER, ALLOCATABLE:: p(:)
INTEGER:: bt
fileID = 10
@ -321,16 +319,9 @@ MODULE moduleMeshInputVTU
p(2) = connectivity(offsets(n) - 1)
p(3) = connectivity(offsets(n))
!Associate boundary condition procedure.
bt = getBoundaryId(entitiesID(n))
!Allocate edge
ALLOCATE(meshEdge3DCartTria:: self%edges(e)%obj)
!Init edge
CALL self%edges(e)%obj%init(n, p, bt, entitiesID(n))
DEALLOCATE(p)
END IF
CASE(2)
@ -340,9 +331,6 @@ MODULE moduleMeshInputVTU
p(1) = connectivity(offsets(n) - 1)
p(2) = connectivity(offsets(n))
!Associate boundary condition procedure.
bt = getBoundaryId(entitiesID(n))
!Allocate edge
SELECT CASE(self%geometry)
CASE("Cyl")
@ -353,10 +341,6 @@ MODULE moduleMeshInputVTU
END SELECT
!Init edge
CALL self%edges(e)%obj%init(n, p, bt, entitiesID(n))
DEALLOCATE(p)
END IF
CASE(1)
@ -365,9 +349,6 @@ MODULE moduleMeshInputVTU
ALLOCATE(p(1:1))
p(1) = connectivity(offsets(n))
!Associate boundary condition procedure.
bt = getBoundaryId(entitiesID(n))
!Allocate edge
SELECT CASE(self%geometry)
CASE("Rad")
@ -378,14 +359,18 @@ MODULE moduleMeshInputVTU
END SELECT
!Init edge
CALL self%edges(e)%obj%init(n, p, bt, entitiesID(n))
DEALLOCATE(p)
END IF
END SELECT
! If an edge was found, init it
if (allocated(p)) then
call self%edges(e)%obj%init(n, p, entitiesID(n))
deallocate(p)
end if
END DO
END SELECT

View file

@ -12,7 +12,7 @@ MODULE moduleMeshOutputVTU
WRITE(fileID,"(A)") '<?xml version="1.0"?>'
WRITE(fileID,"(2X, A)") '<VTKFile type="UnstructuredGrid">'
WRITE(fileID,"(4X, A)") '<UnstructuredGrid>'
WRITE(fileID,"(6X, A, I10, A, I10, A)") '<Piece NumberOfPoints="', nNodes, '" NumberOfCells="', nCells, '">'
WRITE(fileID,"(6X, A, "//fmtInt//", A, "//fmtInt//", A)") '<Piece NumberOfPoints="', nNodes, '" NumberOfCells="', nCells, '">'
END SUBROUTINE writeHeader
@ -87,14 +87,14 @@ MODULE moduleMeshOutputVTU
!Write nodes coordinates
WRITE(fileID, "(8X, A)") '<Points>'
WRITE(fileID, "(10X,A)") '<DataArray type="Float32" NumberOfComponents="3" format="ascii">'
WRITE(fileID, "(6(ES20.6E3))") (self%nodes(e)%obj%getCoordinates()*L_ref, e = 1, self%numNodes)
WRITE(fileID, "(6("//fmtReal//"))") (self%nodes(e)%obj%getCoordinates()*L_ref, e = 1, self%numNodes)
WRITE(fileID, "(10X, A)") '</DataArray>'
WRITE(fileID, "(8X, A)") '</Points>'
WRITE(fileID, "(8X, A)") '<Cells>'
!Write nodes connectivity of each cell
WRITE(fileID, "(10X,A)") '<DataArray type="Int32" Name="connectivity" format="ascii">'
WRITE(fileID, "(6(I10))") (self%cells(e)%obj%getNodes(self%cells(e)%obj%nNodes) - 1, e = 1, self%numCells) !Array starts on 0
WRITE(fileID, "(6("//fmtInt//"))") (self%cells(e)%obj%getNodes(self%cells(e)%obj%nNodes) - 1, e = 1, self%numCells) !Array starts on 0
WRITE(fileID, "(10X, A)") '</DataArray>'
!Write offset of each cell
offset(1) = self%cells(1)%obj%nNodes
@ -103,7 +103,7 @@ MODULE moduleMeshOutputVTU
offset(e) = offset(e - 1) + self%cells(e)%obj%nNodes
END DO
WRITE(fileID, "(6(I10))") offset
WRITE(fileID, "(6("//fmtInt//"))") offset
WRITE(fileID, "(10X, A)") '</DataArray>'
!Write type of each cell
WRITE(fileID, "(10X,A)") '<DataArray type="Int32" Name="types" format="ascii">'
@ -111,7 +111,7 @@ MODULE moduleMeshOutputVTU
types(e) = getCellType(self%cells(e)%obj)
END DO
WRITE(fileID, "(6(I10))") types
WRITE(fileID, "(6("//fmtInt//"))") types
WRITE(fileID, "(10X, A)") '</DataArray>'
WRITE(fileID, "(8X, A)") '</Cells>'
@ -137,19 +137,19 @@ MODULE moduleMeshOutputVTU
WRITE(fileID,"(8X,A)") '<PointData>'
!Write density
WRITE(fileID,"(10X,A)") '<DataArray type="Float64" Name="Density (m^-3)" NumberOfComponents="1">'
WRITE(fileID,"(6(ES20.6E3))") (output(n)%density, n = 1, self%numNodes)
WRITE(fileID,"(6("//fmtReal//"))") (output(n)%density, n = 1, self%numNodes)
WRITE(fileID,"(10X,A)") '</DataArray>'
!Write velocity
WRITE(fileID,"(10X,A)") '<DataArray type="Float64" Name="Velocity (m s^-1)" NumberOfComponents="3">'
WRITE(fileID,"(6(ES20.6E3))") (output(n)%velocity, n = 1, self%numNodes)
WRITE(fileID,"(6("//fmtReal//"))") (output(n)%velocity, n = 1, self%numNodes)
WRITE(fileID,"(10X,A)") '</DataArray>'
!Write pressure
WRITE(fileID,"(10X,A)") '<DataArray type="Float64" Name="Pressure (Pa)" NumberOfComponents="1">'
WRITE(fileID,"(6(ES20.6E3))") (output(n)%pressure, n = 1, self%numNodes)
WRITE(fileID,"(6("//fmtReal//"))") (output(n)%pressure, n = 1, self%numNodes)
WRITE(fileID,"(10X,A)") '</DataArray>'
!Write temperature
WRITE(fileID,"(10X,A)") '<DataArray type="Float64" Name="Temperature (K)" NumberOfComponents="1">'
WRITE(fileID,"(6(ES20.6E3))") (output(n)%temperature, n = 1, self%numNodes)
WRITE(fileID,"(6("//fmtReal//"))") (output(n)%temperature, n = 1, self%numNodes)
WRITE(fileID,"(10X,A)") '</DataArray>'
!End node data
WRITE(fileID,"(8X,A)") '</PointData>'
@ -173,7 +173,7 @@ MODULE moduleMeshOutputVTU
WRITE(cString, "(I2)") c
title = 'Pair ' // interactionMatrix(k)%sp_i%name // '-' // interactionMatrix(k)%sp_j%name // ' collision ' // cString
WRITE(fileID,"(10X,A, A, A)") '<DataArray type="Float64" Name="',title, '" NumberOfComponents="1">'
WRITE(fileID, "(6(I10))") (self%cells(n)%obj%tallyColl(k)%tally(c), n = 1, self%numCells)
WRITE(fileID, "(6("//fmtInt//"))") (self%cells(n)%obj%tallyColl(k)%tally(c), n = 1, self%numCells)
WRITE(fileID, "(10X, A)") '</DataArray>'
END DO
@ -196,11 +196,11 @@ MODULE moduleMeshOutputVTU
WRITE(fileID,"(8X,A)") '<PointData>'
!Electric potential
WRITE(fileID,"(10X,A)") '<DataArray type="Float64" Name="Potential (V)" NumberOfComponents="1">'
WRITE(fileID,"(6(ES20.6E3))") (self%nodes(n)%obj%emData%phi*Volt_ref, n = 1, self%numNodes)
WRITE(fileID,"(6("//fmtReal//"))") (self%nodes(n)%obj%emData%phi*Volt_ref, n = 1, self%numNodes)
WRITE(fileID,"(10X,A)") '</DataArray>'
!Magnetic Field
WRITE(fileID,"(10X,A)") '<DataArray type="Float64" Name="Magnetic Field (T)" NumberOfComponents="3">'
WRITE(fileID,"(6(ES20.6E3))") (self%nodes(n)%obj%emData%B*B_ref, n = 1, self%numNodes)
WRITE(fileID,"(6("//fmtReal//"))") (self%nodes(n)%obj%emData%B*B_ref, n = 1, self%numNodes)
WRITE(fileID,"(10X,A)") '</DataArray>'
WRITE(fileID,"(8X,A)") '</PointData>'
@ -209,7 +209,7 @@ MODULE moduleMeshOutputVTU
WRITE(fileID,"(8X,A)") '<CellData>'
!Electric field
WRITE(fileID,"(10X,A, A, A)") '<DataArray type="Float64" Name="Electric Field (V m^-1)" NumberOfComponents="3">'
WRITE(fileID,"(6(ES20.6E3))") (self%cells(n)%obj%gatherElectricField(Xi)*EF_ref, n = 1, self%numCells)
WRITE(fileID,"(6("//fmtReal//"))") (self%cells(n)%obj%gatherElectricField(Xi)*EF_ref, n = 1, self%numCells)
WRITE(fileID,"(10X,A)") '</DataArray>'
WRITE(fileID,"(8X,A)") '</CellData>'
@ -221,30 +221,31 @@ MODULE moduleMeshOutputVTU
USE moduleRefParam
IMPLICIT NONE
INTEGER:: fileID
CHARACTER(*):: fileNameStep, fileNameCollection
INTEGER, intent(in):: fileID
CHARACTER(*), intent(in):: fileNameStep, fileNameCollection
IF (timeStep == tInitial) THEN
!Create collection file
WRITE(*, "(6X,A15,A)") "Creating file: ", fileNameCollection
OPEN (fileID + 1, file = path // folder // '/' // fileNameCollection)
call informFileCreation(fileNameCollection)
OPEN (fileID + 1, file = generateFilePath(fileNameCollection))
WRITE (fileID + 1, "(A)") '<VTKFile type="Collection">'
WRITE (fileID + 1, "(2X, A)") '<Collection>'
CLOSE(fileID + 1)
else
OPEN (fileID + 1, file = generateFilePath(fileNameCollection), ACCESS='APPEND')
! Append removing the last two lines that close the <tags>
backspace(fileID + 1)
backspace(fileID + 1)
END IF
!Write iteration file in collection
OPEN (fileID + 1, file = path // folder // '/' // fileNameCollection, ACCESS='APPEND')
WRITE(fileID + 1, "(4X, A, ES20.6E3, A, A, A)") &
WRITE(fileID + 1, "(4X, A, "//fmtReal//", A, A, A)") &
'<DataSet timestep="', DBLE(timeStep)*tauMin*ti_ref,'" file="', fileNameStep,'"/>'
!Close collection file
IF (timeStep == tFinal) THEN
WRITE (fileID + 1, "(2X, A)") '</Collection>'
WRITE (fileID + 1, "(A)") '</VTKFile>'
WRITE (fileID + 1, "(2X, A)") '</Collection>'
WRITE (fileID + 1, "(A)") '</VTKFile>'
END IF
CLOSE(fileID + 1)
END SUBROUTINE writeCollection
@ -276,29 +277,29 @@ MODULE moduleMeshOutputVTU
!Write density
WRITE(fileIDMean, "(10X,A)") '<DataArray type="Float64" Name="Density, mean (m^-3)" NumberOfComponents="1">'
WRITE(fileIDDeviation,"(10X,A)") '<DataArray type="Float64" Name="Density, deviation (m^-3)" NumberOfComponents="1">'
WRITE(fileIDMean, "(6(ES20.6E3))") (outputMean(n)%density, n = 1, self%numNodes)
WRITE(fileIDDeviation,"(6(ES20.6E3))") (outputDeviation(n)%density, n = 1, self%numNodes)
WRITE(fileIDMean, "(6("//fmtReal//"))") (outputMean(n)%density, n = 1, self%numNodes)
WRITE(fileIDDeviation,"(6("//fmtReal//"))") (outputDeviation(n)%density, n = 1, self%numNodes)
WRITE(fileIDMean,"(10X,A)") '</DataArray>'
WRITE(fileIDDeviation,"(10X,A)") '</DataArray>'
!Write velocity
WRITE(fileIDMean, "(10X,A)") '<DataArray type="Float64" Name="Velocity, mean (m s^-1)" NumberOfComponents="3">'
WRITE(fileIDDeviation,"(10X,A)") '<DataArray type="Float64" Name="Velocity, deviation (m s^-1)" NumberOfComponents="3">'
WRITE(fileIDMean, "(6(ES20.6E3))") (outputMean(n)%velocity, n = 1, self%numNodes)
WRITE(fileIDDeviation,"(6(ES20.6E3))") (outputDeviation(n)%velocity, n = 1, self%numNodes)
WRITE(fileIDMean, "(6("//fmtReal//"))") (outputMean(n)%velocity, n = 1, self%numNodes)
WRITE(fileIDDeviation,"(6("//fmtReal//"))") (outputDeviation(n)%velocity, n = 1, self%numNodes)
WRITE(fileIDMean, "(10X,A)") '</DataArray>'
WRITE(fileIDDeviation,"(10X,A)") '</DataArray>'
!Write pressure
WRITE(fileIDMean, "(10X,A)") '<DataArray type="Float64" Name="Pressure, mean (Pa)" NumberOfComponents="1">'
WRITE(fileIDDeviation,"(10X,A)") '<DataArray type="Float64" Name="Pressure, deviation (Pa)" NumberOfComponents="1">'
WRITE(fileIDMean, "(6(ES20.6E3))") (outputMean(n)%pressure, n = 1, self%numNodes)
WRITE(fileIDDeviation,"(6(ES20.6E3))") (outputDeviation(n)%pressure, n = 1, self%numNodes)
WRITE(fileIDMean, "(6("//fmtReal//"))") (outputMean(n)%pressure, n = 1, self%numNodes)
WRITE(fileIDDeviation,"(6("//fmtReal//"))") (outputDeviation(n)%pressure, n = 1, self%numNodes)
WRITE(fileIDMean, "(10X,A)") '</DataArray>'
WRITE(fileIDDeviation,"(10X,A)") '</DataArray>'
!Write temperature
WRITE(fileIDMean, "(10X,A)") '<DataArray type="Float64" Name="Temperature, mean (K)" NumberOfComponents="1">'
WRITE(fileIDDeviation,"(10X,A)") '<DataArray type="Float64" Name="Temperature, deviation (K)" NumberOfComponents="1">'
WRITE(fileIDMean, "(6(ES20.6E3))") (outputMean(n)%temperature, n = 1, self%numNodes)
WRITE(fileIDDeviation,"(6(ES20.6E3))") (outputDeviation(n)%temperature, n = 1, self%numNodes)
WRITE(fileIDMean, "(6("//fmtReal//"))") (outputMean(n)%temperature, n = 1, self%numNodes)
WRITE(fileIDDeviation,"(6("//fmtReal//"))") (outputDeviation(n)%temperature, n = 1, self%numNodes)
WRITE(fileIDMean, "(10X,A)") '</DataArray>'
WRITE(fileIDDeviation,"(10X,A)") '</DataArray>'
!End node data
@ -310,7 +311,6 @@ MODULE moduleMeshOutputVTU
SUBROUTINE printOutputVTU(self)
USE moduleMesh
USE moduleSpecies
USE moduleMeshInoutCommon
USE moduleCaseParam, ONLY: timeStep
IMPLICIT NONE
@ -322,8 +322,8 @@ MODULE moduleMeshOutputVTU
DO i = 1, nSpecies
fileName = formatFileName(prefix, species(i)%obj%name, 'vtu', timeStep)
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN (fileID, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
OPEN (fileID, file = generateFilePath(fileName))
CALL writeHeader(self%numNodes, self%numCells, fileID)
@ -346,7 +346,6 @@ MODULE moduleMeshOutputVTU
SUBROUTINE printCollVTU(self)
USE moduleMesh
USE moduleOutput
USE moduleMeshInoutCommon
USE moduleCaseParam, ONLY: timeStep
IMPLICIT NONE
@ -358,8 +357,8 @@ MODULE moduleMeshOutputVTU
IF (collOutput) THEN
fileName = formatFileName(prefix, 'Collisions', 'vtu', timeStep)
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN (fileID, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
OPEN (fileID, file = generateFilePath(fileName))
CALL writeHeader(self%numNodes, self%numCells, fileID)
@ -381,7 +380,6 @@ MODULE moduleMeshOutputVTU
SUBROUTINE printEMVTU(self)
USE moduleMesh
USE moduleMeshInoutCommon
USE moduleCaseParam, ONLY: timeStep
IMPLICIT NONE
@ -393,8 +391,8 @@ MODULE moduleMeshOutputVTU
IF (emOutput) THEN
fileName = formatFileName(prefix, 'EMField', 'vtu', timeStep)
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN (fileID, file = path // folder // '/' // fileName)
call informFileCreation(fileName)
OPEN (fileID, file = generateFilePath(fileName))
CALL writeHeader(self%numNodes, self%numCells, fileID)
@ -416,8 +414,8 @@ MODULE moduleMeshOutputVTU
SUBROUTINE printAverageVTU(self)
USE moduleMesh
use moduleOutput
USE moduleSpecies
USE moduleMeshInoutCommon
IMPLICIT NONE
CLASS(meshParticles), INTENT(in):: self
@ -429,12 +427,12 @@ MODULE moduleMeshOutputVTU
DO i = 1, nSpecies
fileNameMean = formatFileName('Average_mean', species(i)%obj%name, 'vtu')
WRITE(*, "(6X,A15,A)") "Creating file: ", fileNameMean
OPEN (fileIDMean, file = path // folder // '/' // fileNameMean)
call informFileCreation(fileNameMean)
OPEN (fileIDMean, file = generateFilePath(fileNameMean))
fileNameDeviation = formatFileName('Average_deviation', species(i)%obj%name, 'vtu')
WRITE(*, "(6X,A15,A)") "Creating file: ", fileNameDeviation
OPEN (fileIDDeviation, file = path // folder // '/' // fileNameDeviation)
call informFileCreation(fileNameDeviation)
OPEN (fileIDDeviation, file = generateFilePath(fileNameDeviation))
CALL writeHeader(self%numNodes, self%numCells, fileIDMean)
CALL writeHeader(self%numNodes, self%numCells, fileIDDeviation)

View file

@ -1,4 +1,4 @@
all: moduleMesh.o moduleMeshBoundary.o inout.o 3DCart.o 2DCyl.o 2DCart.o 1DRad.o 1DCart.o 0D.o
all: moduleMesh.o inout.o 3DCart.o 2DCyl.o 2DCart.o 1DRad.o 1DCart.o 0D.o
3DCart.o: moduleMesh.o
$(MAKE) -C 3DCart all
@ -18,11 +18,12 @@ all: moduleMesh.o moduleMeshBoundary.o inout.o 3DCart.o 2DCyl.o 2DCart.o 1DRad.o
0D.o: moduleMesh.o
$(MAKE) -C 0D all
moduleMesh.o: moduleMesh.f90
$(FC) $(FCFLAGS) -c $(subst .o,.f90,$@) -o $(OBJDIR)/$@
moduleMeshBoundary.o: moduleMesh.o moduleMeshBoundary.f90
moduleMesh.o: moduleMeshCommon.o moduleMesh.f90
$(FC) $(FCFLAGS) -c $(subst .o,.f90,$@) -o $(OBJDIR)/$@
$(FC) $(FCFLAGS) -c moduleMesh@elements.f90 -o $(OBJDIR)/moduleMesh@elements.o
$(FC) $(FCFLAGS) -c moduleMesh@boundaryParticle.f90 -o $(OBJDIR)/moduleMesh@boundaryParticle.o
$(FC) $(FCFLAGS) -c moduleMesh@boundaryEM.f90 -o $(OBJDIR)/moduleMesh@boundaryEM.o
$(FC) $(FCFLAGS) -c moduleMesh@surfaces.f90 -o $(OBJDIR)/moduleMesh@surfaces.o
inout.o: 3DCart.o 2DCyl.o 2DCart.o 1DRad.o 1DCart.o 0D.o
$(MAKE) -C inout all

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,161 @@
submodule(moduleMesh) boundaryEM
CONTAINS
module function boundaryEMName_to_Index(boundaryName) result(bp)
use moduleErrors
implicit none
character(:), allocatable:: boundaryName
integer:: bp
integer:: b
bp = 0
do b = 1, nBoundariesEM
if (boundaryName == boundariesEM(b)%obj%name) then
bp = boundariesEM(b)%obj%n
end if
end do
if (bp == 0) then
call criticalError('Boundary ' // boundaryName // ' not found', 'boundaryEMName_to_Index')
end if
end function boundaryEMName_to_Index
! Initialize Dirichlet boundary condition
module SUBROUTINE initDirichlet(self, config, object)
use json_module
USE moduleRefParam, ONLY: Volt_ref
use moduleErrors
IMPLICIT NONE
CLASS(boundaryEMGeneric), ALLOCATABLE, INTENT(inout):: self
type(json_file), intent(inout):: config
character(:), allocatable, intent(in):: object
REAL(8):: potential
logical:: found
select type(self)
type is(boundaryEMDirichlet)
call config%get(object // '.potential', potential, found)
if (.not. found) then
call criticalError('Required parameter "potential" for Dirichlet boundary condition not found', 'readBoundaryEM')
end if
self%potential = potential / Volt_ref
end select
end subroutine initDirichlet
! Initialize Dirichlet boundary condition
module subroutine initDirichletTime(self, config, object)
use json_module
use moduleRefParam, ONLY: Volt_ref, ti_ref
use moduleErrors
implicit none
class(boundaryEMGeneric), allocatable, intent(inout):: self
type(json_file), intent(inout):: config
character(:), allocatable, intent(in):: object
real(8):: potential
character(:), allocatable:: temporalProfile
logical:: found
character(:), allocatable:: temporalProfilePath
select type(self)
type is(boundaryEMDirichletTime)
call config%get(object // '.potential', potential, found)
if (.not. found) then
call criticalError('Required parameter "potential" for Dirichlet Time boundary condition not found', &
'readBoundaryEM')
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', &
'readBoundaryEM')
end if
temporalProfilePath = path // temporalProfile
self%potential = potential / Volt_ref
call self%temporalProfile%init(temporalProfile)
call self%temporalProfile%convert(1.D0/ti_ref, 1.D0)
self%update => updateDirichletTime
end select
END SUBROUTINE initDirichletTime
!Apply Dirichlet boundary condition to the poisson equation
module SUBROUTINE applyDirichlet(self, vectorF)
USE moduleMesh
IMPLICIT NONE
CLASS(boundaryEMDirichlet), INTENT(in):: self
REAL(8), INTENT(inout):: vectorF(:)
integer:: n
DO n = 1, self%nNodes
self%nodes(n)%obj%emData%phi = self%potential
vectorF(self%nodes(n)%obj%n) = self%nodes(n)%obj%emData%phi
END DO
END SUBROUTINE applyDirichlet
!Apply Dirichlet boundary condition with time temporal profile
module SUBROUTINE applyDirichletTime(self, vectorF)
USE moduleMesh
USE moduleCaseParam, ONLY: timeStep, tauMin
IMPLICIT NONE
CLASS(boundaryEMDirichletTime), INTENT(in):: self
REAL(8), INTENT(inout):: vectorF(:)
integer:: n
DO n = 1, self%nNodes
self%nodes(n)%obj%emData%phi = self%potential * self%timeFactor
vectorF(self%nodes(n)%obj%n) = self%nodes(n)%obj%emData%phi
END DO
END SUBROUTINE applyDirichletTime
module subroutine updateDirichletTime(self)
implicit none
class(boundaryEMGeneric), intent(inout):: self
select type(self)
type is(boundaryEMDirichletTime)
self%timeFactor = self%temporalProfile%get(DBLE(timeStep)*tauMin)
end select
end subroutine updateDirichletTime
module subroutine boundariesEM_update()
implicit none
integer:: b
do b = 1, nBoundariesEM
if (associated(boundariesEM(b)%obj%update)) then
call boundariesEM(b)%obj%update
end if
end do
end subroutine boundariesEM_update
end submodule boundaryEM

View file

@ -0,0 +1,574 @@
!moduleMeshBoundary: Boundary functions for the mesh edges
submodule(moduleMesh) boundaryParticle
contains
! Returns the array index of the boundary based on the name
module function boundaryParticleName_to_Index(boundaryName) result(bp)
use moduleErrors
implicit none
character(:), allocatable:: boundaryName
integer:: bp
integer:: b
bp = 0
do b = 1, nBoundariesParticle
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', 'boundaryParticleName_to_Index')
end if
end function boundaryParticleName_to_Index
module SUBROUTINE reflection(self, edge, part)
USE moduleCaseParam
USE moduleSpecies
IMPLICIT NONE
class(boundaryReflection), intent(inout):: 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
module SUBROUTINE absorption(self, edge, part)
USE moduleCaseParam
USE moduleSpecies
IMPLICIT NONE
class(boundaryAbsorption), intent(inout):: 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
module SUBROUTINE transparent(self, edge, part)
USE moduleSpecies
IMPLICIT NONE
class(boundaryTransparent), intent(inout):: 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.
module SUBROUTINE symmetryAxis(self, edge, part)
USE moduleSpecies
IMPLICIT NONE
class(boundaryAxis), intent(inout):: self
CLASS(meshEdge), INTENT(inout):: edge
CLASS(particle), INTENT(inout):: part
CALL genericReflection(edge, part)
END SUBROUTINE symmetryAxis
! wallTemperature
! During the collision, the boundary transfers part of the energy to the incident particle
! Init
module SUBROUTINE initWallTemperature(boundary, T, c)
USE moduleRefParam
IMPLICIT NONE
CLASS(boundaryParticleGeneric), ALLOCATABLE, INTENT(inout):: 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
! Apply
module SUBROUTINE wallTemperature(self, edge, part)
USE moduleSpecies
USE moduleRandom
IMPLICIT NONE
class(boundaryWallTemperature), intent(inout):: 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
! An electron will pass through the surface and create a series of ion-electron pairs based on a neutral background
!init
module SUBROUTINE initIonization(boundary, mImpact, m0, n0, v0, T0, ion, effTime, crossSection, eThreshold, electronSecondary)
use moduleRefParam
use moduleSpecies
use moduleCaseParam
use moduleConstParam
use moduleErrors, only: criticalError
use omp_lib, only: omp_init_lock
implicit none
class(boundaryParticleGeneric), allocatable, intent(inout):: boundary
real(8), intent(in):: mImpact
real(8), intent(in):: m0, n0, v0(1:3), T0 !Neutral properties
integer, intent(in):: ion
real(8), intent(in):: effTime
character(:), allocatable, intent(in):: crossSection
real(8), intent(in):: eThreshold
integer, optional, intent(in):: electronSecondary
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)
boundary%update => ionization_resetTally
boundary%print => ionization_print
boundary%tally = 0
call omp_init_lock(boundary%tally_lock)
END SELECT
END SUBROUTINE initIonization
! Apply
module SUBROUTINE ionization(self, edge, part)
USE moduleList
USE moduleSpecies
USE moduleMesh
USE moduleRefParam
USE moduleRandom
USE moduleMath
use omp_lib, only: omp_set_lock, omp_unset_lock
IMPLICIT NONE
class(boundaryIonization), intent(inout):: 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
! Increase the tally
call omp_set_lock(self%tally_lock)
self%tally = self%tally + 1
call omp_unset_lock(self%tally_lock)
END IF
END DO
!Removes ionizing electron regardless the number of pair created
part%n_in = .FALSE.
END SUBROUTINE ionization
! Update
subroutine ionization_resetTally(self)
implicit none
class(boundaryParticleGeneric), intent(inout):: self
select type(self)
type is(boundaryIonization)
self%tally = 0
end select
end subroutine ionization_resetTally
! Print
subroutine ionization_print(self, fileID)
implicit none
class(boundaryParticleGeneric), intent(inout):: self
integer, intent(in):: fileID
write(fileID, '(A)') self%name
select type(self)
type is(boundaryIonization)
write(fileID, '(A)') 'Number of successful ionization events per iteration'
write(fileID, '('//fmtInt//')') self%tally
end select
end subroutine ionization_print
! quasiNeutrality
! Maintains n_incident = n_rest by modifying the reflection parameter of the edge.
! Init
module subroutine initQuasiNeutrality(boundary, s_incident)
implicit none
class(boundaryParticleGeneric), allocatable, intent(inout):: boundary
integer, intent(in):: s_incident
allocate(boundaryQuasiNeutrality:: boundary)
select type(boundary)
type is(boundaryQuasiNeutrality)
allocate(boundary%edges(0))
boundary%s_incident = s_incident
boundary%update => quasiNeutrality_update
boundary%print => quasiNeutrality_print
end select
end subroutine initQuasiNeutrality
! Apply
module subroutine quasiNeutrality(self, edge, part)
use moduleRandom
implicit none
class(boundaryQuasiNeutrality), intent(inout):: self
class(meshEdge), intent(inout):: edge
class(particle), intent(inout):: part
if (random() <= self%alpha(edge%n)) then
call genericReflection(edge, part)
else
call genericTransparent(edge, part)
end if
end subroutine quasiNeutrality
! Update
subroutine quasiNeutrality_update(self)
implicit none
class(boundaryParticleGeneric), intent(inout):: self
integer:: e, n, s
integer, allocatable:: nodes(:)
class(meshEdge), pointer:: edge
real(8), allocatable:: density_nodes(:)
class(meshNode), pointer:: node
real(8):: density_incident, density_rest
real(8):: alpha
select type(self)
type is(boundaryQuasiNeutrality)
do e = 1, size(self%edges)
edge => mesh%edges(e)%obj
density_incident = 0.d0
density_rest = 0.d0
nodes = edge%getNodes(edge%nNodes)
allocate(density_nodes(1:edge%nNodes))
do s = 1, nSpecies
do n = 1, edge%nNodes
node => mesh%nodes(n)%obj
density_nodes(n) = node%output(s)%den
end do
if (s == self%s_incident) then
density_incident = edge%gatherF(edge%centerXi(), edge%nNodes, density_nodes)
else
density_rest = density_rest + edge%gatherF(edge%centerXi(), edge%nNodes, density_nodes)
end if
end do
! Correction for this time step
if (density_rest > 1.0d-10) then
! If there is a rest population, correct
alpha = 1.d0 - density_incident/density_rest
else
! If not, alpha is assumed unchaged
alpha = 0.d0
end if
! Apply correction with a factor of 0.1 to avoid fast changes
self%alpha(edge%n) = self%alpha(edge%n) + 1.0d-2 * alpha
! Limit alpha between 0 and 1
self%alpha(edge%n) = min(self%alpha(edge%n), 1.d0)
self%alpha(edge%n) = max(self%alpha(edge%n), 0.d0)
deallocate(density_nodes)
end do
end select
end subroutine quasiNeutrality_update
! Print output
subroutine quasiNeutrality_print(self, fileID)
implicit none
class(boundaryParticleGeneric), intent(inout):: self
integer, intent(in):: fileID
integer:: e
write(fileID, '(A)') self%name
select type(self)
type is(boundaryQuasiNeutrality)
write(fileID, '(A,",",A)') '"Edge id"', '"alpha"'
do e = 1, size(self%edges)
write(fileID, '('//fmtColInt//','//fmtReal//')') self%edges(e)%obj%n, self%alpha(self%edges(e)%obj%n)
end do
end select
end subroutine quasiNeutrality_print
! Generic boundary conditions for internal use
! reflection
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
! transparent
module 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
! Updates the boundary condition values when %update procedure is associated
module subroutine boundariesParticle_update()
implicit none
integer:: b
do b = 1, nBoundariesParticle
if (associated(boundariesParticle(b)%obj%update)) then
call boundariesParticle(b)%obj%update
end if
end do
end subroutine boundariesParticle_update
! Writes the output into the Step_XXXXX_boundaryParticles file when the %print procedure is associated
module subroutine boundariesParticle_write()
use moduleCaseparam, only: timeStep
use moduleOutput, only:fileID_boundaryParticle, formatFileName, informFileCreation
implicit none
integer:: b
character(:), allocatable:: fileName
if (boundaryParticleOutput) then
fileName = formatFileName(prefix, 'boundariesParticle', 'csv', timeStep)
call informFileCreation(fileName)
open(fileID_boundaryParticle, file = path // folder // '/' // fileName)
do b = 1, nBoundariesParticle
if (associated(boundariesParticle(b)%obj%print)) then
call boundariesParticle(b)%obj%print(fileID_boundaryParticle)
end if
end do
close(fileID_boundaryParticle)
end if
end subroutine boundariesParticle_write
end submodule boundaryParticle

View file

@ -0,0 +1,933 @@
submodule(moduleMesh) elements
CONTAINS
!Reset the output of node
PURE module SUBROUTINE resetOutput(self)
USE moduleSpecies
USE moduleOutput
IMPLICIT NONE
CLASS(meshNode), INTENT(inout):: self
INTEGER:: k
DO k = 1, nSpecies
self%output(k)%den = 0.D0
self%output(k)%mom = 0.D0
self%output(k)%tensorS = 0.D0
END DO
END SUBROUTINE resetOutput
module subroutine meshNodePointer_add(self, node)
implicit none
class(meshNodePointer), allocatable, intent(inout):: self(:)
integer, intent(in):: node
integer:: n
logical:: inArray
type(meshNodePointer):: nodeToAdd
nodeToAdd%obj => mesh%nodes(node)%obj
inArray = .false.
! I cannot use the procedure 'any' for this
do n = 1 , size(self)
IF (self(n) == nodeToAdd) THEN
! Node already in array
inArray = .true.
exit
end if
end do
if (.not. inArray) then
! If not, add element to array of nodes
self = [self, nodeToAdd]
end if
end subroutine meshNodePointer_add
module function meshNodePointer_equal_type_type(self, other) result(isEqual)
implicit none
class(meshNodePointer), intent(in):: self, other
logical:: isEqual
isEqual = self%obj%n == other%obj%n
end function meshNodePointer_equal_type_type
module function meshNodePointer_equal_type_int(self, other) result(isEqual)
implicit none
class(meshNodePointer), intent(in):: self
integer, intent(in):: other
logical:: isEqual
isEqual = self%obj%n == other
end function meshNodePointer_equal_type_int
module function meshEdgePointer_equal_type_type(self, other) result(isEqual)
implicit none
class(meshEdgePointer), intent(in):: self, other
logical:: isEqual
isEqual = self%obj%n == other%obj%n
end function meshEdgePointer_equal_type_type
module subroutine meshEdgePointer_add(self, edge)
implicit none
class(meshEdgePointer), allocatable, intent(inout):: self(:)
integer, intent(in):: edge
integer:: n
logical:: inArray
type(meshEdgePointer):: edgeToAdd
edgeToAdd%obj => mesh%edges(edge)%obj
inArray = .false.
! I cannot use the procedure 'any' for this
do n = 1 , size(self)
IF (self(n) == edgeToAdd) THEN
! Node already in array
inArray = .true.
exit
end if
end do
if (.not. inArray) then
! If not, add element to array of nodes
self = [self, edgeToAdd]
end if
end subroutine meshEdgePointer_add
module function meshEdgePointer_equal_type_int(self, other) result(isEqual)
implicit none
class(meshEdgePointer), intent(in):: self
integer, intent(in):: other
logical:: isEqual
isEqual = self%obj%n == other
end function meshEdgePointer_equal_type_int
!Constructs the global K matrix
module SUBROUTINE constructGlobalK(self)
use moduleErrors, only: criticalError
IMPLICIT NONE
CLASS(meshParticles), INTENT(inout):: self
INTEGER:: c
INTEGER, ALLOCATABLE:: nodes(:)
REAL(8), ALLOCATABLE:: localK(:,:)
INTEGER:: i, j
integer:: n, b, ni
INTEGER:: info
EXTERNAL:: dgetrf
DO c = 1, self%numCells
associate(nNodes => self%cells(c)%obj%nNodes)
ALLOCATE(nodes(1:nNodes))
ALLOCATE(localK(1:nNodes, 1:nNodes))
nodes = self%cells(c)%obj%getNodes(nNodes)
localK = self%cells(c)%obj%elemK(nNodes)
DO i = 1, nNodes
DO j = 1, nNodes
self%K(nodes(i), nodes(j)) = self%K(nodes(i), nodes(j)) + localK(i, j)
END DO
END DO
DEALLOCATE(nodes, localK)
end associate
END DO
! Modify K matrix due to EM boundary conditions
DO b = 1, nBoundariesEM
SELECT TYPE(boundary => boundariesEM(b)%obj)
TYPE IS(boundaryEMDirichlet)
DO n = 1, boundary%nNodes
ni = boundary%nodes(n)%obj%n
self%K(ni, :) = 0.D0
self%K(ni, ni) = 1.D0
END DO
TYPE IS(boundaryEMDirichletTime)
DO n = 1, boundary%nNodes
ni = boundary%nodes(n)%obj%n
self%K(ni, :) = 0.D0
self%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(self%numNodes, self%numNodes, self%K, self%numNodes, self%IPIV, info)
IF (info /= 0) THEN
CALL criticalError('Factorization of K matrix failed', 'constructGlobalK')
END IF
END SUBROUTINE constructGlobalK
! Gather the value of valNodes at position Xi of an edge
pure module function gatherF_edge_scalar(self, Xi, nNodes, valNodes) RESULT(f)
implicit none
class(meshEdge), intent(in):: self
real(8), intent(in):: Xi(1:3)
integer, intent(in):: nNodes
real(8), intent(in):: valNodes(1:nNodes)
real(8):: f
real(8):: fPsi(1:nNodes)
fPsi = self%fPsi(Xi, nNodes)
f = dot_product(fPsi, valNodes)
end function gatherF_edge_scalar
!Gather the value of valNodes (scalar) at position Xi
PURE module FUNCTION gatherF_cell_scalar(self, Xi, nNodes, valNodes) RESULT(f)
IMPLICIT NONE
CLASS(meshCell), INTENT(in):: self
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8), INTENT(in):: valNodes(1:nNodes)
REAL(8):: f
REAL(8):: fPsi(1:nNodes)
fPsi = self%fPsi(Xi, nNodes)
f = DOT_PRODUCT(fPsi, valNodes)
END FUNCTION gatherF_cell_scalar
!Gather the value of valNodes (array) at position Xi
PURE module FUNCTION gatherF_cell_array(self, Xi, nNodes, valNodes) RESULT(f)
IMPLICIT NONE
CLASS(meshCell), INTENT(in):: self
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8), INTENT(in):: valNodes(1:nNodes, 1:3)
REAL(8):: f(1:3)
REAL(8):: fPsi(1:nNodes)
fPsi = self%fPsi(Xi, nNodes)
f = MATMUL(fPsi, valNodes)
END FUNCTION gatherF_cell_array
!Gather the spatial derivative of valNodes (scalar) at position Xi
PURE module FUNCTION gatherDF_cell_scalar(self, Xi, nNodes, valNodes) RESULT(df)
IMPLICIT NONE
CLASS(meshCell), INTENT(in):: self
REAL(8), INTENT(in):: Xi(1:3)
INTEGER, INTENT(in):: nNodes
REAL(8), INTENT(in):: valNodes(1:nNodes)
REAL(8):: df(1:3)
REAL(8):: dPsi(1:3, 1:nNodes)
REAL(8):: pDer(1:3,1:3)
REAL(8):: dPsiR(1:3, 1:nNodes)
REAL(8):: invJ(1:3, 1:3), detJ
dPsi = self%dPsi(Xi, nNodes)
pDer = self%partialDer(nNodes, dPsi)
detJ = self%detJac(pDer)
invJ = self%invJac(pDer)
dPsiR = MATMUL(invJ, dPsi)/detJ
df = (/ DOT_PRODUCT(dPsiR(1,:), valNodes), &
DOT_PRODUCT(dPsiR(2,:), valNodes), &
DOT_PRODUCT(dPsiR(3,:), valNodes) /)
END FUNCTION gatherDF_cell_scalar
!Scatters particle properties into cell nodes
module SUBROUTINE scatter(self, nNodes, part)
USE moduleMath
USE moduleSpecies
USE OMP_LIB
IMPLICIT NONE
CLASS(meshCell), INTENT(inout):: self
INTEGER, INTENT(in):: nNodes
CLASS(particle), INTENT(in):: part
REAL(8):: fPsi(1:nNodes)
INTEGER:: cellNodes(1:nNodes)
REAL(8):: tensorS(1:3, 1:3)
INTEGER:: sp
INTEGER:: i
CLASS(meshNode), POINTER:: node
REAL(8):: pFraction !Particle fraction
cellNodes = self%getNodes(nNodes)
fPsi = self%fPsi(part%Xi, nNodes)
tensorS = outerProduct(part%v, part%v)
sp = part%species%n
DO i = 1, nNodes
node => mesh%nodes(cellNodes(i))%obj
pFraction = fPsi(i)*part%weight
CALL OMP_SET_LOCK(node%lock)
node%output(sp)%den = node%output(sp)%den + pFraction
node%output(sp)%mom(:) = node%output(sp)%mom(:) + pFraction*part%v(:)
node%output(sp)%tensorS(:,:) = node%output(sp)%tensorS(:,:) + pFraction*tensorS
CALL OMP_UNSET_LOCK(node%lock)
END DO
END SUBROUTINE scatter
!Find next cell for particle
RECURSIVE SUBROUTINE findCell(self, part, oldCell)
USE moduleSpecies
USE moduleErrors
USE OMP_LIB
IMPLICIT NONE
CLASS(meshCell), INTENT(inout):: self
CLASS(particle), INTENT(inout), TARGET:: part
CLASS(meshCell), OPTIONAL, INTENT(in):: oldCell
REAL(8):: Xi(1:3)
CLASS(meshElement), POINTER:: neighbourElement
INTEGER:: sp
Xi = self%phy2log(part%r)
!Checks if particle is inside 'self' cell
IF (self%inside(Xi)) THEN
part%cell = self%n
part%Xi = Xi
part%n_in = .TRUE.
!Assign particle to listPart_in
IF (listInCells) THEN
CALL OMP_SET_LOCK(self%lock)
sp = part%species%n
CALL self%listPart_in(sp)%add(part)
self%totalWeight(sp) = self%totalWeight(sp) + part%weight
CALL OMP_UNSET_LOCK(self%lock)
END IF
ELSE
!If not, searches for a neighbour and repeats the process.
CALL self%neighbourElement(Xi, neighbourElement)
!Defines the next step
SELECT TYPE(neighbourElement)
CLASS IS(meshCell)
!Particle moved to new cell, repeat find procedure
CALL neighbourElement%findCell(part, self)
CLASS IS (meshEdge)
!Particle encountered a surface, apply boundary
CALL neighbourElement%boundariesParticle(part%species%n)%obj%apply(neighbourElement,part)
!If particle is still inside the domain, call findCell
IF (part%n_in) THEN
IF(PRESENT(oldCell)) THEN
CALL self%findCell(part, oldCell)
ELSE
CALL self%findCell(part)
END IF
END IF
CLASS DEFAULT
WRITE (*, "(A, I6)") "Element = ", self%n
CALL criticalError("No connectivity found for element", "findCell")
END SELECT
END IF
END SUBROUTINE findCell
!If Coll and Particle are the same, simply copy the part%cell into part%cellColl
SUBROUTINE findCellSameMesh(part)
USE moduleSpecies
IMPLICIT NONE
TYPE(particle), INTENT(inout):: part
part%cellColl = part%cell
END SUBROUTINE findCellSameMesh
!TODO: try to combine this with the findCell for a regular mesh
!Find the volume in which particle reside in the mesh for collisions
!No boundary interaction taken into account
SUBROUTINE findCellCollMesh(part)
USE moduleSpecies
IMPLICIT NONE
TYPE(particle), INTENT(inout):: part
LOGICAL:: found
CLASS(meshCell), POINTER:: cell
REAL(8), DIMENSION(1:3):: Xi
CLASS(meshElement), POINTER:: neighbourElement
INTEGER:: sp
found = .FALSE.
cell => meshColl%cells(part%cellColl)%obj
DO WHILE(.NOT. found)
Xi = cell%phy2log(part%r)
IF (cell%inside(Xi)) THEN
part%cellColl = cell%n
IF (listInCells) THEN
CALL OMP_SET_LOCK(cell%lock)
sp = part%species%n
CALL cell%listPart_in(sp)%add(part)
cell%totalWeight(sp) = cell%totalWeight(sp) + part%weight
CALL OMP_UNSET_LOCK(cell%lock)
END IF
found = .TRUE.
ELSE
CALL cell%neighbourElement(Xi, neighbourElement)
SELECT TYPE(neighbourElement)
CLASS IS(meshCell)
!Try next element
cell => neighbourElement
CLASS DEFAULT
!Should never happend, but just in case, stops loops
found = .TRUE.
END SELECT
END IF
END DO
END SUBROUTINE findCellCollMesh
!Returns index of volume associated to a position (if any)
!If no voulme is found, returns 0
!WARNING: This function is slow and should only be used in initialization phase
FUNCTION findCellBrute(self, r) RESULT(nVol)
USE moduleSpecies
IMPLICIT NONE
CLASS(meshGeneric), INTENT(in):: self
REAL(8), DIMENSION(1:3), INTENT(in):: r
INTEGER:: nVol
INTEGER:: e
REAL(8), DIMENSION(1:3):: Xi
!Inits RESULT
nVol = 0
DO e = 1, self%numCells
Xi = self%cells(e)%obj%phy2log(r)
IF(self%cells(e)%obj%inside(Xi)) THEN
nVol = self%cells(e)%obj%n
EXIT
END IF
END DO
END FUNCTION findCellBrute
!Computes collisions in element
SUBROUTINE doCollisions(self)
USE moduleCollisions
USE moduleSpecies
USE moduleList
use moduleRefParam
USE moduleRandom
USE moduleOutput
USE moduleMath
USE moduleCaseParam, ONLY: timeStep
IMPLICIT NONE
CLASS(meshGeneric), INTENT(inout), TARGET:: self
INTEGER:: e
CLASS(meshCell), POINTER:: cell
INTEGER:: k, i, j
INTEGER:: nPart_i, nPart_j, nPart!Number of particles inside the cell
REAL(8):: pMax !Maximum probability of collision
INTEGER:: nColl
TYPE(pointerArray), ALLOCATABLE:: partTemp_i(:), partTemp_j(:)
TYPE(particle), POINTER:: part_i, part_j
INTEGER:: n, c
REAL(8):: vRel, rMass, eRel
REAL(8):: sigmaVrelTotal
REAL(8), ALLOCATABLE:: sigmaVrel(:), probabilityColl(:)
REAL(8):: rnd_real !Random number for collision
INTEGER:: rnd_int !Random number for collision
IF (MOD(timeStep, everyColl) == 0) THEN
!Collisions need to be performed in this iteration
!$OMP DO SCHEDULE(DYNAMIC) PRIVATE(part_i, part_j, partTemp_i, partTemp_j)
DO e=1, self%numCells
cell => self%cells(e)%obj
!TODO: Simplify this, to many sublevels
!Iterate over the number of pairs
DO k = 1, nCollPairs
!Reset tally of collisions
IF (collOutput) THEN
cell%tallyColl(k)%tally = 0
END IF
IF (interactionMatrix(k)%amount > 0) THEN
!Select the species for the collision pair
i = interactionMatrix(k)%sp_i%n
j = interactionMatrix(k)%sp_j%n
!Number of particles per species in the collision pair
nPart_i = cell%listPart_in(i)%amount
nPart_j = cell%listPart_in(j)%amount
IF (nPart_i > 0 .AND. nPart_j > 0) THEN
!Total number of particles for the collision pair
nPart = nPart_i + nPart_j
!Resets the number of collisions in the cell
nColl = 0
!Probability of collision for pair i-j
pMax = (cell%totalWeight(i) + cell%totalWeight(j))*cell%sigmaVrelMax(k)*tauColl/cell%volume
!Number of collisions in the cell
nColl = NINT(REAL(nPart)*pMax*0.5D0)
!Converts the list of particles to an array for easy access
IF (nColl > 0) THEN
partTemp_i = cell%listPart_in(i)%convert2Array()
partTemp_j = cell%listPart_in(j)%convert2Array()
END IF
DO n = 1, nColl
!Select random particles
part_i => NULL()
part_j => NULL()
rnd_int = random(1, nPart_i)
part_i => partTemp_i(rnd_int)%part
rnd_int = random(1, nPart_j)
part_j => partTemp_j(rnd_int)%part
!If they are the same particle, skip
!TODO: Maybe try to improve this
IF (ASSOCIATED(part_i, part_j)) THEN
CYCLE
END IF
!If particles do not belong to the species, skip collision
!This can happen, for example, if particle has been previously ionized or removed
!TODO: Try to find a way to not lose these collisions. Maybe check new 'k' and use that for the collision?
IF (part_i%species%n /= i .OR. &
part_j%species%n /= j) THEN
CYCLE
END IF
!Obtain the cross sections for the different processes
!TODO: From here it might be a procedure in interactionMatrix
vRel = NORM2(part_i%v-part_j%v)
rMass = reducedMass(part_i%weight*part_i%species%m, part_j%weight*part_j%species%m)
eRel = rMass*vRel**2
CALL interactionMatrix(k)%getSigmaVrel(vRel, eRel, sigmaVrelTotal, sigmaVrel)
!Update maximum sigma*v_rel
IF (sigmaVrelTotal > cell%sigmaVrelMax(k)) THEN
cell%sigmaVrelMax(k) = sigmaVrelTotal
END IF
ALLOCATE(probabilityColl(0:interactionMatrix(k)%amount))
probabilityColl = 0.0
DO c = 1, interactionMatrix(k)%amount
probabilityColl(c) = sigmaVrel(c)/cell%sigmaVrelMax(k) + SUM(probabilityColl(0:c-1))
END DO
!Selects random number between 0 and 1
rnd_real = random()
!If the random number is below the total probability of collision, collide particles
IF (rnd_real < sigmaVrelTotal / cell%sigmaVrelMax(k)) THEN
!Loop over collisions
DO c = 1, interactionMatrix(k)%amount
IF (rnd_real <= probabilityColl(c)) THEN
!$OMP CRITICAL
CALL interactionMatrix(k)%collisions(c)%obj%collide(part_i, part_j, vRel)
!$OMP END CRITICAL
!If collisions are gonna be output, count the collision
IF (collOutput) THEN
cell%tallyColl(k)%tally(c) = cell%tallyColl(k)%tally(c) + 1
END IF
!A collision has ocurred, exit the loop
EXIT
END IF
END DO
END IF
!Deallocate arrays for next collision
DEALLOCATE(sigmaVrel, probabilityColl)
!End loop collisions in cell
END DO
END IF
END IF
!End loop collision pairs
END DO
!End loop volumes
END DO
!$OMP END DO
END IF
END SUBROUTINE doCollisions
SUBROUTINE doCoulomb(self)
USE moduleCoulomb
USE moduleRandom
USE moduleOutput
USE moduleList
USE moduleMath
USE moduleRefParam
USE moduleConstParam
IMPLICIT NONE
CLASS(meshParticles), INTENT(in), TARGET:: self
CLASS(meshCell), POINTER:: cell
TYPE(interactionsCoulomb):: pair
INTEGER:: e
INTEGER:: k
INTEGER:: i, j
INTEGER:: n
INTEGER:: p
TYPE(lNode), POINTER:: partTemp
INTEGER(8), ALLOCATABLE:: cellNodes(:)
CLASS(meshNode), POINTER:: node
TYPE(outputFormat):: output
REAL(8), ALLOCATABLE:: densityNodes(:), velocityNodes(:,:), temperatureNodes(:) !values in node
REAL(8):: density, velocity(1:3), temperature!values at particle position
REAL(8):: C(1:3), C_per, W(1:3) !relative velocity and velocity in the relative frame of reference
REAL(8):: l, lW, l2
REAL(8):: GlW, HlW
REAL(8):: normC
REAL(8):: cosThe, sinThe
REAL(8):: cosPhi, sinPhi
REAL(8):: rotation(1:3,1:3) !Rotation matrix to go back to laboratory frame
REAL(8):: A, AW
REAL(8):: deltaW_par, deltaW_par_square, deltaW_per_square !Increments of W
REAL(8):: theta_per !Random angle for perpendicular direction
REAL(8):: eps = 1.D-12
REAL(8), ALLOCATABLE, DIMENSION(:,:):: deltaV_ij, p_ij
REAL(8), ALLOCATABLE, DIMENSION(:):: mass_ij
REAL(8):: massSum_ij
REAL(8), ALLOCATABLE, DIMENSION(:,:):: deltaV_ji, p_ji
REAL(8), ALLOCATABLE, DIMENSION(:):: mass_ji
REAL(8):: massSum_ji
REAL(8):: alpha_num, alpha_den, alpha, beta(1:3)
!$OMP DO SCHEDULE(DYNAMIC) PRIVATE(partTemp)
DO e = 1, self%numCells
cell => self%cells(e)%obj
cellNodes = cell%getNodes(cell%nNodes)
ALLOCATE(densityNodes(1:cell%nNodes), &
velocityNodes(1:cell%nNodes, 1:3), &
temperatureNodes(1:cell%nNodes))
DO k=1, nCoulombPairs
pair = coulombMatrix(k)
i = pair%sp_i%n
j = pair%sp_j%n
!Do scattering of particles from species_i due to species j
!Compute background properties of species_j
DO n = 1, cell%nNodes
node => self%nodes(cellNodes(n))%obj
CALL calculateOutput(node%output(j), output, node%v, pair%sp_j)
densityNodes(n) = output%density/n_ref
velocityNodes(n,1:3) = output%velocity(1:3)/v_ref
temperatureNodes(n) = output%temperature/T_ref
END DO
ALLOCATE(deltaV_ij(1:cell%listPart_in(i)%amount, 1:3))
ALLOCATE(p_ij(1:cell%listPart_in(i)%amount, 1:3))
ALLOCATE(mass_ij(1:cell%listPart_in(i)%amount))
deltaV_ij = 0.D0
p_ij = 0.D0
mass_ij = 0.D0
!Loop over particles of species_i
partTemp => cell%listPart_in(i)%head
p = 1
DO WHILE(ASSOCIATED(partTemp))
density = cell%gatherF(partTemp%part%Xi, cell%nNodes, densityNodes)
velocity = cell%gatherF(partTemp%part%Xi, cell%nNodes, velocityNodes)
temperature = cell%gatherF(partTemp%part%Xi, cell%nNodes, temperatureNodes)
!If cell temperature is too low, skip particle to avoid division by zero
IF (temperature>eps) THEN
l2 = pair%l2_j/temperature
l = SQRT(l2)
ELSE
partTemp => partTemp%next
CYCLE
END IF
A = pair%A_i*density
C = partTemp%part%v - velocity
normC = NORM2(C)
!C_3 = z; C_1, C2 = x, y (per)
C_per = NORM2(C(1:2))
cosPhi = C(1) / C_per
sinPhi = C(2) / C_per
cosThe = C(3) / normC
sinThe = C_per / normC
!Rotation matrix to go from W to C
rotation = RESHAPE((/ cosThe*cosPhi, cosThe*sinPhi, -sinThe, & !First column
-sinPhi, cosPhi, 0.D0, & !Second column
sinThe*cosPhi, sinThe*sinPhi, cosThe /), & !Third column
(/ 3, 3 /))
!W at start is = (0, 0, normC), so normW = normC
lW = l * normC
GlW = G(lW)
HlW = H(lW)
AW = A / normC
!Calculate changes in W due to collision process
deltaW_par = - A * pair%one_plus_massRatio_ij * l2 * GlW * tauMin
deltaW_par_square = SQRT(AW * GlW * tauMin)*randomMaxwellian()
deltaW_per_square = SQRT(AW * HlW * tauMin)*randomMaxwellian()
!Random angle to distribute perpendicular change in velocity
theta_per = PI2*random()
!Change W
W(1) = deltaW_per_square * COS(theta_per)
W(2) = deltaW_per_square * SIN(theta_per)
W(3) = normC + deltaW_par + deltaW_par_square
!Compute changes in velocity for each particle
deltaV_ij(p,1:3) = MATMUL(rotation, W) + velocity - partTemp%part%v
mass_ij(p) = pair%sp_i%m*partTemp%part%weight
p_ij(p,1:3) = mass_ij(p)*partTemp%part%v
!Move to the next particle in the list
partTemp => partTemp%next
p = p + 1
END DO
!Do corresponding collisions
IF (i /= j) THEN
!Do scattering of particles from species_j due to species i
!Compute background properties of species_i
DO n = 1, cell%nNodes
node => self%nodes(cellNodes(n))%obj
CALL calculateOutput(node%output(i), output, node%v, pair%sp_i)
densityNodes(n) = output%density/n_ref
velocityNodes(n,1:3) = output%velocity(1:3)/v_ref
temperatureNodes(n) = output%temperature/T_ref
END DO
ALLOCATE(deltaV_ji(1:cell%listPart_in(j)%amount, 1:3))
ALLOCATE(p_ji(1:cell%listPart_in(j)%amount, 1:3))
ALLOCATE(mass_ji(1:cell%listPart_in(j)%amount))
deltaV_ji = 0.D0
p_ji = 0.D0
mass_ji = 0.D0
!Loop over particles of species_j
partTemp => cell%listPart_in(j)%head
p = 1
DO WHILE(ASSOCIATED(partTemp))
density = cell%gatherF(partTemp%part%Xi, cell%nNodes, densityNodes)
velocity = cell%gatherF(partTemp%part%Xi, cell%nNodes, velocityNodes)
temperature = cell%gatherF(partTemp%part%Xi, cell%nNodes, temperatureNodes)
!If cell temperature is too low, skip particle to avoid division by zero
IF (temperature>eps) THEN
l2 = pair%l2_i/temperature
l = SQRT(l2)
ELSE
partTemp => partTemp%next
CYCLE
END IF
A = pair%A_j*density
C = partTemp%part%v - velocity
normC = NORM2(C)
!C_3 = z; C_1, C2 = x, y (per)
C_per = NORM2(C(1:2))
cosPhi = C(1) / C_per
sinPhi = C(2) / C_per
cosThe = C(3) / normC
sinThe = C_per / normC
!Rotation matrix to go from W to C
rotation = RESHAPE((/ cosThe*cosPhi, cosThe*sinPhi, -sinThe, & !First column
-sinPhi, cosPhi, 0.D0, & !Second column
sinThe*cosPhi, sinThe*sinPhi, cosThe /), & !Third column
(/ 3, 3 /))
!W at start is = (0, 0, normC), so normW = normC
lW = l * normC
GlW = G(lW)
HlW = H(lW)
AW = A / normC
!Calculate changes in W due to collision process
deltaW_par = - A * pair%one_plus_massRatio_ij * l2 * GlW * tauMin
deltaW_par_square = SQRT(AW * GlW * tauMin)*randomMaxwellian()
deltaW_per_square = SQRT(AW * HlW * tauMin)*randomMaxwellian()
!Random angle to distribute perpendicular change in velocity
theta_per = PI2*random()
!Change W
W(1) = deltaW_per_square * COS(theta_per)
W(2) = deltaW_per_square * SIN(theta_per)
W(3) = normC + deltaW_par + deltaW_par_square
!Compute changes in velocity for each particle
deltaV_ji(p,1:3) = MATMUL(rotation, W) + velocity - partTemp%part%v
mass_ji(p) = pair%sp_j%m*partTemp%part%weight
p_ji(p,1:3) = mass_ji(p)*partTemp%part%v
!Move to the next particle in the list
partTemp => partTemp%next
p = p + 1
END DO
END IF
!Calculate correction
!Total mass
massSum_ij = SUM(mass_ij)
massSum_ji = 0.D0
!Beta
beta = 0.D0
DO p = 1, cell%listPart_in(i)%amount
beta = beta + mass_ij(p) * deltaV_ij(p,1:3)
END DO
IF (i /= j) THEN
massSum_ji = SUM(mass_ji)
DO p = 1, cell%listPart_in(j)%amount
beta = beta + mass_ji(p) * deltaV_ji(p,1:3)
END DO
END IF
beta = beta / (massSum_ij + massSum_ji)
!Alpha
alpha_num = 0.D0
alpha_den = 0.D0
DO p =1, cell%listPart_in(i)%amount
alpha_num = alpha_num + DOT_PRODUCT(p_ij(p,1:3), deltav_ij(p,1:3) - beta(1:3))
alpha_den = alpha_den + mass_ij(p) * NORM2(deltav_ij(p,1:3) - beta(1:3))**2
END DO
IF (i /= j) THEN
DO p = 1, cell%listPart_in(j)%amount
alpha_num = alpha_num + DOT_PRODUCT(p_ji(p,1:3), deltav_ji(p,1:3) - beta(1:3))
alpha_den = alpha_den + mass_ji(p) * NORM2(deltav_ji(p,1:3) - beta(1:3))**2
END DO
END IF
alpha = -2.D0*alpha_num / alpha_den
!Apply correction to particles velocity
partTemp => cell%listPart_in(i)%head
p = 1
DO WHILE(ASSOCIATED(partTemp))
partTemp%part%v = partTemp%part%v + alpha * (deltaV_ij(p,1:3) - beta(1:3))
partTemp => partTemp%next
p = p + 1
END DO
IF (i /= j) THEN
partTemp => cell%listPart_in(j)%head
p = 1
DO WHILE(ASSOCIATED(partTemp))
partTemp%part%v = partTemp%part%v + alpha * (deltaV_ji(p,1:3) - beta(1:3))
partTemp => partTemp%next
p = p + 1
END DO
END IF
DEALLOCATE(deltaV_ij, p_ij, mass_ij)
IF (i /= j) THEN
DEALLOCATE(deltaV_ji, p_ji, mass_ji)
END IF
END DO
DEALLOCATE(densityNodes, velocityNodes, temperatureNodes, cellNodes)
END DO
!$OMP END DO
END SUBROUTINE doCoulomb
end submodule elements

View file

@ -0,0 +1,22 @@
submodule(moduleMesh) surfaces
contains
module function physicalSurface_to_index(ps) result(index)
implicit none
integer:: ps
integer:: index
integer:: i
index = 0
do i = 1, nPhysicalSurfaces
if (physicalSurfaces(i)%index == ps) then
index = i
exit
end if
end do
end function physicalSurface_to_index
end submodule surfaces

View file

@ -1,284 +0,0 @@
!moduleMeshBoundary: Boundary functions for the mesh edges
MODULE moduleMeshBoundary
USE moduleMesh
CONTAINS
SUBROUTINE reflection(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 reflection
!Absoption in a surface
SUBROUTINE absorption(edge, part)
USE moduleCaseParam
USE moduleSpecies
IMPLICIT NONE
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(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 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(edge, part)
USE moduleSpecies
IMPLICIT NONE
CLASS(meshEdge), INTENT(inout):: edge
CLASS(particle), INTENT(inout):: part
CALL reflection(edge, part)
END SUBROUTINE symmetryAxis
!Wall with temperature
SUBROUTINE wallTemperature(edge, part)
USE moduleSpecies
USE moduleBoundary
USE moduleRandom
IMPLICIT NONE
CLASS(meshEdge), INTENT(inout):: edge
CLASS(particle), INTENT(inout):: part
INTEGER:: i
!Modifies particle velocity according to wall temperature
SELECT TYPE(bound => edge%boundary%bTypes(part%species%n)%obj)
TYPE IS(boundaryWallTemperature)
DO i = 1, 3
part%v(i) = part%v(i) + bound%vTh*randomMaxwellian()
END DO
END SELECT
CALL reflection(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(edge, part)
USE moduleList
USE moduleSpecies
USE moduleMesh
USE moduleRefParam
USE moduleRandom
USE moduleMath
IMPLICIT NONE
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
SELECT TYPE(bound => edge%boundary%bTypes(part%species%n)%obj)
TYPE IS(boundaryIonization)
mRel = reducedMass(bound%m0, part%species%m)
vRel = SUM(DABS(part%v-bound%v0))
eRel = mRel*vRel**2*5.D-1
!Maximum number of possible ionizations based on relative energy
nIonizations = FLOOR(eRel/bound%eThreshold)
DO p = 1, nIonizations
!Get probability of ionization
pIonization = 1.D0 - DEXP(-bound%n0*bound%crossSection%get(eRel)*vRel*bound%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) = bound%v0(1) + bound%vTh*randomMaxwellian()
v0(2) = bound%v0(2) + bound%vTh*randomMaxwellian()
v0(3) = bound%v0(3) + bound%vTh*randomMaxwellian()
!Allocates the new particles
ALLOCATE(newElectron)
ALLOCATE(newIon)
IF (ASSOCIATED(bound%electronSecondary)) THEN
newElectron%species => bound%electronSecondary
ELSE
newElectron%species => part%species
END IF
newIon%species => bound%species
newElectron%v = v0 + (1.D0 + bound%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 - bound%eThreshold
vRel = 2.D0*DSQRT(eRel)/mRel
!Reduce number of possible ionizations
nIonizations = nIonizations - 1
END IF
END DO
END SELECT
!Removes ionizing electron regardless the number of pair created
part%n_in = .FALSE.
END SUBROUTINE ionization
subroutine outflowAdaptive(edge, part)
use moduleRandom
use moduleRefParam, only: v_ref
implicit none
class(meshEdge), intent(inout):: edge
class(particle), intent(inout):: part
real(8):: v_cut
v_cut = 2.d0 * 40.d3/v_ref !This will be the drag velocity of the ions in the future
select type(bound => edge%boundary%bTypes(part%species%n)%obj)
type is(boundaryOutflowAdaptive)
if (dot_product(part%v,-edge%normal) > v_cut) then
part%v = part%v + v_cut*edge%normal
call reflection(edge, part)
else
call transparent(edge, part)
end if
end select
end subroutine outflowAdaptive
!Points the boundary function to specific type
SUBROUTINE pointBoundaryFunction(edge, s)
USE moduleErrors
IMPLICIT NONE
CLASS(meshEdge), INTENT(inout):: edge
INTEGER, INTENT(in):: s !Species index
SELECT TYPE(obj => edge%boundary%bTypes(s)%obj)
TYPE IS(boundaryAbsorption)
edge%fBoundary(s)%apply => absorption
TYPE IS(boundaryReflection)
edge%fBoundary(s)%apply => reflection
TYPE IS(boundaryTransparent)
edge%fBoundary(s)%apply => transparent
TYPE IS(boundaryAxis)
edge%fBoundary(s)%apply => symmetryAxis
TYPE IS(boundaryWallTemperature)
edge%fBoundary(s)%apply => wallTemperature
TYPE IS(boundaryIonization)
edge%fBoundary(s)%apply => ionization
type is(boundaryOutflowAdaptive)
edge%fBoundary(s)%apply => outflowAdaptive
CLASS DEFAULT
CALL criticalError("Boundary type not defined in this geometry", 'pointBoundaryFunction')
END SELECT
END SUBROUTINE pointBoundaryFunction
END MODULE moduleMeshBoundary

View file

@ -0,0 +1,227 @@
! Common subroutines and parameters for mesh elements
module moduleMeshCommon
! values for integration gauss integral
! Segment
real(8), parameter:: corSeg(1:3) = (/ -dsqrt(3.D0/5.D0), 0.D0, dsqrt(3.D0/5.D0) /)
real(8), parameter:: wSeg(1:3) = (/ 5.D0/9.D0, 8.D0/9.D0, 5.D0/9.D0 /)
! tria
real(8), parameter:: Xi1Tria(1:4) = (/ 1.D0/3.D0, 1.D0/5.D0, 3.D0/5.D0, 1.D0/5.D0 /)
real(8), parameter:: Xi2Tria(1:4) = (/ 1.D0/3.D0, 1.D0/5.D0, 1.D0/5.D0, 3.D0/5.D0 /)
real(8), parameter:: wTria(1:4) = (/ -27.D0/96.D0, 25.D0/96.D0, 25.D0/96.D0, 25.D0/96.D0 /)
! Quad
real(8), parameter:: corQuad(1:3) = (/ -dsqrt(3.D0/5.D0), 0.D0, dsqrt(3.D0/5.D0) /)
real(8), parameter:: wQuad(1:3) = (/ 5.D0/9.D0, 8.D0/9.D0, 5.D0/9.D0 /)
! Center point in natural coordinates
! Point
real(8), parameter:: cenPoint(1:3) = (/ 0.0d0, 0.0d0, 0.0d0 /)
! Segment
real(8), parameter:: cenSeg(1:3) = (/ 0.0d0, 0.0d0, 0.0d0 /)
! Tria
real(8), parameter:: cenTria(1:3) = (/ 1.0d0/3.0d0, 1.0d0/3.0d0, 0.0d0 /)
! Quad
real(8), parameter:: cenQuad(1:3) = (/ 0.0d0, 0.0d0, 0.0d0 /)
! Tetra
real(8), parameter:: cenTetra(1:3) = (/ 1.0d0/3.0d0, 1.0d0/3.0d0, 1.d0/3.d0 /)
contains
! RETURN CENTER IN NATURAL COORDINATES
! Point
pure function centerXiPoint() result(Xi)
implicit none
real(8):: Xi(1:3)
Xi = cenPoint
end function centerXiPoint
! Segment
pure function centerXiSegm() result(Xi)
implicit none
real(8):: Xi(1:3)
Xi = cenSeg
end function centerXiSegm
! Tria
pure function centerXiTria() result(Xi)
implicit none
real(8):: Xi(1:3)
Xi = cenTria
end function centerXiTria
! Quad
pure function centerXiQuad() result(Xi)
implicit none
real(8):: Xi(1:3)
Xi = cenQuad
end function centerXiQuad
! Quad
pure function centerXiTetra() result(Xi)
implicit none
real(8):: Xi(1:3)
Xi = cenTetra
end function centerXiTetra
! ELEMENT FUNCTIONS
! Point
pure function fPsiPoint(Xi, nNodes) RESULT(fPsi)
implicit none
real(8), intent(in):: Xi(1:3) ! NOTE: Required by interface but unused
integer, intent(in):: nNodes
real(8):: fPsi(1:nNodes)
fPsi = (/ 1.D0 /)
end function fPsiPoint
! Segment
pure function fPsiSegm(Xi, nNodes) result(fPsi)
implicit none
real(8), intent(in):: Xi(1:3)
integer, intent(in):: nNodes
real(8):: fPsi(1:nNodes)
fPsi = (/ 1.D0 - Xi(1), &
1.D0 + Xi(1) /)
fPsi = fPsi * 0.50D0
end function fPsiSegm
! Quad
pure function fPsiQuad(Xi, nNodes) result(fPsi)
implicit none
real(8), intent(in):: Xi(1:3)
integer, intent(in):: nNodes
real(8):: fPsi(1:nNodes)
fPsi = 0.D0
fPsi = (/ (1.D0 - Xi(1)) * (1.D0 - Xi(2)), &
(1.D0 + Xi(1)) * (1.D0 - Xi(2)), &
(1.D0 + Xi(1)) * (1.D0 + Xi(2)), &
(1.D0 - Xi(1)) * (1.D0 + Xi(2)) /)
fPsi = fPsi * 0.25D0
end function fPsiQuad
! Tria
pure function fPsiTria(Xi, nNodes) result(fPsi)
IMPLICIT NONE
real(8), intent(in):: Xi(1:3)
integer, intent(in):: nNodes
real(8):: fPsi(1:nNodes)
fPsi(1) = 1.D0 - Xi(1) - Xi(2)
fPsi(2) = Xi(1)
fPsi(3) = Xi(2)
end function fPsiTria
! Tetra
pure function fPsiTetra(Xi, nNodes) result(fPsi)
implicit none
real(8), intent(in):: Xi(1:3)
integer, intent(in):: nNodes
real(8):: fPsi(1:nNodes)
fPsi(1) = 1.D0 - Xi(1) - Xi(2) - Xi(3)
fPsi(2) = Xi(1)
fPsi(3) = Xi(2)
fPsi(4) = Xi(3)
end function fPsiTetra
! DERIVATIVE FOR ELEMENT FUNCTIONS
! Segment
pure function dPsiSegm(Xi, nNodes) result(dPsi)
implicit none
real(8), intent(in):: Xi(1:3) ! NOTE: Required by interface but unused
integer, intent(in):: nNodes
real(8):: dPsi(1:3,1:nNodes)
dPsi = 0.D0
dPsi(1, 1:2) = (/ -5.D-1, 5.D-1 /)
end function dPsiSegm
! Quad
pure function dPsiQuad(Xi, nNodes) result(dPsi)
implicit none
real(8), intent(in):: Xi(1:3)
integer, intent(in):: nNodes
real(8):: dPsi(1:3,1:nNodes)
dPsi = 0.D0
dPsi(1, 1:4) = (/ -(1.D0 - Xi(2)), &
(1.D0 - Xi(2)), &
(1.D0 + Xi(2)), &
-(1.D0 + Xi(2)) /)
dPsi(2, 1:4) = (/ -(1.D0 - Xi(1)), &
-(1.D0 + Xi(1)), &
(1.D0 + Xi(1)), &
(1.D0 - Xi(1)) /)
dPsi = dPsi * 0.25D0
end function dPsiQuad
! Tria
pure function dPsiTria(Xi, nNodes) result(dPsi)
implicit none
real(8), intent(in):: Xi(1:3) ! NOTE: Required by interface but unused
integer, intent(in):: nNodes
real(8):: dPsi(1:3,1:nNodes)
dPsi = 0.D0
dPsi(1,1:3) = (/ -1.D0, 1.D0, 0.D0 /)
dPsi(2,1:3) = (/ -1.D0, 0.D0, 1.D0 /)
end function dPsiTria
!Compute element derivative functions in point Xi
pure function dPsiTetra(Xi, nNodes) result(dPsi)
implicit none
real(8), intent(in):: Xi(1:3) ! NOTE: Required by interface but unused
integer, intent(in):: nNodes
real(8):: dPsi(1:3, 1:nNodes)
dPsi = 0.D0
dPsi(1,1:4) = (/ -1.D0, 1.D0, 0.D0, 0.D0 /)
dPsi(2,1:4) = (/ -1.D0, 0.D0, 1.D0, 0.D0 /)
dPsi(3,1:4) = (/ -1.D0, 0.D0, 0.D0, 1.D0 /)
end function dPsiTetra
end module moduleMeshCommon

View file

@ -1,166 +0,0 @@
MODULE moduleBoundary
USE moduleTable
USE moduleSpecies
!Generic type for boundaries
TYPE, PUBLIC:: boundaryGeneric
CONTAINS
END TYPE boundaryGeneric
!Reflecting boundary
TYPE, PUBLIC, EXTENDS(boundaryGeneric):: boundaryReflection
CONTAINS
END TYPE boundaryReflection
!Absorption boundary
TYPE, PUBLIC, EXTENDS(boundaryGeneric):: boundaryAbsorption
CONTAINS
END TYPE boundaryAbsorption
!Transparent boundary
TYPE, PUBLIC, EXTENDS(boundaryGeneric):: boundaryTransparent
CONTAINS
END TYPE boundaryTransparent
!Symmetry axis
TYPE, PUBLIC, EXTENDS(boundaryGeneric):: boundaryAxis
CONTAINS
END TYPE boundaryAxis
!Wall Temperature boundary
TYPE, PUBLIC, EXTENDS(boundaryGeneric):: boundaryWallTemperature
!Thermal velocity of the wall: square root(Wall temperature X specific heat)
REAL(8):: vTh
CONTAINS
END TYPE boundaryWallTemperature
!Ionization boundary
TYPE, PUBLIC, EXTENDS(boundaryGeneric):: boundaryIonization
REAL(8):: m0, n0, v0(1:3), vTh !Properties of background neutrals.
CLASS(speciesGeneric), POINTER:: species !Ion species
CLASS(speciesCharged), POINTER:: electronSecondary !Pointer to species considerer as secondary electron
TYPE(table1D):: crossSection
REAL(8):: effectiveTime
REAL(8):: eThreshold
REAL(8):: deltaV
CONTAINS
END TYPE boundaryIonization
!Boundary for quasi-neutral outflow adjusting reflection coefficient
type, public, extends(boundaryGeneric):: boundaryOutflowAdaptive
real(8):: outflowCurrent
real(8):: reflectionFraction
contains
end type boundaryOutflowAdaptive
!Wrapper for boundary types (one per species)
TYPE:: bTypesCont
CLASS(boundaryGeneric), ALLOCATABLE:: obj
CONTAINS
END TYPE bTypesCont
!Wrapper for boundary conditions
TYPE:: boundaryCont
INTEGER:: n = 0
CHARACTER(:), ALLOCATABLE:: name
INTEGER:: physicalSurface = 0 !Physical surface as defined in the mesh file
CLASS(bTypesCont), ALLOCATABLE:: bTypes(:) !Array for boundary per species
CONTAINS
END TYPE boundaryCont
!Number of boundaries
INTEGER:: nBoundary = 0
!Array for boundaries
TYPE(boundaryCont), ALLOCATABLE, TARGET:: boundary(:)
CONTAINS
FUNCTION getBoundaryId(physicalSurface) RESULT(id)
IMPLICIT NONE
INTEGER:: physicalSurface
INTEGER:: id
INTEGER:: i
id = 0
DO i = 1, nBoundary
IF (physicalSurface == boundary(i)%physicalSurface) id = boundary(i)%n
END DO
END FUNCTION getBoundaryId
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
boundary = boundaryWallTemperature(vTh = vTh)
END SUBROUTINE initWallTemperature
SUBROUTINE initIonization(boundary, me, 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):: me !Electron mass
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/me)
END SELECT
END SUBROUTINE initIonization
END MODULE moduleBoundary

View file

@ -123,16 +123,16 @@ MODULE moduleCollisions
END FUNCTION randomDirectionVHS
!Inits the interaction matrix
SUBROUTINE initInteractionMatrix(interactionMatrix)
SUBROUTINE initInteractionMatrix(matrix)
USE moduleSpecies
IMPLICIT NONE
TYPE(interactionsBinary), INTENT(inout), ALLOCATABLE:: interactionMatrix(:)
TYPE(interactionsBinary), INTENT(inout), ALLOCATABLE:: matrix(:)
nCollPairs = (nSpecies*(nSpecies+1))/2
ALLOCATE(interactionMatrix(1:nCollPairs))
ALLOCATE(matrix(1:nCollPairs))
interactionMatrix(:)%amount = 0
matrix(:)%amount = 0
END SUBROUTINE initInteractionMatrix
@ -553,7 +553,7 @@ MODULE moduleCollisions
IMPLICIT NONE
CLASS(collisionBinaryChargeExchange), INTENT(in):: self
REAL(8), INTENT(in):: vRel
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)

View file

@ -1,53 +1,8 @@
!injection of particles
MODULE moduleInject
USE moduleSpecies
!Generic type for velocity distribution function
TYPE, ABSTRACT:: velDistGeneric
CONTAINS
!Returns random velocity from distribution function
PROCEDURE(randomVel_interface), DEFERRED, PASS:: randomVel
END TYPE velDistGeneric
ABSTRACT INTERFACE
FUNCTION randomVel_interface(self) RESULT(v)
IMPORT velDistGeneric
CLASS(velDistGeneric), INTENT(in):: self
REAL(8):: v
END FUNCTION randomVel_interface
END INTERFACE
!Container for velocity distributions
TYPE:: velDistCont
CLASS(velDistGeneric), ALLOCATABLE:: obj
END TYPE velDistCont
!Maxwellian distribution function
TYPE, EXTENDS(velDistGeneric):: velDistMaxwellian
REAL(8):: vTh !Thermal Velocity
CONTAINS
PROCEDURE, PASS:: randomVel => randomVelMaxwellian
END TYPE velDistMaxwellian
TYPE, EXTENDS(velDistGeneric):: velDistHalfMaxwellian
REAL(8):: vTh !Thermal Velocity
CONTAINS
PROCEDURE, PASS:: randomVel => randomVelHalfMaxwellian
END TYPE velDistHalfMaxwellian
!Dirac's delta distribution function
TYPE, EXTENDS(velDistGeneric):: velDistDelta
CONTAINS
PROCEDURE, PASS:: randomVel => randomVelDelta
END TYPE velDistDelta
use velocityDistribution
use moduleMesh, only: meshEdgePointer
!Generic injection of particles
TYPE:: injectGeneric
@ -60,7 +15,7 @@ MODULE moduleInject
INTEGER:: nParticles !Number of particles to introduce each time step
CLASS(speciesGeneric), POINTER:: species !Species of injection
INTEGER:: nEdges
INTEGER, ALLOCATABLE:: edges(:) !Array with edges
type(meshEdgePointer), allocatable:: edges(:)
INTEGER, ALLOCATABLE:: particlesPerEdge(:) ! Particles per edge
REAL(8), ALLOCATABLE:: weightPerEdge(:) ! Weight per edge
REAL(8):: surface ! Total surface of injection
@ -76,7 +31,7 @@ MODULE moduleInject
CONTAINS
!Initialize an injection of particles
SUBROUTINE initInject(self, i, v, n, temperature, flow, units, sp, physicalSurface, particlesPerEdge)
SUBROUTINE initInject(self, i, v, n, temperature, flow, units, sp, ps, particlesPerEdge)
USE moduleMesh
USE moduleRefParam
USE moduleConstParam
@ -88,63 +43,33 @@ MODULE moduleInject
CLASS(injectGeneric), INTENT(inout):: self
INTEGER, INTENT(in):: i
REAL(8), INTENT(in):: v, n(1:3), temperature(1:3)
INTEGER, INTENT(in):: sp, physicalSurface, particlesPerEdge
INTEGER, INTENT(in):: sp, ps, particlesPerEdge
integer:: ps_index
REAL(8):: tauInject
REAL(8), INTENT(in):: flow
CHARACTER(:), ALLOCATABLE, INTENT(in):: units
INTEGER:: e, et
INTEGER:: phSurface(1:mesh%numEdges)
INTEGER:: nVolColl
INTEGER:: e
REAL(8):: fluxPerStep = 0.D0
self%id = i
self%vMod = v / v_ref
self%n = n / NORM2(n)
self%temperature = temperature / T_ref
!Gets the edge elements from which particles are injected
DO e = 1, mesh%numEdges
phSurface(e) = mesh%edges(e)%obj%physicalSurface
END DO
self%nEdges = COUNT(phSurface == physicalSurface)
ALLOCATE(self%edges(1:self%nEdges))
ALLOCATE(self%particlesPerEdge(1:self%nEdges))
ALLOCATE(self%weightPerEdge(1:self%nEdges))
et = 0
DO e=1, mesh%numEdges
IF (mesh%edges(e)%obj%physicalSurface == physicalSurface) THEN
et = et + 1
self%edges(et) = mesh%edges(e)%obj%n
!Assign connectivity between injection edge and meshColl volume
IF (doubleMesh) THEN
nVolColl = findCellBrute(meshColl, mesh%edges(e)%obj%randPos())
IF (nVolColl > 0) THEN
mesh%edges(e)%obj%eColl => meshColl%cells(nVolColl)%obj
!Get array index of physical surface for inject
ps_index = physicalSurface_to_index(ps)
ELSE
CALL criticalError("No connection between edge and meshColl", "initInject")
! Copy array of edges
self%edges = physicalSurfaces(ps_index)%edges
self%nEdges = size(self%edges)
END IF
ELSE
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 IF
END IF
END DO
allocate(self%particlesPerEdge(1:self%nEdges))
allocate(self%weightPerEdge(1:self%nEdges))
!Calculates total area
self%surface = 0.D0
DO et = 1, self%nEdges
self%surface = self%surface + mesh%edges(self%edges(et))%obj%surface
DO e = 1, self%nEdges
self%surface = self%surface + self%edges(e)%obj%surface
END DO
@ -194,8 +119,8 @@ MODULE moduleInject
IF (particlesPerEdge > 0) THEN
! Particles per edge defined by the user
self%particlesPerEdge = particlesPerEdge
DO et = 1, self%nEdges
self%weightPerEdge(et) = fluxPerStep*mesh%edges(self%edges(et))%obj%surface / REAL(particlesPerEdge)
DO e = 1, self%nEdges
self%weightPerEdge(e) = fluxPerStep * self%edges(e)%obj%surface / real(particlesPerEdge)
END DO
@ -204,8 +129,8 @@ MODULE moduleInject
ELSE
! No particles assigned per edge, use the species weight
self%weightPerEdge = self%species%weight
DO et = 1, self%nEdges
self%particlesPerEdge(et) = max(1,FLOOR(fluxPerStep*mesh%edges(self%edges(et))%obj%surface / self%species%weight))
DO e = 1, self%nEdges
self%particlesPerEdge(e) = max(1,FLOOR(fluxPerStep*self%edges(e)%obj%surface / self%species%weight))
END DO
self%nParticles = SUM(self%particlesPerEdge)
@ -280,43 +205,6 @@ MODULE moduleInject
END SUBROUTINE initVelDistDelta
!Random velocity from Maxwellian distribution
FUNCTION randomVelMaxwellian(self) RESULT (v)
USE moduleRandom
IMPLICIT NONE
CLASS(velDistMaxwellian), INTENT(in):: self
REAL(8):: v
v = 0.D0
v = self%vTh*randomMaxwellian()/sqrt(2.d0)
END FUNCTION randomVelMaxwellian
!Random velocity from Half Maxwellian distribution
FUNCTION randomVelHalfMaxwellian(self) RESULT (v)
USE moduleRandom
IMPLICIT NONE
CLASS(velDistHalfMaxwellian), INTENT(in):: self
REAL(8):: v
v = 0.D0
v = self%vTh*randomHalfMaxwellian()/sqrt(2.d0)
END FUNCTION randomVelHalfMaxwellian
!Random velocity from Dirac's delta distribution
PURE FUNCTION randomVelDelta(self) RESULT(v)
IMPLICIT NONE
CLASS(velDistDelta), INTENT(in):: self
REAL(8):: v
v = 0.D0
END FUNCTION randomVelDelta
!Add particles for the injection
SUBROUTINE addParticles(self)
USE moduleSpecies
@ -349,7 +237,7 @@ MODULE moduleInject
!$OMP DO
DO e = 1, self%nEdges
! Select edge for injection
randomEdge => mesh%edges(self%edges(e))%obj
randomEdge => self%edges(e)%obj
! Inject particles in edge
DO i = 1, self%particlesPerEdge(e)
! Index in the global partInj array

View file

@ -208,7 +208,7 @@ MODULE moduleProbe
WRITE(pstring, "(I3.3)") self%id
fileName='Probe_' // tstring// '_f_' // pstring // '.dat'
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
OPEN (10, file = path // folder // '/' // fileName)
OPEN (10, file = generateFilePath(fileName))
WRITE(10, "(A1, 1X, A)") "# ", self%species%name
WRITE(10, "(A6, 3(ES15.6E3), A2)") "# r = ", self%r(:)*L_ref, " m"
WRITE(10, "(A6, ES15.6E3, A2)") "# t = ", REAL(timeStep)*tauMin*ti_ref, " s"
@ -216,10 +216,10 @@ MODULE moduleProbe
DO i = 1, self%nv(1)
DO j = 1, self%nv(2)
DO k = 1, self%nv(3)
WRITE(10, "(4(ES20.6E3))") self%vi(i)*v_ref, &
self%vj(j)*v_ref, &
self%vk(k)*v_ref, &
self%f(i, j, k)
WRITE(10, "(4("//fmtReal//"))") self%vi(i)*v_ref, &
self%vj(j)*v_ref, &
self%vk(k)*v_ref, &
self%f(i, j, k)
END DO
WRITE(10, *)
@ -253,7 +253,6 @@ MODULE moduleProbe
END SUBROUTINE doProbes
SUBROUTINE outputProbes()
USE moduleCaseParam, ONLY: timeStep
IMPLICIT NONE
INTEGER:: i

View file

@ -1,6 +1,40 @@
!Contains information about output
MODULE moduleOutput
IMPLICIT NONE
! Path and folder for the output
CHARACTER(:), ALLOCATABLE:: path
CHARACTER(:), ALLOCATABLE:: folder
! Number of digits for step files
INTEGER:: iterationDigits
CHARACTER(:), ALLOCATABLE:: iterationFormat
! Triggers and counters for output
INTEGER:: triggerOutput, counterOutput = 0
INTEGER:: triggerCPUTime, counterCPUTime = 0
! logicals to activate file output
LOGICAL:: timeOutput = .FALSE.
LOGICAL:: collOutput = .FALSE.
LOGICAL:: emOutput = .FALSE.
logical:: boundaryParticleOutput = .false.
! Prefix for iteration files
character(len=*), parameter:: prefix = 'Step'
! Column separator
character(len=*), parameter:: colSep = '","'
! General format for file outputs
character(len=*), parameter:: fmtColReal = 'ES0.6E3,:,'//colSep ! Column with real
character(len=*), parameter:: fmtColInt = 'I0,:,'//colSep ! Column with integer
character(len=*), parameter:: fmtColStr = 'A,:,'//colSep ! Column with text
character(len=*), parameter:: fmtReal = 'ES14.6E3' ! Fixed size real
character(len=*), parameter:: fmtInt = 'I14' ! Fixed size real
! File IDs for different input/output
integer, parameter:: fileID_mesh = 10 ! Base id for mesh files
integer, parameter:: fileID_output = 20 ! Base id for species/collisions/EM output
integer, parameter:: fileID_boundaryParticle = 30 ! Particle boundaries
integer, parameter:: fileID_boundaryEM = 31 ! EM boundaries
integer, parameter:: fileID_reference = 40 ! Reference values
integer, parameter:: fileID_time =50 ! Computation time
!Output for each node
TYPE, PUBLIC:: outputNode
@ -33,17 +67,68 @@ MODULE moduleOutput
END TYPE
CHARACTER(:), ALLOCATABLE:: path
CHARACTER(:), ALLOCATABLE:: folder
INTEGER:: iterationDigits
CHARACTER(:), ALLOCATABLE:: iterationFormat
INTEGER:: triggerOutput, counterOutput = 0
INTEGER:: triggerCPUTime, counterCPUTime = 0
LOGICAL:: timeOutput = .FALSE.
LOGICAL:: collOutput = .FALSE.
LOGICAL:: emOutput = .FALSE.
CONTAINS
PURE FUNCTION formatFileName(pref, suff, extension, timeStep) RESULT(fileName)
IMPLICIT NONE
CHARACTER(*), INTENT(in):: pref, suff, extension
INTEGER, INTENT(in), OPTIONAL:: timeStep
CHARACTER (LEN=iterationDigits):: tString
CHARACTER(:), ALLOCATABLE:: fileName
IF (PRESENT(timeStep)) THEN
WRITE(tString, iterationFormat) timeStep
fileName = pref // '_' // tString // '_' // suff // '.' // extension
ELSE
fileName = pref // '_' // suff // '.' // extension
END IF
END FUNCTION formatFileName
subroutine createOutputFolder()
implicit none
call execute_command_line('mkdir ' // path // folder )
end subroutine createOutputFolder
subroutine copyFileToOutput(fileName)
implicit none
character(*), intent(in):: fileName
call execute_command_line('cp ' // fileName // ' ' // path // folder)
end subroutine copyFileToOutput
subroutine writeCommit()
implicit none
call system('git rev-parse HEAD > ' // path // folder // '/' // 'fpakc_commit.txt')
end subroutine writeCommit
pure function generateFilePath(filename) result(completePath)
implicit none
character(*), intent(in):: fileName
character(:), allocatable:: completePath
completePath = path // folder // '/' // fileName
end function generateFilePath
subroutine informFileCreation(filename)
implicit none
character(*), intent(in):: fileName
write(*, "(6X,A15,A)") "Creating file: ", fileName
end subroutine informFileCreation
PURE SUBROUTINE outputNode_equal_outputNode(self, from)
IMPLICIT NONE
@ -159,7 +244,7 @@ MODULE moduleOutput
END SUBROUTINE calculateOutput
SUBROUTINE printTime(first)
SUBROUTINE writeTime(first)
USE moduleSpecies
USE moduleCompTime
USE moduleCaseParam, ONLY: timeStep
@ -168,31 +253,43 @@ MODULE moduleOutput
LOGICAL, INTENT(in), OPTIONAL:: first
CHARACTER(:), ALLOCATABLE:: fileName
fileName = 'cpuTime.dat'
fileName = 'cpuTime.csv'
IF (timeOutput) THEN
IF (PRESENT(first)) THEN
IF (first) THEN
OPEN(20, file = path // folder // '/' // fileName, action = 'write')
WRITE(20, "(A1, 8X, A1, 9X, A1, 7(A20))") "#","t","n","total (s)","push (s)","reset (s)", &
"collision (s)","coulomb (s)", &
"weighting (s)","EMField (s)"
WRITE(*, "(6X,A15,A)") "Creating file: ", fileName
CLOSE(20)
OPEN(fileID_time, file = generateFilePath(fileName), action = 'write')
WRITE(fileID_time, "(*("//fmtColStr//"))") "t","n","total (s)","push (s)","reset (s)", &
"collision (s)","coulomb (s)", &
"weighting (s)","EMField (s)"
call informFileCreation(fileName)
CLOSE(fileID_time)
END IF
END IF
OPEN(20, file = path // folder // '/' // fileName, position = 'append', action = 'write')
OPEN(fileID_time, file = generateFilePath(fileName), position = 'append', action = 'write')
WRITE (20, "(I10, I10, 7(ES20.6E3))") timeStep, nPartOld, tStep, tPush, tReset, tColl, tCoul, tWeight, tEMField
WRITE (fileID_time, "(*("//fmtColInt//"),*("//fmtColReal//"))") timeStep, nPartOld, tStep, tPush, tReset, tColl, tCoul, tWeight, tEMField
CLOSE(20)
CLOSE(fileID_time)
END IF
END SUBROUTINE printTime
END SUBROUTINE writeTime
! Write file with reference values
subroutine writeReference()
use moduleRefParam
implicit none
open (fileID_reference, file=generateFilePath('reference.csv'))
write(fileID_reference, "(*("//fmtColStr//"))") '"L_ref"','"v_ref"','"ti_ref"','"Vol_ref"','"EF_ref"','"Volt_ref"','"B_ref"'
write(fileID_reference, "(*("//fmtColReal//"))") L_ref, v_ref, ti_ref, Vol_ref, EF_ref, Volt_ref, B_ref
close(fileID_reference)
end subroutine writeReference
END MODULE moduleOutput

View file

@ -4,197 +4,7 @@ MODULE moduleEM
USE moduleTable
IMPLICIT NONE
! Generic type for electromagnetic boundary conditions
TYPE, PUBLIC, ABSTRACT:: boundaryEMGeneric
INTEGER:: nNodes
TYPE(meshNodePointer), ALLOCATABLE:: nodes(:)
CONTAINS
PROCEDURE(applyEM_interface), DEFERRED, PASS:: apply
END TYPE boundaryEMGeneric
ABSTRACT INTERFACE
! Apply boundary condition to the load vector for the Poission equation
SUBROUTINE applyEM_interface(self, vectorF)
IMPORT boundaryEMGeneric
CLASS(boundaryEMGeneric), INTENT(in):: self
REAL(8), INTENT(inout):: vectorF(:)
END SUBROUTINE applyEM_interface
END INTERFACE
TYPE, EXTENDS(boundaryEMGeneric):: boundaryEMDirichlet
REAL(8):: potential
CONTAINS
! boundaryEMGeneric DEFERRED PROCEDURES
PROCEDURE, PASS:: apply => applyDirichlet
END TYPE boundaryEMDirichlet
TYPE, EXTENDS(boundaryEMGeneric):: boundaryEMDirichletTime
REAL(8):: potential
TYPE(table1D):: temporalProfile
CONTAINS
! boundaryEMGeneric DEFERRED PROCEDURES
PROCEDURE, PASS:: apply => applyDirichletTime
END TYPE boundaryEMDirichletTime
! Container for boundary conditions
TYPE:: boundaryEMCont
CLASS(boundaryEMGeneric), ALLOCATABLE:: obj
END TYPE boundaryEMCont
INTEGER:: nBoundaryEM
TYPE(boundaryEMCont), ALLOCATABLE:: boundaryEM(:)
!Information of charge and reference parameters for rho vector
REAL(8), ALLOCATABLE:: qSpecies(:)
CONTAINS
SUBROUTINE findNodes(self, physicalSurface)
USE moduleMesh
IMPLICIT NONE
CLASS(boundaryEMGeneric), INTENT(inout):: self
INTEGER, INTENT(in):: physicalSurface
CLASS(meshEdge), POINTER:: edge
INTEGER, ALLOCATABLE:: nodes(:), nodesEdge(:)
INTEGER:: nNodes, nodesNew
INTEGER:: e, n
!Temporal array to hold nodes
ALLOCATE(nodes(0))
! Loop thorugh the edges and identify those that are part of the boundary
DO e = 1, mesh%numEdges
edge => mesh%edges(e)%obj
IF (edge%physicalSurface == physicalSurface) THEN
! Edge is of the right boundary index
! Get nodes in the edge
nNodes = edge%nNodes
nodesEdge = edge%getNodes(nNodes)
! Collect all nodes that are not already in the temporal array
DO n = 1, nNodes
IF (ANY(nodes == nodesEdge(n))) THEN
! Node already in array, skip
CYCLE
ELSE
! If not, add element to array of nodes
nodes = [nodes, nodesEdge(n)]
END IF
END DO
END IF
END DO
! Point boundary to nodes
nNodes = SIZE(nodes)
ALLOCATE(self%nodes(nNodes))
self%nNodes = nNodes
DO n = 1, nNodes
self%nodes(n)%obj => mesh%nodes(nodes(n))%obj
END DO
END SUBROUTINE findNodes
! Initialize Dirichlet boundary condition
SUBROUTINE initDirichlet(self, physicalSurface, potential)
USE moduleRefParam, ONLY: Volt_ref
IMPLICIT NONE
CLASS(boundaryEMGeneric), ALLOCATABLE, INTENT(out):: self
INTEGER, INTENT(in):: physicalSurface
REAL(8), INTENT(in):: potential
! Allocate boundary edge
ALLOCATE(boundaryEMDirichlet:: self)
SELECT TYPE(self)
TYPE IS(boundaryEMDirichlet)
self%potential = potential / Volt_ref
CALL findNodes(self, physicalSurface)
END SELECT
END SUBROUTINE initDirichlet
! Initialize Dirichlet boundary condition
SUBROUTINE initDirichletTime(self, physicalSurface, potential, temporalProfile)
USE moduleRefParam, ONLY: Volt_ref, ti_ref
IMPLICIT NONE
CLASS(boundaryEMGeneric), ALLOCATABLE, INTENT(out):: self
INTEGER, INTENT(in):: physicalSurface
REAL(8), INTENT(in):: potential
CHARACTER(:), ALLOCATABLE, INTENT(in):: temporalProfile
! Allocate boundary edge
ALLOCATE(boundaryEMDirichletTime:: self)
SELECT TYPE(self)
TYPE IS(boundaryEMDirichletTime)
self%potential = potential / Volt_ref
CALL findNodes(self, physicalSurface)
CALL self%temporalProfile%init(temporalProfile)
CALL self%temporalProfile%convert(1.D0/ti_ref, 1.D0)
END SELECT
END SUBROUTINE initDirichletTime
!Apply Dirichlet boundary condition to the poisson equation
SUBROUTINE applyDirichlet(self, vectorF)
USE moduleMesh
IMPLICIT NONE
CLASS(boundaryEMDirichlet), INTENT(in):: self
REAL(8), INTENT(inout):: vectorF(:)
INTEGER:: n, ni
DO n = 1, self%nNodes
self%nodes(n)%obj%emData%phi = self%potential
vectorF(self%nodes(n)%obj%n) = self%nodes(n)%obj%emData%phi
END DO
END SUBROUTINE applyDirichlet
!Apply Dirichlet boundary condition with time temporal profile
SUBROUTINE applyDirichletTime(self, vectorF)
USE moduleMesh
USE moduleCaseParam, ONLY: timeStep, tauMin
IMPLICIT NONE
CLASS(boundaryEMDirichletTime), INTENT(in):: self
REAL(8), INTENT(inout):: vectorF(:)
REAL(8):: timeFactor
INTEGER:: n, ni
timeFactor = self%temporalProfile%get(DBLE(timeStep)*tauMin)
DO n = 1, self%nNodes
self%nodes(n)%obj%emData%phi = self%potential * timeFactor
vectorF(self%nodes(n)%obj%n) = self%nodes(n)%obj%emData%phi
END DO
END SUBROUTINE applyDirichletTime
contains
!Assemble the source vector based on the charge density to solve Poisson's equation
SUBROUTINE assembleSourceVector(vectorF, n_e)
USE moduleMesh
@ -250,8 +60,8 @@ MODULE moduleEM
!Apply boundary conditions
!$OMP SINGLE
do b = 1, nBoundaryEM
call boundaryEM(b)%obj%apply(vectorF)
do b = 1, nBoundariesEM
call boundariesEM(b)%obj%apply(vectorF)
end do
!$OMP END SINGLE
@ -311,7 +121,6 @@ MODULE moduleEM
REAL(8), INTENT(in):: phi(1:n)
REAL(8):: n_e(1:n)
REAL(8):: n_e0 = 1.0D16, phi_0 = -500.0D0, T_e = 11604.0
INTEGER:: i
n_e = n_e0 / n_ref * exp(qe * (phi*Volt_ref - phi_0) / (kb * T_e))

View file

@ -129,6 +129,7 @@ MODULE moduleSolver
SUBROUTINE initEM(self, EMType)
USE moduleEM
USE moduleErrors, only: criticalError
IMPLICIT NONE
CLASS(solverGeneric), INTENT(inout):: self
@ -141,6 +142,9 @@ MODULE moduleSolver
CASE('ElectrostaticBoltzmann')
self%solveEM => solveElecFieldBoltzmann
CASE DEFAULT
CALL criticalError('EM Solver ' // EMType // ' not found', 'readSolver')
END SELECT
END SUBROUTINE initEM
@ -531,6 +535,10 @@ MODULE moduleSolver
CALL mesh%printOutput()
IF (ASSOCIATED(meshForMCC)) CALL meshForMCC%printColl()
CALL mesh%printEM()
! Output of boundaries
call boundariesParticle_write()
WRITE(*, "(5X,A21,I10,A1,I10)") "t/tFinal: ", timeStep, "/", tFinal
WRITE(*, "(5X,A21,I10)") "Particles: ", nPartOld
IF (timeStep == 0) THEN
@ -556,7 +564,7 @@ MODULE moduleSolver
!Reset CPU Time counter
counterCPUTime = 0
CALL printTime(timeStep == 0)
CALL writeTime(timeStep == 0)
END IF

View file

@ -231,8 +231,8 @@ MODULE modulePusher
USE moduleSpecies
IMPLICIT NONE
TYPE(particle), INTENT(inout):: part
REAL(8), INTENT(in):: tauIn
TYPE(particle), INTENT(inout):: part ! NOTE: Required by interface but unused
REAL(8), INTENT(in):: tauIn ! NOTE: Required by interface but unused
END SUBROUTINE push0D