!> !> @file poisson_fd.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 !> PROGRAM main ! ! Solving the following 2d PDE using finite differences: ! ! d2/dx2 f(x,y) + tau*d2/dxdy f(x,y) + d2/dy2 f(x,y) = w(x,y), x,y in [0:Lx][0:Ly] ! w(x,y) = -4*pi^2 *[(kx^2/Lx^2+ky^2/Ly^2)*cos(2*kx*pi*x/Lx)*sin(2*ky*pi*y/Ly) ! -tau*(kx*ky)/(Lx*Ly)*sin(2*kx*pi*x/Lx)*cos(2*ky*pi*y/Lx)] ! ! West, East boundaries: Neumann ! South, North boundaries: Dirichlet ! ! Analytic solution : f(x,y) = cos(2*kx*pi*x/Lx)*sin(2*kx*pi*y/Ly) ! USE multigrid USE fdmat_mod IMPLICIT NONE INCLUDE 'mpif.h' ! INTEGER, PARAMETER :: nnumx=32 ! INTEGER :: ierr, np, me DOUBLE PRECISION, PARAMETER :: PI=4.0d0*ATAN(1.0d0) DOUBLE PRECISION :: Lx, Ly, icrosst, beta, miome INTEGER :: n, nx,ny,nz,kx,ky CHARACTER(len=4) :: prb INTEGER :: nits DOUBLE PRECISION :: atol, rtol LOGICAL :: nldirect, nldebug ! TYPE(mg_info) :: info ! info for MG INTEGER :: levels, nnu, mu, nu0 ! INTEGER :: inu, nu1(nnumx), nu2(nnumx), niter(nnumx) DOUBLE PRECISION :: titer(nnumx) ! LOGICAL :: nlfixed DOUBLE PRECISION :: omega CHARACTER(len=4) :: mat_type, relax ! DOUBLE PRECISION :: dx, dy INTEGER :: ix, iy, l, its DOUBLE PRECISION, ALLOCATABLE :: x(:), y(:), dense(:) TYPE(grid2d), ALLOCATABLE :: grids(:) ! DOUBLE PRECISION, ALLOCATABLE, TARGET :: sol_ana2d(:,:), sol_direct2d(:,:) DOUBLE PRECISION, POINTER :: sol_ana(:), sol_direct(:) DOUBLE PRECISION :: err_direct, resid_direct DOUBLE PRECISION :: norma, normb DOUBLE PRECISION, ALLOCATABLE :: rresid(:), resid(:), errdisc(:) DOUBLE PRECISION :: t0, tsetup, tmat(2), tdirect, tbsolve DOUBLE PRECISION, EXTERNAL :: mem ! NAMELIST /parameters/ prb, mat_type,nx, ny, nz, kx, ky, Lx, Ly, icrosst, beta, & & miome, nldebug, nlfixed, levels, nnu, nu1, nu2, mu, nu0, & & relax,omega, nldirect, nits, atol, rtol !-------------------------------------------------------------------------------- ! 1.0 Prologue ! CALL MPI_INIT(ierr) CALL MPI_COMM_RANK(MPI_COMM_WORLD,me,ierr) CALL MPI_COMM_SIZE(MPI_COMM_WORLD,np,ierr) ! ! Default inputs ! nx=32 ny=32 nz=1 kx=1 ky=1 icrosst=1.0d0 Lx = 1.0D0 Ly = 1.0D0 beta = 0d0 miome = 200d0 nldebug = .FALSE. prb = 'dddd' mat_type = 'cds' nldirect = .TRUE. ! nlfixed = .FALSE. levels = 2 nnu = 1 nu1 = 1 nu2 = 1 mu = 1 nu0 = 1 nits = 10 atol = 1.e-8; rtol = 1.e-8 relax = 'jac' omega = 0.6667 ! IF(me==0) THEN READ(*,parameters) WRITE(*,parameters) END IF ! ! Send input parameters to other processors ! CALL MPI_BCAST(nx, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL MPI_BCAST(ny, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL MPI_BCAST(nz, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL MPI_BCAST(kx, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL MPI_BCAST(ky, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL MPI_BCAST(icrosst, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL MPI_BCAST(Lx,1,MPI_DOUBLE_PRECISION,0,MPI_COMM_WORLD, ierr) CALL MPI_BCAST(Ly,1,MPI_DOUBLE_PRECISION,0,MPI_COMM_WORLD, ierr) CALL MPI_BCAST(beta,1,MPI_DOUBLE_PRECISION,0,MPI_COMM_WORLD, ierr) CALL MPI_BCAST(miome, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) ! CALL mpi_bcast(nldebug, 1, MPI_LOGICAL, 0, MPI_COMM_WORLD, ierr) CALL mpi_bcast(nlfixed, 1, MPI_LOGICAL, 0, MPI_COMM_WORLD, ierr) CALL mpi_bcast(nldirect, 1, MPI_LOGICAL, 0, MPI_COMM_WORLD, ierr) CALL mpi_bcast(nnu, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL mpi_bcast(nu1, nnumx, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr) CALL mpi_bcast(nu2, nnumx, 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(mat_type, 4, MPI_CHARACTER, 0, MPI_COMM_WORLD, ierr) CALL mpi_bcast(omega, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL mpi_bcast(atol, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL mpi_bcast(rtol, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, ierr) CALL mpi_bcast(prb, LEN(prb), MPI_CHARACTER, 0, MPI_COMM_WORLD, ierr) ! IF(nnu.GT.nnumx) THEN IF(me.EQ.0) THEN PRINT*, 'Value of nnu larger than', nnumx END IF CALL mpi_finalize(ierr) STOP END IF ! ! Adjust number of levels and fill mg info. ! levels = MIN(levels, get_lmax(nx), get_lmax(ny)) info%nu1 = nu1(1) info%nu2 = nu2(1) info%mu = mu info%nu0 = nu0 info%levels = levels info%relax = relax info%omega = omega !-------------------------------------------------------------------------------- ! 2.0 Setup grids ! ! Grid on the finest level ! dx = lx/REAL(nx,8) dy = ly/REAL(ny,8) ALLOCATE(x(0:nx), y(0:ny)) DO ix=0,nx x(ix) = ix*dx END DO DO iy=0,ny y(iy) = iy*dy END DO WRITE(*,'(a,3(1pe12.3))') 'dx, dy, dx/dy =', dx, dy, dx/dy ! ALLOCATE(dense(0:nx)) dense = fdense(x) ! ! Set up grids ! t0 = mpi_wtime() ALLOCATE(grids(levels)) CALL create_grid_fd(x, y, grids, info, mat_type=mat_type, debug=nldebug) WRITE(*,'(5a6)') 'l', 'nx', 'ny', 'rx', 'ry' WRITE(*,'(5i6)') (l, grids(l)%n, grids(l)%rank, l=1,levels) IF(nldebug) THEN CALL printmat('** Prolongation matrix in 1st dim.**', grids(2)%transf(1)) CALL printmat('** Prolongation matrix in 2nd dim.**', grids(2)%transf(2)) END IF ! ! Set BC on grid transfer matrices ! IF(prb.EQ.'dddd') CALL ibc_transf(grids,1,3) ! Direction X CALL ibc_transf(grids,2,3) ! Direction Y tsetup = mpi_wtime()-t0 !-------------------------------------------------------------------------------- ! 3.0 Problem discretization ! ! Construct FD matrix and impose BC on all grids ! t0=mpi_wtime() DO l=1,levels CALL fdmat(grids(l), fdense, icrosst) IF(mat_type.EQ.'csr') CALL to_mat(grids(l)%mata) END DO tmat(1) = mpi_wtime()-t0 ! t0=mpi_wtime() DO l=1,levels CALL ibc_fdmat(grids(l), prb) END DO tmat(2) = mpi_wtime()-t0 ! ! Set RHS and impose BC on the fiest grid ! grids(1)%f(:,:) = frhs(x,y) ! IF(prb.EQ.'dddd') THEN grids(1)%f(0,:) = 0.0d0 ! Dirichlet on west and east grids(1)%f(nx,:) = 0.0d0 ELSE IF(prb.EQ.'nndd') THEN ! Neumann on west and east grids(1)%f(0,:) = 0.5d0*grids(1)%f(0,:) grids(1)%f(nx,:) = 0.5d0*grids(1)%f(nx,:) END IF grids(1)%f(:,0) = 0.0d0 ! Dirichlet on south and north grids(1)%f(:,ny) = 0.0d0 ! !-------------------------------------------------------------------------------- ! 4.0 Analytical solutions and RHS at the finest grid (l=1) ! n = (nx+1)*(ny+1) ! Number of unknowns ALLOCATE(sol_ana2d(0:nx,0:ny)) sol_ana(1:n) => sol_ana2d sol_ana2d(:,:) = fsol(x,y) !-------------------------------------------------------------------------------- ! 5.0 Direct solution at the finest grid (l=1) ! IF(nldirect) THEN WRITE(*,'(/a)') 'Direct solution for the finest grid problem ...' ALLOCATE(sol_direct2d(0:nx,0:ny)) sol_direct(1:n) => sol_direct2d ! t0 = mpi_wtime() sol_direct = grids(1)%f1d CALL direct_solve(grids(1), sol_direct, debug=nldebug) tdirect = mpi_wtime()-t0 ! t0 = mpi_wtime() sol_direct = grids(1)%f1d CALL direct_solve(grids(1), sol_direct, debug=nldebug) tbsolve = mpi_wtime()-t0 ! ! Max norm and residual ! err_direct = MAXVAL(ABS(sol_direct-sol_ana)) resid_direct = residue(grids(1), grids(1)%f1d, sol_direct, 'inf') WRITE(*,'(a,2(1pe12.3))') 'Max norm of error and residual norm', & & err_direct, resid_direct END IF !-------------------------------------------------------------------------------- ! 5.0 Iterative solution using MG V-cycle ! WRITE(*,'(/a)') 'Multigrid MG V-cycles ...' ALLOCATE(errdisc(0:nits)) ALLOCATE(resid(0:nits)) ALLOCATE(rresid(0:nits)) ! ! Norm of A and b ! IF(mat_type.EQ.'csr') THEN norma = matnorm(grids(1)%mata, 'inf') ELSE norma = matnorm(grids(1)%mata_cds, 'inf') END IF normb = MAXVAL(ABS(grids(1)%f1d)) WRITE(*,'(a,2(1pe12.3))') 'Norm A and RHS', norma, normb ! ! Initial guess ! DO inu=1,nnu info%nu1 = nu1(inu) info%nu2 = nu2(inu) WRITE(*,'(/2(a5,i3,2x))') 'nu1 =', nu1(inu), 'nu2 =', nu2(inu) IF(nlfixed .AND. nldirect) THEN grids(1)%v = sol_direct2d ELSE grids(1)%v = 0.0d0 END IF ! errdisc(0) = MAXVAL(ABS(grids(1)%v1d-sol_ana)) resid(0) = residue(grids(1), grids(1)%f1d, grids(1)%v1d, 'inf') rresid(0) = resid(0) / ( norma*MAXVAL(ABS(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) = MAXVAL(ABS(grids(1)%v1d-sol_ana)) resid(its) = residue(grids(1), grids(1)%f1d, grids(1)%v1d, 'inf') rresid(its) = resid(its) / ( norma*MAXVAL(ABS(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. atol .or. rresid(its) .le. rtol) EXIT END DO niter(inu) = MIN(nits,its) titer(inu) = mpi_wtime() - t0 END DO !-------------------------------------------------------------------------------- ! 9.0 Epilogue ! ! Display timing ! WRITE(*,'(/a)') 'Timing ...' WRITE(*,'(a,1pe12.3,i5)') 'Setup time (s) ', tsetup WRITE(*,'(a,2(1pe12.3))') 'Matrix construction time(s)', tmat WRITE(*,'(a,2(1pe12.3))') 'Direct and bsolve time (s) ', tdirect, tbsolve WRITE(*,'(/3a6,a15)') 'nu1', 'nu2', 'niter', 'Iter time(s)' DO inu=1,nnu WRITE(*,'(3i6,3x,1pe12.3)') nu1(inu), nu2(inu), niter(inu), titer(inu) END DO ! WRITE(*,'(/a,f12.3)') 'Mem used so far (MB)', mem() ! ! Creata HDF5 file ! IF(me.EQ.0) CALL h5file ! ! Clean up ! DEALLOCATE(x) DEALLOCATE(y) DEALLOCATE(dense) DEALLOCATE(grids) DEALLOCATE(sol_ana2d) IF(nldirect) DEALLOCATE(sol_direct2d) DEALLOCATE(errdisc) DEALLOCATE(resid) DEALLOCATE(rresid) ! CALL MPI_FINALIZE(ierr) !-------------------------------------------------------------------------------- CONTAINS !+++ FUNCTION fdense(x) ! ! Return density ! DOUBLE PRECISION, INTENT(in) :: x(:) DOUBLE PRECISION :: fdense(SIZE(x)) fdense(:) = 0.5d0*beta*miome*EXP( -(x(:)-Lx/3d0)**2d0/(Lx/2d0)**2d0 ); END FUNCTION fdense !+++ FUNCTION frhs(x,y) ! ! Return RHS ! DOUBLE PRECISION, INTENT(in) :: x(:), y(:) DOUBLE PRECISION :: frhs(SIZE(x),SIZE(y)) DOUBLE PRECISION :: c, s, d(SIZE(x)) DOUBLE PRECISION :: corr INTEGER :: j corr = 1.d0+icrosst**2/4.0d0 d(:) = fdense(x(:)) IF(prb.EQ.'dddd') THEN DO j=1,SIZE(y) c = COS(2.0d0*pi*ky*y(j)/Ly) s = SIN(2.0d0*pi*ky*y(j)/Ly) frhs(:,j) = -4.0d0*pi*pi*( (kx*kx/Lx/Lx+corr*ky*ky/Ly/Ly) * SIN(2.0d0*pi*kx*x(:)/Lx)*s & & -icrosst*kx*ky/Lx/Ly * COS(2.0d0*pi*kx*x(:)/Lx)*c ) & & + d(:) * SIN(2.0d0*pi*kx*x(:)/Lx)*s END DO ELSE IF (prb.EQ.'nndd') THEN DO j=1,SIZE(y) c = COS(2.0d0*pi*ky*y(j)/Ly) s = SIN(2.0d0*pi*ky*y(j)/Ly) frhs(:,j) = -4.0d0*pi*pi*( (kx*kx/Lx/Lx+corr*ky*ky/Ly/Ly) * COS(2.0d0*pi*kx*x(:)/Lx)*s & & +icrosst*kx*ky/Lx/Ly * SIN(2.0d0*pi*kx*x(:)/Lx)*c ) & & + d(:) * COS(2.0d0*pi*kx*x(:)/Lx)*s END DO END IF !!$ frhs = -frhs END FUNCTION frhs !+++ FUNCTION fsol(x,y) ! ! Return analytical solution ! DOUBLE PRECISION, INTENT(in) :: x(:), y(:) DOUBLE PRECISION :: fsol(SIZE(x),SIZE(y)) DOUBLE PRECISION :: c INTEGER :: j IF(prb.EQ.'dddd') THEN DO j=1,SIZE(y) c = SIN(2.0d0*pi*ky*y(j)/Ly) fsol(:,j) = SIN(2.0d0*pi*kx*x(:)/Lx)*c END DO ELSE IF (prb.EQ.'nndd') THEN DO j=1,SIZE(y) c = SIN(2.0d0*pi*ky*y(j)/Ly) fsol(:,j) = COS(2.0d0*pi*kx*x(:)/Lx)*c END DO END IF END FUNCTION fsol !+++ SUBROUTINE h5file USE futils CHARACTER(len=128) :: file='poisson_mg.h5' INTEGER :: fid INTEGER :: l CHARACTER(len=64) :: dsname CALL creatf(file, fid, real_prec='d') CALL attach(fid, '/', 'NX', nx) CALL attach(fid, '/', 'NY', ny) CALL attach(fid, '/', 'KX', kx) CALL attach(fid, '/', 'KY', ky) CALL attach(fid, '/', 'LX', Lx) CALL attach(fid, '/', 'LY', Ly) CALL attach(fid, '/', 'BETA', beta) CALL attach(fid, '/', 'OMEGA', omega) CALL attach(fid, '/', 'RELAX', relax) CALL attach(fid, '/', 'MAT_TYPE', mat_type) CALL attach(fid, '/', 'NITS', nits) CALL attach(fid, '/', 'LEVELS', levels) CALL attach(fid, '/', 'NNU', nnu) CALL attach(fid, '/', 'NU0', nu0) CALL attach(fid, '/', 'MU', mu) ! CALL putarr(fid, '/nu1', nu1(1:nnu)) CALL putarr(fid, '/nu2', nu2(1:nnu)) CALL putarr(fid, '/dense', dense) CALL creatg(fid, '/mglevels') DO l=1,levels WRITE(dsname,'("/mglevels/level.",i2.2)') l CALL creatg(fid, TRIM(dsname)) IF(mat_type.EQ.'csr') THEN CALL putmat(fid, TRIM(dsname)//'/mata', grids(l)%mata) ELSE CALL putmat(fid, TRIM(dsname)//'/mata', grids(l)%mata_cds) END IF 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/calc', grids(1)%v) CALL putarr(fid, '/solutions/anal', sol_ana2d) IF(nldirect) CALL putarr(fid, '/solutions/direct', sol_direct2d) ! nits=niter(nnu) 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 main