!>
!> @file multigrid_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 multigrid
!
! MULTIGRID: Implement Multigrid solver for Finite Elements
! and Fiinite Differences.
!
! T.M. Tran, CRPP-EPFL
! September 2012
!
USE bsplines
USE matrix
USE conmat_mod
USE csr
USE cds
IMPLICIT NONE
!
TYPE grid1d
INTEGER :: n ! Number of intervals
INTEGER :: rank ! Dimension of FE space
DOUBLE PRECISION :: h
DOUBLE PRECISION, ALLOCATABLE :: x(:)
DOUBLE PRECISION, ALLOCATABLE :: v(:)
DOUBLE PRECISION, ALLOCATABLE :: f(:)
TYPE(spline1d) :: spl
TYPE(gemat) :: transf ! Coarse to fine transfer matrix
TYPE(gbmat), ALLOCATABLE :: mata ! FE matrix
TYPE(gbmat), ALLOCATABLE :: matm ! mass matrix
TYPE(gbmat), ALLOCATABLE :: mata_copy ! Used for direct_solve
TYPE(gemat), ALLOCATABLE :: matap ! FE matrix
TYPE(gemat), ALLOCATABLE :: matmp ! mass matrix
TYPE(gemat), ALLOCATABLE :: matap_copy! Used for direct_solve
END TYPE grid1d
!
TYPE grid2d
INTEGER :: n(2) ! Number of intervals
INTEGER :: rank(2) ! Dimension of FE space
DOUBLE PRECISION :: h(2)
DOUBLE PRECISION, ALLOCATABLE :: x(:), y(:)
!
DOUBLE PRECISION, ALLOCATABLE :: v(:,:) ! sol
DOUBLE PRECISION, ALLOCATABLE :: f(:,:) ! rhs
DOUBLE PRECISION, POINTER :: v1d(:) ! flatten sol
DOUBLE PRECISION, POINTER :: f1d(:) ! flatten rhs
!
TYPE(csr_mat),ALLOCATABLE :: mata
TYPE(cds_mat),ALLOCATABLE :: mata_cds
TYPE(spline2d) :: spl
TYPE(gemat) :: transf(2)
!
TYPE(csr_mat) :: matp(2)
END TYPE grid2d
!
TYPE mg_info
INTEGER :: nu1 ! Relaxation down sweeps
INTEGER :: nu2 ! Relaxation up sweeps
INTEGER :: mu ! mu-cycle number
INTEGER :: nu0 ! Number of FMG cycles
INTEGER :: levels ! Number of mg levels
CHARACTER(len=4) :: relax ! Type of relation
DOUBLE PRECISION :: omega ! for weighted Jacobi relaxation
LOGICAL :: nlscale=.FALSE. ! Scale restriction if .TRUE.
END TYPE mg_info
!
INTERFACE create_grid
MODULE PROCEDURE create_grid_1d, create_grid_2d
END INTERFACE create_grid
INTERFACE disrhs
MODULE PROCEDURE disrhs_1d, disrhs_2d
END INTERFACE disrhs
INTERFACE direct_solve
MODULE PROCEDURE direct_solve_1d, direct_solve_2d
END INTERFACE direct_solve
INTERFACE mg
MODULE PROCEDURE mg_1d, mg_2d
END INTERFACE mg
INTERFACE disc_err
MODULE PROCEDURE disc_err_1d, disc_err_2d
END INTERFACE disc_err
INTERFACE jacobi
MODULE PROCEDURE jacobi_cds, jacobi_csr, jacobi_gb, jacobi_ge
END INTERFACE jacobi
INTERFACE gs
MODULE PROCEDURE gs_cds, gs_csr, gs_gb, gs_ge
END INTERFACE gs
INTERFACE restrict
MODULE PROCEDURE restrict_1d, restrict_2d, restrict_2d_csr
END INTERFACE restrict
INTERFACE prolong
MODULE PROCEDURE prolong_1d, prolong_2d, prolong_2d_csr
END INTERFACE prolong
INTERFACE printmat
MODULE PROCEDURE printmat_mat, printmat_ge, printmat_gb, printmat_periodic
END INTERFACE printmat
INTERFACE massmat
MODULE PROCEDURE massmat_ge, massmat_gb, massmat_periodic
END INTERFACE massmat
INTERFACE femat
MODULE PROCEDURE femat_2d_csr, femat_ge, femat_gb, femat_periodic
END INTERFACE femat
INTERFACE ibcmat
MODULE PROCEDURE ibcmat_1d, ibcmat_2d
END INTERFACE ibcmat
INTERFACE mod_transf
MODULE PROCEDURE mod_transf_full, mod_transf_csr
END INTERFACE mod_transf
INTERFACE normf
MODULE PROCEDURE normf_gb, normf_ge
END INTERFACE normf
INTERFACE residue
MODULE PROCEDURE residue_gen, residue_csr, residue_cds, residue_gb, residue_ge
END INTERFACE residue
!
CONTAINS
!--------------------------------------------------------------------------------
SUBROUTINE create_grid_1d(n, nidbas, ng_in, alpha, grids, period)
!
! Create an array of levels grids
! Compute mass matrix and prolongation matrices.
!
INTEGER, INTENT(in) :: n ! Number of intervals in the finest grid
INTEGER, INTENT(in) :: nidbas ! Order of splines
INTEGER, INTENT(in) :: ng_in ! Number of proposed Gauss points
INTEGER, INTENT(in) :: alpha ! geometric exponent
TYPE(grid1d), INTENT(out) :: grids(:)
LOGICAL, INTENT(in), OPTIONAL :: period
!
LOGICAL :: nlper
INTEGER :: n_current, nrank, ngauss
INTEGER :: levels, l, i
DOUBLE PRECISION :: h_current
TYPE(gbmat) :: matm
TYPE(gemat) :: matmp
!
levels = SIZE(grids)
nlper = .FALSE.
IF(PRESENT(period)) nlper = period
!
ngauss = CEILING(REAL(2*nidbas+alpha+1,8)/2.d0)
ngauss = MAX(ng_in, ngauss)
WRITE(*,'(a,i0)') 'ngauss = ', ngauss
!
! Allocate some matrices
!
DO l=1,levels
IF(nlper) THEN
ALLOCATE(grids(l)%matmp)
ALLOCATE(grids(l)%matap)
ELSE
ALLOCATE(grids(l)%matm)
ALLOCATE(grids(l)%mata)
END IF
END DO
!
n_current = n
h_current = 1.0d0/REAL(n_current,8)
DO l=1,levels
IF(n_current .LT. 2 ) THEN
PRINT*, 'CREATE_GRID: number intervals too small!'
STOP
END IF
grids(l)%n = n_current
grids(l)%h = h_current
ALLOCATE(grids(l)%x(0:n_current))
grids(l)%x(0:n_current) = (/ (REAL(i,8)*h_current, i=0,n_current) /)
CALL set_spline(nidbas, ngauss, grids(l)%x, grids(l)%spl, period=nlper)
CALL get_dim(grids(l)%spl, nrank)
IF(nlper) nrank = n_current
grids(l)%rank = nrank
ALLOCATE(grids(l)%v(nrank))
ALLOCATE(grids(l)%f(nrank))
IF(nlper) THEN
CALL massmat(grids(l)%spl, alpha, grids(l)%matmp)
ELSE
CALL massmat(grids(l)%spl, alpha, grids(l)%matm)
END IF
IF(l.GT.1) THEN
CALL ctof_massmat(grids(l-1)%spl, grids(l)%spl, alpha, grids(l)%transf)
IF(nlper) THEN
CALL mcopy(grids(l-1)%matmp, matmp)
CALL factor(matmp)
CALL bsolve(matmp, grids(l)%transf%val)
CALL destroy(matmp)
ELSE
CALL mcopy(grids(l-1)%matm, matm)
CALL factor(matm)
CALL bsolve(matm, grids(l)%transf%val)
CALL destroy(matm)
END IF
END IF
n_current = n_current/2
h_current = 2.0d0*h_current
END DO
END SUBROUTINE create_grid_1d
!--------------------------------------------------------------------------------
SUBROUTINE create_grid_2d(x, y, nidbas, ng_in, alpha, grids, mat_type, period, &
& debug_in)
!
! Create an array of levels grids
! Compute mass matrix and prolongation matrices.
!
DOUBLE PRECISION, INTENT(in) :: x(0:), y(0:) ! Finest grid points
INTEGER, INTENT(in) :: nidbas(2) ! Order of splines
INTEGER, INTENT(in) :: alpha(2) ! geometric exponent
INTEGER, INTENT(in) :: ng_in(2) ! Number of proposed Gauss points
TYPE(grid2d), INTENT(out), TARGET :: grids(:)
CHARACTER(*), INTENT(in), OPTIONAL :: mat_type ! csr (default) or cds
LOGICAL, INTENT(in), OPTIONAL :: period(2)
LOGICAL, INTENT(in), OPTIONAL :: debug_in
!
LOGICAL, DIMENSION(2) :: nlper
INTEGER, DIMENSION(2) :: n, sp_dim, ngauss
LOGICAL :: nlcds
LOGICAL :: debug
INTEGER :: levels, l, rank2d
TYPE(gemat) :: matm
!
DOUBLE PRECISION, PARAMETER :: pi = 4.0d0*ATAN(1.0d0)
!
! Process input args
!
n(1) = SIZE(x)-1
n(2) = SIZE(y)-1
levels = SIZE(grids)
nlper = .FALSE.
IF(PRESENT(period)) THEN
nlper = period
END IF
IF(PRESENT(debug_in)) THEN
debug = debug_in
ELSE
debug = .FALSE.
END IF
IF(PRESENT(mat_type)) THEN ! CSR matrix by default
nlcds = mat_type.EQ.'cds'
ELSE
nlcds = .FALSE.
END IF
!
! WARNING: Assume that only 2nd dim can be periodic!!!
IF(nlper(1)) THEN
WRITE(*,'(A)') 'CREATE_GRID: First dimension could not be periodic!'
STOP
END IF
!
ngauss = CEILING(REAL(2*nidbas+1,8)/2.d0)
ngauss = MAX(ng_in, ngauss)
WRITE(*,'(a,2i4)') 'ngauss = ', ngauss
!
DO l=1,levels
!
! Create mesh from finest grid mesh
IF(MINVAL(n) .LT. 2 ) THEN
PRINT*, 'CREATE_GRID: number intervals too small!'
STOP
END IF
grids(l)%n = n
ALLOCATE(grids(l)%x(0:n(1)))
ALLOCATE(grids(l)%y(0:n(2)))
IF(l.EQ.1) THEN
grids(1)%x = x
grids(1)%y = y
ELSE
grids(l)%x(:) = grids(l-1)%x(0::2)
grids(l)%y(:) = grids(l-1)%y(0::2)
END IF
IF(debug) THEN
WRITE(*,'(/a,i4,a,2l2)') 'l =', l, ' nlper =', nlper
WRITE(*,'(a/(10(1pe12.3)))') 'x', grids(l)%x
WRITE(*,'(a/(10(1pe12.3)))') 'y', grids(l)%y
END IF
!
! Allocate mem for solution v and RHS f
CALL set_spline(nidbas, ngauss, grids(l)%x, grids(l)%y, grids(l)%spl, period=nlper)
CALL get_dim(grids(l)%spl%sp1, sp_dim(1))
CALL get_dim(grids(l)%spl%sp2, sp_dim(2))
ALLOCATE(grids(l)%v(sp_dim(1), sp_dim(2)))
ALLOCATE(grids(l)%f(sp_dim(1), sp_dim(2)))
!
! WARNING: Assume that only 2nd dim can be periodic!!!
grids(l)%rank = sp_dim
IF(nlper(2)) THEN
grids(l)%rank(2) = n(2)
END IF
IF(debug) THEN
WRITE(*,'(a,2i6)') 'Grid ranks', grids(l)%rank
END IF
!
! Flatten version of sol and rhs
rank2d = PRODUCT(grids(l)%rank)
grids(l)%v1d(1:rank2d) => grids(l)%v
grids(l)%f1d(1:rank2d) => grids(l)%f
!
! Matrix format for FE matrix
IF(nlcds) THEN
ALLOCATE(grids(l)%mata_cds)
ELSE
ALLOCATE(grids(l)%mata)
END IF
!
! Coarse to fine mesh transfers for l>1
IF(l.GT.1) THEN
CALL ctof_massmat(grids(l-1)%spl%sp1, grids(l)%spl%sp1, alpha(1), grids(l)%transf(1))
CALL ctof_massmat(grids(l-1)%spl%sp2, grids(l)%spl%sp2, alpha(2), grids(l)%transf(2))
!
CALL massmat(grids(l-1)%spl%sp1, alpha(1), matm)
CALL factor(matm)
CALL bsolve(matm, grids(l)%transf(1)%val)
CALL full_to_csr(grids(l)%transf(1)%val, grids(l)%matp(1))
CALL destroy(matm)
CALL destroy(grids(l)%transf(1))
!
CALL massmat(grids(l-1)%spl%sp2, alpha(2), matm)
CALL factor(matm)
CALL bsolve(matm, grids(l)%transf(2)%val)
CALL full_to_csr(grids(l)%transf(2)%val, grids(l)%matp(2))
CALL destroy(matm)
CALL destroy(grids(l)%transf(2))
END IF
!
! Next coarse grid
n = n/2
END DO
END SUBROUTINE create_grid_2d
!--------------------------------------------------------------------------------
SUBROUTINE create_grid_fd(x, y, grids, info, mat_type, period, debug)
!
! FD version of create_grid
!
DOUBLE PRECISION, INTENT(in) :: x(0:), y(0:)
TYPE(grid2d), INTENT(out), TARGET :: grids(:)
TYPE(mg_info), INTENT(inout) :: info ! info for MG
CHARACTER(*), INTENT(in), OPTIONAL :: mat_type ! csr (default) or cds
LOGICAL, INTENT(in), OPTIONAL :: period(2)
LOGICAL, INTENT(in), OPTIONAL :: debug
!
INTEGER :: nidbas(2)=1, ngauss(2)=4 ! Linear Splines \equiv 1st FD
INTEGER :: alpha(2) = 1 ! Cartesian coordinate
LOGICAL, DIMENSION(2) :: nlper
LOGICAL :: nldebug
LOGICAL :: nlcds
INTEGER :: levels, n(2), sp_dim(2)
INTEGER :: l, rank2d
TYPE(gemat) :: matm
!--------------------------------------------------------------------------------
!
! Process input args
!
n(1) = SIZE(x)-1
n(2) = SIZE(y)-1
levels = SIZE(grids)
info%nlscale = .TRUE. ! Restriction should be scaled for FD
nlper = .FALSE.
IF(PRESENT(period)) nlper = period
nldebug = .FALSE.
IF(PRESENT(debug)) nldebug = debug
IF(PRESENT(mat_type)) THEN ! CSR matrix by default
nlcds = mat_type.EQ.'cds'
ELSE
nlcds = .FALSE.
END IF
!
DO l=1,levels
!
! Create mesh from finest grid mesh
IF(MINVAL(n) .LT. 2 ) THEN
PRINT*, 'CREATE_GRID: number intervals too small!'
STOP
END IF
grids(l)%n = n
ALLOCATE(grids(l)%x(0:n(1)))
ALLOCATE(grids(l)%y(0:n(2)))
IF(l.EQ.1) THEN
grids(1)%x = x
grids(1)%y = y
ELSE
grids(l)%x(:) = grids(l-1)%x(0::2)
grids(l)%y(:) = grids(l-1)%y(0::2)
END IF
IF(nldebug) THEN
WRITE(*,'(/a,i4,a,2l2)') 'l =', l, ' nlper =', nlper
WRITE(*,'(a/(10(1pe12.3)))') 'x', grids(l)%x
WRITE(*,'(a/(10(1pe12.3)))') 'y', grids(l)%y
END IF
!
! Allocate mem for solution v and RHS f
CALL set_spline(nidbas, ngauss, grids(l)%x, grids(l)%y, grids(l)%spl, period=nlper)
CALL get_dim(grids(l)%spl%sp1, sp_dim(1))
CALL get_dim(grids(l)%spl%sp2, sp_dim(2))
ALLOCATE(grids(l)%v(0:sp_dim(1)-1, 0:sp_dim(2)-1))
ALLOCATE(grids(l)%f(0:sp_dim(1)-1, 0:sp_dim(2)-1))
!
! WARNING: Assume that only 2nd dim can be periodic!!!
grids(l)%rank = sp_dim
IF(nlper(2)) THEN
grids(l)%rank(2) = n(2)
END IF
IF(nldebug) THEN
WRITE(*,'(a,2i6)') 'Grid ranks', grids(l)%rank
END IF
!
! Flatten version of sol and rhs
rank2d = PRODUCT(grids(l)%rank)
grids(l)%v1d(1:rank2d) => grids(l)%v
grids(l)%f1d(1:rank2d) => grids(l)%f
!
! Matrix format for FD matrix
IF(nlcds) THEN
ALLOCATE(grids(l)%mata_cds)
ELSE
ALLOCATE(grids(l)%mata)
END IF
!
! Coarse to fine mesh transfers for l>1
IF(l.GT.1) THEN
CALL ctof_massmat(grids(l-1)%spl%sp1, grids(l)%spl%sp1, alpha(1), grids(l)%transf(1))
CALL ctof_massmat(grids(l-1)%spl%sp2, grids(l)%spl%sp2, alpha(2), grids(l)%transf(2))
!
CALL massmat(grids(l-1)%spl%sp1, alpha(1), matm)
CALL factor(matm)
CALL bsolve(matm, grids(l)%transf(1)%val)
CALL full_to_csr(grids(l)%transf(1)%val, grids(l)%matp(1))
CALL destroy(matm)
CALL destroy(grids(l)%transf(1))
!
CALL massmat(grids(l-1)%spl%sp2, alpha(2), matm)
CALL factor(matm)
CALL bsolve(matm, grids(l)%transf(2)%val)
CALL full_to_csr(grids(l)%transf(2)%val, grids(l)%matp(2))
CALL destroy(matm)
CALL destroy(grids(l)%transf(2))
END IF
!
! Next coarse grid
n = n/2
END DO
END SUBROUTINE create_grid_fd
!--------------------------------------------------------------------------------
RECURSIVE SUBROUTINE fmg(grids, info, l)
!
! Execute a full multigrid V-cycle
!
TYPE(grid1d), INTENT(inout) :: grids(:)
TYPE(mg_info), INTENT(in) :: info
INTEGER, INTENT(in) :: l
INTEGER :: levels, k
levels = info%levels
!
IF(l.EQ.levels) THEN
CALL direct_solve(grids(levels), grids(levels)%v)
ELSE
grids(l+1)%f = restrict(grids(l+1)%transf,grids(l)%f)
CALL fmg(grids, info, l+1)
grids(l)%v = prolong(grids(l+1)%transf,grids(l+1)%v)
DO k=1,info%nu0
CALL mg(grids, info, l)
END DO
END IF
END SUBROUTINE fmg
!--------------------------------------------------------------------------------
RECURSIVE SUBROUTINE mg_1d(grids, info, l)
!
! Execute a recursive V-cycle
!
TYPE(grid1d), INTENT(inout) :: grids(:)
TYPE(mg_info), INTENT(in) :: info
INTEGER, INTENT(in) :: l
INTEGER :: levels, k
LOGICAL :: nlper
!
levels = info%levels
nlper = grids(1)%spl%period
!
IF(l.EQ.levels) THEN
CALL direct_solve(grids(levels), grids(levels)%v)
ELSE
CALL relax(info%nu1)
IF(nlper) THEN
grids(l+1)%f = restrict(grids(l+1)%transf, &
& grids(l)%f-vmx(grids(l)%matap, grids(l)%v))
ELSE
grids(l+1)%f = restrict(grids(l+1)%transf, &
& grids(l)%f-vmx(grids(l)%mata, grids(l)%v))
END IF
grids(l+1)%v = 0.0d0
!
! Only 1 call to the coarsest level
DO k=1,MERGE(info%mu, 1, (l+1).NE.levels)
CALL mg(grids, info, l+1)
END DO
!
grids(l)%v = grids(l)%v + prolong(grids(l+1)%transf,grids(l+1)%v)
CALL relax(info%nu2)
END IF
!
CONTAINS
SUBROUTINE relax(nu)
INTEGER, INTENT(in) :: nu
SELECT CASE (TRIM(info%relax))
CASE ("jac")
IF(nlper) THEN
CALL jacobi(grids(l)%matap, info%omega, nu, grids(l)%v, grids(l)%f)
ELSE
CALL jacobi(grids(l)%mata, info%omega, nu, grids(l)%v, grids(l)%f)
END IF
CASE ("gs")
IF(nlper) THEN
CALL gs(grids(l)%matap, nu, grids(l)%v, grids(l)%f)
ELSE
CALL gs(grids(l)%mata, nu, grids(l)%v, grids(l)%f)
END IF
CASE default
PRINT*, "relax ", info%relax, " NOT IMPLEMENTED!"
STOP
END SELECT
END SUBROUTINE relax
END SUBROUTINE mg_1d
!--------------------------------------------------------------------------------
RECURSIVE SUBROUTINE mg_2d(grids, info, l)
!
! Execute a recursive V-cycle
!
TYPE(grid2d), INTENT(inout) :: grids(:)
TYPE(mg_info), INTENT(in) :: info
INTEGER, INTENT(in) :: l
!
DOUBLE PRECISION, ALLOCATABLE, TARGET :: resid(:,:)
DOUBLE PRECISION, POINTER :: resid1d(:)
INTEGER :: levels, k, m1, m2
!
levels = info%levels
m1 = SIZE(grids(l)%v,1)
m2 = SIZE(grids(l)%v,2)
!
IF(l.EQ.levels) THEN
grids(levels)%v = grids(levels)%f
CALL direct_solve(grids(levels), grids(levels)%v1d)
ELSE
CALL relax(info%nu1)
ALLOCATE(resid(m1,m2)); resid1d(1:m1*m2) => resid
IF(ALLOCATED(grids(l)%mata)) THEN
resid1d = grids(l)%f1d - vmx(grids(l)%mata, grids(l)%v1d)
ELSE
resid1d = grids(l)%f1d - vmx(grids(l)%mata_cds, grids(l)%v1d)
END IF
grids(l+1)%f = restrict(grids(l+1)%matp, resid)
IF(info%nlscale) grids(l+1)%f = 0.25d0*grids(l+1)%f
DEALLOCATE(resid)
grids(l+1)%v = 0.0d0
!
! Only 1 call to the coarsest level
DO k=1,MERGE(info%mu, 1, (l+1).NE.levels)
CALL mg(grids, info, l+1)
END DO
!
grids(l)%v = grids(l)%v + prolong(grids(l+1)%matp,grids(l+1)%v)
CALL relax(info%nu2)
END IF
!
CONTAINS
SUBROUTINE relax(nu)
INTEGER, INTENT(in) :: nu
SELECT CASE (TRIM(info%relax))
CASE ("jac")
IF(ALLOCATED(grids(l)%mata)) THEN
CALL jacobi(grids(l)%mata, info%omega, nu, grids(l)%v1d, grids(l)%f1d)
ELSE
CALL jacobi(grids(l)%mata_cds, info%omega, nu, grids(l)%v1d, grids(l)%f1d)
END IF
CASE ("gs")
IF(ALLOCATED(grids(l)%mata)) THEN
CALL gs(grids(l)%mata, nu, grids(l)%v1d, grids(l)%f1d)
ELSE
CALL gs(grids(l)%mata_cds, nu, grids(l)%v1d, grids(l)%f1d)
END IF
CASE default
PRINT*, "relax ", info%relax, " NOT IMPLEMENTED!"
STOP
END SELECT
END SUBROUTINE relax
END SUBROUTINE mg_2d
!--------------------------------------------------------------------------------
RECURSIVE SUBROUTINE mg_cyl(grids, info, l, nluniq_in)
!
! Execute a recursive V-cycle
!
TYPE(grid2d), INTENT(inout) :: grids(:)
TYPE(mg_info), INTENT(in) :: info
INTEGER, INTENT(in) :: l
LOGICAL, INTENT(in), OPTIONAL :: nluniq_in
!
DOUBLE PRECISION, ALLOCATABLE, TARGET :: resid(:,:)
DOUBLE PRECISION, POINTER :: resid1d(:)
INTEGER :: levels, k, m1, m2, r1, r2
LOGICAL :: nluniq
!
levels = info%levels
m1 = SIZE(grids(l)%v,1)
m2 = SIZE(grids(l)%v,2)
r1 = grids(l)%rank(1) ! r1 = m1
r2 = grids(l)%rank(2) ! r2 = m2-1
IF(PRESENT(nluniq_in)) THEN
nluniq = nluniq_in
ELSE
nluniq = .TRUE.
END IF
!
IF(l.EQ.levels) THEN
grids(levels)%v1d = grids(levels)%f1d
CALL direct_solve(grids(levels), grids(levels)%v1d, debug=.FALSE.)
ELSE
CALL relax(info%nu1)
ALLOCATE(resid(m1,m2)); resid1d(1:r1*r2) => resid
IF(ALLOCATED(grids(l)%mata)) THEN
resid1d(:) = grids(l)%f1d(:) - vmx(grids(l)%mata, grids(l)%v1d)
ELSE
resid1d(:) = grids(l)%f1d(:) - vmx(grids(l)%mata_cds, grids(l)%v1d)
END IF
!
grids(l+1)%f(:,:) = restrict_cyl(grids(l+1), resid, nluniq)
!
DEALLOCATE(resid)
grids(l+1)%v1d = 0.0d0
!
! Only 1 call to the coarsest level
DO k=1,MERGE(info%mu, 1, (l+1).NE.levels)
CALL mg_cyl(grids, info, l+1, nluniq)
END DO
!
grids(l)%v(:,1:r2) = grids(l)%v(:,1:r2) + &
& prolong_cyl(grids(l+1),grids(l+1)%v, nluniq)
!
CALL relax(info%nu2)
END IF
!
CONTAINS
SUBROUTINE relax(nu)
INTEGER, INTENT(in) :: nu
SELECT CASE (TRIM(info%relax))
CASE ("jac")
IF(ALLOCATED(grids(l)%mata)) THEN
CALL jacobi(grids(l)%mata, info%omega, nu, grids(l)%v1d, grids(l)%f1d)
ELSE
CALL jacobi(grids(l)%mata_cds, info%omega, nu, grids(l)%v1d, grids(l)%f1d)
END IF
CASE ("gs")
IF(ALLOCATED(grids(l)%mata)) THEN
CALL gs(grids(l)%mata, nu, grids(l)%v1d, grids(l)%f1d)
ELSE
CALL gs(grids(l)%mata_cds, nu, grids(l)%v1d, grids(l)%f1d)
END IF
CASE default
PRINT*, "relax ", info%relax, " NOT IMPLEMENTED!"
STOP
END SELECT
END SUBROUTINE relax
END SUBROUTINE mg_cyl
!--------------------------------------------------------------------------------
FUNCTION prolong_1d(matp,vcoarse) RESULT(vfine)
!
! Prolongation
!
TYPE(gemat), INTENT(in) :: matp
DOUBLE PRECISION, INTENT(in) :: vcoarse(:)
DOUBLE PRECISION :: vfine(matp%mrows)
!
vfine = vmx(matp,vcoarse)
END FUNCTION prolong_1d
!--------------------------------------------------------------------------------
FUNCTION restrict_1d(matp,vfine) RESULT(vcoarse)
!
! Restriction
!
TYPE(gemat), INTENT(in) :: matp
DOUBLE PRECISION, INTENT(in) :: vfine(:)
DOUBLE PRECISION :: vcoarse(matp%ncols)
!
vcoarse = vmx(matp,vfine,'T')
END FUNCTION restrict_1d
!--------------------------------------------------------------------------------
FUNCTION prolong_2d(matp,vcoarse) RESULT(vfine)
!
! Prolongation
!
TYPE(gemat), INTENT(in) :: matp(2)
DOUBLE PRECISION, INTENT(in) :: vcoarse(:,:)
DOUBLE PRECISION, ALLOCATABLE :: vfine(:,:)
!
DOUBLE PRECISION, POINTER :: pmat1(:,:), pmat2(:,:)
DOUBLE PRECISION, ALLOCATABLE :: temp(:,:)
DOUBLE PRECISION :: one=1.0d0, zero=0.0d0
INTEGER :: m1, m1p, m2, m2p
!
pmat1 => matp(1)%val
pmat2 => matp(2)%val
m1 = SIZE(pmat1,1); m1p = SIZE(pmat1,2)
m2 = SIZE(pmat2,1); m2p = SIZE(pmat2,2)
ALLOCATE(vfine(m1,m2))
ALLOCATE(temp(m1,m2p))
!
! Compute (P1) * V
!
CALL dgemm('N', 'N', m1, m2p, m1p, one, pmat1, m1, vcoarse, m1p, zero, &
& temp, m1)
!
! Compute (P1) * V * (P2)^T
!
CALL dgemm('N', 'T', m1, m2, m2p, one, temp, m1, pmat2, m2, zero, &
& vfine, m1)
!
DEALLOCATE(temp)
END FUNCTION prolong_2d
!--------------------------------------------------------------------------------
FUNCTION prolong_2d_csr(matp,vcoarse) RESULT(vfine)
!
! Prolongation using CSR prolongation matrix
!
TYPE(csr_mat), INTENT(in) :: matp(2)
DOUBLE PRECISION, INTENT(in) :: vcoarse(:,:)
DOUBLE PRECISION, ALLOCATABLE :: vfine(:,:)
!
DOUBLE PRECISION, ALLOCATABLE :: temp(:,:)
INTEGER :: m1, m1p, m2, m2p
INTEGER :: i, j, k, kk
!
m1 = matp(1)%mrows; m1p = matp(1)%ncols
m2 = matp(2)%mrows; m2p = matp(2)%ncols
ALLOCATE(vfine(m1,m2))
ALLOCATE(temp(m1p,m2))
temp = 0.0d0
vfine = 0.0d0
!
! Compute temp = V * (P2)^T
! t_ij = sum_{k=1}^{m2p} V_ik (P2)_jk, i=1:m1p, j=1:m2
!
DO j=1,m2
DO kk=matp(2)%irow(j),matp(2)%irow(j+1)-1
k = matp(2)%cols(kk)
temp(:,j) = temp(:,j) + vcoarse(:,k)*matp(2)%val(kk)
END DO
END DO
!
! Compute (P1) * V * (P2)^T
! V_ij = sum_{k=1}^{m1p} (P1)_ik t_kj, i=1:m1, j=1:m2
!
DO i=1,m1
DO kk=matp(1)%irow(i),matp(1)%irow(i+1)-1
k = matp(1)%cols(kk)
vfine(i,:) = vfine(i,:) + matp(1)%val(kk)*temp(k,:)
END DO
END DO
!
DEALLOCATE(temp)
END FUNCTION prolong_2d_csr
!--------------------------------------------------------------------------------
FUNCTION restrict_2d(matp,vfine) RESULT(vcoarse)
!
! Restriction
!
TYPE(gemat), INTENT(in) :: matp(2)
DOUBLE PRECISION, INTENT(in) :: vfine(:,:)
DOUBLE PRECISION, ALLOCATABLE :: vcoarse(:,:)
!
DOUBLE PRECISION, POINTER :: pmat1(:,:), pmat2(:,:)
DOUBLE PRECISION, ALLOCATABLE :: temp(:,:)
DOUBLE PRECISION :: one=1.0d0, zero=0.0d0
INTEGER :: m1, m1p, m2, m2p
!
pmat1 => matp(1)%val
pmat2 => matp(2)%val
m1 = SIZE(pmat1,1); m1p = SIZE(pmat1,2)
m2 = SIZE(pmat2,1); m2p = SIZE(pmat2,2)
ALLOCATE(vcoarse(m1p,m2p))
ALLOCATE(temp(m1p,m2))
!
! Compute (P1)^T * V
!
CALL dgemm('T', 'N', m1p, m2, m1, one, pmat1, m1, vfine, m1, zero, &
& temp, m1p)
!
! Compute (P1)^T * V * P2
!
CALL dgemm('N', 'N', m1p, m2p, m2, one, temp, m1p, pmat2, m2, zero, &
& vcoarse, m1p)
!
DEALLOCATE(temp)
END FUNCTION restrict_2d
!--------------------------------------------------------------------------------
FUNCTION restrict_2d_csr(matp,vfine) RESULT(vcoarse)
!
! Restriction using CSR prolongation matrix
!
TYPE(csr_mat), INTENT(in) :: matp(2)
DOUBLE PRECISION, INTENT(in) :: vfine(:,:)
DOUBLE PRECISION, ALLOCATABLE :: vcoarse(:,:)
!
DOUBLE PRECISION, ALLOCATABLE :: temp(:,:)
INTEGER :: m1, m1p, m2, m2p
INTEGER :: i, ii, j, jj, k
!
m1 = matp(1)%mrows; m1p = matp(1)%ncols
m2 = matp(2)%mrows; m2p = matp(2)%ncols
ALLOCATE(vcoarse(m1p,m2p))
ALLOCATE(temp(m1,m2p))
temp = 0.0d0
vcoarse = 0.0d0
!
! Compute temp = V * (R2)^T = V * (P2)
! t_ij = sum_{k=1}^{m2} V_ik (P2)_kj, i=1:m1, j=1:m2p
!
DO k=1,m2
DO jj=matp(2)%irow(k),matp(2)%irow(k+1)-1
j = matp(2)%cols(jj)
temp(:,j) = temp(:,j) + vfine(:,k)*matp(2)%val(jj)
END DO
END DO
!
! Compute (R1) * V * (R2)^T = (P1)^T) * V * (P2)
! V_ij = sum_{k=1}^{m1p} (P1)_ki t_kj, i=1:m1p, j=1:m2p
!
DO k=1,m1
DO ii=matp(1)%irow(k),matp(1)%irow(k+1)-1
i = matp(1)%cols(ii)
vcoarse(i,:) = vcoarse(i,:) + matp(1)%val(ii)*temp(k,:)
END DO
END DO
!
DEALLOCATE(temp)
END FUNCTION restrict_2d_csr
!--------------------------------------------------------------------------------
FUNCTION prolong_cyl(grid, vcoarse, nluniq) RESULT(vfine)
!
! Prolongation (cylindrical case)
!
TYPE(grid2d) :: grid
DOUBLE PRECISION, INTENT(inout) :: vcoarse(:,:)
DOUBLE PRECISION, ALLOCATABLE :: vfine(:,:)
LOGICAL, INTENT(in) :: nluniq
!
DOUBLE PRECISION, ALLOCATABLE :: temp(:,:)
INTEGER :: m1, m1p, m2, m2p
INTEGER :: i, j, k, kk
!
m1 = grid%matp(1)%mrows; m1p = grid%matp(1)%ncols
m2 = grid%matp(2)%mrows; m2p = grid%matp(2)%ncols
ALLOCATE(vfine(m1,m2))
ALLOCATE(temp(m1p,m2))
temp = 0.0d0
vfine = 0.0d0
!
IF(nluniq) vcoarse(1,1:m2p-1) = vcoarse(1,m2p)
!
! Compute temp = V * (P2)^T
! t_ij = sum_{k=1}^{m2p} V_ik (P2)_jk, i=1:m1p, j=1:m2
!
DO j=1,m2
DO kk=grid%matp(2)%irow(j),grid%matp(2)%irow(j+1)-1
k = grid%matp(2)%cols(kk)
temp(:,j) = temp(:,j) + vcoarse(:,k)*grid%matp(2)%val(kk)
END DO
END DO
!
! Compute (P1) * V * (P2)^T
! V_ij = sum_{k=1}^{m1p} (P1)_ik t_kj, i=1:m1, j=1:m2
!
DO i=1,m1
DO kk=grid%matp(1)%irow(i),grid%matp(1)%irow(i+1)-1
k = grid%matp(1)%cols(kk)
vfine(i,:) = vfine(i,:) + grid%matp(1)%val(kk)*temp(k,:)
END DO
END DO
!!$!
!!$! Compute (P1) * V
!!$!
!!$ CALL dgemm('N', 'N', m1, m2p, m1p, one, pmat1, m1, vcoarse, m1p, zero, &
!!$ & temp, m1)
!!$!
!!$! Compute (P1) * V * (P2)^T
!!$!
!!$ CALL dgemm('N', 'T', m1, m2, m2p, one, temp, m1, pmat2, m2, zero, &
!!$ & vfine, m1)
!
IF(nluniq) THEN
vcoarse(1,1:m2p-1) = vcoarse(1,1:m2p-1) - vcoarse(1,m2p)
vfine(1,1:m2-1) = vfine(1,1:m2-1) - vfine(1,m2)
END IF
!
DEALLOCATE(temp)
END FUNCTION prolong_cyl
!--------------------------------------------------------------------------------
FUNCTION restrict_cyl(grid, vfine, nluniq) RESULT(vcoarse)
!
! Restriction (cylindrical case)
!
TYPE(grid2d) :: grid
DOUBLE PRECISION, INTENT(inout) :: vfine(:,:)
DOUBLE PRECISION, ALLOCATABLE :: vcoarse(:,:)
LOGICAL, INTENT(in) :: nluniq
!
DOUBLE PRECISION, ALLOCATABLE :: temp(:,:)
INTEGER :: m1, m1p, m2, m2p
INTEGER :: i, ii, j, jj, k
!
m1 = grid%matp(1)%mrows; m1p = grid%matp(1)%ncols
m2 = grid%matp(2)%mrows; m2p = grid%matp(2)%ncols
ALLOCATE(vcoarse(m1p,m2p))
ALLOCATE(temp(m1,m2p))
temp = 0.0d0
vcoarse = 0.0d0
!
IF(nluniq) vfine(1,1:m2) = vfine(1,m2)/REAL(m2,8)
!
! Compute temp = V * (R2)^T = V * (P2)
! t_ij = sum_{k=1}^{m2} V_ik (P2)_kj, i=1:m1, j=1:m2p
!
DO k=1,m2
DO jj=grid%matp(2)%irow(k),grid%matp(2)%irow(k+1)-1
j = grid%matp(2)%cols(jj)
temp(:,j) = temp(:,j) + vfine(:,k)*grid%matp(2)%val(jj)
END DO
END DO
!
! Compute (R1) * V * (R2)^T = (P1)^T) * V * (P2)
! V_ij = sum_{k=1}^{m1p} (P1)_ki t_kj, i=1:m1p, j=1:m2p
!
DO k=1,m1
DO ii=grid%matp(1)%irow(k),grid%matp(1)%irow(k+1)-1
i = grid%matp(1)%cols(ii)
vcoarse(i,:) = vcoarse(i,:) + grid%matp(1)%val(ii)*temp(k,:)
END DO
END DO
!
!!$! Compute (P1)^T * V
!!$!
!!$ CALL dgemm('T', 'N', m1p, m2, m1, one, pmat1, m1, vfine, m1, zero, &
!!$ & temp, m1p)
!!$!
!!$! Compute (P1)^T * V * P2
!!$!
!!$ CALL dgemm('N', 'N', m1p, m2p, m2, one, temp, m1p, pmat2, m2, zero, &
!!$ & vcoarse, m1p)
!
IF(nluniq) THEN
vfine(1,m2) = SUM(vfine(1,1:m2)); vfine(1,1:m2-1) = 0.0d0
vcoarse(1,m2p) = SUM(vcoarse(1,1:m2p)); vcoarse(1,m2p-1) = 0.0d0
END IF
!
DEALLOCATE(temp)
!
END FUNCTION restrict_cyl
!--------------------------------------------------------------------------------
SUBROUTINE massmat_ge(spl, alpha, matm)
!
! Compute mass matrix
!
TYPE(spline1d), INTENT(in) :: spl
INTEGER, INTENT(in) :: alpha
TYPE(gemat), INTENT(out) :: matm
!
INTEGER :: nrank, nx, nidbas, kl, ku
!
CALL get_dim(spl, nrank, nx, nidbas)
kl=nidbas; ku=kl
IF(spl%period) nrank = nx
CALL init(nrank, 1, matm)
CALL conmat(spl, matm, coefeq)
CONTAINS
SUBROUTINE coefeq(x, idt, idw, c)
DOUBLE PRECISION, INTENT(in) :: x
INTEGER, INTENT(out) :: idt(:), idw(:)
DOUBLE PRECISION, INTENT(out) :: c(:)
c(1) = x**alpha
idt(1) = 0
idw(1) = 0
END SUBROUTINE coefeq
END SUBROUTINE massmat_ge
!--------------------------------------------------------------------------------
SUBROUTINE massmat_gb(spl, alpha, matm)
!
! Compute mass matrix
!
TYPE(spline1d), INTENT(in) :: spl
INTEGER, INTENT(in) :: alpha
TYPE(gbmat), INTENT(out) :: matm
!
INTEGER :: nrank, nx, nidbas, kl, ku
!
CALL get_dim(spl, nrank, nx, nidbas)
kl=nidbas; ku=kl
CALL init(kl, ku, nrank, 1, matm)
CALL conmat(spl, matm, coefeq)
CONTAINS
SUBROUTINE coefeq(x, idt, idw, c)
DOUBLE PRECISION, INTENT(in) :: x
INTEGER, INTENT(out) :: idt(:), idw(:)
DOUBLE PRECISION, INTENT(out) :: c(:)
c(1) = x**alpha
idt(1) = 0
idw(1) = 0
END SUBROUTINE coefeq
END SUBROUTINE massmat_gb
!--------------------------------------------------------------------------------
SUBROUTINE massmat_periodic(spl, alpha, matm)
!
! Compute mass matrix (periodic case)
!
TYPE(spline1d), INTENT(in) :: spl
INTEGER, INTENT(in) :: alpha
TYPE(periodic_mat), INTENT(out) :: matm
!
INTEGER :: dim, nrank, nx, nidbas, kl, ku
!
CALL get_dim(spl, dim, nx, nidbas)
kl=nidbas; ku=kl
nrank = nx
CALL init(kl, ku, nrank, 1, matm)
CALL conmat(spl, matm, coefeq)
CONTAINS
SUBROUTINE coefeq(x, idt, idw, c)
DOUBLE PRECISION, INTENT(in) :: x
INTEGER, INTENT(out) :: idt(:), idw(:)
DOUBLE PRECISION, INTENT(out) :: c(:)
c(1) = x**alpha
idt(1) = 0
idw(1) = 0
END SUBROUTINE coefeq
END SUBROUTINE massmat_periodic
!--------------------------------------------------------------------------------
SUBROUTINE femat_2d_csr(spl, mat, coefeq, nterms, maxder_in, nat_order_in, &
& noinit)
!
! Compute 2d fe CSR matrix
!
TYPE(spline2d), INTENT(in) :: spl
TYPE(csr_mat), INTENT(inout) :: mat
INTEGER, INTENT(in) :: nterms
INTEGER, INTENT(in), OPTIONAL :: maxder_in(2)
LOGICAL, INTENT(in), OPTIONAL :: nat_order_in
LOGICAL, INTENT(in), OPTIONAL :: noinit
INTERFACE
SUBROUTINE coefeq(x, y, idt, idw, c)
DOUBLE PRECISION, INTENT(in) :: x, y
INTEGER, INTENT(out) :: idt(:,:), idw(:,:)
DOUBLE PRECISION, INTENT(out) :: c(:)
END SUBROUTINE coefeq
END INTERFACE
!
INTEGER :: nrank, ndim(2), nints(2), maxder(2)
LOGICAL :: nat_order, run_init
!
CALL get_dim(spl, ndim, nints)
IF(spl%sp2%period) THEN
nrank = ndim(1)*nints(2)
ELSE
nrank = PRODUCT(ndim)
END IF
!
maxder = 1; IF(PRESENT(maxder_in)) maxder = maxder_in
nat_order = .TRUE.; IF(PRESENT(nat_order_in)) nat_order = nat_order_in
!
run_init = .TRUE.
IF(PRESENT(noinit)) run_init = .NOT.noinit
IF(run_init) CALL init(nrank, nterms, mat)
!
CALL conmat(spl, mat, coefeq, maxder, nat_order)
END SUBROUTINE femat_2d_csr
!--------------------------------------------------------------------------------
SUBROUTINE femat_ge(spl, mat, coefeq)
!
! Compute fe matrix
!
TYPE(spline1d), INTENT(in) :: spl
TYPE(gemat), INTENT(out) :: mat
INTERFACE
SUBROUTINE coefeq(x, idt, idw, c)
DOUBLE PRECISION, INTENT(in) :: x
INTEGER, INTENT(out) :: idt(:), idw(:)
DOUBLE PRECISION, INTENT(out) :: c(:)
END SUBROUTINE coefeq
END INTERFACE
!
INTEGER :: nrank, nx, nidbas, kl, ku
!
CALL get_dim(spl, nrank, nx, nidbas)
kl=nidbas; ku=kl
IF(spl%period) nrank = nx
CALL init(nrank, 2, mat)
CALL conmat(spl, mat, coefeq)
END SUBROUTINE femat_ge
!--------------------------------------------------------------------------------
SUBROUTINE femat_gb(spl, mat, coefeq)
!
! Compute fe matrix
!
TYPE(spline1d), INTENT(in) :: spl
TYPE(gbmat), INTENT(out) :: mat
INTERFACE
SUBROUTINE coefeq(x, idt, idw, c)
DOUBLE PRECISION, INTENT(in) :: x
INTEGER, INTENT(out) :: idt(:), idw(:)
DOUBLE PRECISION, INTENT(out) :: c(:)
END SUBROUTINE coefeq
END INTERFACE
!
INTEGER :: nrank, nx, nidbas, kl, ku
!
CALL get_dim(spl, nrank, nx, nidbas)
kl=nidbas; ku=kl
CALL init(kl, ku, nrank, 2, mat)
CALL conmat(spl, mat, coefeq)
END SUBROUTINE femat_gb
!--------------------------------------------------------------------------------
SUBROUTINE femat_periodic(spl, mat, coefeq)
!
! Compute fe matrix
!
TYPE(spline1d), INTENT(in) :: spl
TYPE(periodic_mat), INTENT(out) :: mat
INTERFACE
SUBROUTINE coefeq(x, idt, idw, c)
DOUBLE PRECISION, INTENT(in) :: x
INTEGER, INTENT(out) :: idt(:), idw(:)
DOUBLE PRECISION, INTENT(out) :: c(:)
END SUBROUTINE coefeq
END INTERFACE
!
INTEGER :: nrank, dim, nx, nidbas, kl, ku
!
CALL get_dim(spl, dim, nx, nidbas)
kl=nidbas; ku=kl
nrank = nx
CALL init(kl, ku, nrank, 2, mat)
CALL conmat(spl, mat, coefeq)
END SUBROUTINE femat_periodic
!--------------------------------------------------------------------------------
SUBROUTINE ibcmat_1d(irow, mat)
!
! Set BC at row irow to 0
!
INTEGER, INTENT(in) :: irow
TYPE(gbmat), INTENT(inout) :: mat
!
DOUBLE PRECISION :: a(mat%rank)
!
a(:)=0.0d0; a(irow)=1.0d0
CALL putrow(mat, irow, a)
CALL putcol(mat, irow, a)
END SUBROUTINE ibcmat_1d
!--------------------------------------------------------------------------------
SUBROUTINE ibcmat_2d(grid, mat, nluniq_in)
!
! Impose BC on matrix (asume natural ordering)
! I = (j-1)*N1 + i, i=1:N1, j=1:N2
!
TYPE(grid2d), INTENT(in) :: grid
TYPE(csr_mat), INTENT(inout) :: mat
LOGICAL, INTENT(in), OPTIONAL :: nluniq_in
!
DOUBLE PRECISION :: temp(mat%rank)
INTEGER :: n1e, n2e, nrank, i, j, irow, jcol
LOGICAL :: nlper1, nlper2, nlcart, nluniq
!
n1e = grid%rank(1)
n2e = grid%rank(2)
nrank = mat%rank
nlper1 = grid%spl%sp1%period
nlper2 = grid%spl%sp2%period
nlcart = (.NOT.nlper1) .AND. (.NOT.nlper2)
IF(PRESENT(nluniq_in)) THEN
nluniq = nluniq_in
ELSE
nluniq = .TRUE.
END IF
!
! BC at x=0 ! Dirichlet for Cartesian, unicity for cylindrical problem
IF(nlcart) THEN
i=1
DO j=1,n2e
irow = (j-1)*n1e + i
temp = 0.0d0; temp(irow) = 1.0d0
CALL putrow(mat, irow, temp)
CALL putcol(mat, irow, temp)
END DO
ELSE
i=1
IF(nluniq) THEN
CALL unicity
END IF
END IF
!
! BC at x=1 ! For both Cartesian and cylindrical
i=n1e
DO j=1,n2e
irow = (j-1)*n1e + i
temp = 0.0d0; temp(irow) = 1.0d0
CALL putrow(mat, irow, temp)
CALL putcol(mat, irow, temp)
END DO
!
! BC at y=0 ! Only for Cartesian problem
IF(nlcart) THEN
j=1
DO i=1,n1e
irow = (j-1)*n1e + i
temp = 0.0d0; temp(irow) = 1.0d0
CALL putrow(mat, irow, temp)
CALL putcol(mat, irow, temp)
END DO
END IF
!
! BC at y=1 ! Only for Cartesian problem
IF(nlcart) THEN
j=n2e
DO i=1,n1e
irow = (j-1)*n1e + i
temp = 0.0d0; temp(irow) = 1.0d0
CALL putrow(mat, irow, temp)
CALL putcol(mat, irow, temp)
END DO
END IF
!
CONTAINS
SUBROUTINE unicity
INTEGER :: irow0, jcol0
DOUBLE PRECISION :: temp_sum(mat%rank)
!
irow0 = (n2e-1)*n1e + i
jcol0 = irow0
!
! Vertical sum
temp_sum(:) = 0.0d0
DO j=1,n2e
irow = (j-1)*n1e + i
temp = 0.0d0
CALL getrow(mat, irow, temp)
temp_sum(:) = temp_sum(:) + temp(:)
END DO
CALL putrow(mat, irow0, temp_sum)
!
! Horizontal sum
temp_sum(:) = 0.0d0
DO j=1,n2e
jcol = (j-1)*n1e + i
temp = 0.0d0
CALL getcol(mat, jcol, temp)
temp_sum(:) = temp_sum(:) + temp(:)
END DO
CALL putcol(mat, jcol0, temp_sum)
!
! The away operator
DO j=1,n2e-1
irow = (j-1)*n1e + i
temp = 0.0d0; temp(irow) = 1.0d0
CALL putrow(mat, irow, temp)
CALL putcol(mat, irow, temp)
END DO
END SUBROUTINE unicity
END SUBROUTINE ibcmat_2d
!--------------------------------------------------------------------------------
SUBROUTINE mod_transf_full(mat,k)
!
! Modify grid transfer matrix.
!
DOUBLE PRECISION, INTENT(inout) :: mat(:,:)
INTEGER, INTENT(in) :: k
INTEGER :: m, n
!
m=SIZE(mat,1)
n=SIZE(mat,2)
!
! Clear matrix small elements.
WHERE( ABS(mat) < 1.d-8) mat=0.0d0
!
! Left boundary
IF(k.EQ.1 .OR. k.EQ.3) THEN
mat(2:m,1) = 0.0d0
END IF
!
! Right boundary
IF(k.EQ.2 .OR. k.EQ.3) THEN
mat(1:m-1,n) = 0.0d0
END IF
END SUBROUTINE mod_transf_full
!--------------------------------------------------------------------------------
SUBROUTINE mod_transf_csr(mat,k)
!
! Modify grid transfer matrix.
!
TYPE(csr_mat), INTENT(inout) :: mat
INTEGER, INTENT(in) :: k
!
DOUBLE PRECISION :: acol(mat%mrows)
INTEGER :: m, n
!
m=mat%mrows
n=mat%ncols
!
! Left boundary
acol = 0.0d0
IF(k.EQ.1 .OR. k.EQ.3) THEN
CALL getele(mat, 1, 1, acol(1))
CALL putcol(mat, 1, acol)
END IF
!
! Right boundary
acol = 0.0d0
IF(k.EQ.2 .OR. k.EQ.3) THEN
CALL getele(mat, m, n, acol(m))
CALL putcol(mat, n, acol)
END IF
END SUBROUTINE mod_transf_csr
!--------------------------------------------------------------------------------
SUBROUTINE calc_pmat(grid1, grid2, pmat, debug_in)
!
! Compute prolongation matrix by collocation
!
TYPE(grid1d), INTENT(in) :: grid1, grid2
TYPE(gemat), INTENT(out) :: pmat
LOGICAL, OPTIONAL, INTENT(in) :: debug_in
!
TYPE(gemat) :: mat_interp
DOUBLE PRECISION, ALLOCATABLE :: fun(:,:), fun2(:,:)
DOUBLE PRECISION :: xinter
INTEGER, ALLOCATABLE :: jcol(:)
INTEGER :: nidbas, nfine, ncoarse, nderv
LOGICAL :: nlper, debug
INTEGER :: i, i0, ii, k, irow
!================================================================================
! 0. Prologue
!
debug = .FALSE.
IF(PRESENT(debug_in)) debug = debug_in
!
nfine = grid1%n
ncoarse = grid2%n
nidbas = grid1%spl%order - 1
nlper = grid1%spl%period
!
IF(nlper) THEN
IF(ncoarse .LT. nidbas+1) THEN
WRITE(*,'(/a)') '** NX/2 should be larger or equal to NIDBAS+1 **'
STOP
END IF
END IF
!
IF(debug) THEN
WRITE(*,'(/2(a,i0))') 'nfine = ', nfine, ', ncoarse = ', ncoarse
IF(nlper) WRITE(*,'(a)') 'Grids are periodic!'
END IF
!
ALLOCATE(jcol(0:nidbas))
ALLOCATE(fun(0:nidbas,1))
IF(nlper) THEN
CALL init(ncoarse, 1, pmat, mrows=nfine)
CALL init(nfine, 1, mat_interp)
ELSE
CALL init(ncoarse+nidbas, 1, pmat, mrows=nfine+nidbas)
CALL init(nfine+nidbas, 1, mat_interp)
END IF
!================================================================================
! 1. Interpolation matrix
!
irow = 0
i0 = 1
!
! Left bound
IF(.NOT.nlper) THEN
IF(MODULO(nidbas,2).EQ.1) THEN
nderv = (nidbas-1)/2 ! ndidbas = 1, 3, 5, ...
ALLOCATE(fun2(0:nidbas,nderv+1))
CALL basfun(grid1%x(0), grid1%spl, fun2, 1)
jcol = 1 + (/ (i, i=0,nidbas) /)
DO k=1,nderv+1
irow = irow+1
mat_interp%val(irow,jcol) = fun2(0:nidbas,k)
END DO
i0 = 2 ! Skip the first grid point
ELSE
nderv = nidbas/2-1 ! ndidbas = 2, 4, ...
ALLOCATE(fun2(0:nidbas,nderv+1))
CALL basfun(grid1%x(0), grid1%spl, fun2, 1)
jcol = 1 + (/ (i, i=0,nidbas) /)
DO k=1,nderv+1
irow = irow+1
mat_interp%val(irow,jcol) = fun2(0:nidbas,k)
END DO
END IF
END IF
DO i=i0,nfine
IF(MODULO(nidbas,2).EQ.0) THEN
xinter = (grid1%x(i-1)+grid1%x(i))/2.0d0 ! Left bound of interval
ELSE
xinter = grid1%x(i-1) ! Left bound of interval
END IF
CALL basfun(xinter, grid1%spl, fun, i)
irow = irow+1
DO k=0,nidbas
jcol(k) = i+k
END DO
IF(nlper) jcol = MODULO(jcol-1,nfine)+1
mat_interp%val(irow,jcol) = fun(0:nidbas,1)
END DO
!
! Right bound
IF(.NOT.nlper) THEN
CALL basfun(grid1%x(nfine), grid1%spl, fun2, nfine)
jcol = nfine + (/ (i, i=0,nidbas) /)
DO k=nderv+1,1,-1
irow = irow+1
mat_interp%val(irow,jcol) = fun2(0:nidbas,k)
END DO
END IF
IF(debug) CALL printmat('** Interpolation matrix **', mat_interp)
!================================================================================
! 2. RHS matrix
!
irow = 0
i0 = 1
DO i=1,ncoarse
ii = 2*i-1
CALL comp_rhs(ii)
CALL comp_rhs(ii+1)
END DO
IF(debug) CALL printmat('** RHS matrix **', pmat)
!================================================================================
! 3. Compute prolongation matrix
!
CALL factor(mat_interp)
CALL bsolve(mat_interp, pmat%val)
!================================================================================
! 9. Epilogue
!
CALL destroy(mat_interp)
DEALLOCATE(jcol)
DEALLOCATE(fun)
IF(ALLOCATED(fun2)) DEALLOCATE(fun2)
!
CONTAINS
SUBROUTINE comp_rhs(ii)
INTEGER, INTENT(in) :: ii
INTEGER :: k
!
! Left bounds for non-periodic cases
IF(.NOT.nlper .AND. ii.EQ.1) THEN
CALL basfun(grid1%x(0), grid2%spl, fun2, 1)
jcol = 1 + (/ (k, k=0,nidbas) /)
DO k=1,nderv+1
irow = irow+1
pmat%val(irow,jcol) = fun2(0:nidbas,k)
END DO
IF(MODULO(nidbas,2).EQ.1) RETURN ! Skip
END IF
!
IF(MODULO(nidbas,2).EQ.0) THEN
xinter = (grid1%x(ii-1)+grid1%x(ii))/2.0d0 ! Left bound of interval
ELSE
xinter = grid1%x(ii-1) ! Left bound of interval
END IF
CALL basfun(xinter, grid2%spl, fun, i)
irow = irow+1
DO k=0,nidbas
jcol(k) = i+k
END DO
IF(nlper) jcol = MODULO(jcol-1,ncoarse)+1
pmat%val(irow,jcol) = fun(0:nidbas,1)
!
! Right bounds for non-periodic cases
IF(.NOT.nlper .AND. ii.EQ.nfine) THEN
CALL basfun(grid1%x(nfine), grid2%spl, fun2, ncoarse)
jcol = ncoarse + (/ (k, k=0,nidbas) /)
DO k=nderv+1,1,-1
irow = irow+1
pmat%val(irow,jcol) = fun2(0:nidbas,k)
END DO
END IF
END SUBROUTINE comp_rhs
END SUBROUTINE calc_pmat
!--------------------------------------------------------------------------------
SUBROUTINE disrhs_1d(spl, farr, frhs)
!
! Projection of RHS on spline basis functions
!
TYPE(spline1d) :: spl
DOUBLE PRECISION, INTENT(out) :: farr(:)
INTERFACE
DOUBLE PRECISION FUNCTION frhs(x)
DOUBLE PRECISION, INTENT(in) :: x
END FUNCTION frhs
END INTERFACE
DOUBLE PRECISION :: contrib
!
DOUBLE PRECISION, POINTER :: xg(:,:), wg(:,:)
DOUBLE PRECISION, ALLOCATABLE :: fun(:,:)
INTEGER :: ndim, n, nidbas, ng
INTEGER :: i, ig, it, irow
LOGICAL :: nlper
!
CALL get_dim(spl, ndim, n, nidbas)
nlper = spl%period
xg => spl%gausx ! xg(ng,n)
wg => spl%gausw ! wg(ng,n)
ng = SIZE(xg,1)
ALLOCATE(fun(0:nidbas,1))
!
farr = 0.0d0
DO i=1,n
DO ig=1,ng
CALL basfun(xg(ig,i), spl, fun, i)
contrib = wg(ig,i)*frhs(xg(ig,i))
DO it=0,nidbas
irow = i+it
IF(nlper) irow = MODULO(irow-1,n) +1
farr(irow) = farr(irow)+contrib*fun(it,1)
END DO
END DO
END DO
!
DEALLOCATE(fun)
END SUBROUTINE disrhs_1d
!--------------------------------------------------------------------------------
SUBROUTINE disrhs_2d(spl, farr, frhs)
!
! Projection of RHS on spline basis functions
!
TYPE(spline2d) :: spl
DOUBLE PRECISION, INTENT(out) :: farr(:,:)
INTERFACE
DOUBLE PRECISION FUNCTION frhs(x,y)
DOUBLE PRECISION, INTENT(in) :: x,y
END FUNCTION frhs
END INTERFACE
DOUBLE PRECISION :: contrib
!
DOUBLE PRECISION, POINTER :: xg1(:,:), wg1(:,:)
DOUBLE PRECISION, POINTER :: xg2(:,:), wg2(:,:)
DOUBLE PRECISION, ALLOCATABLE :: fun1(:,:)
DOUBLE PRECISION, ALLOCATABLE :: fun2(:,:)
INTEGER :: ndim1, n1, nidbas1, ng1
INTEGER :: ndim2, n2, nidbas2, ng2
INTEGER :: i1, ig1, it1, irow
INTEGER :: i2, ig2, it2, jcol
!
CALL get_dim(spl%sp1, ndim1, n1, nidbas1)
CALL get_dim(spl%sp2, ndim2, n2, nidbas2)
!
xg1 => spl%sp1%gausx ! xg(ng,n)
wg1 => spl%sp1%gausw ! wg(ng,n)
ng1 = SIZE(xg1,1)
xg2 => spl%sp2%gausx ! xg(ng,n)
wg2 => spl%sp2%gausw ! wg(ng,n)
ng2 = SIZE(xg2,1)
!
ALLOCATE(fun1(0:nidbas1,1))
ALLOCATE(fun2(0:nidbas2,1))
!
farr = 0.0d0
DO i1=1,n1
DO ig1=1,ng1
CALL basfun(xg1(ig1,i1), spl%sp1, fun1, i1)
DO i2=1,n2
DO ig2=1,ng2
CALL basfun(xg2(ig2,i2), spl%sp2, fun2, i2)
contrib = wg1(ig1,i1)*wg2(ig2,i2)* &
& frhs(xg1(ig1,i1), xg2(ig2,i2))
DO it1=0,nidbas1
irow = i1+it1
DO it2=0,nidbas2
jcol = i2+it2
farr(irow,jcol) = farr(irow,jcol) + &
& contrib*fun1(it1,1)*fun2(it2,1)
END DO
END DO
END DO
END DO
END DO
END DO
!
! Case of periodic BC (only in 2nd dimension!)
!
IF(spl%sp2%period) THEN
DO jcol=1,nidbas2
farr(:,jcol) = farr(:,jcol)+farr(:,jcol+n2)
farr(:,jcol+n2) = 0.0d0
END DO
END IF
DEALLOCATE(fun1)
DEALLOCATE(fun2)
END SUBROUTINE disrhs_2d
!--------------------------------------------------------------------------------
SUBROUTINE ibcrhs(grid, f, nluniq_in)
!
! Impose BC on RHS
!
TYPE(grid2d) :: grid
DOUBLE PRECISION, INTENT(inout) :: f(:,:)
LOGICAL, INTENT(in), OPTIONAL :: nluniq_in
!
DOUBLE PRECISION :: temp
INTEGER :: n1, n2
LOGICAL :: nlper1, nlper2, nlcyl, nluniq
!
n1 = grid%rank(1)
n2 = grid%rank(2)
nlper1 = grid%spl%sp1%period
nlper2 = grid%spl%sp2%period
nlcyl = (.NOT.nlper1) .AND. (nlper2)
IF(PRESENT(nluniq_in)) THEN
nluniq = nluniq_in
ELSE
nluniq=.TRUE.
END IF
!
! Cylindrical case, unicity at the axis, 0 at the right side
IF(nlcyl) THEN !
IF(nluniq) THEN
temp = SUM(f(1,1:n2))
f(1,n2) = temp
f(1,1:n2-1) = 0.0d0
END IF
f(n1,1:n2) = 0.0d0
!
! Cartesian case: 0 on all 4 boundaries
ELSE
f(1,:) = 0.0d0; f(n1,:) = 0.0d0
f(:,1) = 0.0d0; f(:,n2) = 0.0d0
END IF
END SUBROUTINE ibcrhs
!--------------------------------------------------------------------------------
FUNCTION disc_err_1d(spl, f, fexact) RESULT(disc_err)
!
! L2 norm of discretization error
!
TYPE(spline1d) :: spl
DOUBLE PRECISION, INTENT(in) :: f(:)
DOUBLE PRECISION :: disc_err
INTERFACE
FUNCTION fexact(x)
DOUBLE PRECISION, INTENT(in) :: x(:)
DOUBLE PRECISION :: fexact(SIZE(x))
END FUNCTION fexact
END INTERFACE
!
DOUBLE PRECISION, ALLOCATABLE :: err(:,:)
DOUBLE PRECISION, POINTER :: xg(:,:), wg(:,:)
INTEGER :: ndim, n, nidbas, ng
INTEGER :: ig
!
CALL get_dim(spl, ndim, n, nidbas)
xg => spl%gausx ! xg(ng,n)
wg => spl%gausw ! wg(ng,n)
ng = SIZE(xg,1)
!
ALLOCATE(err(ng,n))
CALL gridval(spl, xg(1,:), err(1,:), 0, f)
err(1,:) = (err(1,:) - fexact(xg(1,:)))**2
DO ig=2,ng
CALL gridval(spl, xg(ig,:), err(ig,:), 0)
err(ig,:) = (err(ig,:) - fexact(xg(ig,:)))**2
END DO
!
disc_err = SQRT(SUM(wg*err))
!
DEALLOCATE(err)
END FUNCTION disc_err_1d
!--------------------------------------------------------------------------------
FUNCTION disc_err_2d(spl, f, fexact) RESULT(disc_err)
!
! L2 norm of discretization error
!
TYPE(spline2d) :: spl
DOUBLE PRECISION, INTENT(in) :: f(:,:)
DOUBLE PRECISION :: disc_err
INTERFACE
FUNCTION fexact(x,y)
DOUBLE PRECISION, INTENT(in) :: x(:), y(:)
DOUBLE PRECISION :: fexact(SIZE(x),SIZE(y))
END FUNCTION fexact
END INTERFACE
!
DOUBLE PRECISION, ALLOCATABLE :: err(:,:)
DOUBLE PRECISION, POINTER :: xg(:,:), wg1(:,:), yg(:,:), wg2(:,:)
INTEGER, DIMENSION(2) :: ndim, n, ng
INTEGER :: i, j, ig, jg
LOGICAL :: nlper1, nlper2, nlcyl
!
CALL get_dim(spl, ndim, n)
xg => spl%sp1%gausx ! xg(ng,n)
wg1 => spl%sp1%gausw ! wg(ng,n)
ng(1) = SIZE(xg,1)
yg => spl%sp2%gausx ! xg(ng,n)
wg2 => spl%sp2%gausw ! wg(ng,n)
ng(2) = SIZE(yg,1)
!
nlper1 = spl%sp1%period
nlper2 = spl%sp2%period
nlcyl = (.NOT.nlper1) .AND. (nlper2)
!
disc_err = 0.0d0
ALLOCATE(err(n(1),n(2)))
DO ig=1,ng(1)
DO jg=1,ng(2)
IF(ig.EQ.1.AND.jg.EQ.1) THEN
CALL gridval(spl, xg(ig,:), yg(jg,:), err, [0,0], f)
ELSE
CALL gridval(spl, xg(ig,:), yg(jg,:), err, [0,0])
END IF
err = (err - fexact(xg(ig,:), yg(jg,:)))**2
DO i=1,n(1)
DO j=1,n(2)
IF(nlcyl) THEN
disc_err = disc_err + xg(ig,i)*wg1(ig,i)*wg2(jg,j)*err(i,j)
ELSE
disc_err = disc_err + wg1(ig,i)*wg2(jg,j)*err(i,j)
END IF
END DO
END DO
END DO
END DO
disc_err = SQRT(disc_err)
!
DEALLOCATE(err)
END FUNCTION disc_err_2d
!--------------------------------------------------------------------------------
SUBROUTINE back_transf(grid, u, nluniq_in)
!
! Back transform solution and use periodicity (cylindrical problem)
!
TYPE(grid2d), INTENT(in) :: grid
DOUBLE PRECISION, INTENT(inout) :: u(:,:)
LOGICAL, INTENT(in), OPTIONAL :: nluniq_in
!
LOGICAL :: nluniq
INTEGER :: n, nidbas, j
!
n = grid%n(2)
nidbas = grid%spl%sp2%order-1
IF(PRESENT(nluniq_in)) THEN
nluniq = nluniq_in
ELSE
nluniq = .TRUE.
END IF
!
! Back transform
IF(nluniq) THEN
u(1,1:n-1) = u(1,n)
END IF
!
! Periodicity
DO j=1,nidbas
u(:,j+n) = u(:,j)
END DO
END SUBROUTINE back_transf
!--------------------------------------------------------------------------------
DOUBLE PRECISION FUNCTION normf_gb(matm, f)
!
! L2 norm of f represented by its expansion coefficients.
!
TYPE(gbmat), INTENT(in) :: matm
DOUBLE PRECISION, INTENT(in) :: f(:)
normf_gb = SQRT(DOT_PRODUCT(f, vmx(matm,f)))
END FUNCTION normf_gb
!--------------------------------------------------------------------------------
DOUBLE PRECISION FUNCTION normf_ge(matm, f)
!
! L2 norm of f represented by its expansion coefficients.
!
TYPE(gemat), INTENT(in) :: matm
DOUBLE PRECISION, INTENT(in) :: f(:)
normf_ge = SQRT(DOT_PRODUCT(f, vmx(matm,f)))
END FUNCTION normf_ge
!--------------------------------------------------------------------------------
DOUBLE PRECISION FUNCTION residue_gen(grid, f, u, p)
!
! Generic version of residue
!
TYPE(grid2d) :: grid
DOUBLE PRECISION, INTENT(in) :: f(:), u(:)
DOUBLE PRECISION :: r(SIZE(f))
CHARACTER(len=*), OPTIONAL, INTENT(in) :: p
!
IF(ALLOCATED(grid%mata)) THEN
residue_gen = residue_csr(grid%mata, f, u, p)
ELSE
residue_gen = residue_cds(grid%mata_cds, f, u, p)
END IF
END FUNCTION residue_gen
!--------------------------------------------------------------------------------
DOUBLE PRECISION FUNCTION residue_csr(mat, f, u, p)
!
! L2 norm of residue ||f-Av||
!
TYPE(csr_mat), INTENT(in) :: mat
DOUBLE PRECISION, INTENT(in) :: f(:), u(:)
DOUBLE PRECISION :: r(SIZE(f))
CHARACTER(len=*), OPTIONAL, INTENT(in) :: p
!
CHARACTER(len=4) :: norm_type
norm_type = '2'
IF(PRESENT(p)) norm_type = p
!
r = f-vmx(mat,u)
SELECT CASE (norm_type)
CASE('1')
residue_csr = SUM(ABS(r))
CASE ('2')
residue_csr = SQRT(DOT_PRODUCT(r,r))
CASE ('inf')
residue_csr = MAXVAL(ABS(r))
END SELECT
END FUNCTION residue_csr
!--------------------------------------------------------------------------------
DOUBLE PRECISION FUNCTION residue_cds(mat, f, u, p)
!
! L2 norm of residue ||f-Av||
!
TYPE(cds_mat), INTENT(in) :: mat
DOUBLE PRECISION, INTENT(in) :: f(:), u(:)
DOUBLE PRECISION :: r(SIZE(f))
CHARACTER(len=*), OPTIONAL, INTENT(in) :: p
!
CHARACTER(len=4) :: norm_type
norm_type = '2'
IF(PRESENT(p)) norm_type = p
!
r = f-vmx(mat,u)
SELECT CASE (norm_type)
CASE('1')
residue_cds = SUM(ABS(r))
CASE ('2')
residue_cds = SQRT(DOT_PRODUCT(r,r))
CASE ('inf')
residue_cds = MAXVAL(ABS(r))
END SELECT
END FUNCTION residue_cds
!--------------------------------------------------------------------------------
DOUBLE PRECISION FUNCTION residue_ge(mat, f, u)
!
! L2 norm of residue ||f-Av||
!
TYPE(gemat), INTENT(in) :: mat
DOUBLE PRECISION, INTENT(in) :: f(:), u(:)
DOUBLE PRECISION :: r(SIZE(f))
r = f-vmx(mat,u)
residue_ge = SQRT(DOT_PRODUCT(r,r))
END FUNCTION residue_ge
!--------------------------------------------------------------------------------
DOUBLE PRECISION FUNCTION residue_gb(mat, f, u)
!
! L2 norm of residue ||f-Av||
!
TYPE(gbmat), INTENT(in) :: mat
DOUBLE PRECISION, INTENT(in) :: f(:), u(:)
DOUBLE PRECISION :: r(SIZE(f))
r = f-vmx(mat,u)
residue_gb = SQRT(DOT_PRODUCT(r,r))
END FUNCTION residue_gb
!--------------------------------------------------------------------------------
SUBROUTINE ctof_massmat(splf, splc, alpha, matm)
!
! Compute coarse to fine mass matrix M(h,2h)
!
TYPE(spline1d), INTENT(in) :: splf ! Spline on fine mesh
TYPE(spline1d), INTENT(in) :: splc ! Spline on coarse mesh
INTEGER, INTENT(in) :: alpha
TYPE(gemat), INTENT(out) :: matm
!
LOGICAL :: nlper
INTEGER :: nf, nxf, nc, nxc, nidbas, kl, ku
INTEGER :: ig, ngauss
INTEGER :: i, ic, it, jw, irow, jcol
!
DOUBLE PRECISION :: contrib
DOUBLE PRECISION, ALLOCATABLE :: xg(:), wg(:)
DOUBLE PRECISION, ALLOCATABLE :: funf(:,:), func(:,:)
!
nlper = splf%period .OR. splc%period
CALL get_dim(splf, nf, nxf, nidbas)
CALL get_gauss(splf, ngauss)
CALL get_dim(splc, nc, nxc, nidbas)
kl=nidbas; ku=kl
IF(nlper) THEN
nf = nxf
nc = nxc
END IF
CALL init(nc, 1, matm, mrows=nf) ! Defne nf x nc matrix
!
ALLOCATE(xg(ngauss),wg(ngauss))
ALLOCATE(funf(0:nidbas,1))
ALLOCATE(func(0:nidbas,1))
DO i=1,nxf
ic = (i-1)/2+1
CALL get_gauss(splf, ngauss, i, xg, wg)
DO ig=1,ngauss
CALL basfun(xg(ig), splf, funf, i)
CALL basfun(xg(ig), splc, func, ic)
DO it=0,nidbas
DO jw=0,nidbas
contrib = wg(ig)*funf(it,1)*func(jw,1)*xg(ig)**alpha
irow = i+it; IF(nlper) irow=MODULO(irow-1,nxf)+1
jcol = ic+jw; IF(nlper) jcol=MODULO(jcol-1,nxc)+1
CALL updtmat(matm, irow, jcol, contrib)
END DO
END DO
END DO
END DO
!
DEALLOCATE(xg,wg)
DEALLOCATE(funf,func)
END SUBROUTINE ctof_massmat
!--------------------------------------------------------------------------------
SUBROUTINE direct_solve_1d(grid, v)
!
! 1D direct solver
!
TYPE(grid1d), INTENT(inout) :: grid
DOUBLE PRECISION, INTENT(out) :: v(:)
LOGICAL :: nlper
!
nlper = grid%spl%period
IF(nlper) THEN
IF(.NOT.ALLOCATED(grid%matap_copy)) THEN
ALLOCATE(grid%matap_copy)
CALL mcopy(grid%matap, grid%matap_copy)
CALL factor(grid%matap_copy)
END IF
CALL bsolve(grid%matap_copy, grid%f, v)
ELSE
IF(.NOT.ALLOCATED(grid%mata_copy)) THEN
ALLOCATE(grid%mata_copy)
CALL mcopy(grid%mata, grid%mata_copy)
CALL factor(grid%mata_copy)
END IF
CALL bsolve(grid%mata_copy, grid%f, v)
END IF
END SUBROUTINE direct_solve_1d
!--------------------------------------------------------------------------------
SUBROUTINE direct_solve_2d(grid, v, debug)
!
! 2D direct solver
!
TYPE(grid2d), INTENT(inout) :: grid
DOUBLE PRECISION, INTENT(inout) :: v(:)
LOGICAL, INTENT(in), OPTIONAL :: debug
LOGICAL :: dbg
!
dbg = .FALSE.
IF(PRESENT(debug)) dbg=debug
!
IF(ALLOCATED(grid%mata)) THEN
IF(.NOT.ALLOCATED(grid%mata%mumps)) THEN
ALLOCATE(grid%mata%mumps)
CALL csr2mumps(grid%mata, grid%mata%mumps)
CALL factor(grid%mata%mumps, debug=dbg)
END IF
CALL bsolve(grid%mata%mumps, v, debug=dbg)
ELSE
IF(.NOT.ALLOCATED(grid%mata_cds%mumps)) THEN
ALLOCATE(grid%mata_cds%mumps)
CALL cds2mumps(grid%mata_cds, grid%mata_cds%mumps)
CALL factor(grid%mata_cds%mumps, debug=dbg)
END IF
CALL bsolve(grid%mata_cds%mumps, v, debug=dbg)
END IF
!
! Only cylindrical case
IF(.NOT.grid%spl%sp1%period .AND. grid%spl%sp2%period) THEN
END IF
END SUBROUTINE direct_solve_2d
!--------------------------------------------------------------------------------
SUBROUTINE jacobi_gb(mat, omega, nu, v, f)
!
! Weighted Jacobi relaxation
!
TYPE(gbmat),INTENT(in) :: mat
DOUBLE PRECISION, INTENT(in) :: omega
INTEGER, INTENT(in) :: nu
DOUBLE PRECISION, INTENT(inout) :: v(:)
DOUBLE PRECISION, INTENT(in) :: f(:)
!
DOUBLE PRECISION :: temp(SIZE(v))
DOUBLE PRECISION :: inv_diag(SIZE(v))
INTEGER :: k, kl, ku, n, i, j, jmin, jmax
!
kl = mat%kl
ku = mat%ku
n = mat%rank
!
inv_diag(:) = omega/mat%val(kl+ku+1,:)
DO k=1,nu
DO i=1,n
jmin = MAX(1,i-kl)
jmax = MIN(n, i+ku)
temp(i) = f(i)
DO j=jmin,i-1
temp(i) = temp(i) - mat%val(kl+ku+i-j+1,j)*v(j)
END DO
DO j=i+1,jmax
temp(i) = temp(i) - mat%val(kl+ku+i-j+1,j)*v(j)
END DO
temp(i) = temp(i)*inv_diag(i)
END DO
v(:) = (1.d0-omega)*v(:) + temp(:)
END DO
END SUBROUTINE jacobi_gb
!--------------------------------------------------------------------------------
SUBROUTINE jacobi_ge(mat, omega, nu, v, f)
!
! Weighted Jacobi relaxation
!
TYPE(gemat),INTENT(in) :: mat
DOUBLE PRECISION, INTENT(in) :: omega
INTEGER, INTENT(in) :: nu
DOUBLE PRECISION, INTENT(inout) :: v(:)
DOUBLE PRECISION, INTENT(in) :: f(:)
!
DOUBLE PRECISION :: temp(SIZE(v))
DOUBLE PRECISION :: inv_diag(SIZE(v))
INTEGER :: k, n, i, j
!
n = mat%rank
!
DO i=1,n
inv_diag(i) = omega/mat%val(i,i)
END DO
!
DO k=1,nu
DO i=1,n
temp(i) = f(i)
DO j=1,i-1
temp(i) = temp(i) - mat%val(i,j)*v(j)
END DO
DO j=i+1,n
temp(i) = temp(i) - mat%val(i-j+1,j)*v(j)
END DO
temp(i) = temp(i)*inv_diag(i)
END DO
v(:) = (1.d0-omega)*v(:) + temp(:)
END DO
END SUBROUTINE jacobi_ge
!--------------------------------------------------------------------------------
SUBROUTINE jacobi_csr(mat, omega, nu, v, f)
!
! Weighted Jacobi relaxation
!
TYPE(csr_mat),INTENT(in) :: mat
DOUBLE PRECISION, INTENT(in) :: omega
INTEGER, INTENT(in) :: nu
DOUBLE PRECISION, INTENT(inout) :: v(:)
DOUBLE PRECISION, INTENT(in) :: f(:)
!
DOUBLE PRECISION :: temp(SIZE(v))
DOUBLE PRECISION :: inv_diag(SIZE(v))
INTEGER :: k, n, i, j, jcol
!
n = mat%rank
!
inv_diag(:) = omega/mat%val(mat%idiag)
DO k=1,nu
temp(:) = f(:)
DO i=1,n
DO j = mat%irow(i), mat%irow(i+1)-1
jcol = mat%cols(j)
IF(jcol.NE.i) THEN ! The diagonal
temp(i) = temp(i) - mat%val(j)*v(jcol)
END IF
END DO
END DO
temp(:) = temp(:)*inv_diag(:)
v(:) = (1.d0-omega)*v(:) + temp(:)
END DO
END SUBROUTINE jacobi_csr
!--------------------------------------------------------------------------------
SUBROUTINE jacobi_cds(mat, omega, nu, v, f)
!
! Weighted Jacobi relaxation
!
TYPE(cds_mat),INTENT(in) :: mat
DOUBLE PRECISION, INTENT(in) :: omega
INTEGER, INTENT(in) :: nu
DOUBLE PRECISION, INTENT(inout) :: v(:)
DOUBLE PRECISION, INTENT(in) :: f(:)
!
DOUBLE PRECISION :: temp(SIZE(v))
DOUBLE PRECISION :: inv_diag(SIZE(v))
INTEGER :: k, n, i, id, d
!
n = mat%rank
!
inv_diag(:) = omega/mat%val(:,mat%dists(0))
DO k=1,nu
temp(:) = f(:)
DO id=-mat%kl,mat%ku ! f - (L+U)*v
IF(id.EQ.0) CYCLE
d = mat%dists(id)
DO i=MAX(1,1-d), MIN(n,mat%rank-d)
temp(i) = temp(i) - mat%val(i,id)*v(i+d)
END DO
END DO
temp(:) = temp(:)*inv_diag(:)
v(:) = (1.d0-omega)*v(:) + temp(:)
END DO
END SUBROUTINE jacobi_cds
!--------------------------------------------------------------------------------
SUBROUTINE gs_gb(mat, nu, v, f)
!
! Gauss-Seidel relaxation
!
TYPE(gbmat),INTENT(in) :: mat
INTEGER, INTENT(in) :: nu
DOUBLE PRECISION, INTENT(inout) :: v(:)
DOUBLE PRECISION, INTENT(in) :: f(:)
!
INTEGER :: k, kl, ku, n, i, j, jmin, jmax
DOUBLE PRECISION :: inv_diag(SIZE(v))
!
kl = mat%kl
ku = mat%ku
n = mat%rank
!
inv_diag(:) = 1.d0/mat%val(kl+ku+1,:)
DO k=1,nu
DO i=1,n
jmin = MAX(1,i-kl)
jmax = MIN(n, i+ku)
v(i) = f(i)
DO j=jmin,i-1
v(i) = v(i) - mat%val(kl+ku+i-j+1,j)*v(j)
END DO
DO j=i+1,jmax
v(i) = v(i) - mat%val(kl+ku+i-j+1,j)*v(j)
END DO
v(i) = inv_diag(i)*v(i)
END DO
END DO
END SUBROUTINE gs_gb
!--------------------------------------------------------------------------------
SUBROUTINE gs_ge(mat, nu, v, f)
!
! Gauss-Seidel relaxation
!
TYPE(gemat),INTENT(in) :: mat
INTEGER, INTENT(in) :: nu
DOUBLE PRECISION, INTENT(inout) :: v(:)
DOUBLE PRECISION, INTENT(in) :: f(:)
!
INTEGER :: k, n, i, j
DOUBLE PRECISION :: inv_diag(SIZE(v))
!
n = mat%rank
!
DO i=1,n
inv_diag(i) = 1.d0/mat%val(i,i)
END DO
DO k=1,nu
DO i=1,n
v(i) = f(i)
DO j=1,i-1
v(i) = v(i) - mat%val(i,j)*v(j)
END DO
DO j=i+1,n
v(i) = v(i) - mat%val(i,j)*v(j)
END DO
v(i) = inv_diag(i)*v(i)
END DO
END DO
END SUBROUTINE gs_ge
!--------------------------------------------------------------------------------
SUBROUTINE gs_csr(mat, nu, v, f)
!
! Gauss-Seidel relaxation
!
TYPE(csr_mat),INTENT(in) :: mat
INTEGER, INTENT(in) :: nu
DOUBLE PRECISION, INTENT(inout) :: v(:)
DOUBLE PRECISION, INTENT(in) :: f(:)
!
DOUBLE PRECISION :: inv_diag(SIZE(v))
INTEGER :: k, n, i, j, jcol
!
n = mat%rank
!
inv_diag(:) = 1.0d0/mat%val(mat%idiag)
DO k=1,nu
DO i=1,n
v(i) = f(i)
DO j = mat%irow(i), mat%irow(i+1)-1
jcol = mat%cols(j)
IF(jcol.NE.i) THEN ! The diagonal
v(i) = v(i) - mat%val(j)*v(jcol)
END IF
END DO
v(i) = v(i)*inv_diag(i)
END DO
END DO
END SUBROUTINE gs_csr
!--------------------------------------------------------------------------------
SUBROUTINE gs_cds(mat, nu, v, f)
!
! Gauss-Seidel relaxation
!
TYPE(cds_mat),INTENT(in) :: mat
INTEGER, INTENT(in) :: nu
DOUBLE PRECISION, INTENT(inout) :: v(:)
DOUBLE PRECISION, INTENT(in) :: f(:)
!
DOUBLE PRECISION :: temp(SIZE(v))
DOUBLE PRECISION :: inv_diag(SIZE(v))
INTEGER :: k, n, i, id, d
!
n = mat%rank
!
inv_diag(:) = 1.0d0/mat%val(:,mat%dists(0))
DO k=1,nu
!
temp(:) = f(:)
DO id=1,mat%ku ! t <- f - U*v
d = mat%dists(id)
DO i=MAX(1,1-d), MIN(n,n-d)
temp(i) = temp(i) - mat%val(i,id)*v(i+d)
END DO
END DO
!
DO i=1,n ! Solve (L+D)v=t
v(i) = temp(i)
DO id=-1,-mat%kl,-1
d = mat%dists(id)
IF(i+d.LT.1) EXIT
v(i) = v(i) - mat%val(i,id)*v(i+d)
END DO
v(i) = v(i)*inv_diag(i)
END DO
END DO
END SUBROUTINE gs_cds
!--------------------------------------------------------------------------------
SUBROUTINE printmat_mat(str, val)
!
CHARACTER(len=*), INTENT(in) :: str
DOUBLE PRECISION, INTENT(in) :: val(:,:)
INTEGER :: mrows, ncols,i
mrows=SIZE(val,1)
ncols=SIZE(val,2)
WRITE(*,'(/a)') TRIM(str)
WRITE(*,'(2(a,i6))') 'M =', mrows, ', N =', ncols
DO i=1,mrows
WRITE(*,'(12(1pe12.3))') val(i,:)
END DO
WRITE(*,'(a/(12(1pe12.3)))') 'Sum or rows', SUM(val,2)
WRITE(*,'(a/(12(1pe12.3)))') 'Sum or cols', SUM(val,1)
END SUBROUTINE printmat_mat
!--------------------------------------------------------------------------------
SUBROUTINE printmat_ge(str, mat)
!
CHARACTER(len=*), INTENT(in) :: str
TYPE(gemat), INTENT(in) :: mat
INTEGER :: i
DOUBLE PRECISION :: arow(mat%ncols)
DOUBLE PRECISION :: sum_cols(mat%ncols), sum_rows(mat%mrows)
sum_cols = 0.0d0
arow = 0.0d0
WRITE(*,'(/a)') TRIM(str)
WRITE(*,'(2(a,i6))') 'M =', mat%mrows, ', N =', mat%ncols
DO i=1,mat%mrows
CALL getrow(mat,i,arow)
sum_rows(i) = SUM(arow)
sum_cols(:) = sum_cols(:) + arow(:)
WRITE(*,'(12(1pe12.3))') arow
END DO
WRITE(*,'(a/(12(1pe12.3)))') 'Sum or rows', sum_rows
WRITE(*,'(a/(12(1pe12.3)))') 'Sum or cols', sum_cols
END SUBROUTINE printmat_ge
!--------------------------------------------------------------------------------
SUBROUTINE printmat_gb(str, mat)
!
CHARACTER(len=*), INTENT(in) :: str
TYPE(gbmat), INTENT(in) :: mat
INTEGER :: i
DOUBLE PRECISION :: arow(mat%ncols)
DOUBLE PRECISION :: sum_cols(mat%ncols), sum_rows(mat%mrows)
sum_cols = 0.0d0
WRITE(*,'(/a)') TRIM(str)
WRITE(*,'(2(a,i6))') 'M =', mat%mrows, ', N =', mat%ncols
DO i=1,mat%mrows
CALL getrow(mat,i,arow)
sum_rows(i) = SUM(arow)
sum_cols(:) = sum_cols(:) + arow(:)
WRITE(*,'(8(1pe12.3))') arow
END DO
WRITE(*,'(a/(12(1pe12.3)))') 'Sum or rows', sum_rows
WRITE(*,'(a/(12(1pe12.3)))') 'Sum or cols', sum_cols
END SUBROUTINE printmat_gb
!--------------------------------------------------------------------------------
SUBROUTINE printmat_periodic(str, mat)
!
CHARACTER(len=*), INTENT(in) :: str
TYPE(periodic_mat), INTENT(in) :: mat
INTEGER :: i
DOUBLE PRECISION :: arow(mat%mat%ncols)
DOUBLE PRECISION :: sum_cols(mat%mat%ncols), sum_rows(mat%mat%mrows)
sum_cols = 0.0d0
WRITE(*,'(/a)') TRIM(str)
WRITE(*,'(2(a,i6))') 'M =', mat%mat%mrows, ', N =', mat%mat%ncols
DO i=1,mat%mat%mrows
CALL getrow(mat,i,arow)
sum_rows(i) = SUM(arow)
sum_cols(:) = sum_cols(:) + arow(:)
WRITE(*,'(8(1pe12.3))') arow
END DO
WRITE(*,'(a/(8(1pe12.3)))') 'Sum or rows', sum_rows
WRITE(*,'(a/(8(1pe12.3)))') 'Sum or cols', sum_cols
END SUBROUTINE printmat_periodic
!--------------------------------------------------------------------------------
SUBROUTINE printdiag_gb(str, mat)
!
CHARACTER(len=*), INTENT(in) :: str
TYPE(gbmat), INTENT(in) :: mat
INTEGER :: ku, kl
kl = mat%kl
ku = mat%ku
WRITE(*,'(a/(8(1pe12.3)))') str, mat%val(kl+ku+1,:)
END SUBROUTINE printdiag_gb
!--------------------------------------------------------------------------------
INTEGER FUNCTION get_lmax(n)
INTEGER, INTENT(in) :: n
INTEGER :: l, ncur
l=1
ncur = n
DO
IF(ncur.EQ.2 .OR. MODULO(ncur,2).NE.0) EXIT ! Minimum N is 2 or odd.
l=l+1
ncur = ncur/2
END DO
get_lmax = l
END FUNCTION get_lmax
!--------------------------------------------------------------------------------
SUBROUTINE ibc_transf(grids, dir, k)
!
! Impose BC on transfer matrix
!
TYPE(grid2d), INTENT(inout) :: grids(:)
INTEGER, INTENT(in) :: dir
INTEGER, INTENT(in) :: k
!
INTEGER :: levels, l
levels = SIZE(grids)
DO l=2,levels
CALL mod_transf(grids(l)%matp(dir),k)
END DO
END SUBROUTINE ibc_transf
!--------------------------------------------------------------------------------
END MODULE multigrid