fpakc/src/modules/moduleMeshCyl.f95
Jorge Gonzalez ffb03e634b Minor improvements in performance and code clarity.
Still no solution for the reset subroutine. It is really time
consumming.
2020-10-13 18:16:18 +02:00

565 lines
16 KiB
Fortran

!moduleMeshCyl: 2D axial symmetric extension of generic mesh from GMSH format.
! x == z
! y == r
! z == theta (unused)
MODULE moduleMeshCyl
USE moduleMesh
IMPLICIT NONE
!TODO: Move this, this is horrible
REAL(8), PARAMETER:: w(1:3) = (/ 5.D0/9.D0, 8.D0/9.D0, 5.D0/9.D0 /)
REAL(8), PARAMETER:: cor(1:3) = (/ -DSQRT(3.D0/5.D0), 0.D0, DSQRT(3.D0/5.D0) /)
TYPE, PUBLIC, EXTENDS(meshNode):: meshNodeCyl
!Element coordinates
REAL(8):: r = 0.D0, z = 0.D0
CONTAINS
PROCEDURE, PASS:: init => initNodeCyl
PROCEDURE, PASS:: getCoordinates => getCoordCyl
END TYPE meshNodeCyl
TYPE, PUBLIC, ABSTRACT, EXTENDS(meshEdge):: meshEdgeCyl
!Element coordinates
REAL(8):: r(1:2) = 0.D0, z(1:2) = 0.D0
!Connectivity to nodes
CLASS(meshNode), POINTER:: n1 => NULL(), n2 => NULL()
CONTAINS
PROCEDURE, PASS:: init => initEdgeCyl
PROCEDURE(boundary_interface), PASS, DEFERRED:: fBoundary
END TYPE meshEdgeCyl
ABSTRACT INTERFACE
SUBROUTINE boundary_interface(self, part)
USE moduleSpecies
IMPORT:: meshEdgeCyl
CLASS (meshEdgeCyl), INTENT(inout):: self
CLASS (particle), INTENT(inout):: part
END SUBROUTINE
END INTERFACE
TYPE, PUBLIC, ABSTRACT, EXTENDS(meshVol):: meshVolCyl
CONTAINS
PROCEDURE, PASS:: collision => collision2DCyl
END TYPE meshVolCyl
TYPE, PUBLIC, EXTENDS(meshVolCyl):: meshVolCylQuad
!Element coordinates
REAL(8):: r(1:4) = 0.D0, z(1:4) = 0.D0
!Connectivity to nodes
CLASS(meshNode), POINTER:: n1 => NULL(), n2 => NULL(), n3 => NULL(), n4 => NULL()
!Connectivity to adjacent elements
CLASS(*), POINTER:: e1 => NULL(), e2 => NULL(), e3 => NULL(), e4 => NULL()
!Maximum collision rate
!TODO: Move to other more appropiate section
REAL(8):: phi(1:4) = 0.D0
REAL(8):: Ke(1:4,1:4) = 0.D0
REAL(8):: arNodes(1:4) = 0.D0
CONTAINS
PROCEDURE, PASS:: init => initVolQuadCyl
PROCEDURE, PASS:: locKe => localKeQuad
PROCEDURE, PASS:: detJac => detJQuad
PROCEDURE, PASS:: invJ => invJQuad
PROCEDURE, PASS:: area => areaQuad
PROCEDURE, NOPASS:: weight => weightQuad
PROCEDURE, NOPASS:: inside => insideQuad
PROCEDURE, PASS:: dVal => dValQuad
PROCEDURE, PASS:: scatter => scatterQuad
PROCEDURE, PASS:: phy2log => phy2logQuad
PROCEDURE, PASS:: findCell => findCellCylQuad
PROCEDURE, PASS:: resetOutput => resetOutputQuad
END TYPE meshVolCylQuad
CONTAINS
!Inits node element
SUBROUTINE initNodeCyl(self, n, r)
USE moduleSpecies
USE moduleRefParam
IMPLICIT NONE
CLASS(meshNodeCyl), INTENT(out):: self
INTEGER, INTENT(in):: n
REAL(8), INTENT(in):: r(1:3)
self%n = n
self%r = r(1)/L_ref
self%z = r(2)/L_ref
!Node volume, to be determined in mesh
self%v = 0.D0
!Allocates output:
ALLOCATE(self%output(1:nSpecies))
END SUBROUTINE initNodeCyl
FUNCTION getCoordCyl(self) RESULT(r)
IMPLICIT NONE
CLASS(meshNodeCyl):: self
REAL(8):: r(1:3)
r = (/self%z, self%r, 0.D0/)
END FUNCTION getCoordCyl
!Edge functions
SUBROUTINE initEdgeCyl(self, n, p, bt, physicalSurface)
IMPLICIT NONE
CLASS(meshEdgeCyl), INTENT(out):: self
INTEGER, INTENT(in):: n
INTEGER, INTENT(in):: p(:)
INTEGER, INTENT(in):: bt
INTEGER, INTENT(in):: physicalSurface
REAL(8):: r1(1:3), r2(1:3)
self%n = n
self%n1 => mesh%nodes(p(1))%obj
self%n2 => mesh%nodes(p(2))%obj
!Get element coordinates
r1 = self%n1%getCoordinates()
r2 = self%n2%getCoordinates()
self%z = (/r1(1), r2(1)/)
self%r = (/r1(2), r2(2)/)
!Normal vector
self%normal = (/ self%r(2)-self%r(1), &
self%z(2)-self%z(1), &
0.D0 /)
!Boundary index
self%bt = bt
!Phyiscal Surface
self%physicalSurface = physicalSurface
END SUBROUTINE initEdgeCyl
!Vol functions
!Quad Volume
SUBROUTINE initVolQuadCyl(self, n, p)
USE moduleRefParam
IMPLICIT NONE
CLASS(meshVolCylQuad), INTENT(out):: self
INTEGER, INTENT(in):: n
INTEGER, INTENT(in):: p(:)
REAL(8):: r1(1:3), r2(1:3), r3(1:3), r4(1:3)
self%n = n
self%n1 => mesh%nodes(p(1))%obj
self%n2 => mesh%nodes(p(2))%obj
self%n3 => mesh%nodes(p(3))%obj
self%n4 => mesh%nodes(p(4))%obj
!Get element coordinates
r1 = self%n1%getCoordinates()
r2 = self%n2%getCoordinates()
r3 = self%n3%getCoordinates()
r4 = self%n4%getCoordinates()
self%z = (/r1(1), r2(1), r3(1), r4(1)/)
self%r = (/r1(2), r2(2), r3(2), r4(2)/)
!Assign node volume
CALL self%area()
self%n1%v = self%n1%v + self%arNodes(1)
self%n2%v = self%n2%v + self%arNodes(2)
self%n3%v = self%n3%v + self%arNodes(3)
self%n4%v = self%n4%v + self%arNodes(4)
CALL self%locKe()
self%sigmaVrelMax = sigma_ref/L_ref**2
END SUBROUTINE initVolQuadCyl
FUNCTION fpsi(xi1,xi2) RESULT(psi)
IMPLICIT NONE
REAL(8),INTENT(in):: xi1,xi2
REAL(8):: psi(1:4)
psi(1) = (1.D0-xi1)*(1.D0-xi2)
psi(2) = (1.D0+xi1)*(1.D0-xi2)
psi(3) = (1.D0+xi1)*(1.D0+xi2)
psi(4) = (1.D0-xi1)*(1.D0+xi2)
psi = psi*0.25D0
END FUNCTION fpsi
!Derivative element function (xi1)
FUNCTION dpsiXi1(xi2) RESULT(psiXi1)
IMPLICIT NONE
REAL(8),INTENT(in):: xi2
REAL(8):: psiXi1(1:4)
psiXi1(1) = -(1.D0-xi2)
psiXi1(2) = (1.D0-xi2)
psiXi1(3) = (1.D0+xi2)
psiXi1(4) = -(1.D0+xi2)
psiXi1 = psiXi1*0.25D0
END FUNCTION dpsiXi1
!Derivative element function (xi2)
FUNCTION dpsiXi2(xi1) RESULT(psiXi2)
IMPLICIT NONE
REAL(8),INTENT(in):: xi1
REAL(8):: psiXi2(1:4)
psiXi2(1) = -(1.D0-xi1)
psiXi2(2) = -(1.D0+xi1)
psiXi2(3) = (1.D0+xi1)
psiXi2(4) = (1.D0-xi1)
psiXi2 = psiXi2*0.25D0
END FUNCTION dpsiXi2
!Transforms physical coordinates to element coordinates
FUNCTION phy2logQuad(this,r) RESULT(xN)
IMPLICIT NONE
CLASS(meshVolCylQuad):: this
REAL(8):: r(1:2)
REAL(8):: xN(1:2), xO(1:2), dPsi(1:2,1:4), detJ, j(1:2,1:2), psi(1:4), f(1:2)
REAL(8):: conv
!Iterative newton method to transform coordinates
conv=1.D0
xO=0.D0
DO WHILE(conv>1.D-4)
dPsi(1,:) = dpsiXi1(xO(2))
dPsi(2,:) = dpsiXi2(xO(1))
j = this%invJ(xO(1),xO(2),dPsi)
psi = fpsi(xO(1), xO(2))
f(1) = DOT_PRODUCT(psi,this%z)-r(1)
f(2) = DOT_PRODUCT(psi,this%r)-r(2)
detJ = this%detJac(xO(1),xO(2),dPsi)
xN=xO - MATMUL(j, f)/detJ
conv=MAXVAL(DABS(xN-xO),1)
xO=xN
END DO
END FUNCTION phy2logQuad
!Computes element local stiffness matrix
SUBROUTINE localKeQuad(this)
USE moduleConstParam
IMPLICIT NONE
CLASS(meshVolCylQuad):: this
REAL(8):: r, xi1, xi2, dPsi(1:2,1:4)
REAL(8):: j(1:2,1:2), detJ
INTEGER:: l, m
this%Ke=0.D0
!Start 2D Gauss Quad Integral
DO l=1, 3
DO m = 1, 3
xi1 = cor(l)
xi2 = cor(m)
dPsi(1,:) = dpsiXi1(xi2)
dPsi(2,:) = dpsiXi2(xi1)
detJ = this%detJac(xi1,xi2,dPsi)
j = this%invJ(xi1,xi2,dPsi)
r = DOT_PRODUCT(fpsi(xi1,xi2),this%r)
this%Ke = this%Ke + MATMUL(TRANSPOSE(MATMUL(j,dPsi)),MATMUL(j,dPsi))*r*w(l)*w(m)/detJ
END DO
END DO
this%Ke = this%Ke*2.D0*PI
END SUBROUTINE localKeQuad
!Computes element Jacobian determinant
FUNCTION detJQuad(this,xi1,xi2,dPsi_in) RESULT(dJ)
IMPLICIT NONE
REAL(8),OPTIONAL:: dPsi_in(1:2,1:4)
REAL(8):: dPsi(1:2,1:4)
REAL(8):: dz(1:2), dr(1:2)
REAL(8):: xi1,xi2, dJ
CLASS(meshVolCylQuad):: this
IF(PRESENT(dPsi_in)) THEN
dPsi=dPsi_in
ELSE
dPsi(1,:) = dpsiXi1(xi2)
dPsi(2,:) = dpsiXi2(xi1)
END IF
dz(1) = DOT_PRODUCT(dPsi(1,:),this%z)
dz(2) = DOT_PRODUCT(dPsi(2,:),this%z)
dr(1) = DOT_PRODUCT(dPsi(1,:),this%r)
dr(2) = DOT_PRODUCT(dPsi(2,:),this%r)
dJ = dz(1)*dr(2)-dz(2)*dr(1)
END FUNCTION detJQuad
FUNCTION invJQuad(this,xi1,xi2,dPsi_in) RESULT(j) !Computes element Jacobian inverse matrix (without determinant)
IMPLICIT NONE
REAL(8),OPTIONAL:: dpsi_in(1:2,1:4)
REAL(8):: dPsi(1:2,1:4)
REAL(8):: dz(1:2), dr(1:2)
REAL(8):: xi1,xi2, j(1:2,1:2)
CLASS(meshVolCylQuad):: this
IF(PRESENT(dPsi_in)) THEN
dPsi=dPsi_in
ELSE
dPsi(1,:) = dpsiXi1(xi2)
dPsi(2,:) = dpsiXi2(xi1)
END IF
dz(1) = DOT_PRODUCT(dPsi(1,:),this%z)
dz(2) = DOT_PRODUCT(dPsi(2,:),this%z)
dr(1) = DOT_PRODUCT(dPsi(1,:),this%r)
dr(2) = DOT_PRODUCT(dPsi(2,:),this%r)
j(1,:) = (/ dr(2), -dz(2) /)
j(2,:) = (/ -dr(1), dz(1) /)
END FUNCTION invJQuad
SUBROUTINE areaQuad(this)!Computes element area
USE moduleConstParam
IMPLICIT NONE
CLASS(meshVolCylQuad):: this
REAL(8):: r, xi1, xi2
REAL(8):: detJ, fpsi0(1:4)
this%volume = 0.D0
this%arNodes = 0.D0
!2D 1 point Gauss Quad Integral
xi1 = 0.D0
xi2 = 0.D0
detJ = this%detJac(xi1,xi2)*8.D0*PI !4*2*pi
fpsi0 = fpsi(xi1,xi2)
r = DOT_PRODUCT(fpsi0,this%r)
this%volume = r*detJ
this%arNodes = fpsi0*r*detJ
END SUBROUTINE areaQuad
FUNCTION weightQuad(xi1_p, xi2_p) RESULT(w) !Computes weights in the four vertices. '_p' references particle position in the logical space
IMPLICIT NONE
REAL(8):: xi1_p, xi2_p
REAL(8):: w(1:4)
w = fpsi(xi1_p, xi2_p)
END FUNCTION weightQuad
FUNCTION insideQuad(xi1_p, xi2_p) RESULT(ins) !Checks if a particle is inside a quad element
IMPLICIT NONE
REAL(8):: xi1_p, xi2_p
LOGICAL:: ins
ins = (xi1_p >= -1.D0 .AND. xi1_p <= 1.D0) .AND. &
(xi2_p >= -1.D0 .AND. xi2_p <= 1.D0)
END FUNCTION insideQuad
FUNCTION dValQuad(this,xi1,xi2) RESULT(EF)!Computes electric field in xi1,xi2
IMPLICIT NONE
CLASS(meshVolCylQuad):: this
REAL(8):: xi1, xi2, dPsi(1:2,1:4)
REAL(8):: j(1:2,1:2), detJ
REAL(8):: EF(1:3)
dPsi(1,:) = dpsiXi1(xi2)
dPsi(2,:) = dpsiXi2(xi1)
detJ = this%detJac(xi1,xi2,dPsi)
j = this%invJ(xi1,xi2,dPsi)
EF(1) = -DOT_PRODUCT(dPsi(1,:), this%phi)*j(2,2)/detJ
EF(2) = -DOT_PRODUCT(dPsi(2,:), this%phi)*j(1,1)/detJ
EF(3) = 0.D0
END FUNCTION dValQuad
SUBROUTINE scatterQuad(self, part)
USE moduleOutput
USE moduleSpecies
IMPLICIT NONE
CLASS(meshVolCylQuad), INTENT(in):: self
CLASS(particle), INTENT(in):: part
TYPE(outputNode), POINTER:: vertex
REAL(8):: w_p(1:4)
REAL(8):: tensorS(1:3,1:3)
w_p = self%weight(part%xLog(1), part%xLog(2))
tensorS = outerProduct(part%v, part%v)
vertex => self%n1%output(part%pt)
vertex%den = vertex%den + part%weight*w_p(1)
vertex%mom(:) = vertex%mom(:) + part%weight*w_p(1)*part%v(:)
vertex%tensorS(:,:) = vertex%tensorS(:,:) + part%weight*w_p(1)*tensorS
vertex => self%n2%output(part%pt)
vertex%den = vertex%den + part%weight*w_p(2)
vertex%mom(:) = vertex%mom(:) + part%weight*w_p(2)*part%v(:)
vertex%tensorS(:,:) = vertex%tensorS(:,:) + part%weight*w_p(2)*tensorS
vertex => self%n3%output(part%pt)
vertex%den = vertex%den + part%weight*w_p(3)
vertex%mom(:) = vertex%mom(:) + part%weight*w_p(3)*part%v(:)
vertex%tensorS(:,:) = vertex%tensorS(:,:) + part%weight*w_p(3)*tensorS
vertex => self%n4%output(part%pt)
vertex%den = vertex%den + part%weight*w_p(4)
vertex%mom(:) = vertex%mom(:) + part%weight*w_p(4)*part%v(:)
vertex%tensorS(:,:) = vertex%tensorS(:,:) + part%weight*w_p(4)*tensorS
END SUBROUTINE scatterQuad
RECURSIVE SUBROUTINE findCellCylQuad(self, part, oldCell)
USE moduleSpecies
IMPLICIT NONE
CLASS(meshVolCylQuad), INTENT(inout):: self
CLASS(meshVol), OPTIONAL, INTENT(in):: oldCell
CLASS(particle), INTENT(inout):: part
REAL(8):: xLog(1:2)
REAL(8):: xLogArray(1:4)
CLASS(*), POINTER:: nextElement
INTEGER:: nextInt
xLog = self%phy2log(part%r(1:2))
IF (self%inside(xLog(1), xLog(2))) THEN
!Checks if particle is inside of current cell
IF (PRESENT(oldCell)) THEN
!If oldCell, recalculate particle weight, as particle has entered a new cell.
part%weight = part%weight*oldCell%volume/self%volume
END IF
part%e_p = self%n
part%xLog = xLog
ELSE
!If not, searches for a neighbour and repeats the process.
xLogArray = (/ -xLog(2), xLog(1), xLog(2), -xLog(1) /)
nextInt = MAXLOC(xLogArray,1)
!Selects the higher value of directions and searches in that direction
NULLIFY(nextElement)
SELECT CASE (nextInt)
CASE (1)
nextElement => self%e1
CASE (2)
nextElement => self%e2
CASE (3)
nextElement => self%e3
CASE (4)
nextElement => self%e4
END SELECT
!Defines the next step
SELECT TYPE(nextElement)
CLASS IS(meshVolCyl)
!Particle moved to new cell, repeat find procedure
CALL nextElement%findCell(part, self)
CLASS IS (meshEdgeCyl)
!Particle encountered an edge, execute boundary
CALL nextElement%fBoundary(part)
CLASS DEFAULT
WRITE(*,*) "ERROR, CHECK findCellCylQuad"
END SELECT
END IF
END SUBROUTINE findCellCylQuad
PURE SUBROUTINE resetOutputQuad(self)
USE moduleSpecies
USE moduleOutput
IMPLICIT NONE
CLASS(meshVolCylQuad), INTENT(inout):: self
INTEGER:: k
DO k = 1, nSpecies
self%n1%output(k)%den = 0.D0
self%n1%output(k)%mom = 0.D0
self%n1%output(k)%tensorS = 0.D0
self%n2%output(k)%den = 0.D0
self%n2%output(k)%mom = 0.D0
self%n2%output(k)%tensorS = 0.D0
self%n3%output(k)%den = 0.D0
self%n3%output(k)%mom = 0.D0
self%n3%output(k)%tensorS = 0.D0
self%n4%output(k)%den = 0.D0
self%n4%output(k)%mom = 0.D0
self%n4%output(k)%tensorS = 0.D0
END DO
END SUBROUTINE resetOutputQuad
SUBROUTINE collision2DCyl(self)
USE moduleRefParam
USE moduleConstParam
USE moduleCollisions
USE moduleSpecies
USE moduleList
IMPLICIT NONE
CLASS(meshVolCyl), INTENT(inout):: self
INTEGER:: Npart !Number of particles inside the cell
REAL(8):: Fn !Specific weight
REAL(8):: Pmax !Maximum probability of collision
INTEGER:: rnd !random index
TYPE(particle), POINTER:: part_i, part_j
INTEGER:: n !collision
INTEGER:: ij, k
REAL(8):: sigmaVrelMaxNew
Fn = species(1)%obj%weight!TODO: Check how to do this for multiple species
Npart = self%listPart_in%amount
Pmax = Fn*self%sigmaVrelMax*tau/self%volume
self%nColl = INT(REAL(Npart*(Npart-1))*Pmax*0.5D0)
DO n = 1, self%NColl
!Select random numbers
rnd = 1 + FLOOR(Npart*RAND())
part_i => self%listPart_in%get(rnd)
rnd = 1 + FLOOR(Npart*RAND())
part_j => self%listPart_in%get(rnd)
ij = interactionIndex(part_i%pt, part_j%pt)
sigmaVrelMaxNew = 0.D0
DO k = 1, interactionMatrix(ij)%amount
CALL interactionMatrix(ij)%collisions(k)%obj%collide(self%sigmaVrelMax, sigmaVrelMaxNew, part_i, part_j)
END DO
!Update maximum cross section times vrelative per each collision
IF (sigmaVrelMaxNew > self%sigmaVrelMax) self%sigmaVrelMax = sigmaVrelMaxNew
END DO
!Reset output in nodes
CALL self%resetOutput()
!Erase the list of particles inside the cell
CALL self%listPart_in%erase()
END SUBROUTINE collision2DCyl
END MODULE moduleMeshCyl