vlaplex/vlaplex.f90
JGonzalez 052a4dc05e Corrections to tag v2.5
Small set of corrections to the tag v2.5.

Includes changes to python scripts to plot data.

Includes new 'fast' setup conditions that allow to output cases in an
hour or so with still a good CFL condition and grid resolution.
2025-04-08 16:32:59 +02:00

466 lines
14 KiB
Fortran

module eos
use constantParameters, only: dp
implicit none
private
public:: T_to_Z
contains
pure function T_to_Z(T) result(Z)
use constantParameters, only: eV_to_K
use referenceValues, only: Temp_ref
implicit none
real(dp), intent(in):: T
real(dp):: Z
Z = 22.5 * (Temp_ref * T / eV_to_K / 100.0)**0.6
end function T_to_Z
end module eos
program VlaPlEx
use constantParameters, only: dp, kb, qe, eps_0, ev_to_K, cm3_to_m3, PI
use output
use referenceValues
use eos, only: T_to_Z
use moduleTableBC
use moduleTableTtoZ
use moduleTableTtoZne
use omp_lib
implicit none
real(dp), parameter:: m_i = 1.9712258e-25_dp ! Tin atom mass in kg
real(dp), parameter:: gamma_i = 1.0_dp ! Adiabatic coefficient for ions
real(dp), parameter:: m_e = 9.1093837e-31_dp ! Electron mass in kg
real(dp), parameter:: gamma_e = 4.0_dp / 3.0_dp ! Adiabatic coefficient for electrons
real(dp), parameter:: gamma_e_exp = 1.0_dp /(gamma_e - 1.0_dp) ! Exponent for polytropic electrons
real(dp), parameter:: gamma_e_dexp = (2.0_dp - gamma_e)/(gamma_e - 1.0_dp) ! Exponent for polytropic db_dphi
real(dp), parameter:: n_epsilon = 1.0e-16_dp
real(dp):: r0, rf
real(dp), allocatable, dimension(:):: r
real(dp):: v0, vf
real(dp), allocatable, dimension(:):: v
real(dp):: t0, tf
real(dp):: time
real(dp):: dr, dv, dt
integer:: nr, nv, nt, nz
integer:: nzMin, nzMax
integer:: i, iz, j, t, z_inj
integer:: j0 ! First integer of positive velocity
real(dp):: Temp_bc ! Temperature
real(dp):: Zave_bc, Zave_bc_old ! Average charge state
real(dp):: u_bc ! Injection velocity
real(dp):: n_bc ! Injection density
real(dp):: c_s ! Ion sound speed
type(tableBC):: boundaryConditions
character(:), allocatable:: bc_file
type(tableTtoZ):: TtoZ
character(:), allocatable:: TtoZ_file
type(tableTtoZne):: TtoZne
character(:), allocatable:: TtoZne_file
real(dp), allocatable, dimension(:,:,:):: f_i, f_i_old
real(dp), allocatable, dimension(:):: f0 ! Boundary at r = x_0
real(dp), allocatable, dimension(:,:):: n_i
real(dp), allocatable, dimension(:):: sum_ni
real(dp), allocatable, dimension(:,:):: u_i
real(dp), allocatable, dimension(:):: E_i
real(dp), allocatable, dimension(:,:):: T_i
real(dp), allocatable, dimension(:):: n_e
real(dp), allocatable, dimension(:):: Zave
real(dp), allocatable, dimension(:):: Z_list
real(dp), allocatable, dimension(:):: diag, diag_low, diag_high
real(dp), allocatable, dimension(:,:):: A
real(dp), allocatable, dimension(:):: Res
real(dp), allocatable, dimension(:):: b
integer:: info
real(dp), allocatable, dimension(:):: phi, phi_old, E, db_dphi
real(dp):: phiConv
real(dp):: phi0
real(dp):: T_e
! real(dp):: phiF
integer:: k
real(dp), allocatable, dimension(:,:):: fCum_i
real(dp):: rCum
integer:: rCum_index
! Set number of threads
call omp_set_num_threads(16)
! Set reference numbers (in SI units)
Temp_ref = 30.0_dp * eV_to_K
n_ref = 1.0e20_dp * cm3_to_m3
t_ref = sqrt(eps_0 * m_i / (n_ref * 1.0_dp * qe**2)) ! 1.0_dp represents Z = 1 for reference values
u_ref = sqrt(kb * Temp_ref / m_i)
L_ref = u_ref * t_ref
phi_ref = kb * Temp_ref / qe
! Set input parameters (remember these have to be in non-dimensional units)
c_s = sqrt(11.0_dp * gamma_i * 1.0_dp)
bc_file = 'bc.csv'
call boundaryConditions%init(bc_file)
TtoZ_file = 'average_TtoZ_curve.csv'
call TtoZ%init(TtoZ_file)
TtoZne_file = 'Zave_values_per_Ne.csv'
call TtoZne%init(TtoZne_file)
! Set domain boundaries (non-dimensional units)
r0 = 10.0e-6_dp / L_ref
rf = 2.0e-3_dp / L_ref
dr = 1.0e-6_dp / L_ref
nr = nint((rf - r0) / dr) + 1
dr = (rf - r0) / float(nr-1)
allocate(r(1:nr))
do i = 1, nr
r(i) = dr * float(i-1) + r0
end do
! Set position to calculate cumulative sum of f (non-dimensional units)
rCum = 1.0e-3 / L_ref
! Index for cumulative sum
rCum_index = minloc(abs(r - rCum), 1)
v0 =-0.5e1_dp*c_s
vf = 1.0e1_dp*c_s
dv = 2.0e-1_dp
nv = nint((vf - v0) / dv) + 1
dv = (vf - v0) / float(nv-1)
allocate(v(1:nv))
do j = 1, nv
v(j) = dv * float(j-1) + v0
end do
! Shift v mesh so it passes by 0
v = v - (minval(abs(v)))
j0 = minloc(abs(v), 1)
if (v(j0) < 0.0_dp) then
j0 = j0 + 1
end if
t0 = 0.0_dp
tf = 2.0e-7_dp / t_ref
! tf = 1.0e1_dp * (rf - r0) / c_s
dt = 5.0e-2_dp*dr/c_s
nt = nint((tf - t0) / dt)
dt = (tf - t0) / float(nt)
everyOutput = nint(1.0e-9_dp/t_ref/dt)
if (everyOutput == 0) then
everyOutput = 1
end if
everyWrite = everyOutput/10
if (everyWrite == 0) then
everyWrite = 1
end if
write(*, '(A,ES0.4e3)') 'CFL: ', dt*vf/dr
nzMin = 3
nzMax = 14
nz = nzMax - nzMin + 1
nz = nz + 1 ! Add bin for low Z plasma
! Allocate vectors
allocate(f_i(1:nz,1:nr,1:nv), f_i_old(1:nz,1:nr,1:nv))
allocate(n_i(1:nz,1:nr))
allocate(sum_ni(1:nr))
allocate(u_i(1:nz,1:nr), E_i(1:nr), T_i(1:nz,1:nr))
allocate(Zave(1:nr))
allocate(Z_list(1:nz))
allocate(n_e(1:nr))
allocate(phi(1:nr), phi_old(1:nr), E(1:nr))
allocate(fCum_i(1:nz,1:nv))
f_i = 0.0_dp
f_i_old = 0.0_dp
n_i = 0.0_dp
sum_ni = 0.0_dp
u_i = 0.0_dp
E_i = 0.0_dp
T_i = 0.0_dp
n_e = 0.0_dp
T_e = 0.0_dp
Zave = 0.0_dp
Z_list(1) = 1.0_dp ! Low Z bin
do iz = nzMin, nzMax
Z_list(iz-nzMin+1+1) = float(iz)
end do
Zave_bc_old = 0.0_dp
phi = 0.0_dp
phi_old = 0.0_dp
E = 0.0_dp
fCum_i = 0.0_dp
! Allocate matrix for Poisson equation
allocate(diag(1:nr), diag_low(1:nr-1), diag_high(1:nr-1))
allocate(b(1:nr))
allocate(db_dphi(1:nr))
diag = 0.0_dp
diag_low = 0.0_dp
diag_high = 0.0_dp
b = 0.0_dp
db_dphi = 0.0_dp
diag = -2.0_dp / dr**2
diag_low = 1.0_dp / dr**2 - 1.0_dp / (r(2:nr) * dr)
diag_high = 1.0_dp / dr**2 + 1.0_dp / (r(1:nr-1) * dr)
diag(1) = 1.0_dp ! Dirichlet
diag_high(1) = 0.0_dp ! Dirichlet
! diag_high(1) = 2.0_dp / dr**2 ! Neumann
! diag(nr) = 1.0_dp ! Dirichlet
! diag_low(nr-1) = 0.0_dp ! Dirichlet
diag_low(nr-1) = 2.0_dp / dr**2 ! Neumann
allocate(A(1:nr,1:nr))
A = 0.0_dp
A(1,1) = diag(1)
A(1,2) = diag_high(1)
do i = 2, nr - 1
A(i, i-1) = diag_low(i-1)
A(i, i) = diag(i)
A(i, i+1) = diag_high(i)
end do
A(nr,nr-1) = diag_low(nr-1)
A(nr,nr) = diag(nr)
allocate(Res(1:nr))
Res = 0.0_dp
! Set boundary values
phi0 = 1.0e2_dp / phi_ref ! Dirichlet
phi(1) = phi0 ! Dirichlet
! phi0 = phi(1) ! Neumann
allocate(f0(j0:nv))
f0 = 0.0_dp
! Output initial values
call createPath()
call setTimeFormat(nt)
t = 0
call writeOutputRef()
! call writeOutputF(t, dt, nr, r, nv, v, f_i_old)
call writeOutputFCum(t, dt, nz, r(rCum_index), nv, v, fCum_i, Z_list)
call writeOutputPhi(t, dt, nr, r, phi, E, n_e)
call writeOutputMom(t, dt, nz, nr, r, n_i, u_i, T_i, Z_list)
call writeOutputZList(nz, Z_list)
! Main loop
do t = 1, nt
time = t * dt + t0
! Find new \bar{Z}_i based on T and density
call boundaryConditions%get(time, n_bc, u_bc, Temp_bc)
! Reset previous value
Zave_bc_old = 0.0_dp
! Initial guess based on average table
call TtoZ%get(Temp_bc, Zave_bc)
z_inj = minloc(abs(Z_list - Zave_bc),1)
Zave_bc = Z_list(z_inj)
! Start iterative process based on T, n_e table
do while (Zave_bc - Zave_bc_old > 0.1_dp)
Zave_bc_old = Zave_bc
call TtoZne%get(Temp_bc, Zave_bc * n_bc, Zave_bc)
z_inj = minloc(abs(Z_list - Zave_bc),1)
Zave_bc = Z_list(z_inj)
end do
u_bc = sqrt(Zave_bc * Temp_bc)
call writeOutputBoundary(t, dt, n_bc, u_bc, Temp_bc, Zave_bc)
! f0(j0:nv) = v(j0:nv)**2 / sqrt(PI*Temp_bc**3) * exp(-(v(j0:nv) - u_bc)**2 / Temp_bc)
f0(j0:nv) = 1.0_dp / sqrt(PI*Temp_bc) * exp(-(v(j0:nv) - u_bc)**2 / Temp_bc)
f0 = f0 * n_bc / (sum(f0)*dv)
f_i_old(:,1,j0:nv) = 0.0_dp
f_i_old(z_inj,1,j0:nv) = f0
f_i(:,1,j0:nv) = f_i_old(:,1,j0:nv)
T_e = Temp_bc
! r = rf, v<0
f_i_old(:,nr,1:j0-1) = 0.0_dp
f_i(:,nr,1:j0-1) = f_i_old(:,nr,1:j0-1)
! set edge velocities to 0
f_i_old(:,:,1) = 0.0_dp
f_i_old(:,:,nv) = 0.0_dp
sum_ni = 0.0_dp
! Advect in the r direction
do iz = 1, nz
if (all(n_i(iz,:) < n_epsilon) .and. iz .ne. z_inj) then
cycle
end if
!$omp parallel do
do i = 1, nr
! Advect negative velocity
if (i < nr) then
f_i(iz,i,1:j0-1) = f_i_old(iz,i,1:j0-1) - v(1:j0-1)*dt/dr/r(i)**2*(r(i+1)**2*f_i_old(iz,i+1,1:j0-1) - &
r(i )**2*f_i_old(iz,i ,1:j0-1))
end if
! Advect positive velocity
if (i > 1) then
f_i(iz,i,j0:nv) = f_i_old(iz,i, j0:nv) - v( j0:nv)*dt/dr/r(i)**2*(r(i )**2*f_i_old(iz,i , j0:nv) - &
r(i-1)**2*f_i_old(iz,i-1, j0:nv))
end if
n_i(iz,i) = sum(f_i(iz,i,:))*dv
if (n_i(iz,i) > n_epsilon) then
u_i(iz,i) = sum(v(:) *f_i(iz,i,:))*dv / n_i(iz,i)
E_i(i) = sum(v(:)**2*f_i(iz,i,:))*dv / n_i(iz,i)
T_i(iz,i) = 2.0_dp*E_i(i) - 2.0_dp*u_i(iz,i)**2
else
f_i(iz,i,:) = 0.0_dp
n_i(iz,i) = 0.0_dp
u_i(iz,i) = 0.0_dp
T_i(iz,i) = 0.0_dp
end if
end do
!$omp end parallel do
sum_ni = sum_ni + Z_list(iz) * n_i(iz,:)
end do
! Assume quasi-neutrality to start iterating
n_e = sum_ni
db_dphi = 0.0_dp
! Solve Poission (maximum number of iterations, break if convergence is reached before)
do k = 1, 2000
! Store previous value
phi_old = phi
diag = -2.0_dp / dr**2 - db_dphi
diag_low = 1.0_dp / dr**2 - 1.0_dp / (r(2:nr) * dr)
diag_high = 1.0_dp / dr**2 + 1.0_dp / (r(1:nr-1) * dr)
diag(1) = 1.0_dp ! Dirichlet
diag_high(1) = 0.0_dp ! Dirichlet
! diag(nr) = 1.0_dp ! Dirichlet
! diag_low(nr-1) = 0.0_dp ! Dirichlet
diag_low(nr-1) = 2.0_dp / dr**2 - db_dphi(nr) ! Neumann
! Calculate charge density
b = - (sum_ni - n_e)
! Apply boundary conditions
b(1) = phi0 ! Dirichlet
! b(nr) = 0.0_dp ! Dirichlet
! Calculate residual
!$omp parallel workshare
Res = -(MATMUL(A, phi_old) - b)
!$omp end parallel workshare
! Iterate system
call dgtsv(nr, 1, diag_low, diag, diag_high, Res, nr, info)
phi = phi_old + Res
! phi0=phi(1) ! Neumann
! Calculate distribution of electrons
! n_e = sum_ni(1) * exp((phi- phi0) / T_e) ! Isothermal (Boltzmann)
n_e = sum_ni(1) * (1.0_dp + (gamma_e - 1.0_dp)/gamma_e*(phi-phi0)/T_e)**gamma_e_exp !Polytropic
! Diagonal matrix for Newton integration scheme
! db_dphi = n_e / T_e ! Isotropic
db_dphi = sum_ni(1) / (gamma_e * T_e) * &
(1.0_dp + (gamma_e - 1.0_dp)/gamma_e*(phi-phi0)/T_e)**gamma_e_dexp !Polytropic
! Check if the solution has converged
phiConv = maxval(abs(Res),1)
if (phiConv < 1.0e-6_dp) then
exit
end if
! ! Calculate new potential to ensure 0 current at the edge
! if (n_i(nr) > n_epsilon) then
! phiF = phi0 + T_e * log((2.0_dp*sqrt(pi)*Zave(nr)*n_i(nr)*u_i(nr)) / (Zave(1)*n_i(1)*sqrt(m_i*T_e/m_e)))
!
! else
! phiF = phi(nr-5)
!
! end if
end do
! Calculate electric field
E(1) = - (phi(2) - phi(1)) / dr ! Dirichlet
! E(1) = 0.0_dp ! Neumann
!$omp parallel do
do i = 2, nr-1
E(i) = - 0.5_dp*(phi(i+1) - phi(i-1)) / dr
end do
!$omp end parallel do
! E(nr) = - (phi(nr) - phi(nr-1)) / dr ! Dirichlet
E(nr) = 0.0_dp ! Neumann
! Update intermediate f
f_i_old = f_i
do iz = 1, nz
if (all(n_i(iz,:) < n_epsilon) .and. iz .ne. z_inj) then
cycle
end if
! Advect in the v direction
! i = 1, v<0
i = 1
if (E(i) >= 0.0_dp) then
f_i(iz,i,2:j0-2) = f_i_old(iz,i,2:j0-2) - Z_list(iz)*E(i)*dt/dv*(f_i_old(iz,i,2:j0-2) - f_i_old(iz,i,1:j0-3))
else
f_i(iz,i,2:j0-2) = f_i_old(iz,i,2:j0-2) - Z_list(iz)*E(i)*dt/dv*(f_i_old(iz,i,3:j0-1) - f_i_old(iz,i,2:j0-2))
end if
! i = 2, nr-1; all v
!$omp parallel do
do i = 2, nr-1
if (E(i) >= 0.0_dp) then
f_i(iz,i,2:nv-1) = f_i_old(iz,i,2:nv-1) - Z_list(iz)*E(i)*dt/dv*(f_i_old(iz,i,2:nv-1) - f_i_old(iz,i,1:nv-2))
else
f_i(iz,i,2:nv-1) = f_i_old(iz,i,2:nv-1) - Z_list(iz)*E(i)*dt/dv*(f_i_old(iz,i,3:nv) - f_i_old(iz,i,2:nv-1))
end if
end do
!$omp end parallel do
! i = nr, v>=0
i = nr
if (E(i) >= 0.0_dp) then
f_i(iz,i,j0+1:nv-1) = f_i_old(iz,i,j0+1:nv-1) - Z_list(iz)*E(i)*dt/dv*(f_i_old(iz,i,j0+1:nv-1) - f_i_old(iz,i,j0:nv-2))
else
f_i(iz,i,j0+1:nv-1) = f_i_old(iz,i,j0+1:nv-1) - Z_list(iz)*E(i)*dt/dv*(f_i_old(iz,i,j0+2:nv) - f_i_old(iz,i,j0+1:nv-1))
end if
end do
! Reset values for next iteration
f_i_old = f_i
do iz = 1, nz
if (all(n_i(iz,:) < n_epsilon) .and. iz .ne. z_inj) then
cycle
end if
fCum_i(iz,:) = fCum_i(iz,:) + f_i_old(iz,rCum_index,:)
end do
! Write output
if (mod(t,everyOutput) == 0 .or. t == nt) then
! call writeOutputF(t, dt, nz, nr, r, nv, v, f_i_old, Z_list)
call writeOutputPhi(t, dt, nr, r, phi, E, n_e)
call writeOutputMom(t, dt, nz, nr, r, n_i, u_i, T_i, Z_list)
call writeOutputFCum(t, dt, nz, r(rCum_index), nv, v, fCum_i, Z_list)
end if
! Write progress
if (mod(t,everyWrite) == 0) then
write (*, '(I10, A, I10)' ) t, '/', nt
write (*, '(A, ES0.4e3,","ES0.4e3)') 'phi max,min: ', maxval(phi)*phi_ref, minval(phi)*phi_ref
end if
end do
end program VlaPlEx