!>
!> @file pde1dp_cmpl_pardiso.f90
!>
!> @brief
!>
!> @copyright
!> Copyright (©) 2021 EPFL (Ecole Polytechnique Fédérale de Lausanne)
!> SPC (Swiss Plasma Center)
!>
!> spclibs is free software: you can redistribute it and/or modify it under
!> the terms of the GNU Lesser General Public License as published by the Free
!> Software Foundation, either version 3 of the License, or (at your option)
!> any later version.
!>
!> spclibs is distributed in the hope that it will be useful, but WITHOUT ANY
!> WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
!> FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
!>
!> You should have received a copy of the GNU Lesser General Public License
!> along with this program. If not, see .
!>
!> @authors
!> (in alphabetical order)
!> @author Trach-Minh Tran
!>
MODULE pde1dp_cmpl_pardiso_mod
USE bsplines
USE pardiso_bsplines
IMPLICIT NONE
!
CONTAINS
SUBROUTINE disrhs(spl, rhs, mmode, alpha, beta)
!
TYPE(spline1d) :: spl
INTEGER, INTENT(in) :: mmode
DOUBLE COMPLEX, INTENT(in) :: alpha, beta
DOUBLE COMPLEX, INTENT(out) :: rhs(:)
!
INTEGER :: dim, nrank, nx, nidbas, ngauss
INTEGER :: i, igauss, it, irow
DOUBLE PRECISION, ALLOCATABLE :: fun(:,:)
DOUBLE PRECISION, ALLOCATABLE :: xgauss(:), wgauss(:)
DOUBLE COMPLEX :: contrib
!===========================================================================
! 1.0 Prologue
!
! Properties of spline space
!
nrank = SIZE(rhs)
CALL get_dim(spl, dim, nx, nidbas)
!
ALLOCATE(fun(0:nidbas,1)) ! needs only basis functions (no derivatives)
!
! Gauss quadature
!
CALL get_gauss(spl, ngauss)
ALLOCATE(xgauss(ngauss), wgauss(ngauss))
!===========================================================================
! 2.0 Assembly loop
!
rhs(:) = 0.0d0
!
DO i=1,nx
CALL get_gauss(spl, ngauss, i, xgauss, wgauss)
DO igauss=1,ngauss
CALL basfun(xgauss(igauss), spl, fun, i)
contrib = wgauss(igauss) * rhseq(xgauss(igauss))
DO it=0,nidbas
irow=MODULO(i+it-1,nx) + 1 ! Periodic BC
rhs(irow) = rhs(irow) + contrib*fun(it,1)
END DO
END DO
END DO
!===========================================================================
! 9.0 Epilogue
!
DEALLOCATE(fun)
DEALLOCATE(xgauss, wgauss)
CONTAINS
DOUBLE COMPLEX FUNCTION rhseq(x)
DOUBLE PRECISION, INTENT(in) :: x
DOUBLE PRECISION :: arg
arg = mmode*x
rhseq = (mmode**2*alpha-beta)*COS(arg)
END FUNCTION rhseq
END SUBROUTINE disrhs
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SUBROUTINE dismat(spl, mat, alpha, beta)
!
TYPE(spline1d) :: spl
TYPE(zpardiso_mat) :: mat
DOUBLE COMPLEX, INTENT(in) :: alpha, beta
!
INTEGER :: dim, nx, nidbas, ngauss
INTEGER :: i, igauss, iterm, iw, jt, irow, jcol
DOUBLE PRECISION, ALLOCATABLE :: fun(:,:)
DOUBLE PRECISION, ALLOCATABLE :: xgauss(:), wgauss(:)
DOUBLE COMPLEX :: contrib
!
INTEGER :: kterms ! Number of terms in weak form
INTEGER, ALLOCATABLE :: idert(:), iderw(:) ! Derivative order
DOUBLE COMPLEX, ALLOCATABLE :: coefs(:)
!===========================================================================
! 1.0 Prologue
!
! Properties of spline space
!
CALL get_dim(spl, dim, nx, nidbas)
ALLOCATE(fun(0:nidbas,0:1)) ! Spline and its 1st derivative
!
! Weak form
!
kterms = mat%nterms
ALLOCATE(idert(kterms), iderw(kterms), coefs(kterms))
!
! Gauss quadature
!
CALL get_gauss(spl, ngauss)
ALLOCATE(xgauss(ngauss), wgauss(ngauss))
!===========================================================================
! 2.0 Assembly loop
!
DO i=1,nx
CALL get_gauss(spl, ngauss, i, xgauss, wgauss)
DO igauss=1,ngauss
CALL basfun(xgauss(igauss), spl, fun, i)
CALL coefeq(xgauss(igauss), idert, iderw, coefs)
DO iterm=1,kterms
DO jt=0,nidbas
DO iw=0,nidbas
contrib = fun(jt,idert(iterm)) * coefs(iterm) * &
& fun(iw,iderw(iterm)) * wgauss(igauss)
irow=MODULO(i+iw-1,nx) + 1 ! Periodic BC
jcol=MODULO(i+jt-1,nx) + 1
CALL updtmat(mat, irow, jcol, contrib)
END DO
END DO
END DO
END DO
END DO
!===========================================================================
! 9.0 Epilogue
!
DEALLOCATE(fun)
DEALLOCATE(xgauss, wgauss)
DEALLOCATE(iderw, idert, coefs)
!
CONTAINS
SUBROUTINE coefeq(x, idt, idw, c)
DOUBLE PRECISION, INTENT(in) :: x
INTEGER, INTENT(out) :: idt(:), idw(:)
DOUBLE COMPLEX, INTENT(out) :: c(SIZE(idt))
!
c(1) = alpha
idt(1) = 1
idw(1) = 1
!
c(2) = -beta
idt(2) = 0
idw(2) = 0
END SUBROUTINE coefeq
END SUBROUTINE dismat
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
FUNCTION norm2(x)
!
! Compute the 2-norm of complex array x
!
IMPLICIT NONE
DOUBLE PRECISION :: norm2
DOUBLE COMPLEX, INTENT(in) :: x(:)
DOUBLE PRECISION :: sum2
!
sum2 = DOT_PRODUCT(x,x)
norm2 = SQRT(sum2)
END FUNCTION norm2
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SUBROUTINE meshdist(mmode, x)
!
! Construct a 1d non-equidistant mesh given a
! mesh distribution function.
!
INTEGER, INTENT(in) :: mmode
DOUBLE PRECISION, INTENT(inout) :: x(0:)
INTEGER :: nx, nintg
DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: xint, fint
DOUBLE PRECISION :: a, b, dx, f0, f1, scal
INTEGER :: i, k
!
nx = SIZE(x)-1
a=x(0)
b=x(nx)
nintg = 10*nx
ALLOCATE(xint(0:nintg), fint(0:nintg))
!
! Mesh distribution
!
dx = (b-a)/REAL(nintg)
xint(0) = a
fint(0) = 0.0d0
f1 = fdist(xint(0))
DO i=1,nintg
f0 = f1
xint(i) = xint(i-1) + dx
f1 = fdist(xint(i))
fint(i) = fint(i-1) + 0.5*(f0+f1)
END DO
!
! Normalization
!
scal = REAL(nx) / fint(nintg)
fint(0:nintg) = fint(0:nintg) * scal
!
! Obtain mesh point by (inverse) interpolation
!
k = 1
DO i=1,nintg-1
IF( fint(i) .GE. REAL(k) ) THEN
x(k) = xint(i) + (xint(i+1)-xint(i))/(fint(i+1)-fint(i)) * &
& (k-fint(i))
k = k+1
END IF
END DO
!
DEALLOCATE(xint, fint)
CONTAINS
DOUBLE PRECISION FUNCTION fdist(x)
DOUBLE PRECISION, INTENT(in) :: x
fdist = 2.0 + COS(mmode*x)
END FUNCTION fdist
END SUBROUTINE meshdist
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
END MODULE pde1dp_cmpl_pardiso_mod
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
!
PROGRAM main
!
! 1D complex PDE with periodic BC
!
USE pde1dp_cmpl_pardiso_mod
USE futils
USE conmat_mod
!
IMPLICIT NONE
TYPE(spline1d) :: splx
TYPE(zpardiso_mat) :: mat
TYPE(zpardiso_mat) :: newmat
INTEGER :: kl, ku, nrank
!
CHARACTER(len=128) :: file='pde1dp_cmpl_pardiso.h5'
INTEGER :: fid
INTEGER :: nx, nidbas, ngauss, mmode, npt, dim
LOGICAL :: nlequid
LOGICAL :: nlsym, nlherm, nlpos
DOUBLE PRECISION, PARAMETER :: pi=3.1415926535897931d0
DOUBLE PRECISION :: dx
DOUBLE COMPLEX :: alpha, beta
DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: xgrid, x, err
DOUBLE COMPLEX, DIMENSION(:), ALLOCATABLE :: sol, rhs, bcoef
DOUBLE COMPLEX, DIMENSION(:), ALLOCATABLE :: newsol, arow
DOUBLE COMPLEX, DIMENSION(:), ALLOCATABLE :: solcal, solana
DOUBLE PRECISION, ALLOCATABLE :: xgauss(:), wgauss(:)
DOUBLE PRECISION :: err_norm
INTEGER :: i
!
NAMELIST /newrun/ nx, nidbas, ngauss, nlequid, alpha, beta, mmode, npt, &
& nlsym, nlherm, nlpos
!===========================================================================
! 1.0 Prologue
!
! Read in data specific to run
!
nx = 8 ! Number oh intevals in x
nidbas = 3 ! Degree of splines
ngauss = 4 ! Number of Gauss points/interval
nlequid = .TRUE. ! Use exact sol. as mesh dist. function if .FALSE.
mmode = 1 ! Fourier mode
alpha = (1.0, 1.0) ! Complex "diffusion"
beta = 1.0
npt = 100
nlsym = .TRUE. ! Is matrice symmetric
nlherm = .FALSE. ! Is matrice hermitian
nlpos = .TRUE. ! and positive definite ?
!
READ(*,newrun)
WRITE(*,newrun)
!
! Define grid on x axis
!
ALLOCATE(xgrid(0:nx))
dx = 2.d0*pi/REAL(nx,8)
xgrid = (/ (i*dx,i=0,nx) /)
IF( .NOT. nlequid ) THEN
CALL meshdist(mmode, xgrid)
END IF
WRITE(*,'(a/(10f8.3))') 'xgrid', xgrid
!
! Create hdf5 file
!
CALL creatf(file, fid, 'PDE1DP Result File', real_prec='d')
CALL attach(fid, '/', 'NX', nx)
CALL attach(fid, '/', 'NIDBAS', nidbas)
CALL putarr(fid, '/xgrid', xgrid)
!===========================================================================
! 2.0 Discretize the PDE
!
! Set up periodic spline
!
CALL set_spline(nidbas, ngauss, xgrid, splx, period=.TRUE.)
WRITE(*,'(a,l6)') 'nlequid =', nlequid
nrank = nx ! Rank of the FE matrix
!
! FE matrix assembly
!
CALL init(nrank, 2, mat, nlsym=nlsym, nlherm=nlherm, nlpos=nlpos)
CALL get_dim(splx, dim)
WRITE(*,'(/a,4i6)') 'nrank, dim', nrank, dim
!!$ CALL dismat(splx, mat, alpha, beta)
CALL conmat(splx, mat, coefeq)
!
! RHS assembly
!
ALLOCATE(rhs(nrank), sol(nrank))
CALL disrhs(splx, rhs, mmode, alpha, beta)
!
CALL putarr(fid, '/rhs', rhs)
!
! Factor and solve
!
WRITE(*,'(a/(10i6))') 'iparm', mat%p%iparm
CALL factor(mat)
CALL putmat(fid,'/MAT', mat)
CALL bsolve(mat, rhs, sol)
CALL putarr(fid, '/sol', sol)
WRITE(*,'(/a,i8)') 'Number of non-zeros of matrix = ',get_count(mat)
WRITE(*,'(a,i8)') 'Number of nonzeros in factors of A = ',mat%p%iparm(18)
!
! Compute residue
!
WRITE(*,'(a,1pe12.4)') 'Residue =', norm2(vmx(mat,sol)-rhs)
!===========================================================================
! 3.0 Check solution
!
! Exact solution
ALLOCATE(x(0:npt), solcal(0:npt), solana(0:npt), err(0:npt))
dx=2.0d0*pi/REAL(npt,8)
x = (/ (i*dx, i=0,npt) /)
solana = COS(mmode*x)
!
! Prolongate solution using periodicity
!
ALLOCATE(bcoef(dim))
bcoef(1:nrank) = sol(1:nrank)
DO i=nrank+1,dim
bcoef(i) = bcoef(MODULO(i-1,nrank)+1)
END DO
!
! Interpolate field
!
CALL gridval(splx, x, solcal, 0, bcoef)
!
err = ABS(solcal-solana)
CALL putarr(fid, '/x', x)
CALL putarr(fid, '/solana', solana)
CALL putarr(fid, '/solcal', solcal)
CALL putarr(fid, '/err', err)
!
! Compute discretization error norm by Gauss integration
!
err_norm=0.0
ALLOCATE(xgauss(ngauss), wgauss(ngauss))
DO i=1,nx
CALL get_gauss(splx, ngauss, i, xgauss, wgauss)
CALL gridval(splx, xgauss, solcal(1:ngauss), 0)
solana(1:ngauss) = COS(mmode*xgauss)
err(1:ngauss) = DOT_PRODUCT(solana(1:ngauss)-solcal(1:ngauss), &
& solana(1:ngauss)-solcal(1:ngauss))
err_norm = err_norm + SUM(wgauss*err(1:ngauss))
END DO
err_norm = SQRT(err_norm)
WRITE(*,'(a,1pe12.3)') 'Discretization error ', err_norm
!
!===========================================================================
! 4.0 Test of getrow/putrow, getcol/putcol and mcopy
!
CALL init(nrank, 2, newmat, nlsym=nlsym, nlherm=nlherm, nlpos=nlpos)
ALLOCATE(arow(nrank), newsol(nrank))
!
DO i=1,nrank
CALL getrow(mat, i, arow)
CALL putrow(newmat, i, arow)
END DO
CALL factor(newmat)
CALL bsolve(newmat, rhs, newsol)
WRITE(*,'(/a)') 'putrow/getrow ...'
WRITE(*,'(a,1pe12.4)') 'Residue =', norm2(vmx(newmat,newsol)-rhs)
WRITE(*,'(a,1pe12.3)') 'Error ', norm2(sol-newsol)
!
DO i=1,nrank
CALL getcol(mat, i, arow)
CALL putcol(newmat, i, arow)
END DO
CALL factor(newmat)
CALL bsolve(newmat, rhs, newsol)
WRITE(*,'(/a)') 'putcol/getcol ...'
WRITE(*,'(a,1pe12.4)') 'Residue =', norm2(vmx(newmat,newsol)-rhs)
WRITE(*,'(a,1pe12.3)') 'Error ', norm2(sol-newsol)
!
CALL clear_mat(newmat)
CALL mcopy(mat, newmat)
WRITE(*,'(/a)') 'mcopy ...'
newmat%val = (1000.0d0,0.0d0)*newmat%val
CALL factor(newmat)
CALL bsolve(newmat, rhs, newsol)
WRITE(*,'(a)') 'Backsolve the new system'
WRITE(*,'(a,1pe12.4)') 'Norm =', norm2(newsol)
!
WRITE(*,'(a)') 'Destroy NEWMAT ...'
CALL destroy(newmat)
!
CALL bsolve(mat, rhs, sol)
WRITE(*,'(/a)') 'Backsolve the old system'
WRITE(*,'(a,1pe12.4)') 'Norm =', norm2(sol)
!
WRITE(*,'(a)') 'Destroy MAT ...'
CALL destroy(mat)
!!$!
!!$ WRITE(*,'(/a)') 'Should crash since NEWMAT is gone!'
!!$ CALL bsolve(newmat, rhs, newsol)
!!$ WRITE(*,'(a)') 'Backsolve the new system'
!!$ WRITE(*,'(a,1pe12.4)') 'Norm =', norm2(newsol)
!===========================================================================
! 9.0 Clean up
!
DEALLOCATE(x, solcal, solana, err)
DEALLOCATE(xgauss, wgauss)
DEALLOCATE(bcoef)
DEALLOCATE(xgrid)
DEALLOCATE(rhs, sol)
DEALLOCATE(arow, newsol)
CALL destroy_sp(splx)
CALL closef(fid)
CONTAINS
SUBROUTINE coefeq(x, idt, idw, c)
DOUBLE PRECISION, INTENT(in) :: x
INTEGER, INTENT(out) :: idt(:), idw(:)
DOUBLE COMPLEX, INTENT(out) :: c(:)
!
c(1) = alpha
idt(1) = 1
idw(1) = 1
!
c(2) = -beta
idt(2) = 0
idw(2) = 0
END SUBROUTINE coefeq
END PROGRAM main