!>
!> @file pde2d_sym_pardiso_dft.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 .
!>
!> @author
!> (in alphabetical order)
!> @author Trach-Minh Tran
!>
!
! Solving the 2d PDE using splines and PARDISO symmetric matrix.
! The periodic coordinate y is discrete Fourier transformed.
!
! -d/dx[x C d/dx]f - 1x/d/dy[Cd/dy] f = \rho, with f(x=1,y) = 0
! C(x,y) = 1 + \epsilon x cos(y)
! exact solution: f(x,y) = (1-x^2) x^m cos(my)
!
MODULE pde2d_sym_pardiso_dft_mod
USE bsplines
USE pardiso_bsplines
IMPLICIT NONE
!
CONTAINS
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SUBROUTINE dismat(spl, epsi, mat)
!
! Assembly of FE matrix mat using spline spl
!
TYPE(spline2d), INTENT(in) :: spl
DOUBLE PRECISION, INTENT(in) :: epsi
TYPE(zpardiso_mat), INTENT(inout) :: mat
!
INTEGER :: n1, nidbas1, ndim1, ng1
INTEGER :: n2, nidbas2, ndim2, ng2
INTEGER :: kmin, kmax, dk
INTEGER :: i, j, ig1, ig2, kc
INTEGER :: iterm, iw1, mw, igw1, it1, mt, igt1, irow, jcol
DOUBLE PRECISION, ALLOCATABLE :: xg1(:), wg1(:), fun1(:,:,:)
DOUBLE PRECISION, ALLOCATABLE :: xg2(:), wg2(:)
DOUBLE COMPLEX, ALLOCATABLE :: ft_fun2(:,:,:), fft_temp(:)
DOUBLE COMPLEX :: contrib
!
INTEGER :: kterms ! Number of terms in weak form
INTEGER :: kcoupl ! Number of mode couplings
INTEGER, ALLOCATABLE :: idert(:,:,:,:), iderw(:,:,:,:) ! Derivative order
DOUBLE COMPLEX, ALLOCATABLE :: coefs(:,:,:,:) ! Terms in weak form
INTEGER, ALLOCATABLE :: left1(:), left2(:)
!===========================================================================
! 1.0 Prologue
!
! Properties of spline space
!
CALL get_dim(spl%sp1, ndim1, n1, nidbas1)
CALL get_dim(spl%sp2, ndim2, n2, nidbas2)
kmin = spl%sp2%dft%kmin
kmax = spl%sp2%dft%kmax
dk = spl%sp2%dft%dk
WRITE(*,'(/a, 5i6)') 'n1, nidbas1, ndim1 =', n1, nidbas1, ndim1
WRITE(*,'(a, 5i6)') 'n2, nidbas2, ndim2 =', n2, nidbas2, ndim2
WRITE(*,'(a, 5i6)') 'kmin, kmax, dk =', kmin, kmax, dk
!
! Gauss quadature
!
CALL get_gauss(spl%sp1, ng1)
CALL get_gauss(spl%sp2, ng2)
ALLOCATE(xg1(ng1), wg1(ng1))
ALLOCATE(xg2(ng2), wg2(ng2))
!
! Weak form
!
kterms = mat%nterms
kcoupl = SIZE(spl%sp2%dft%mode_couplings)
ALLOCATE(idert(kterms,2,ng1,ng2), iderw(kterms,2,ng1,ng2))
ALLOCATE(coefs(kterms,kcoupl,ng1,ng2))
!
! Splines and derivatives at all Gauss points
!
ALLOCATE(fun1(0:nidbas1,0:1,ng1)) ! Spline and 1st derivative
ALLOCATE(ft_fun2(kmin:kmax,0:1,ng2)) ! DFT of splines and 1st derivative
ALLOCATE(fft_temp(0:n2-1)) ! Used in coefeq
!===========================================================================
! 2.0 Assembly loop
!
ALLOCATE(left1(ng1))
ALLOCATE(left2(ng2))
!
! First interval in 2nd (periodic) coordinate
!
j = 1
CALL get_gauss(spl%sp2, ng2, 1, xg2, wg2)
left2 = j
CALL ft_basfun(xg2, spl%sp2, ft_fun2, left2)
DO i=1,n1
CALL get_gauss(spl%sp1, ng1, i, xg1, wg1)
left1 = i
CALL basfun(xg1, spl%sp1, fun1, left1)
DO ig1=1,ng1
DO ig2=1,ng2
CALL coefeq(xg1(ig1), xg2(ig2), &
& idert(:,:,ig1,ig2), &
& iderw(:,:,ig1,ig2), &
& coefs(:,:,ig1,ig2))
END DO
END DO
!
DO iw1=0,nidbas1 ! Weight function in dir 1
igw1 = i+iw1
DO it1=0,nidbas1 ! Test function in dir 1
igt1 = i+it1
DO mt=kmin,kmax ! Test Fourier mode
DO kc=1,kcoupl
mw = mt + spl%sp2%dft%mode_couplings(kc)
IF(mw.LT.kmin .OR. mw.GT.kmax) CYCLE
!-------------
contrib = (0.0d0, 0.0d0)
DO ig1=1,ng1
DO ig2=1,ng2
DO iterm=1,kterms
contrib = contrib + &
& fun1(iw1,iderw(iterm,1,ig1,ig2),ig1) * &
& ft_fun2(mw,iderw(iterm,2,ig1,ig2),ig2) * &
& coefs(iterm,kc,ig1,ig2) * &
& CONJG(ft_fun2(mt,idert(iterm,2,ig1,ig2),ig2)) * &
& fun1(it1,idert(iterm,1,ig1,ig2),ig1) * &
& wg1(ig1) * wg2(ig2) /REAL(n2,8)
END DO
END DO
END DO
irow = (igw1-1)*dk + (mw-kmin)+1 ! Number first mode m then radial coord.
jcol = (igt1-1)*dk + (mt-kmin)+1
CALL updtmat(mat, irow, jcol, contrib)
!-------------
END DO
END DO
END DO
END DO
END DO
!===========================================================================
! 9.0 Epilogue
!
DEALLOCATE(xg1, wg1, fun1)
DEALLOCATE(xg2, wg2, ft_fun2)
DEALLOCATE(idert, iderw, coefs)
DEALLOCATE(left1,left2)
DEALLOCATE(fft_temp)
!
CONTAINS
SUBROUTINE coefeq(x, y, idt, idw, c)
USE fft
DOUBLE PRECISION, INTENT(in) :: x, y
INTEGER, INTENT(out) :: idt(:,:), idw(:,:)
DOUBLE COMPLEX, INTENT(out) :: c(:,:)
!
DOUBLE PRECISION :: zcoef, dy
INTEGER :: j, k, kc, kp
!
! Weak form = Int(x*C*dw/dx*dt/dx + C/x*dw/dy*dt/dy)dxdy
! C(x,y) = 1 + epsilon*x*cos(y)
!
dy = spl%sp2%dft%dx
kc = SIZE(spl%sp2%dft%mode_couplings)
DO j=0,n2-1
fft_temp(j) = 1.0d0+epsi*x*COS(y+j*dy)
END DO
CALL fourcol(fft_temp,1)
DO k=1,kc
kp = spl%sp2%dft%mode_couplings(k)
IF(kp.LT.0) kp=kp+n2
c(1,k) = x*fft_temp(kp)
c(2,k) = fft_temp(kp)/x
END DO
!!$ WRITE(*,'(a/(10(1pe12.4)))') 'fft_temp', ABS(fft_temp)
!!$ WRITE(*,'(a/(10(1pe12.4)))') 'c1', ABS(c(1,:))
!!$ WRITE(*,'(a/(10(1pe12.4)))') 'c2', ABS(c(2,:))
!
idt(1,1) = 1
idt(1,2) = 0
idw(1,1) = 1
idw(1,2) = 0
!
idt(2,1) = 0
idt(2,2) = 1
idw(2,1) = 0
idw(2,2) = 1
END SUBROUTINE coefeq
END SUBROUTINE dismat
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SUBROUTINE disrhs(mbess, epsi, spl, rhs)
!
! Assembly the RHS using 2d spline spl
!
INTEGER, INTENT(in) :: mbess
DOUBLE PRECISION, INTENT(in) :: epsi
TYPE(spline2d), INTENT(in) :: spl
DOUBLE PRECISION, INTENT(out) :: rhs(:)
!
INTEGER :: n1, nidbas1, ndim1, ng1
INTEGER :: n2, nidbas2, ndim2, ng2
INTEGER :: i, j, ig1, ig2, k1, k2, i1, j2, ij, nrank
DOUBLE PRECISION, ALLOCATABLE :: xg1(:), wg1(:), fun1(:,:)
DOUBLE PRECISION, ALLOCATABLE :: xg2(:), wg2(:), fun2(:,:)
DOUBLE PRECISION:: contrib
!===========================================================================
! 1.0 Prologue
!
! Properties of spline space
!
CALL get_dim(spl%sp1, ndim1, n1, nidbas1)
CALL get_dim(spl%sp2, ndim2, n2, nidbas2)
!
ALLOCATE(fun1(0:nidbas1,1)) ! needs only basis functions (no derivatives)
ALLOCATE(fun2(0:nidbas2,1)) ! needs only basis functions (no derivatives)
!
! Gauss quadature
!
CALL get_gauss(spl%sp1, ng1)
CALL get_gauss(spl%sp2, ng2)
ALLOCATE(xg1(ng1), wg1(ng1))
ALLOCATE(xg2(ng1), wg2(ng1))
!===========================================================================
! 2.0 Assembly loop
!
nrank = SIZE(rhs)
rhs(1:nrank) = 0.0d0
!
DO i=1,n1
CALL get_gauss(spl%sp1, ng1, i, xg1, wg1)
DO ig1=1,ng1
CALL basfun(xg1(ig1), spl%sp1, fun1, i)
DO j=1,n2
CALL get_gauss(spl%sp2, ng2, j, xg2, wg2)
DO ig2=1,ng2
CALL basfun(xg2(ig2), spl%sp2, fun2, j)
contrib = wg1(ig1)*wg2(ig2) * &
& rhseq(xg1(ig1),xg2(ig2), mbess)
DO k1=0,nidbas1
i1 = i+k1
DO k2=0,nidbas2
j2 = MODULO(j+k2-1,n2) + 1
ij = j2 + (i1-1)*n2
rhs(ij) = rhs(ij) + contrib*fun1(k1,1)*fun2(k2,1)
END DO
END DO
END DO
END DO
END DO
END DO
!===========================================================================
! 9.0 Epilogue
!
DEALLOCATE(xg1, wg1, fun1)
DEALLOCATE(xg2, wg2, fun2)
!
CONTAINS
DOUBLE PRECISION FUNCTION rhseq(x, y, m)
DOUBLE PRECISION, INTENT(in) :: x, y
INTEGER, INTENT(in) :: m
!
DOUBLE PRECISION :: xm
xm = REAL(m,8)
rhseq = x**(m+1) * ( 4.0d0*(xm+1.0d0)*COS(xm*y) + &
& epsi*x*( &
& ( (3.0d0*(xm+1.0d0) - xm/x**2)*COS((xm-1.0d0)*y) + &
& (3.0d0+2.0d0*xm)*COS((xm+1.0d0)*y) ) &
& ))
END FUNCTION rhseq
END SUBROUTINE disrhs
!
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SUBROUTINE ibcmat(mat, dft)
!
! Apply BC on matrix
!
TYPE(zpardiso_mat), INTENT(inout) :: mat
TYPE(dftmap), INTENT(in) :: dft
INTEGER :: nrank, k, kmin, kmax, dk, i
DOUBLE COMPLEX :: arr(mat%rank)
!===========================================================================
! 1.0 Prologue
!
nrank = mat%rank
kmin = dft%kmin
kmax = dft%kmax
dk = dft%dk
!===========================================================================
! 2.0 BC at the axis
!
! zero for non-zero modes
!
DO k=kmin,kmax
IF(k.NE.0) THEN
i = k-kmin+1
arr = 0.0d0; arr(i) = 1.0d0
CALL putrow(mat, i, arr)
END IF
END DO
!===========================================================================
! 3.0 Dirichlet on right boundary
!
DO i = nrank, nrank-dk+1, -1
arr = 0.0d0; arr(i) = 1.0d0
CALL putrow(mat, i, arr)
END DO
!
END SUBROUTINE ibcmat
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SUBROUTINE ibcrhs(rhs, dft)
!
! Apply BC on RHS
!
DOUBLE COMPLEX, INTENT(inout) :: rhs(:)
TYPE(dftmap), INTENT(in) :: dft
INTEGER :: nrank, kmin, kmax, dk, k
!===========================================================================
! 1.0 Prologue
!
nrank = SIZE(rhs,1)
kmin = dft%kmin
kmax = dft%kmax
dk = dft%dk
!===========================================================================
! 2.0 BC at the axis
!
! zero for non-zero modes
!
DO k=kmin,kmax
IF(k.NE.0) rhs(k-kmin+1) = 0.0
END DO
!===========================================================================
! 3.0 Dirichlet on right boundary
!
rhs(nrank-dk+1:nrank) = 0.0d0
END SUBROUTINE ibcrhs
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SUBROUTINE spectrum0(spl, carr, xpt, cspec)
!
! DFT modes at xpt (integration on the first interval)
!
DOUBLE COMPLEX, PARAMETER :: ci = (0.0d0,1.0d0)
DOUBLE PRECISION, PARAMETER :: pi = 3.141592653589793d0
TYPE(spline2d), INTENT(in) :: spl
DOUBLE COMPLEX, INTENT(in) :: carr(:)
DOUBLE PRECISION, INTENT(in) :: xpt
DOUBLE COMPLEX, INTENT(out) :: cspec(:)
!
INTEGER :: ndim1, n1, nidbas1, ndim2, n2, nidbas2
INTEGER :: k, kmin, kmax, dk, kk
INTEGER :: ng2, ig2
INTEGER, ALLOCATABLE :: left2(:)
DOUBLE PRECISION :: temp(1)
DOUBLE PRECISION, ALLOCATABLE :: xg2(:), wg2(:)
DOUBLE COMPLEX, ALLOCATABLE :: ft_fun2(:,:,:), psi(:), coefs(:,:)
!
CALL get_dim(spl%sp1, ndim1, n1, nidbas1)
CALL get_dim(spl%sp2, ndim2, n2, nidbas2)
kmin = spl%sp2%dft%kmin
kmax = spl%sp2%dft%kmax
dk = spl%sp2%dft%dk
!
CALL get_gauss(spl%sp2, ng2)
ALLOCATE(left2(ng2))
ALLOCATE(xg2(ng2), wg2(ng2))
ALLOCATE(ft_fun2(kmin:kmax,1,ng2)) ! DFT of splines
ALLOCATE(psi(kmin:kmax))
!
! Integration over first interval
!
left2 = 1
CALL get_gauss(spl%sp2, ng2, 1, xg2, wg2)
CALL ft_basfun(xg2, spl%sp2, ft_fun2, left2)
psi = (0.0d0,0.0d0)
DO k=kmin,kmax
DO ig2=1,ng2
psi(k) = psi(k) + wg2(ig2)*EXP(k*ci*xg2(ig2))*CONJG(ft_fun2(k,1,ig2))
END DO
END DO
!
ALLOCATE(coefs(dk,ndim1))
coefs = RESHAPE(carr, SHAPE(coefs))
temp = xpt
DO kk=kmin,kmax
k=kk-kmin+1
coefs(k,:) = psi(kk)*coefs(k,:)
CALL gridval(spl%sp1, temp, cspec(k:k), 0, coefs(k,:))
END DO
cspec = cspec/(2.0d0*pi)
!
DEALLOCATE(left2)
DEALLOCATE(xg2, wg2)
DEALLOCATE(ft_fun2)
DEALLOCATE(psi)
DEALLOCATE(coefs)
END SUBROUTINE spectrum0
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SUBROUTINE spectrum1(spl, carr, xpt, ypt0, cspec)
!
! DFT modes at xpt (at the initial ypt0)
!
DOUBLE COMPLEX, PARAMETER :: ci = (0.0d0,1.0d0)
!
TYPE(spline2d), INTENT(in) :: spl
DOUBLE COMPLEX, INTENT(in) :: carr(:)
DOUBLE PRECISION, INTENT(in) :: xpt, ypt0
DOUBLE COMPLEX, INTENT(out) :: cspec(:)
!
INTEGER :: ndim1, n1, nidbas1, ndim2, n2, nidbas2
INTEGER :: k, kmin, kmax, dk
DOUBLE PRECISION :: temp(1)
DOUBLE COMPLEX :: ctemp(1)
DOUBLE COMPLEX, ALLOCATABLE :: ft_fun2(:,:), coefs(:,:)
!
CALL get_dim(spl%sp1, ndim1, n1, nidbas1)
CALL get_dim(spl%sp2, ndim2, n2, nidbas2)
kmin = spl%sp2%dft%kmin
kmax = spl%sp2%dft%kmax
dk = spl%sp2%dft%dk
!
! DFT of splines at ypt0
ALLOCATE(ft_fun2(kmin:kmax,1))
ALLOCATE(coefs(kmin:kmax,ndim1))
CALL ft_basfun(ypt0, spl%sp2, ft_fun2, 1)
coefs = RESHAPE(carr, SHAPE(coefs))
!
temp = xpt
DO k=kmin,kmax
CALL gridval(spl%sp1, temp, ctemp, 0, coefs(k,:))
cspec(k-kmin+1) = CONJG(ft_fun2(k,1))*ctemp(1)*EXP(k*ci*ypt0)
END DO
cspec = cspec/REAL(n2,8)
!
DEALLOCATE(ft_fun2)
DEALLOCATE(coefs)
END SUBROUTINE spectrum1
!!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SUBROUTINE spectrum2(spl, xpt, ypt0, cspec)
!
! DFT modes at xpt (at the initial ypt0)
!
USE fft
USE bsplines
!
DOUBLE COMPLEX, PARAMETER :: ci = (0.0d0,1.0d0)
!
TYPE(spline2d) :: spl
DOUBLE PRECISION, INTENT(in) :: xpt, ypt0
DOUBLE COMPLEX, INTENT(out) :: cspec(:)
!
INTEGER :: ndim1, n1, nidbas1, ndim2, n2, nidbas2
INTEGER :: k, kmin, kmax, dk
DOUBLE PRECISION, ALLOCATABLE :: ypt(:), fun(:,:)
DOUBLE COMPLEX, ALLOCATABLE :: ft_fun(:)
DOUBLE PRECISION :: temp(1)
!
CALL get_dim(spl%sp1, ndim1, n1, nidbas1)
CALL get_dim(spl%sp2, ndim2, n2, nidbas2)
kmin = spl%sp2%dft%kmin
kmax = spl%sp2%dft%kmax
dk = spl%sp2%dft%dk
!
ALLOCATE(ypt(0:n2-1))
ALLOCATE(fun(1, 0:n2-1))
ALLOCATE(ft_fun(0:n2-1))
!
! Function values at points ypt
!
ypt(0:n2-1) = ypt0 + spl%sp2%knots(0:n2-1)
temp = xpt
CALL gridval(spl, temp, ypt, fun, (/0,0/))
ft_fun = fun(1,:)
!
! Discrete Fourier Transform
!
CALL fourcol(ft_fun, 1)
DO k=kmin,kmax
IF(k.LT.0) THEN
cspec(k-kmin+1) = ft_fun(k+n2)*EXP(k*ci*ypt0)
ELSE
cspec(k-kmin+1) = ft_fun(k)*EXP(k*ci*ypt0)
END IF
END DO
cspec = cspec/REAL(n2,8)
!
DEALLOCATE(ypt)
DEALLOCATE(fun)
DEALLOCATE(ft_fun)
END SUBROUTINE spectrum2
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
END MODULE pde2d_sym_pardiso_dft_mod
PROGRAM main
USE pde2d_sym_pardiso_dft_mod
USE futils
USE fft
!
IMPLICIT NONE
INTEGER :: nx, ny, nidbas(2), ngauss(2), mbess, nterms, kmin, kmax, dk
INTEGER :: n_mode_couplings
INTEGER, ALLOCATABLE :: mode_couplings(:)
LOGICAL :: nlppform
INTEGER :: i, j, ij, dimx, dimy, nrank, nrank_full, jder(2), it, i0, i0_r
INTEGER :: k, kp, ik
DOUBLE PRECISION :: pi, epsi, coefx(5), coefy(5)
DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: xgrid, ygrid, rhs, sol
DOUBLE COMPLEX, ALLOCATABLE :: crhs(:), crhs_r(:), csol(:), csol_r(:)
TYPE(spline2d) :: splxy
TYPE(zpardiso_mat) :: mat
!
CHARACTER(len=128) :: file='pde2d_sym_pardiso_dft.h5'
INTEGER :: fid
DOUBLE COMPLEX, DIMENSION(:), ALLOCATABLE :: arr, srow
DOUBLE PRECISION, DIMENSION(:,:), ALLOCATABLE :: bcoef, solcal, solana, errsol
DOUBLE PRECISION :: seconds, mem, dopla
DOUBLE PRECISION :: t0, tmat, tfact, tsolv, tfour, tfour0, tgrid, gflops1
INTEGER :: nits=100
LOGICAL :: nlmetis, nlforce_zero, nlpos
!
DOUBLE PRECISION :: xpt, ypt0
DOUBLE COMPLEX, ALLOCATABLE :: cspec0(:), cspec(:), energy_k(:)
DOUBLE COMPLEX :: energy, energy_exact
!
NAMELIST /newrun/ nx, ny, nidbas, ngauss, kmin, kmax, mbess, epsi, &
& nlppform, nlmetis, nlforce_zero, nlpos, coefx, coefy
!===========================================================================
! 1.0 Prologue
!
! Read in data specific to run
!
nx = 8 ! Number of intervals in x
ny = 8 ! Number of intervals in y
nidbas = (/3,3/) ! Degree of splines
ngauss = (/4,4/) ! Number of Gauss points/interval
kmin = -3 ! Minimum Fourier mode number
kmax = 3 ! Maximum Fourier mode number
mbess = 2 ! Exponent of differential problem
epsi = 0.5 ! Non-uniformity in the Laplacian coefficicient
nterms = 2 ! Number of terms in weak form
nlppform = .TRUE. ! Use PPFORM for gridval or not
nlmetis = .FALSE. ! Use metis ordering or minimum degree
nlforce_zero = .FALSE. ! Remove existing nodes with zero val in putele/row/ele
nlpos = .TRUE. ! Matrix is positive definite
coefx(1:5) = (/1.0d0, 0.d0, 0.d0, 0.d0, 1.d0/) ! Mesh point distribution function
coefy(1:5) = (/1.0d0, 0.d0, 0.d0, 0.d0, 1.d0/) ! Mesh point distribution function
!
READ(*,newrun)
WRITE(*,newrun)
!!
! Read table of mode couplings
!
READ(*,*) n_mode_couplings
ALLOCATE(mode_couplings(n_mode_couplings))
READ(*,*) mode_couplings
WRITE(*,'(/a/(20i4))') 'Mode couplings', mode_couplings
!
! Define grid on x (=radial) & y (=poloidal) axis
!
pi = 4.0d0*ATAN(1.0d0)
ALLOCATE(xgrid(0:nx), ygrid(0:ny))
xgrid(0) = 0.0d0; xgrid(nx) = 1.0d0
CALL meshdist(coefx, xgrid, nx)
ygrid(0) = 0.0d0; ygrid(ny) = 2.d0*pi
CALL meshdist(coefy, ygrid, ny)
!
! Exact energy
!
energy_exact = 2.0d0*pi/REAL(2+mbess,8)
!
! Create hdf5 file
!
CALL creatf(file, fid, 'PDE2D Result File', real_prec='d')
CALL attach(fid, '/', 'NX', nx)
CALL attach(fid, '/', 'NY', ny)
CALL attach(fid, '/', 'NIDBAS1', nidbas(1))
CALL attach(fid, '/', 'NIDBAS2', nidbas(2))
CALL attach(fid, '/', 'NGAUSS1', ngauss(1))
CALL attach(fid, '/', 'NGAUSS2', ngauss(2))
CALL attach(fid, '/', 'MBESS', mbess)
CALL attach(fid, '/', 'EPSI', epsi)
CALL attach(fid, '/', 'KMIN', kmin)
CALL attach(fid, '/', 'KMAX', kmax)
!===========================================================================
! 2.0 Discretize the PDE
!
! Set up spline
!
CALL set_spline(nidbas, ngauss, xgrid, ygrid, splxy, &
& (/.FALSE., .TRUE./))
!
! Init DFT for spline in 2nd direction
!
CALL init_dft(splxy%sp2, kmin, kmax, mode_couplings)
dk = splxy%sp2%dft%dk
!
! FE matrix assembly
!
dimx = splxy%sp1%dim
dimy = splxy%sp2%dim
nrank = (nx+nidbas(1))*dk ! Rank of restricted matrix
nrank_full = (nx+nidbas(1))*ny ! Rank of full matrix
!
ALLOCATE(rhs(nrank_full), sol(nrank_full))
ALLOCATE(crhs(nrank_full), csol(nrank_full))
ALLOCATE(crhs_r(nrank), csol_r(nrank))
!
WRITE(*,'(a,i8)') 'nrank_full', nrank_full
WRITE(*,'(a,i8)') 'nrank ', nrank
!
t0 = seconds()
CALL init(nrank, nterms, mat, nlherm=.TRUE.)
CALL dismat(splxy, epsi, mat)
ALLOCATE(arr(nrank))
ALLOCATE(srow(nrank))
DO i=1,nrank
CALL getrow(mat, i, arr)
srow(i) = SUM(arr)
END DO
!!$ WRITE(*,'(a/(10(1pe12.3)))') 'Real of Sum of matrix rows before BC', REAL(srow)
!!$ WRITE(*,'(a/(10(1pe12.3)))') 'Imag of Sum of matrix rows before BC', AIMAG(srow)
PRINT*, 'Sum of mat before BC', SUM(srow)
!
! BC on Matrix
!
WRITE(*,'(/a,l4)') 'nlforce_zero = ', mat%nlforce_zero
WRITE(*,'(a,i8)') 'Number of non-zeros of matrix = ', get_count(mat)
CALL ibcmat(mat, splxy%sp2%dft)
tmat = seconds() - t0
DO i=1,nrank
CALL getrow(mat, i, arr)
srow(i) = SUM(arr)
END DO
!!$ WRITE(*,'(a/(10(1pe12.3)))') 'Real of Sum of matrix rows after BC', REAL(srow)
!!$ WRITE(*,'(a/(10(1pe12.3)))') 'Imag of Sum of matrix rows after BC', AIMAG(srow)
PRINT*, 'Sum of mat after BC', SUM(srow)
!
WRITE(*,'(/a,i8)') 'Number of non-zeros of matrix = ', get_count(mat)
WRITE(*,'(/a,1pe12.3)') 'Mem used (MB) after DISMAT', mem()
!
! RHS assembly
!
CALL disrhs(mbess, epsi, splxy, rhs)
!
! Init FFT
!
t0 = seconds()
CALL fourcol(crhs(1:ny),1)
CALL fourcol(crhs(1:ny),-1)
tfour0 = seconds()-t0
crhs = crhs/REAL(ny,8)
!
! DFT of RHS
!
t0 = seconds()
crhs = rhs
DO i=1,nx+nidbas(1)
i0 = (i-1)*ny
CALL fourcol(crhs(i0+1:i0+ny), 1)
END DO
tfour = seconds()-t0
!
! Restriction in Fourier space
! k = kmin:kmax (restricted)
! kp = 0:ny-1 (full)
!
DO i=1,nx+nidbas(1)
i0 = (i-1)*ny
i0_r = (i-1)*dk
DO k=kmin,kmax
kp = k
IF(kp.LT.0) kp = kp+ny
crhs_r(i0_r+k-kmin+1) = crhs(i0+kp+1)
END DO
END DO
!
! BC on RHS
!
CALL ibcrhs(crhs_r, splxy%sp2%dft)
!
IF(nrank.LT.100) THEN
WRITE(*,'(a/(10(1pe12.3)))') 'Real of crhs', REAL(crhs)
WRITE(*,'(a/(10(1pe12.3)))') 'Imag of crhs', AIMAG(crhs)
END IF
!===========================================================================
! 3.0 Solve the dicretized system
!
! Matrix factorization
!
t0 = seconds()
!!$ CALL factor(mat, nlmetis=nlmetis)
CALL to_mat(mat)
CALL reord_mat(mat, nlmetis=nlmetis); CALL putmat(fid, '/MAT1', mat)
CALL numfact(mat)
tfact = seconds() - t0
DO i=1,nrank
CALL getrow(mat, i, arr)
srow(i) = SUM(arr)
END DO
PRINT*, 'Sum of mat after factor', SUM(srow)
WRITE(*,'(/a,1pe12.3)') 'Mem used (MB) after FACTOR', mem()
WRITE(*,'(/a,i12)') 'Number of nonzeros in factors of A = ',mat%p%iparm(18)
WRITE(*,'(a,i12)') 'Number of factorization MFLOPS = ',mat%p%iparm(19)
gflops1 = mat%p%iparm(19) / tfact / 1.d3
!
! Backsolve
!
t0 = seconds()
PRINT*, 'SUM of crhs_r', SUM(crhs_r)
CALL bsolve(mat, crhs_r, csol_r, debug=.FALSE.)
WRITE(*,'(a,1pe12.4)') 'Residue =', cnorm2(vmx(mat,csol_r)-crhs_r)
tsolv = seconds() - t0
PRINT*, 'SUM of csol_r', SUM(csol_r)
!
CALL putarr(fid, '/FT_RHS', crhs_r, 'DFT of RHS')
CALL putarr(fid, '/FT_SOL', csol_r, 'DFT of Spline coefficients')
!===========================================================================
! 4.0 Perform some diagnostics in Fourier space
!
! Fourier spectrum at xpt
!
xpt = SQRT(REAL(mbess,8)/REAL(mbess+2,8))
ALLOCATE(cspec0(dk))
CALL spectrum0(splxy, csol_r, xpt, cspec0)
WRITE(*,'(/a,f10.5)') 'DFT spectrum (by integration) at x = ', xpt
DO k=kmin,kmax
WRITE(*,'(i5,3(1pe15.3))') k, cspec0(k-kmin+1), ABS(cspec0(k-kmin+1))
END DO
!
ypt0 = 0.0d0
WRITE(*,'(/a,2f10.5)') 'DFT spectrum1 at x, y0 = ', xpt, ypt0
CALL spectrum1(splxy, csol_r, xpt, ypt0, cspec0)
DO k=kmin,kmax
WRITE(*,'(i5,3(1pe15.3))') k, cspec0(k-kmin+1), ABS(cspec0(k-kmin+1))
END DO
ypt0 = splxy%sp2%dft%dx/2.0d0 ! Center of first interval
WRITE(*,'(/a,2f10.5)') 'DFT spectrum1 at x, y0 = ', xpt, ypt0
CALL spectrum1(splxy, csol_r, xpt, ypt0, cspec0)
DO k=kmin,kmax
WRITE(*,'(i5,3(1pe15.3))') k, cspec0(k-kmin+1), ABS(cspec0(k-kmin+1))
END DO
!
! Spectral energy
!
WRITE(*,'(/a)') 'Spectral energies'
ALLOCATE(energy_k(kmin:kmax))
energy_k = (0.0d0,0.0d0)
DO i=1,dimx
i0_r = (i-1)*dk
DO k=kmin,kmax
ik = i0_r+k-kmin+1
energy_k(k) = energy_k(k) + csol_r(ik)*CONJG(crhs_r(ik))
END DO
END DO
energy_k = energy_k/REAL(ny,8)
energy = SUM(energy_k)
DO k=kmin,kmax
WRITE(*,'(i5,3(1pe15.3))') k, energy_k(k), ABS(energy_k(k))
END DO
WRITE(*,'(a5,4(1pe15.3))') 'Sum', energy, ABS(energy), REAL(energy-energy_exact)
!
CALL putarr(fid, '/ENERGY_K', energy_k, 'Spectral energies')
!===========================================================================
! 5.0 Transform back to real space
!
! Expand to full Fourier space
! k = kmin:kmax (restricted)
! kp = 0:ny-1 (full)
!
crhs = (0.0d0,0.0d0)
DO i=1,nx+nidbas(1)
i0 = (i-1)*ny
i0_r = (i-1)*dk
DO k=kmin,kmax
kp = k
IF(kp.LT.0) kp = kp+ny
csol(i0+kp+1) = csol_r(i0_r+k-kmin+1)
END DO
END DO
!
! Fourier transform back to real space
!
t0 = seconds()
DO i=1,nx+nidbas(1)
i0 = (i-1)*ny
CALL fourcol(csol(i0+1:i0+ny),-1)
END DO
sol = REAL(csol)/REAL(ny,8)
tfour = tfour + seconds()-t0
!
!
! Spline coefficients, taking into account of periodicity in y
! Note: in SOL, y was numbered first.
!
ALLOCATE(bcoef(0:dimx-1, 0:dimy-1))
DO j=0,dimy-1
DO i=0,dimx-1
ij = MODULO(j,ny) + i*ny + 1
bcoef(i,j) = sol(ij)
END DO
END DO
CALL putarr(fid, '/SOL', sol, 'Spline coefficients of solution')
!
! Total energy
!
WRITE(*,'(/a, 2(1pe15.3))') 'Total energy and error(real space)', &
& DOT_PRODUCT(rhs,sol), &
& DOT_PRODUCT(rhs,sol)-REAL(energy_exact)
WRITE(*,'(/a,1pe12.3)') 'Mem used (MB) after BSOLVE', mem()
!===========================================================================
! 6.0 Check the solution
!
! Check function values computed with various method
!
ALLOCATE(solcal(0:nx,0:ny), solana(0:nx,0:ny), errsol(0:nx,0:ny))
DO i=0,nx
DO j=0,ny
solana(i,j) = (1-xgrid(i)**2) * xgrid(i)**mbess * COS(mbess*ygrid(j))
END DO
END DO
jder = (/0,0/)
!
! Compute PPFORM/BCOEFS at first call to gridval
!
CALL gridval(splxy, xgrid, ygrid, solcal, jder, bcoef)
!
! Fourier spectrum at xpt
!
xpt = SQRT(REAL(mbess,8)/REAL(mbess+2,8))
ALLOCATE(cspec(dk))
!
ypt0 = 0.0d0
WRITE(*,'(/a,2f10.5)') 'DFT spectrum2 at x, y0 = ', xpt, ypt0
CALL spectrum2(splxy, xpt, ypt0, cspec)
DO k=kmin,kmax
WRITE(*,'(i5,3(1pe15.3))') k, cspec(k-kmin+1), ABS(cspec(k-kmin+1))
END DO
!
ypt0 = splxy%sp2%dft%dx/2.0d0 ! Center of first interval
WRITE(*,'(/a,2f10.5)') 'DFT spectrum2 at x, y0 = ', xpt, ypt0
CALL spectrum2(splxy, xpt, ypt0, cspec)
DO k=kmin,kmax
WRITE(*,'(i5,3(1pe15.3))') k, cspec(k-kmin+1), ABS(cspec(k-kmin+1))
END DO
!
WRITE(*,'(/a)') '*** Checking solutions'
t0 = seconds()
DO it=1,nits ! nits iterations for timing
CALL gridval(splxy, xgrid, ygrid, solcal, jder)
END DO
tgrid = (seconds() - t0)/REAL(nits)
errsol = solana - solcal
IF( SIZE(bcoef,2) .LE. 10 ) THEN
CALL prnmat('BCOEF', bcoef)
CALL prnmat('SOLANA', solana)
CALL prnmat('SOLCAL', solcal)
CALL prnmat('ERRSOL', errsol)
END IF
WRITE(*,'(a,2(1pe12.3))') 'Relative discretization errors', &
& norm2(errsol) / norm2(solana)
WRITE(*,'(a,1pe12.3)') 'GRIDVAL2 time (s) ', tgrid
!
CALL putarr(fid, '/xgrid', xgrid, 'r')
CALL putarr(fid, '/ygrid', ygrid, '\theta')
CALL putarr(fid, '/sol', solcal, 'Solutions')
CALL putarr(fid, '/solana', solana,'Exact solutions')
CALL putarr(fid, '/errors', errsol, 'Errors')
!
! Check derivatives d/dx and d/dy
!
WRITE(*,'(/a)') '*** Checking gradient'
DO i=0,nx
DO j=0,ny
IF( mbess .EQ. 0 ) THEN
solana(i,j) = -2.0d0 * xgrid(i)
ELSE
solana(i,j) = (-(mbess+2)*xgrid(i)**2 + mbess) * &
& xgrid(i)**(mbess-1) * COS(mbess*ygrid(j))
END IF
END DO
END DO
!
jder = (/1,0/)
CALL gridval(splxy, xgrid, ygrid, solcal, jder)
errsol = solana - solcal
CALL putarr(fid, '/derivx', solcal, 'd/dx of solutions')
WRITE(*,'(a,2(1pe12.3))') 'Error in d/dx', norm2(errsol)
!
DO i=0,nx
DO j=0,ny
solana(i,j) = -mbess * (1-xgrid(i)**2) * xgrid(i)**mbess * SIN(mbess*ygrid(j))
END DO
END DO
!
jder = (/0,1/)
CALL gridval(splxy, xgrid, ygrid, solcal, jder)
CALL putarr(fid, '/derivy', solcal, 'd/dy of solutions')
errsol = solana - solcal
WRITE(*,'(a,2(1pe12.3))') 'Error in d/dy', norm2(errsol)
!
WRITE(*,'(/a)') '---'
WRITE(*,'(a,1pe12.3)') 'Matrice construction time (s) ', tmat
WRITE(*,'(a,1pe12.3)') 'Matrice factorisation time (s)', tfact
WRITE(*,'(a,1pe12.3)') 'Backsolve(1) time (s) ', tsolv
WRITE(*,'(a,1pe12.3)') 'Init FFT time (s) ', tfour0
WRITE(*,'(a,1pe12.3)') 'FFT time (s) ', tfour
WRITE(*,'(a,2f10.3)') 'Factor Gflop/s', gflops1
!===========================================================================
! 9.0 Epilogue
!
DEALLOCATE(cspec0, cspec)
DEALLOCATE(mode_couplings)
DEALLOCATE(xgrid, ygrid, rhs, sol)
DEALLOCATE(crhs, csol)
DEALLOCATE(crhs_r, csol_r)
DEALLOCATE(solcal, solana, errsol)
DEALLOCATE(bcoef)
DEALLOCATE(arr)
DEALLOCATE(srow)
DEALLOCATE(energy_k)
CALL destroy_sp(splxy)
CALL destroy(mat)
!
CALL closef(fid)
!===========================================================================
!
CONTAINS
FUNCTION norm2(x)
!
! Compute the 2-norm of array x
!
IMPLICIT NONE
DOUBLE PRECISION :: norm2
DOUBLE PRECISION, INTENT(in) :: x(:,:)
DOUBLE PRECISION :: sum2
INTEGER :: i, j
!
sum2 = 0.0d0
DO i=1,SIZE(x,1)
DO j=1,SIZE(x,2)
sum2 = sum2 + x(i,j)**2
END DO
END DO
norm2 = SQRT(sum2)
END FUNCTION norm2
!
FUNCTION cnorm2(x)
DOUBLE COMPLEX, INTENT(in) :: x(:)
DOUBLE PRECISION :: cnorm2
cnorm2 = SQRT(DOT_PRODUCT(x,x))
END FUNCTION cnorm2
!
SUBROUTINE prnmat(label, mat)
CHARACTER(len=*) :: label
DOUBLE PRECISION, DIMENSION(:,:), INTENT(in) :: mat
INTEGER :: i
WRITE(*,'(/a)') TRIM(label)
DO i=1,SIZE(mat,1)
WRITE(*,'(10(1pe12.3))') mat(i,:)
END DO
END SUBROUTINE prnmat
END PROGRAM main
!
!+++
SUBROUTINE meshdist(c, x, nx)
!
! Construct an 1d non-equidistant mesh given a
! mesh distribution function.
!
IMPLICIT NONE
DOUBLE PRECISION, INTENT(in) :: c(5)
INTEGER, INTENT(iN) :: nx
DOUBLE PRECISION, INTENT(inout) :: x(0:nx)
INTEGER :: nintg
DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: xint, fint
DOUBLE PRECISION :: a, b, dx, f0, f1, scal
INTEGER :: i, k
!
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
!!$ WRITE(*,'(a/(10f8.3))') 'FINT', fint
!
! 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 = c(1) + c(2)*x + c(3)*EXP(-((x-c(4))/c(5))**2)
END FUNCTION fdist
END SUBROUTINE meshdist
!+++