!> !> @file driv4.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 ! ! Computation of croos mass matrix between two splines sp1 & sp2 ! sp1 and sp2 can be splines of any type (i.e. either set up with set_spline or ! set_splcoef) and of any order. ! USE bsplines USE matrix ! IMPLICIT NONE TYPE(gbmat) :: matm INTEGER :: mrows, ncols, kl, ku DOUBLE PRECISION, ALLOCATABLE :: avec(:,:), bvec(:,:), matfull(:,:) ! TYPE(zgbmat) :: zmatm DOUBLE COMPLEX, ALLOCATABLE :: zavec(:,:), zbvec(:,:) DOUBLE PRECISION :: dznrm2 ! TYPE(spline1d) :: sp1, sp2 INTEGER :: nx, nidbas1, nidbas2, ngauss INTEGER :: i, j DOUBLE PRECISION :: a, b, coefx(5) DOUBLE PRECISION, ALLOCATABLE :: xgrid(:) DOUBLE PRECISION, DIMENSION(:, :), POINTER :: MassMat LOGICAL :: periodic1, periodic2 NAMELIST /newrun/ nx, a, b, coefx, nidbas1, nidbas2, periodic1, periodic2 !=========================================================================== ! 1.0 Set up grids ! ! Read in data specific to run ! nx = 8 ! Number of intevals in x a = 0.0d0 ! Left boundary of interval b = 1.0d0 ! Right boundary of interval coefx(1:5) = (/1.0d0, 0.d0, 0.d0, 0.d0, 1.d0/) ! Mesh point distribution function periodic1 = .FALSE. periodic2 = .FALSE. nidbas1 = 3 nidbas2 = 2 READ(*,newrun) WRITE(*,newrun) ! ! Define grid/knots ! ALLOCATE(xgrid(0:nx)) xgrid(0 ) = a xgrid(nx) = b CALL meshdist(coefx, xgrid, nx) WRITE(*,'(a/(10f8.3))') 'XGRID', xgrid(0:nx) !=========================================================================== ! 2.0 Set up splines ! ngauss = 1 ! Gauss points initialized with set_spline are in fact not used ! for computing cross mass matrix ! First spline set up as for solving a PDE with FEMs CALL set_spline(nidbas1, ngauss, xgrid, sp1, periodic1) ! Second spline set up as for interpolation !!$ CALL set_splcoef(nidbas2, xgrid, sp2, periodic2) CALL set_spline(nidbas2, ngauss, xgrid, sp2, periodic2) WRITE(*,'(/a,i6,a,i6/(10(f8.3)))') 'KNOTS of spline sp1', LBOUND(sp1%knots), & & ':',UBOUND(sp1%knots), sp1%knots WRITE(*,'(/a,i6,a,i6/(10(f8.3)))') 'KNOTS of spline sp2', LBOUND(sp2%knots), & & ':',UBOUND(sp2%knots), sp2%knots WRITE(*,'(3(a,i5, 2x))') 'NX =', nx, 'DIM sp1 =', sp1%dim, 'DIM sp2 =', sp2%dim !=========================================================================== ! 3.0 Compute cross mass matrix ! CALL CompMassMatrix(sp1, sp2, a, b, MassMat) WRITE(*, "(a)") "Cross-mass matrix between splines sp1 & sp2:" DO i = 1, SIZE(MassMat, 1) WRITE(*, "(15f10.5)") (MassMat(i, j), j = 1, MIN(SIZE(MassMat, 2), 15)) END DO ! ! Should equal to 1 for splines i "not close to the boundaries": ! p1 .LT. i .LE. N ! WRITE(*,'(/a/(15f8.5))') 'Sum of cols * NX', SUM(MassMat,dim=2)*REAL(nx,8) !=========================================================================== ! 3.0 Use DGB matrice ! !!$ mrows = nx+nidbas1 !!$ ncols = nx+nidbas2 CALL get_dim(sp1, mrows) CALL get_dim(sp2, ncols) kl = nidbas1 ku = nidbas2 CALL init(kl, ku, ncols, 1, matm, mrows=mrows) WRITE(*,'(/a, 2i3)') 'Band matrix:, kl, ku =', kl, ku ! CALL CompMassMatrix(sp1, sp2, a, b, matm) ! DO i=1,SIZE(matm%val,1) WRITE(*,'(15f10.5)') matm%val(i,:) END DO ! WRITE(*,'(/a)') 'Full matrix' ALLOCATE(matfull(mrows,ncols)) matfull = 0.0d0 DO i=1,mrows CALL getrow(matm, i, matfull(i,:)) WRITE(*,'(15f10.5)') matfull(i,:) END DO WRITE(*,'(a,1pe12.4)') 'error =', MAXVAL(ABS(matfull-MassMat)) ! ! Check VMX ALLOCATE(avec(ncols,2)) ALLOCATE(bvec(mrows,2)) avec = 1.0d0 bvec = vmx(matm,avec)*REAL(nx,8) WRITE(*,'(a)') 'M*a, with a=1' DO j=1,2 WRITE(*,'(15f8.5)') bvec(:,j) END DO !=========================================================================== ! 4.0 Test complex version ! CALL init(kl, ku, ncols, 1, zmatm, mrows=mrows) CALL CompMassMatrix(sp1, sp2, a, b, zmatm) ALLOCATE(zavec(ncols,2)) ALLOCATE(zbvec(mrows,2)) zavec = (1.0d0,0.0d0) zbvec = vmx(zmatm,zavec)*REAL(nx,8) zbvec = zbvec-bvec WRITE(*,'(/a)') 'Check complex version' WRITE(*,'(a,2(1pe12.4))') 'Norm of errors =', & & (dznrm2(mrows, zbvec(1,j), 1), j=1,2) zmatm%val = zmatm%val-matm%val WRITE(*,'(a,1pe12.4)') 'Diff of matrix elements =', MAXVAL(ABS(zmatm%val)) !=========================================================================== ! 9.0 Epilogue ! DEALLOCATE(MassMat) DEALLOCATE(xgrid) CALL destroy_sp(sp1) CALL destroy_sp(sp2) call destroy(matm) END PROGRAM main !+++ SUBROUTINE meshdist(c, x, nx) ! ! Construct an 1d non-equidistant mesh given a ! mesh distribution function. ! 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 !+++