!>
!> @file test_mg2d.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
!>
PROGRAM main
!
! Test 2d multigrid: Cartesian case
!
USE multigrid
USE csr
IMPLICIT NONE
INCLUDE 'mpif.h'
!
DOUBLE PRECISION, PARAMETER :: PI=4.0d0*ATAN(1.0d0)
INTEGER, DIMENSION(2) :: n, nidbas, ngauss, alpha
DOUBLE PRECISION :: kx=4.d0, ky=3.d0, sigma=10.0d0
CHARACTER(len=4) :: prb='poly'
INTEGER :: levels=1, nu1=1, nu2=1, mu=1, nu0=1, nits
CHARACTER(len=4) :: relax='jac '
DOUBLE PRECISION :: omega=2.0d0/3.0d0, tol
LOGICAL :: nlfixed=.FALSE.
DOUBLE PRECISION :: t0, tsetup(2), tdirect, tbsolve, titer, titer_per_step
DOUBLE PRECISION :: resid_direct, errdisc_direct
DOUBLE PRECISION :: norma, normb
!
DOUBLE PRECISION, ALLOCATABLE :: x(:), y(:)
DOUBLE PRECISION :: dx, dy
INTEGER :: ix, iy
!
DOUBLE PRECISION, ALLOCATABLE :: sol_anal_grid(:,:)
DOUBLE PRECISION, ALLOCATABLE :: sol_calc_grid(:,:)
DOUBLE PRECISION, ALLOCATABLE :: rresid(:), resid(:), errdisc(:)
!
DOUBLE PRECISION, ALLOCATABLE, TARGET :: sol_direct(:,:)
DOUBLE PRECISION, POINTER :: sol_direct_1d(:)
DOUBLE PRECISION, ALLOCATABLE :: sol_direct_grid(:,:)
!
INTEGER :: ierr, me
INTEGER :: l, nterms
INTEGER :: its
!
TYPE(grid2d), ALLOCATABLE :: grids(:)
TYPE(mg_info) :: info
!
NAMELIST /newrun/ n, nidbas, ngauss, kx, ky, sigma, alpha, levels, prb, &
& nu1, nu2, mu, nu0, relax, nits, tol, nlfixed, omega
!--------------------------------------------------------------------------------
! 1. Prologue
!
CALL mpi_init(ierr)
CALL mpi_comm_rank(MPI_COMM_WORLD, me, ierr)
!
! Inputs
!
n = (/8, 8/)
nidbas=(/3,3/)
ngauss=(/2,2/)
alpha = (/0,0/)
kx=4
ky=3
sigma=10.0d0
levels=2
prb='poly'
nu1 = 1
nu2 = 1
mu = 1
nu0 = 1
nits = 10
tol = 1.e-8
relax = 'jac'
nlfixed= .FALSE.
!
IF(me.EQ.0) THEN
READ(*,newrun)
WRITE(*,newrun)
END IF
CALL mpi_bcast(n, 2, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(nidbas, 2, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(ngauss, 2, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(alpha, 2, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(kx, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(ky, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(sigma, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(levels, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(prb, LEN(prb), MPI_CHARACTER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(nu1, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(nu2, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(nu0, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(mu, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(nits, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(relax, 4, MPI_CHARACTER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(nlfixed, 1, MPI_LOGICAL, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(omega, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(tol, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr)
!
! Adjust number of levels and fill mg info.
!
levels = MIN(levels, get_lmax(n(1)), get_lmax(n(2)))
info%nu1 = nu1
info%nu2 = nu2
info%mu = mu
info%nu0 = nu0
info%levels = levels
info%relax = relax
info%omega = omega
!
! Create grids
!
t0 = mpi_wtime()
!
dx = 1.0d0/REAL(n(1),8)
dy = 1.0d0/REAL(n(2),8)
ALLOCATE(x(0:n(1)), y(0:n(2)))
DO ix=0,n(1)
x(ix) = ix*dx
END DO
DO iy=0,n(2)
y(iy) = iy*dy
END DO
!
ALLOCATE(grids(levels))
CALL create_grid(x, y, nidbas, ngauss, alpha, grids)
WRITE(*,'(5a6)') 'l', 'nx', 'ny', 'rx', 'ry'
WRITE(*,'(5i6)') (l, grids(l)%n, grids(l)%rank, l=1,levels)
!
! Construct RHS and set BC only on the finest grid
!
CALL disrhs(grids(1)%spl, grids(1)%f, rhs)
CALL ibcrhs(grids(1), grids(1)%f)
!
! Build FE matrices and set BC
!
nterms = 3
DO l=1,levels
CALL femat(grids(l)%spl, grids(l)%mata, coefeq, nterms)
CALL ibcmat(grids(l), grids(l)%mata)
CALL to_mat(grids(l)%mata)
END DO
!
! Set BC on grid transfer matrices
!
CALL ibc_transf(grids,1,3)
CALL ibc_transf(grids,2,3)
!
tsetup(1) = mpi_wtime()-t0
!
! Clear and rebuild FE matrices and set BC
!
t0 = mpi_wtime()
nterms = 2
DO l=1,levels
CALL clear_mat(grids(l)%mata)
CALL femat(grids(l)%spl, grids(l)%mata, coefeq, nterms, noinit=.TRUE.)
CALL ibcmat(grids(l), grids(l)%mata)
END DO
tsetup(2) = mpi_wtime()-t0
!--------------------------------------------------------------------------------
! 1. Analytical solution (at the finest grid, l=1)
!
ALLOCATE(sol_anal_grid(0:n(1),0:n(2)))
sol_anal_grid = sol(grids(1)%x, grids(1)%y)
!--------------------------------------------------------------------------------
! 2. Direct solution (at the finest grid, l=1)
!
WRITE(*,'(//a)') 'Direct solution for the finest grid problem'
!
ALLOCATE(sol_direct(SIZE(grids(1)%v,1), SIZE(grids(1)%v,2)), &
& source=grids(1)%f)
ALLOCATE(sol_direct_grid(0:n(1),0:n(2)))
!
sol_direct_1d(1:SIZE(grids(1)%v1d)) => sol_direct
!
!
PRINT*, 'shape of sol_direct', SHAPE(sol_direct)
PRINT*, 'shape of sol_direct_1d', SHAPE(sol_direct_1d)
!
t0 = mpi_wtime()
sol_direct = grids(1)%f
CALL direct_solve(grids(1), sol_direct_1d, debug=.FALSE.)
tdirect = mpi_wtime()-t0
!
t0 = mpi_wtime()
sol_direct = grids(1)%f
CALL direct_solve(grids(1), sol_direct_1d, debug=.FALSE.)
resid_direct = residue(grids(1)%mata, grids(1)%f1d, sol_direct_1d)
errdisc_direct = disc_err(grids(1)%spl, sol_direct, sol)
!
tbsolve = mpi_wtime()-t0
!
CALL gridval(grids(1)%spl, grids(1)%x, grids(1)%y, sol_direct_grid, &
& [0,0], sol_direct)
!
WRITE(*,'(a,2(1pe12.3))') 'Discretization error and residue =', &
& errdisc_direct, resid_direct
WRITE(*,'(a,2(1pe12.3))') 'Relative discretization errors', &
& NORM2(sol_anal_grid-sol_direct_grid) / NORM2(sol_anal_grid)
!
WRITE(*, '(a,1pe12.3)') 'Frobenius norm of A', matnorm(grids(1)%mata)
WRITE(*, '(a,1pe12.3)') 'Infinity norm of A ', matnorm(grids(1)%mata, 'inf')
WRITE(*, '(a,1pe12.3)') '1 norm of A ', matnorm(grids(1)%mata, '1')
!--------------------------------------------------------------------------------
! 3. Test multigrid V-cycle
!
WRITE(*,'(/a)') 'Multigrid MG V-cycles ...'
ALLOCATE(sol_calc_grid(0:n(1),0:n(2)))
ALLOCATE(errdisc(0:nits))
ALLOCATE(resid(0:nits))
ALLOCATE(rresid(0:nits))
!
! Norm of A and b
!
norma = matnorm(grids(1)%mata)
normb = NORM2(grids(1)%f1d)
!
! Initial guess
!
IF(nlfixed) THEN
grids(1)%v = sol_direct
ELSE
grids(1)%v = 0.0d0
END IF
!
errdisc(0) = disc_err(grids(1)%spl, grids(1)%v, sol)
resid(0) = residue(grids(1)%mata, grids(1)%f1d, grids(1)%v1d)
rresid(0) = resid(0) / ( norma*NORM2(grids(1)%v1d) + normb )
WRITE(*,'(a4,3(a12,a8))') 'its', 'residue', 'ratio', 'disc. err', 'ratio', &
& 'rel. resid', 'ratio'
WRITE(*,'(i4,3(1pe12.3,8x))') 0, resid(0), errdisc(0), rresid(0)
!
! Iterations
!
t0 = mpi_wtime()
DO its=1,nits
CALL mg(grids, info, 1)
errdisc(its) = disc_err(grids(1)%spl, grids(1)%v, sol)
resid(its) = residue(grids(1)%mata, grids(1)%f1d, grids(1)%v1d)
rresid(its) = resid(its) / ( norma*NORM2(grids(1)%v1d) + normb )
WRITE(*,'((i4,3(1pe12.3,0pf8.2)))') its, &
& resid(its), resid(its)/resid(its-1), &
& errdisc(its), errdisc(its)/errdisc(its-1), &
& rresid(its), rresid(its)/rresid(its-1)
IF(resid(its) .LE. tol) EXIT
END DO
nits = MIN(nits,its)
titer = mpi_wtime() - t0
titer_per_step = titer/REAL(its,8)
!
CALL gridval(grids(1)%spl, grids(1)%x, grids(1)%y, sol_calc_grid, &
& [0,0], grids(1)%v)
!--------------------------------------------------------------------------------
! 9. Epilogue
!
! Display timing
!
WRITE(*,'(a,2(1pe12.3))') 'Set up time (s) ', tsetup
WRITE(*,'(a,2(1pe12.3))') 'Direct and solve time (s) ', tdirect, tbsolve
WRITE(*,'(a,1pe12.3,i5)') 'Iter time (s) ', titer, nits
!
! Creata HDF5 file
!
IF(me.EQ.0) CALL h5file
!
CALL mpi_finalize(ierr)
!--------------------------------------------------------------------------------
CONTAINS
!+++
FUNCTION rhs(x, y)
!
! Return problem RHS
!
DOUBLE PRECISION, INTENT(in) :: x, y
DOUBLE PRECISION :: rhs
DOUBLE PRECISION :: x2, y2
SELECT CASE(TRIM(prb))
CASE('poly')
x2 = x*x; y2 = y*y;
rhs = 2.d0 * ( (1.0d0-6.d0*x2)*y2*(1.d0-y2) + &
& (1.0d0-6.d0*y2)*x2*(1.d0-x2) )
CASE('trig')
rhs = SIN(PI*kx*x)*SIN(PI*ky*y)
END SELECT
END FUNCTION rhs
!+++
FUNCTION sol(x, y)
!
! Return exact problem solution
!
DOUBLE PRECISION, INTENT(in) :: x(:), y(:)
DOUBLE PRECISION :: sol(SIZE(x),SIZE(y))
DOUBLE PRECISION :: c
DOUBLE PRECISION :: x2(SIZE(x)), y2(SIZE(y))
INTEGER :: j
SELECT CASE(TRIM(prb))
CASE('poly')
x2 = x*x; y2 = y*y;
DO j=1,SIZE(y)
c = y2(j)*(y2(j)-1.d0)
sol(:,j) = c*x2(:)*(1.0d0-x2(:))
END DO
CASE('trig')
DO j=1,SIZE(y)
c = SIN(PI*ky*y(j)) / (PI**2*(kx**2+ky**2) + sigma**2)
sol(:,j) = c * SIN(PI*kx*x(:))
END DO
END SELECT
END FUNCTION sol
!+++
SUBROUTINE coefeq(x, y, idt, idw, c)
!
! Weak form = Int( \nabla(w).\nabla(t) + \sigma.t.w) dV)
!
DOUBLE PRECISION, INTENT(in) :: x, y
INTEGER, INTENT(out) :: idt(:,:), idw(:,:)
DOUBLE PRECISION, INTENT(out) :: c(:)
!
c(1) = 1.0d0
idt(1,1) = 1
idt(1,2) = 0
idw(1,1) = 1
idw(1,2) = 0
!
c(2) = 1.0d0
idt(2,1) = 0
idt(2,2) = 1
idw(2,1) = 0
idw(2,2) = 1
!
c(3) = sigma
idt(3,1) = 0
idt(3,2) = 0
idw(3,1) = 0
idw(3,2) = 0
END SUBROUTINE coefeq
!+++
SUBROUTINE h5file
USE futils
CHARACTER(len=128) :: file='test_mg2d.h5'
INTEGER :: fid
INTEGER :: l
CHARACTER(len=64) :: dsname
CALL creatf(file, fid, real_prec='d')
CALL attach(fid, '/', 'NX', n(1))
CALL attach(fid, '/', 'NY', n(2))
CALL attach(fid, '/', 'NIDBAS1', nidbas(1))
CALL attach(fid, '/', 'NIDBAS2', nidbas(2))
CALL attach(fid, '/', 'KX', kx)
CALL attach(fid, '/', 'KY', ky)
CALL attach(fid, '/', 'SIGMA', sigma)
CALL attach(fid, '/', 'ALPHA1', alpha(1))
CALL attach(fid, '/', 'ALPHA2', alpha(2))
CALL attach(fid, '/', 'RELAX', relax)
CALL attach(fid, '/', 'NITS', nits)
CALL attach(fid, '/', 'LEVELS', levels)
CALL attach(fid, '/', 'NU1', nu1)
CALL attach(fid, '/', 'NU2', nu2)
CALL attach(fid, '/', 'NU0', nu0)
CALL attach(fid, '/', 'MU', mu)
CALL creatg(fid, '/mglevels')
DO l=1,levels
WRITE(dsname,'("/mglevels/level.",i2.2)') l
CALL creatg(fid, TRIM(dsname))
CALL putmat(fid, TRIM(dsname)//'/mata', grids(l)%mata)
IF(l.GT.1) THEN
CALL putmat(fid, TRIM(dsname)//'/matpx', grids(l)%matp(1))
CALL putmat(fid, TRIM(dsname)//'/matpy', grids(l)%matp(2))
END IF
CALL putarr(fid, TRIM(dsname)//'/x', grids(l)%x)
CALL putarr(fid, TRIM(dsname)//'/y', grids(l)%y)
CALL putarr(fid, TRIM(dsname)//'/f', grids(l)%f)
CALL putarr(fid, TRIM(dsname)//'/v', grids(l)%v)
CALL putarr(fid, TRIM(dsname)//'/f1d', grids(l)%f1d)
CALL putarr(fid, TRIM(dsname)//'/v1d', grids(l)%v1d)
END DO
!
! Solutions at finest grid
!
CALL creatg(fid, '/solutions')
CALL putarr(fid, '/solutions/xg', grids(1)%x)
CALL putarr(fid, '/solutions/yg', grids(1)%y)
CALL putarr(fid, '/solutions/anal', sol_anal_grid)
CALL putarr(fid, '/solutions/calc', sol_calc_grid)
CALL putarr(fid, '/solutions/direct', sol_direct_grid)
!
CALL creatg(fid, '/Iterations')
CALL putarr(fid, '/Iterations/residues', resid(0:nits))
CALL putarr(fid, '/Iterations/disc_errors', errdisc(0:nits))
!
CALL closef(fid)
END SUBROUTINE h5file
!+++
END PROGRAM