!> !> @file pde1d_eig_zmumps.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 ! ! Solving the following 1d differential eqation using splines: ! ! Solve the standard eigenvalue: ! A*x = \lambda *x or inv(A)*x = 1/\lambda * x using Arpack and MUMPS. ! where A is obtained from discretozation of ! -d/dr[r d/dr] f = k^2 r^(k-1) ! USE bsplines USE mumps_bsplines USE futils IMPLICIT NONE INTEGER :: nx, nidbas, ngauss, kdiff INTEGER :: i, nrank LOGICAL :: nlppform DOUBLE PRECISION :: coefs(5) DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: xgrid TYPE(spline1d) :: splx TYPE(zmumps_mat) :: mat ! INTEGER :: ierr INTEGER :: fid CHARACTER(len=32) :: str DOUBLE PRECISION :: seconds, t0, tmat, tfac, tarpack ! ! Arpack: Solve the standard eigenvalue problem ! INTEGER :: nev = 10, ncv = 10 LOGICAL :: nlinv = .FALSE. ! Solve inv(A) = 1/\lambda * x if nlinv=.TRUE. CHARACTER(len=2) :: which='SM' INTEGER :: info=0 ! Use random vector to start the Arnoldi iterations INTEGER :: ido=0 ! Reverse communications LOGICAL :: rvec LOGICAL, ALLOCATABLE :: select(:) INTEGER :: iparam(11), ipntr(14), nconv DOUBLE PRECISION :: tol=0.0d0 CHARACTER(len=1) :: bmat='I' ! INTEGER :: lworkl DOUBLE COMPLEX, ALLOCATABLE :: workl(:), workd(:), workev(:) DOUBLE COMPLEX, ALLOCATABLE :: eig_vals(:), eig_vecs(:,:), resid(:) DOUBLE COMPLEX :: sigma DOUBLE PRECISION, ALLOCATABLE :: rwork(:) ! INTERFACE SUBROUTINE dismat(spl, mat) USE bsplines USE mumps_bsplines TYPE(spline1d), INTENT(in) :: spl TYPE(zmumps_mat), INTENT(inout) :: mat END SUBROUTINE dismat SUBROUTINE disrhs(kdiff, spl, rhs) USE bsplines INTEGER, INTENT(in) :: kdiff TYPE(spline1d), INTENT(in) :: spl DOUBLE PRECISION, INTENT(out) :: rhs(:) END SUBROUTINE disrhs SUBROUTINE meshdist(coefs, x, nx) DOUBLE PRECISION, INTENT(in) :: coefs(5) INTEGER, INTENT(iN) :: nx DOUBLE PRECISION, INTENT(inout) :: x(0:nx) END SUBROUTINE meshdist END INTERFACE ! NAMELIST /newrun/ nx, nidbas, ngauss, kdiff, nlppform, coefs, & & nev, ncv, nlinv, which, tol !=========================================================================== ! 1.0 Prologue ! CALL mpi_init(ierr) ! ! Read in data specific to run ! nx = 8 ! Number oh intevals in x nidbas = 3 ! Degree of splines ngauss = 4 ! Number of Gauss points/interval kdiff = 2 ! Exponent of differential problem nlppform = .TRUE. ! Use PPFORM for gridval or not coefs(1:5) = (/0.2d0, 1.d0, 0.d0, 0.d0, 1.d0/) ! Mesh point distribution function ! see function FDIST in MESHDIST ! READ(*,newrun) WRITE(*,newrun) ! ! Define grid on x axis ! ALLOCATE(xgrid(0:nx)) xgrid(0) = 0.0d0 xgrid(nx) = 1.0d0 CALL meshdist(coefs, xgrid, nx) WRITE(*,'(a/(10f8.3))') 'XGRID', xgrid(0:nx) WRITE(*,'(a/(10f8.3))') 'Diff XGRID', (xgrid(i)-xgrid(i-1), i=1,nx) WRITE(*,'(a/(10f8.3))') 'Diff XGRID**2', (xgrid(i)**2-xgrid(i-1)**2, i=1,nx) !=========================================================================== ! 2.0 Discretize the PDE ! ! Set up spline ! t0 = seconds() CALL set_spline(nidbas, ngauss, xgrid, splx, nlppform=nlppform) CALL get_dim(splx, nrank) ! Rank of the FE matrix WRITE(*,'(a,l6)') 'splx%nlequid', splx%nlequid ! ! FE matrix assembly ! WRITE(*,'(a,i6)') ' nrank', nrank CALL init(nrank, 1, mat) CALL dismat(splx, mat) CALL to_mat(mat) tmat = seconds() - t0 ! mat%mumps_par%IRN => mat%mumps_par%IRN_loc ! Work around for single proc. PRINT*, 'nnz_loc', mat%nnz_loc PRINT*, 'mat%mumps_par%N', mat%mumps_par%N PRINT*, 'mat%mumps_par%NZ_loc', mat%mumps_par%NZ_loc PRINT*, 'size of mat%mumps_par%IRN', SIZE(mat%mumps_par%IRN) PRINT*, 'mat%istart,mat%iend', mat%istart,mat%iend ! CALL creatf('pde1d_eig_zmumps.h5', fid, 'PDE1D Result File', real_prec='d') PRINT*, 'rank', mat%rank PRINT*, 'nnz', mat%nnz ! CALL putmat(fid,'/MAT',mat,'FE matrix') ! IF(nlinv) THEN t0 = seconds() CALL factor(mat) tfac = seconds()-t0 END IF !=========================================================================== ! 3.0 Solve the standard eigenvalue problem ! lworkl = 3*ncv**2 + 5*ncv ALLOCATE(workl(lworkl)) ALLOCATE(workd(3*nrank)) ALLOCATE(workev(2*ncv)) ALLOCATE(eig_vals(ncv), eig_vecs(nrank,ncv)) ALLOCATE(resid(nrank)) ALLOCATE(rwork(ncv)) ! iparam(1) = 1 ! shfts iparam(3) = 300 ! Max. number of iterations iparam(7) = 1 ! Regular mode ! ! The reverse communication loop ! t0 = seconds() DO CALL znaupd (ido, bmat, nrank, which, nev, tol, resid, ncv, & & eig_vecs, nrank, iparam, ipntr, workd, workl, lworkl, & & rwork, info ) ! IF(ido .EQ. -1 .OR. ido .EQ. 1) THEN ! Compute A*v CALL av(nrank, workd(ipntr(1)), workd(ipntr(2))) CYCLE END IF ! IF(info .LT. 0) THEN ! Error PRINT*, 'Error in _naupd with info =', info EXIT ELSE rvec = .TRUE. ALLOCATE(select(ncv)) CALL zneupd (rvec, 'A', select, eig_vals, eig_vecs, nrank, & & sigma, workev, bmat, nrank, which, nev, tol, resid, & & ncv, eig_vecs, nrank, iparam, ipntr, workd, workl, lworkl,& & rwork, ierr) IF(ierr .NE. 0) THEN PRINT*, 'Error in _neupd with ierr =', ierr EXIT ELSE nconv = iparam(5) PRINT*,'Number of converged eigenvalues', nconv IF(nlinv) THEN eig_vals(1:nconv) = (1.d0,0.0d0) / eig_vals(1:nconv) END IF WRITE(*,'(2(1pe12.3))') eig_vals(1:nconv) CALL putarr(fid, '/eig_vals', eig_vals(1:nconv)) CALL putarr(fid, '/eig_vecs', eig_vecs(1:nrank,1:nconv)) DO i=1,nconv !!$ WRITE(*,'(/a,2(pe20.6))') '*** eigen value =', eig_vals(i) !!$ WRITE(*,'(a/(10(1pe12.4)))') 'Real of eigen vector', & !!$ & REAL(eig_vecs(1:nrank,i)) !!$ WRITE(*,'(a/(10(1pe12.4)))') 'Imag of eigen vector', & !!$ & aimag(eig_vecs(1:nrank,i)) WRITE(str,'(a,i3.3)') '/eig_vecs_',i CALL putarr(fid, TRIM(str), eig_vecs(1:nrank,i)) END DO EXIT END IF END IF END DO ! End of reverse commuinication loop IF(info .EQ. 1) THEN PRINT*, 'Maximum number of iterations reached!' PRINT*, 'IPARAM(5) =', iparam(5) END IF PRINT*, 'Number of Arnoldi iterations', iparam(3) tarpack = seconds() - t0 !=========================================================================== ! 9.0 Epilogue ! WRITE(*,'(a)') '---' WRITE(*,'(a,1pe12.3)') 'Matrice construction time (s) ', tmat IF(nlinv) THEN WRITE(*,'(a,1pe12.3)') 'Matrice factorization time (s) ', tfac END IF WRITE(*,'(a,1pe12.3)') 'Arpack time (s) ', tarpack ! DEALLOCATE(xgrid) CALL destroy(mat) CALL destroy_sp(splx) CALL closef(fid) CALL mpi_finalize(ierr) ! CONTAINS SUBROUTINE av(n,v,w) ! INTEGER, INTENT(in) :: n DOUBLE COMPLEX, INTENT(in) :: v(*) DOUBLE COMPLEX, INTENT(out) :: w(*) ! IF(nlinv) THEN w(1:n) = v(1:n) CALL bsolve(mat,w(1:n)) ! Solve A*w = v or w=inv(A)*v ELSE w(1:n) = vmx(mat,v(1:n)) ! A*v matrix product END IF END SUBROUTINE av ! SUBROUTINE coefeq(x, idt, idw, c) DOUBLE PRECISION, INTENT(in) :: x INTEGER, INTENT(out) :: idt(:), idw(:) DOUBLE PRECISION, INTENT(out) :: c(:) ! ! Weak form = Int(x*dw/dx*dt/dx)dx ! c(1) = x idt(1) = 1 idw(1) = 1 END SUBROUTINE coefeq END PROGRAM main ! !+++ SUBROUTINE meshdist(c, x, nx) ! ! Construct an 1d non-equidistant mesh given a ! mesh distribution function defined in FDIST ! 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 !+++ SUBROUTINE dismat(spl, mat) ! ! Assembly FE matrix mat using spline spl ! USE bsplines USE mumps_bsplines IMPLICIT NONE TYPE(spline1d), INTENT(in) :: spl TYPE(zmumps_mat), INTENT(inout) :: mat INTEGER :: nrank, nx, nidbas, ngauss INTEGER :: i, igauss, iterm, iw, jt, irow, jcol DOUBLE PRECISION, ALLOCATABLE :: fun(:,:) DOUBLE PRECISION, ALLOCATABLE :: xgauss(:), wgauss(:) DOUBLE COMPLEX :: contrib ! INTEGER :: kterms ! Number of terms in weak form INTEGER, ALLOCATABLE :: idert(:), iderw(:) ! Derivative order DOUBLE PRECISION, ALLOCATABLE :: coefs(:) !=========================================================================== ! 1.0 Prologue ! ! Properties of spline space ! CALL get_dim(spl, nrank, nx, nidbas) ALLOCATE(fun(0:nidbas,0:1)) ! Spline and its 1st derivative ! ! Weak form ! kterms = mat%nterms ALLOCATE(idert(kterms), iderw(kterms), coefs(kterms)) ! ! Gauss quadature ! CALL get_gauss(spl, ngauss) ALLOCATE(xgauss(ngauss), wgauss(ngauss)) !=========================================================================== ! 2.0 Assembly loop ! DO i=1,nx CALL get_gauss(spl, ngauss, i, xgauss, wgauss) DO igauss=1,ngauss CALL basfun(xgauss(igauss), spl, fun, i) CALL coefeq(xgauss(igauss), idert, iderw, coefs) DO iterm=1,kterms DO jt=0,nidbas DO iw=0,nidbas contrib = fun(jt,idert(iterm)) * coefs(iterm) * & & fun(iw,iderw(iterm)) * wgauss(igauss) irow=i+iw; jcol=i+jt CALL updtmat(mat, irow, jcol, contrib) END DO END DO END DO END DO END DO !=========================================================================== ! 9.0 Epilogue ! DEALLOCATE(fun) DEALLOCATE(xgauss, wgauss) DEALLOCATE(iderw, idert, coefs) ! CONTAINS SUBROUTINE coefeq(x, idt, idw, c) DOUBLE PRECISION, INTENT(in) :: x INTEGER, INTENT(out) :: idt(:), idw(:) DOUBLE PRECISION, INTENT(out) :: c(SIZE(idt)) ! ! Weak form = Int(x*dw/dx*dt/dx)dx ! c(1) = x idt(1) = 1 idw(1) = 1 END SUBROUTINE coefeq END SUBROUTINE dismat !+++ SUBROUTINE disrhs(kdiff, spl, rhs) ! ! Assenbly the RHS using spline spl ! USE bsplines IMPLICIT NONE INTEGER, INTENT(in) :: kdiff TYPE(spline1d), INTENT(in) :: spl DOUBLE PRECISION, INTENT(out) :: rhs(:) INTEGER :: nrank, nx, nidbas, ngauss INTEGER :: i, igauss, left DOUBLE PRECISION, ALLOCATABLE :: fun(:,:) DOUBLE PRECISION, ALLOCATABLE :: xgauss(:), wgauss(:) DOUBLE PRECISION:: contrib !=========================================================================== ! 1.0 Prologue ! ! Properties of spline space ! CALL get_dim(spl, nrank, nx, nidbas) !!$ WRITE(*,'(/a, 5i3)') 'nrank, nx, nidbas =', nrank, nx, nidbas ! ALLOCATE(fun(nidbas+1,1)) ! needs only basis functions (no derivatives) ! ! Gauss quadature ! CALL get_gauss(spl, ngauss) !!$ WRITE(*,'(/a, i3)') 'Gauss points and weights, ngauss =', ngauss ALLOCATE(xgauss(ngauss), wgauss(ngauss)) !=========================================================================== ! 2.0 Assembly loop ! rhs(1:nrank) = 0.0d0 ! DO i=1,nx CALL get_gauss(spl, ngauss, i, xgauss, wgauss) !!$ WRITE(*,'(a,i3,(8f10.4))') 'i=',i, xgauss, wgauss DO igauss=1,ngauss CALL basfun(xgauss(igauss), spl, fun, i) left = i-1 !!$ WRITE(*,'(a,5i4)') '>>>igauss, left', igauss, left contrib = wgauss(igauss)*rhseq(xgauss(igauss), kdiff) rhs(i:i+nidbas) = rhs(i:i+nidbas) + contrib*fun(:,1) END DO END DO !=========================================================================== ! 9.0 Epilogue ! DEALLOCATE(fun) DEALLOCATE(xgauss, wgauss) ! CONTAINS DOUBLE PRECISION FUNCTION rhseq(x,k) DOUBLE PRECISION, INTENT(in) :: x INTEGER, INTENT(in) :: k rhseq = k*k*x**(k-1) END FUNCTION rhseq END SUBROUTINE disrhs