!> !> @file poisson_mod.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 !> MODULE poisson_mod IMPLICIT NONE ! ! Dirichlet BC encapsulated in a derived datatype ! TYPE dirich INTEGER :: meth, mbess, n1, n2, nidbas1, nidbas2 INTEGER :: i0, i1 DOUBLE PRECISION, POINTER :: amat(:,:) => NULL() DOUBLE PRECISION, POINTER :: g(:) => NULL() END TYPE dirich ! CONTAINS SUBROUTINE disrhs(mbess, spl, rhs) ! ! Assembly the RHS using 2d spline spl ! USE bsplines INTEGER, INTENT(in) :: mbess TYPE(spline2d), INTENT(in) :: spl DOUBLE PRECISION, INTENT(out) :: rhs(:) ! ! The RHS is 0 ! rhs(:) = 0.0d0 END SUBROUTINE disrhs ! SUBROUTINE ibcmat(mat, bc) ! ! Apply BC on matrix ! USE matrix TYPE(pbmat), INTENT(inout) :: mat TYPE(dirich) :: bc INTEGER :: ny INTEGER :: kl, ku, nrank, i, j, k DOUBLE PRECISION, ALLOCATABLE :: zsum(:), arr(:) INTEGER :: i0, i1, ii !=========================================================================== ! 1.0 Prologue ! ku = mat%ku kl = ku nrank = mat%rank ny = bc%n2 !=========================================================================== ! 2.0 Unicity at the axis ! ! The vertical sum on the NY-th row ! ALLOCATE(zsum(nrank)) ALLOCATE(arr(nrank)) zsum = 0.0d0 DO i=1,ny arr = 0.0d0 CALL getrow(mat, i, arr) DO j=1,ny+ku zsum(j) = zsum(j) + arr(j) END DO END DO ! zsum(ny) = SUM(zsum(1:ny)) ! using symmetry CALL putrow(mat, ny, zsum) DEALLOCATE(zsum) ! ! The away operator ! DO i = 1,ny-1 arr = 0.0d0; arr(i) = 1.0d0 CALL putrow(mat, i, arr) END DO DEALLOCATE(arr) !=========================================================================== ! 3.0 Dirichlet on right boundary ! !!$ i0 = nrank - ku !!$ i1 = nrank - ny i0 = (bc%n1-1)*bc%n2 + 1 i1 = nrank - bc%n2 bc%i0 = i0 bc%i1 = i1 ! IF(ASSOCIATED(bc%amat)) DEALLOCATE(bc%amat) IF(ASSOCIATED(bc%g)) DEALLOCATE(bc%g) ALLOCATE(bc%amat(i0:i1,ny)) ALLOCATE(bc%g(ny)) ! WRITE(*,'(/a,2i6)') 'IBCMAT: i0, i1 =', i0, i1 ! ! Extract and save the last ny columns of matrix ! ALLOCATE(arr(nrank)) DO k=1,ny j = nrank-ny+k CALL getcol(mat, j, arr) bc%amat(i0:i1,k) = arr(i0:i1) IF( ANY(arr(1:i0-1) .NE. 0.0d0) ) THEN WRITE(*,'(a,i4)') 'i0 is underestimated for j =', j END IF END DO ! ! The away operator ! DO k=1,ny j = nrank-ny+k arr = 0.0d0; arr(j) = 1.0d0 CALL putrow(mat, j, arr) END DO ! DEALLOCATE(arr) ! END SUBROUTINE ibcmat !+++ SUBROUTINE ibcrhs(rhs, ygrid, bc) ! ! Apply BC on RHS ! DOUBLE PRECISION, INTENT(inout) :: rhs(:) DOUBLE PRECISION, INTENT(in) :: ygrid(:) TYPE(dirich) :: bc ! INTEGER :: nrank, ny, m, i0, i1 DOUBLE PRECISION :: zsum !=========================================================================== ! 1.0 Prologue ! nrank = SIZE(rhs,1) ny = bc%n2 m = bc%mbess !=========================================================================== ! 2.0 Unicity at the axis ! ! The vertical sum ! zsum = SUM(rhs(1:ny)) rhs(ny) = zsum rhs(1:ny-1) = 0.0d0 !=========================================================================== ! 3.0 Dirichlet on right boundary ! ! Get spline coefs at boundary r=1 ! SELECT CASE (bc%meth) CASE(1) CALL dirich_interp(ygrid, bc, frhs) CASE(2) CALL dirich_minres(ygrid, bc, frhs) END SELECT ! ! Modify RHS ! i0 = bc%i0 i1 = bc%i1 rhs(i0:i1) = rhs(i0:i1) - MATMUL(bc%amat, bc%g) rhs(i1+1:nrank) = bc%g(1:ny) CONTAINS DOUBLE PRECISION FUNCTION frhs(x) DOUBLE PRECISION, INTENT(in) :: x frhs = COS(m*x) END FUNCTION frhs END SUBROUTINE ibcrhs !++++ SUBROUTINE coefeq(x, y, idt, idw, c) DOUBLE PRECISION, INTENT(in) :: x, y INTEGER, INTENT(out) :: idt(:,:), idw(:,:) DOUBLE PRECISION, INTENT(out) :: c(:) ! ! Weak form = Int(x*dw/dx*dt/dx + 1/x*dw/dy*dt/dy)dxdy ! c(1) = x ! idt(1,1) = 1 idt(1,2) = 0 idw(1,1) = 1 idw(1,2) = 0 ! c(2) = 1.d0/x idt(2,1) = 0 idt(2,2) = 1 idw(2,1) = 0 idw(2,2) = 1 END SUBROUTINE coefeq !++++ SUBROUTINE dirich_interp(ygrid, bc, frhs) ! ! Dirichlet BC by interpolation ! USE bsplines DOUBLE PRECISION, INTENT(in) :: ygrid(:) TYPE(dirich) :: bc INTERFACE DOUBLE PRECISION FUNCTION frhs(x) DOUBLE PRECISION, INTENT(in) :: x END FUNCTION frhs END INTERFACE ! INTEGER :: nidbas, dim, n2, i DOUBLE PRECISION :: shifty DOUBLE PRECISION :: gval(SIZE(ygrid)) DOUBLE PRECISION, ALLOCATABLE :: coefs(:) DOUBLE PRECISION :: ygrid_interp(SIZE(ygrid)) TYPE(spline1d) :: spl_interp ! nidbas = bc%nidbas2 n2 = bc%n2 ! IF(MODULO(nidbas,2) .EQ. 0 ) THEN shifty = 0.5d0*(ygrid(2)-ygrid(1)) ygrid_interp(:) = ygrid(:) + shifty ELSE ygrid_interp(:) = ygrid(:) END IF CALL set_splcoef(nidbas, ygrid_interp, spl_interp, period=.TRUE.) CALL get_dim(spl_interp, dim) ALLOCATE(coefs(dim)) ! DO i=1,SIZE(ygrid) gval(i) = frhs(ygrid_interp(i)) END DO CALL get_splcoef(spl_interp, gval, coefs) ! ! Store spline coefs in bc ! bc%g(1:n2) = coefs(1:n2) ! DEALLOCATE(coefs) CALL destroy_sp(spl_interp) END SUBROUTINE dirich_interp !++++ SUBROUTINE dirich_minres(xgrid, bc, frhs) ! ! Dirichlet BC by minimization of residual ! USE bsplines USE matrix USE conmat_mod DOUBLE PRECISION, INTENT(in) :: xgrid(:) TYPE(dirich) :: bc INTERFACE DOUBLE PRECISION FUNCTION frhs(x) DOUBLE PRECISION, INTENT(in) :: x END FUNCTION frhs END INTERFACE ! INTEGER :: nx, nidbas, ngauss, kl, ku TYPE(periodic_mat) :: mass_mat TYPE(spline1d) :: spl ! nidbas = bc%nidbas2 ngauss = nidbas+1 nx = bc%n2 kl = nidbas ku = kl ! CALL set_spline(nidbas, ngauss, xgrid, spl, period=.TRUE.) CALL init(kl, ku, nx, 1, mass_mat) CALL conmat(spl, mass_mat, coefeq_mass) CALL conrhs(spl, bc%g, frhs) CALL factor(mass_mat) CALL bsolve(mass_mat, bc%g) ! CALL destroy(mass_mat) CALL destroy_sp(spl) ! CONTAINS SUBROUTINE coefeq_mass(x, idt, idw, c) DOUBLE PRECISION, INTENT(in) :: x INTEGER, INTENT(out) :: idt(:), idw(:) DOUBLE PRECISION, INTENT(out) :: c(:) c(1) = 1.0d0 idt(1) = 0 idw(1) = 0 END SUBROUTINE coefeq_mass END SUBROUTINE dirich_minres !++++ DOUBLE PRECISION FUNCTION err2_norm(spl, jder, mbess, fexact) ! ! Compute error L2 norm unsing Gauss points ! USE bsplines TYPE(spline2d) :: spl INTEGER, INTENT(in) :: jder(:) INTEGER, INTENT(in) :: mbess INTERFACE DOUBLE PRECISION FUNCTION fexact(m,x,y) INTEGER, INTENT(in) :: m DOUBLE PRECISION, INTENT(in) :: x, y END FUNCTION fexact END INTERFACE ! DOUBLE PRECISION, POINTER :: xg1(:,:), xg2(:,:), wg1(:,:), wg2(:,:) INTEGER :: i1, ig1, n1, nidbas1, ndim1, ng1 INTEGER :: i2, ig2, n2, nidbas2, ndim2, ng2 DOUBLE PRECISION :: contrib DOUBLE PRECISION, ALLOCATABLE :: x(:), y(:), sol(:,:) ! ! Gauss points and weights on all intervals ! CALL get_dim(spl%sp1, ndim1, n1, nidbas1) CALL get_dim(spl%sp2, ndim2, n2, nidbas2) xg1 => spl%sp1%gausx ! xg1(ng1,n1) wg1 => spl%sp1%gausw ! wg1(ng1,n1) ng1 = SIZE(xg1,1) xg2 => spl%sp2%gausx wg2 => spl%sp2%gausw ng2 = SIZE(xg2,1) ! err2_norm = 0.0d0 ALLOCATE(x(ng1), y(ng2)) ALLOCATE(sol(ng1,ng2)) DO i1=1,n1 x=xg1(:,i1) DO i2=1,n2 y=xg2(:,i2) CALL gridval(spl, x, y, sol, jder) DO ig1=1,ng1 DO ig2=1,ng2 contrib = wg1(ig1,i1)*wg2(ig2,i2)*(sol(ig1,ig2) - & & fexact(mbess,x(ig1),y(ig2)))**2 err2_norm = err2_norm + x(ig1)*contrib !use same inner-product in weak-form END DO END DO END DO END DO DEALLOCATE(x) DEALLOCATE(y) DEALLOCATE(sol) err2_norm = SQRT(err2_norm) END FUNCTION err2_norm END MODULE poisson_mod