!>
!> @file poisson_mumps.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
USE mumps_bsplines
USE cds
!
IMPLICIT NONE
INCLUDE 'mpif.h'
TYPE(mumps_mat) :: amat
TYPE(cds_mat) :: amat_cds
DOUBLE PRECISION, ALLOCATABLE :: rhs(:), sol(:), arow(:)
INTEGER :: nx=5, ny=4
INTEGER :: n
INTEGER :: i, j, irow
INTEGER :: ierr, me
INTEGER, ALLOCATABLE :: dists(:)
DOUBLE PRECISION :: t0
!
CALL mpi_init(ierr)
CALL mpi_comm_rank(MPI_COMM_WORLD, me, ierr)
!
IF(me.EQ.0) THEN
WRITE(*,'(a)', advance='no') 'Enter nx, ny: '
READ(*,*) nx, ny
END IF
CALL mpi_bcast(nx, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
CALL mpi_bcast(ny, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
n = nx*ny ! Rank of the matrix
ALLOCATE(rhs(n))
ALLOCATE(sol(n))
ALLOCATE(arow(n))
!
WRITE(*,'(/a)') 'Mumps using CSR mat ...'
CALL init(n, 1, amat)
!
! Construct the matrix and RHS
!
t0 = mpi_wtime(0)
DO j=1,ny
DO i=1,nx
arow = 0.0d0
irow = numb(i,j)
arow(irow) = 4.0d0
IF(i.GT.1) arow(numb(i-1,j)) = -1.0d0
IF(i.LT.nx) arow(numb(i+1,j)) = -1.0d0
IF(j.GT.1) arow(numb(i,j-1)) = -1.0d0
IF(j.LT.ny) arow(numb(i,j+1)) = -1.0d0
CALL putrow(amat, irow, arow)
rhs(irow) = SUM(arow) ! => the exact solution is 1
END DO
END DO
!
WRITE(*,'(a,i6)') 'Rank of matrix', n
WRITE(*,'(a,i6)') 'Number of non-zeros of matrix', get_count(amat)
WRITE(*,'(a,1pe12.3)') 'Matrix construction time (s)', mpi_wtime()-t0
!
! Factor the amat matrix (Reordering, symbolic and numerical factorization)
!
t0 = mpi_wtime(0)
CALL factor(amat, nlmetis=.TRUE.)
sol=rhs
CALL bsolve(amat, sol)
WRITE(*,'(a,1pe12.3)') 'Direct solve time (s)', mpi_wtime()-t0
!
IF(me.EQ.0) THEN
WRITE(*,'(a,1pe12.3)') 'Error', MAXVAL(ABS(sol-1.0d0))
END IF
CALL destroy(amat)
!
! CDS matrix
!
WRITE(*,'(/a)') 'Mumps using CDS mat ...'
IF(ALLOCATED(dists)) DEALLOCATE(dists)
ALLOCATE(dists(-2:2))
dists = [-nx, -1, 0, 1, nx]
WRITE(*,'(a/(20i4))') 'dists used in INIT =', dists
CALL init(n, dists, 1, amat_cds)
!
t0 = mpi_wtime(0)
DO j=1,ny
DO i=1,nx
arow = 0.0d0
irow = numb(i,j)
amat_cds%val(irow,0) = 4.0d0
IF(i.GT.1) amat_cds%val(irow,-1) = -1.0d0
IF(i.LT.nx) amat_cds%val(irow,+1) = -1.0d0
IF(j.GT.1) amat_cds%val(irow,-2) = -1.0d0
IF(j.LT.ny) amat_cds%val(irow,+2) = -1.0d0
END DO
END DO
WRITE(*,'(a,1pe12.3)') 'Matrix construction time (s)', mpi_wtime()-t0
!
! Compute dists of amat
PRINT*, 'stat of mata%mat', ASSOCIATED(amat%mat)
PRINT*, 'rank of mata', amat%mat%rank
CALL mstruct(amat%mat, dists)
WRITE(*,'(A/(20i4))') 'dists from MSTRUCT=', dists
!
t0 = mpi_wtime(0)
CALL cds2mumps(amat_cds, amat)
CALL factor(amat, debug=.FALSE.)
sol = rhs
CALL bsolve(amat, sol)
WRITE(*,'(a,1pe12.3)') 'Direct solve time (s)', mpi_wtime()-t0
!
IF(me.EQ.0) THEN
WRITE(*,'(a,1pe12.3)') 'Error', MAXVAL(ABS(sol-1.0d0))
END IF
!
! Clean up
!
DEALLOCATE(rhs)
DEALLOCATE(sol)
DEALLOCATE(arow)
CALL destroy(amat)
CALL mpi_finalize(ierr)
CONTAINS
SUBROUTINE mstruct(mat, dists)
TYPE(spmat), INTENT(in) :: mat
INTEGER, ALLOCATABLE, INTENT(inout) :: dists(:)
TYPE(elt), POINTER :: t
INTEGER :: n, i, j0
j0 = LBOUND(dists,1)
n = mat%rank
PRINT*, 'rank of mat', n
DO i=1,n ! scan the matrix rows
t => mat%row(i)%row0
DO WHILE(ASSOCIATED(t)) ! walk thru the linked list row(i)
j = t%index
IF(ABS(t%val) .LE. EPSILON(0.0d0)) THEN
dists(j0) = t%index-i ! distance from main diag
j0 = j0+1
END IF
t => t%next
END DO
END DO
END SUBROUTINE mstruct
INTEGER FUNCTION numb(i,j)
!
! One-dimensional numbering
! Number first x then y
!
INTEGER, INTENT(in) :: i, j
numb = (j-1)*nx + i
END FUNCTION numb
END PROGRAM main