!>
!> @file test_stencil.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 mod
USE iso_fortran_env, ONLY : real64
IMPLICIT NONE
!
INTEGER, PARAMETER :: rkind = real64
LOGICAL, PARAMETER :: nldebug=.FALSE.
REAL(rkind), PARAMETER :: PI=4.d0*ATAN(1.0d0)
CONTAINS
END MODULE mod
!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
PROGRAM main
USE mpi
USE pputils2, ONLY : dist1d, exchange, norm2_vec=>ppnorm2
USE stencil, ONLY : stencil_2d, init, laplacian, vmx, putmat
USE mod
IMPLICIT NONE
!
INTEGER, PARAMETER :: ndims=2
!
INTEGER :: me, neighs(4), npes, ierr
INTEGER, DIMENSION(ndims) :: dims=[0,0]
INTEGER, DIMENSION(ndims) :: coords, comm1d
LOGICAL, DIMENSION(ndims) :: periods=[.FALSE.,.FALSE.]
LOGICAL :: reorder =.FALSE.
INTEGER :: comm_cart
!
INTEGER :: nx=4, ny=4 ! Number of intervals
INTEGER, DIMENSION(ndims) :: e, s, lb, ub, npt_glob, npt_loc
!
REAL(rkind), ALLOCATABLE :: xgrid(:), ygrid(:)
REAL(rkind), ALLOCATABLE :: arr(:,:), fexact(:,:)
REAL(rkind), ALLOCATABLE :: barr1(:,:), barr2(:,:), barr3(:,:)
REAL(rkind) :: dx, dy
REAL(rkind) :: err
INTEGER, DIMENSION(5,2) :: id ! 5-point stencil
INTEGER :: npoints
TYPE(stencil_2d) :: mat
INTEGER :: i, j
!
NAMELIST /in/ nx, ny
!================================================================================
! 1.0 Prologue
!
! 2D process grid
CALL mpi_init(ierr)
CALL mpi_comm_rank(MPI_COMM_WORLD, me, ierr)
CALL mpi_comm_size(MPI_COMM_WORLD, npes, ierr)
CALL mpi_dims_create(npes, ndims, dims, ierr)
CALL mpi_cart_create(MPI_COMM_WORLD, ndims, dims, periods, reorder, comm_cart,&
& ierr)
!
CALL mpi_comm_rank(comm_cart, me, ierr)
CALL mpi_cart_coords(comm_cart, me, ndims, coords, ierr)
CALL mpi_cart_shift(comm_cart, 0, 1, neighs(1), neighs(2), ierr)
CALL mpi_cart_shift(comm_cart, 1, 1, neighs(3), neighs(4), ierr)
!
CALL mpi_cart_sub(comm_cart, [.TRUE.,.FALSE.], comm1d(1), ierr)
CALL mpi_cart_sub(comm_cart, [.FALSE.,.TRUE.], comm1d(2), ierr)
!
! Read problem inputs
IF(me.EQ.0) THEN
READ(*,in)
WRITE(*,in)
END IF
CALL mpi_bcast(nx, 1, MPI_INTEGER, 0, comm_cart, ierr)
CALL mpi_bcast(ny, 1, MPI_INTEGER, 0, comm_cart, ierr)
!================================================================================
! 2.0 2d Grid construction
!
! Partition 2D grid
npt_glob(1) = nx+1
npt_glob(2) = ny+1
DO i=1,ndims
CALL dist1d(comm1d(i), 0, npt_glob(i), s(i), npt_loc(i))
e(i) = s(i) + npt_loc(i) - 1
END DO
WRITE(*,'(a,i3.3,a,2(3i4,", "))') 'PE', me, ' coords, s,e:', &
& (coords(i),s(i),e(i),i=1,ndims)
!
! Global mesh
dx = 1.0d0/REAL(nx)
dy = 1.0d0/REAL(ny)
ALLOCATE(xgrid(0:nx))
ALLOCATE(ygrid(0:ny))
xgrid = [ (i*dx, i=0,nx) ]
ygrid = [ (i*dy, i=0,ny) ]
!================================================================================
! 3.0 FD Laplacian
!
id=RESHAPE([ 0, -1, 0, 1, 0, &
0, 0,-1, 0, 1], &
[5,2])
npoints = 5
CALL init(s, e, id, .FALSE., mat, comm_cart)
!
CALL laplacian(dx, dy, mat)
!================================================================================
! 4.0 Check matrice-vector product
!
! Local arrays with ghost cells
lb = mat%s-1
ub = mat%e+1
ALLOCATE(arr(lb(1):ub(1),lb(2):ub(2)))
ALLOCATE(fexact(lb(1):ub(1),lb(2):ub(2)))
ALLOCATE(barr1(lb(1):ub(1),lb(2):ub(2)))
ALLOCATE(barr2(lb(1):ub(1),lb(2):ub(2)))
ALLOCATE(barr3(lb(1):ub(1),lb(2):ub(2)))
!
! Constant vector => Laplacian = 0
barr1 = 0
arr = 1.0
barr1 = vmx(mat,arr)
IF(mat%s(1).EQ.0) barr1(0,:) = 0.0 ! discard boundary values
IF(mat%e(1).EQ.nx) barr1(nx,:) = 0.0
IF(mat%s(2).EQ.0) barr1(:,0) = 0.0
IF(mat%e(2).EQ.ny) barr1(:,ny) = 0.0
err = norm2_vec(barr1,comm_cart,root=0,garea=[1,1])
IF(me.EQ.0) THEN
WRITE(*,'(/a,1pe12.3)') 'Constant vector: ||B1|| =', err
END IF
!
! Bilinear vector => Laplacian = 0
arr =0.0d0
barr2=0.0d0
DO j=mat%s(2),mat%e(2)
DO i=mat%s(1),mat%e(1)
arr(i,j) = xgrid(i)*ygrid(j)
END DO
END DO
CALL exchange(comm_cart, arr)
barr2 = vmx(mat,arr)
IF(mat%s(1).EQ.0) barr2(0,:) = 0.0 ! discard boundary values
IF(mat%e(1).EQ.nx) barr2(nx,:) = 0.0
IF(mat%s(2).EQ.0) barr2(:,0) = 0.0
IF(mat%e(2).EQ.ny) barr2(:,ny) = 0.0
err = norm2_vec(barr2, comm_cart,root=0,garea=[1,1])
IF(me.EQ.0) THEN
WRITE(*,'(a,1pe12.3)') 'Bilinear vector: ||B2|| =', err
END IF
!
! Biquadratic vector => Laplacian = fexact
DO j=mat%s(2),mat%e(2)
DO i=mat%s(1),mat%e(1)
arr(i,j) = (xgrid(i)*ygrid(j))**2/4.0d0
fexact(i,j) = (xgrid(i)**2 + ygrid(j)**2)/2.0d0
END DO
END DO
CALL exchange(comm_cart, arr)
CALL exchange(comm_cart, fexact)
barr3 = vmx(mat,arr) - fexact
IF(mat%s(1).EQ.0) barr3(0,:) = 0.0 ! discard boundary values
IF(mat%e(1).EQ.nx) barr3(nx,:) = 0.0
IF(mat%s(2).EQ.0) barr3(:,0) = 0.0
IF(mat%e(2).EQ.ny) barr3(:,ny) = 0.0
err = norm2_vec(barr3,comm_cart,root=0,garea=[1,1])
IF(me.EQ.0) THEN
WRITE(*,'(a,1pe12.3)') 'Biquadratic vector: ||B3|| =', err
END IF
!================================================================================
! 9.0 Epilogue
CALL h5file
CALL MPI_FINALIZE(ierr)
CONTAINS
SUBROUTINE disp(str, arr)
CHARACTER(len=*), INTENT(in) :: str
REAL(rkind), INTENT(in) :: arr(:,:)
INTEGER :: j
WRITE(*,'(/a)') str
DO j=1,SIZE(arr,2)
WRITE(*,'(10f8.3)') arr(:,j)
END DO
END SUBROUTINE disp
!
SUBROUTINE h5file
USE futils
CHARACTER(len=128) :: file='test_stencil.h5'
INTEGER :: fid
CALL creatf(file, fid, real_prec='d', mpicomm=comm_cart)
CALL putarr(fid, '/xgrid', xgrid, ionode=0) ! only rank 0 does IO
CALL putarr(fid, '/ygrid', ygrid, ionode=0) ! only rank 0 does IO
CALL putarrnd(fid, '/barr1', barr1,(/1,2/), garea=(/1,1/))
CALL putarrnd(fid, '/barr2', barr2,(/1,2/), garea=(/1,1/))
CALL putarrnd(fid, '/barr3', barr3,(/1,2/), garea=(/1,1/))
CALL putmat(fid, '/MAT', mat)
CALL closef(fid)
END SUBROUTINE h5file
!
FUNCTION outerprod(x, y) RESULT(r)
!
! outer product
!
REAL(rkind), INTENT(in) :: x(:), y(:)
REAL(rkind) :: r(SIZE(x),SIZE(y))
INTEGER :: i, j
DO j=1,SIZE(y)
DO i=1,SIZE(x)
r(i,j) = x(i)*y(j)
END DO
END DO
END FUNCTION outerprod
!
FUNCTION rhs(x,y)
REAL(rkind), INTENT(in) :: x(:), y(:)
REAL(rkind) :: rhs(SIZE(x),SIZE(y))
rhs = -10.d0*pi**2 * outerprod(SIN(pi*x), SIN(3.d0*pi*y))
END FUNCTION rhs
!
FUNCTION exact(x,y)
REAL(rkind), INTENT(in) :: x(:), y(:)
REAL(rkind) :: exact(SIZE(x),SIZE(y))
exact = outerprod(SIN(pi*x), SIN(3.d0*pi*y))
END FUNCTION exact
END PROGRAM main