Browse Source

Bulk commit:

- Move LibHYPRE submodule to it's own file
 - Move Internals stub module to it's own file
 - Add conversions from PartitionedArrays.(PSparseMatrix|PVector)
 - More tests...
fe/wip
Fredrik Ekre 3 years ago
parent
commit
6d0f338540
  1. 4
      Project.toml
  2. 373
      src/HYPRE.jl
  3. 14
      src/Internals.jl
  4. 74
      src/LibHYPRE.jl
  5. 156
      test/runtests.jl

4
Project.toml

@ -7,11 +7,13 @@ CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" @@ -7,11 +7,13 @@ CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82"
HYPRE_jll = "0a602bbd-b08b-5d75-8d32-0de6eef44785"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195"
PartitionedArrays = "5a9dfac6-5c52-46f7-8278-5e2210713be9"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
SparseMatricesCSR = "a0a7dd2c-ebf4-11e9-1f05-cf50bc540ca1"
[extras]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[targets]
test = ["Test"]
test = ["LinearAlgebra", "Test"]

373
src/HYPRE.jl

@ -1,78 +1,97 @@ @@ -1,78 +1,97 @@
# SPDX-License-Identifier: MIT
module HYPRE
module LibHYPRE
include("../lib/LibHYPRE.jl")
using MPI: MPI
using PartitionedArrays: IndexRange, MPIData, PSparseMatrix, PVector, PartitionedArrays,
SequentialData, map_parts
using SparseArrays: SparseArrays, SparseMatrixCSC, nnz, nonzeros, nzrange, rowvals
using SparseMatricesCSR: SparseMatrixCSR, colvals, getrowptr
# Add manual methods for some ::Function signatures where the library wants function
# pointers. Instead of creating function pointers to the Julia wrappers we can just look
# up the pointer in the library and pass that.
# TODO: Maybe this can be done automatically as post-process pass in Clang.jl
export HYPREMatrix, HYPREVector
import Libdl: dlsym
function HYPRE_PCGSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_PCGSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_GMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_GMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_FlexGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_FlexGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_LGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_LGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_COGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_COGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_BiCGSTABSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_BiCGSTABSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_CGNRSetPrecond(solver, precond::Function, precondT::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precondT_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precondT))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_CGNRSetPrecond(solver, precond_ptr, precondT_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_LOBPCGSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_LOBPCGSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
# Clang.jl auto-generated bindings and some manual methods
include("LibHYPRE.jl")
using .LibHYPRE
using .LibHYPRE: @check
# Internal namespace to hide utility functions
include("Internals.jl")
###############################
# HYPREMatrix and HYPREVector #
###############################
# Export everything with HYPRE_ prefix
for name in names(@__MODULE__; all=true)
if startswith(string(name), "HYPRE_")
@eval export $name
mutable struct HYPREMatrix # <: AbstractMatrix{HYPRE_Complex}
IJMatrix::HYPRE_IJMatrix
ParCSRMatrix::HYPRE_ParCSRMatrix
HYPREMatrix() = new(C_NULL, C_NULL)
end
mutable struct HYPREVector # <: AbstractVector{HYPRE_Complex}
IJVector::HYPRE_IJVector
ParVector::HYPRE_ParVector
HYPREVector() = new(C_NULL, C_NULL)
end
# Create a new IJMatrix, set the object type, prepare for setting values
function Internals.init_matrix(comm::MPI.Comm, ilower, iupper)
# Create the IJ matrix
A = HYPREMatrix()
IJMatrixRef = Ref{HYPRE_IJMatrix}(C_NULL)
@check HYPRE_IJMatrixCreate(comm, ilower, iupper, ilower, iupper, IJMatrixRef)
A.IJMatrix = IJMatrixRef[]
# Attach a finalizer
finalizer(x -> HYPRE_IJMatrixDestroy(x.IJMatrix), A)
# Set storage type
@check HYPRE_IJMatrixSetObjectType(A.IJMatrix, HYPRE_PARCSR)
# Initialize to make ready for setting values
@check HYPRE_IJMatrixInitialize(A.IJMatrix)
return A
end
using SparseArrays: SparseArrays, SparseMatrixCSC, nnz, rowvals, nonzeros, nzrange
using SparseMatricesCSR: SparseMatrixCSR, colvals
using MPI: MPI
using .LibHYPRE
# Finalize the matrix and fetch the assembled matrix
# This should be called after setting all the values
function Internals.assemble_matrix(A::HYPREMatrix)
# Finalize after setting all values
@check HYPRE_IJMatrixAssemble(A.IJMatrix)
# Fetch the assembled CSR matrix
ParCSRMatrixRef = Ref{Ptr{Cvoid}}(C_NULL)
@check HYPRE_IJMatrixGetObject(A.IJMatrix, ParCSRMatrixRef)
A.ParCSRMatrix = convert(Ptr{HYPRE_ParCSRMatrix}, ParCSRMatrixRef[])
return A
end
export HYPREMatrix, HYPREVector
function Internals.init_vector(comm::MPI.Comm, ilower, iupper)
# Create the IJ vector
b = HYPREVector()
b_ref = Ref{HYPRE_IJVector}(C_NULL)
@check HYPRE_IJVectorCreate(comm, ilower, iupper, b_ref)
b.IJVector = b_ref[]
# Attach a finalizer
finalizer(x -> HYPRE_IJVectorDestroy(x.IJVector), b) # Set storage type
# Set storage type
@check HYPRE_IJVectorSetObjectType(b.IJVector, HYPRE_PARCSR)
# Initialize to make ready for setting values
@check HYPRE_IJVectorInitialize(b.IJVector)
return b
end
module Internals
function check_n_rows end
function to_hypre_data end
function Internals.assemble_vector(b::HYPREVector)
# Finalize after setting all values
@check HYPRE_IJVectorAssemble(b.IJVector)
# Fetch the assembled vector
par_b_ref = Ref{Ptr{Cvoid}}(C_NULL)
@check HYPRE_IJVectorGetObject(b.IJVector, par_b_ref)
b.ParVector = convert(Ptr{HYPRE_ParVector}, par_b_ref[])
return b
end
######################################
# SparseMatrixCS(C|R) -> HYPREMatrix #
######################################
function Internals.check_n_rows(A, ilower, iupper)
if size(A, 1) != (iupper - ilower + 1)
throw(ArgumentError("number of rows in matrix does not match global start/end rows ilower and iupper"))
@ -85,7 +104,7 @@ function Internals.to_hypre_data(A::SparseMatrixCSC, ilower, iupper) @@ -85,7 +104,7 @@ function Internals.to_hypre_data(A::SparseMatrixCSC, ilower, iupper)
A_rows = rowvals(A)
A_vals = nonzeros(A)
# Initialize data as HYPRE expects
# Initialize the data buffers HYPRE wants
nrows = HYPRE_Int(iupper - ilower + 1) # Total number of rows
ncols = zeros(HYPRE_Int, nrows) # Number of colums for each row
rows = collect(HYPRE_BigInt, ilower:iupper) # The row indices
@ -123,7 +142,7 @@ function Internals.to_hypre_data(A::SparseMatrixCSR, ilower, iupper) @@ -123,7 +142,7 @@ function Internals.to_hypre_data(A::SparseMatrixCSR, ilower, iupper)
A_cols = colvals(A)
A_vals = nonzeros(A)
# Initialize data as HYPRE expects
# Initialize the data buffers HYPRE wants
nrows = HYPRE_Int(iupper - ilower + 1) # Total number of rows
ncols = Vector{HYPRE_Int}(undef, nrows) # Number of colums for each row
rows = collect(HYPRE_BigInt, ilower:iupper) # The row indices
@ -147,42 +166,17 @@ function Internals.to_hypre_data(A::SparseMatrixCSR, ilower, iupper) @@ -147,42 +166,17 @@ function Internals.to_hypre_data(A::SparseMatrixCSR, ilower, iupper)
return nrows, ncols, rows, cols, values
end
mutable struct HYPREMatrix # <: AbstractMatrix{HYPRE_Complex}
IJMatrix::HYPRE_IJMatrix
ParCSRMatrix::HYPRE_ParCSRMatrix
HYPREMatrix() = new(C_NULL, C_NULL)
end
function HYPREMatrix(B::Union{SparseMatrixCSC,SparseMatrixCSR}, ilower, iupper, comm::MPI.Comm=MPI.COMM_WORLD)
# Compute indices/values in the format SetValues expect
A = Internals.init_matrix(comm, ilower, iupper)
nrows, ncols, rows, cols, values = Internals.to_hypre_data(B, ilower, iupper)
# Create the IJ matrix
A = HYPREMatrix()
IJMatrixRef = Ref{HYPRE_IJMatrix}(C_NULL)
HYPRE_IJMatrixCreate(comm, ilower, iupper, ilower, iupper, IJMatrixRef)
A.IJMatrix = IJMatrixRef[]
# Attach a finalizer
finalizer(x -> HYPRE_IJMatrixDestroy(x.IJMatrix), A)
# Set storage type
HYPRE_IJMatrixSetObjectType(A.IJMatrix, HYPRE_PARCSR)
# Initialize to make ready for setting values
HYPRE_IJMatrixInitialize(A.IJMatrix)
# Set all the values
HYPRE_IJMatrixSetValues(A.IJMatrix, nrows, ncols, rows, cols, values)
# Finalize
HYPRE_IJMatrixAssemble(A.IJMatrix)
# Fetch the assembled CSR matrix
ParCSRMatrixRef = Ref{Ptr{Cvoid}}(C_NULL)
HYPRE_IJMatrixGetObject(A.IJMatrix, ParCSRMatrixRef)
A.ParCSRMatrix = convert(Ptr{HYPRE_ParCSRMatrix}, ParCSRMatrixRef[])
@check HYPRE_IJMatrixSetValues(A.IJMatrix, nrows, ncols, rows, cols, values)
Internals.assemble_matrix(A)
return A
end
mutable struct HYPREVector # <: AbstractVector{HYPRE_Complex}
IJVector::HYPRE_IJVector
ParVector::HYPRE_ParVector
HYPREVector() = new(C_NULL, C_NULL)
end
#########################
# Vector -> HYPREVector #
#########################
function Internals.to_hypre_data(x::Vector, ilower, iupper)
Internals.check_n_rows(x, ilower, iupper)
@ -190,25 +184,188 @@ function Internals.to_hypre_data(x::Vector, ilower, iupper) @@ -190,25 +184,188 @@ function Internals.to_hypre_data(x::Vector, ilower, iupper)
values = convert(Vector{HYPRE_Complex}, x)
return HYPRE_Int(length(indices)), indices, values
end
# TODO: Internals.to_hypre_data(x::SparseVector, ilower, iupper) (?)
function HYPREVector(x::Vector, ilower, iupper, comm=MPI.COMM_WORLD)
b = Internals.init_vector(comm, ilower, iupper)
nvalues, indices, values = Internals.to_hypre_data(x, ilower, iupper)
b = HYPREVector()
b_ref = Ref{HYPRE_IJVector}(C_NULL)
HYPRE_IJVectorCreate(comm, ilower, iupper, b_ref)
b.IJVector = b_ref[]
finalizer(x -> HYPRE_IJVectorDestroy(x.IJVector), b) # Set storage type
HYPRE_IJVectorSetObjectType(b.IJVector, HYPRE_PARCSR)
# Initialize to make ready for setting values
HYPRE_IJVectorInitialize(b.IJVector)
# Set the values
HYPRE_IJVectorSetValues(b.IJVector, nvalues, indices, values)
@check HYPRE_IJVectorSetValues(b.IJVector, nvalues, indices, values)
Internals.assemble_vector(b)
return b
end
##################################################
# PartitionedArrays.PSparseMatrix -> HYPREMatrix #
##################################################
# TODO: This has some duplicated code with to_hypre_data(::SparseMatrixCSC, ilower, iupper)
function Internals.to_hypre_data(A::SparseMatrixCSC, r::IndexRange, c::IndexRange)
@assert r.oid_to_lid isa UnitRange && r.oid_to_lid.start == 1
ilower = r.lid_to_gid[r.oid_to_lid.start]
iupper = r.lid_to_gid[r.oid_to_lid.stop]
a_rows = rowvals(A)
a_vals = nonzeros(A)
# Initialize the data buffers HYPRE wants
nrows = HYPRE_Int(iupper - ilower + 1) # Total number of rows
ncols = zeros(HYPRE_Int, nrows) # Number of colums for each row
rows = collect(HYPRE_BigInt, ilower:iupper) # The row indices
# cols = Vector{HYPRE_BigInt}(undef, nnz) # The column indices
# values = Vector{HYPRE_Complex}(undef, nnz) # The values
# First pass to count nnz per row (note that the fact that columns are permuted
# doesn't matter for this pass)
a_rows = rowvals(A)
a_vals = nonzeros(A)
@inbounds for j in 1:size(A, 2)
for i in nzrange(A, j)
row = a_rows[i]
row > r.oid_to_lid.stop && continue # Skip ghost rows
# grow = r.lid_to_gid[lrow]
ncols[row] += 1
end
end
# Initialize remaining buffers now that nnz is known
nnz = sum(ncols)
cols = Vector{HYPRE_BigInt}(undef, nnz)
values = Vector{HYPRE_Complex}(undef, nnz)
# Keep track of the last index used for every row
lastinds = zeros(Int, nrows)
cumsum!((@view lastinds[2:end]), (@view ncols[1:end-1]))
# Second pass to populate the output -- here we need to take care of the permutation
# of columns. TODO: Problem that they are not sorted?
@inbounds for j in 1:size(A, 2)
for i in nzrange(A, j)
row = a_rows[i]
row > r.oid_to_lid.stop && continue # Skip ghost rows
k = lastinds[row] += 1
val = a_vals[i]
cols[k] = c.lid_to_gid[j]
values[k] = val
end
end
return nrows, ncols, rows, cols, values
end
# TODO: Possibly this can be optimized if it is possible to pass overlong vectors to HYPRE.
# At least values should be possible to directly share, but cols needs to translated
# to global ids.
function Internals.to_hypre_data(A::SparseMatrixCSR, r::IndexRange, c::IndexRange)
@assert r.oid_to_lid isa UnitRange && r.oid_to_lid.start == 1
ilower = r.lid_to_gid[r.oid_to_lid.start]
iupper = r.lid_to_gid[r.oid_to_lid.stop]
a_cols = colvals(A)
a_vals = nonzeros(A)
nnz = getrowptr(A)[r.oid_to_lid.stop + 1] - 1
# Initialize the data buffers HYPRE wants
nrows = HYPRE_Int(iupper - ilower + 1) # Total number of rows
ncols = zeros(HYPRE_Int, nrows) # Number of colums for each row
rows = collect(HYPRE_BigInt, ilower:iupper) # The row indices
cols = Vector{HYPRE_BigInt}(undef, nnz) # The column indices
values = Vector{HYPRE_Complex}(undef, nnz) # The values
# Loop over the (owned) rows and collect all values
k = 0
@inbounds for i in r.oid_to_lid
nzr = nzrange(A, i)
ncols[i] = length(nzr)
for j in nzr
k += 1
col = a_cols[j]
val = a_vals[j]
cols[k] = c.lid_to_gid[col]
values[k] = val
end
end
@assert nnz == k
return nrows, ncols, rows, cols, values
end
function Internals.get_comm(A::Union{PSparseMatrix{<:Any,<:M}, PVector{<:Any,<:M}}) where M <: MPIData
return A.rows.partition.comm
end
Internals.get_comm(_::Union{PSparseMatrix,PVector}) = MPI.COMM_WORLD
function Internals.get_proc_rows(A::Union{PSparseMatrix{<:Any,<:M}, PVector{<:Any,<:M}}) where M <: MPIData
r = A.rows.partition.part
ilower::HYPRE_BigInt = r.lid_to_gid[r.oid_to_lid[1]]
iupper::HYPRE_BigInt = r.lid_to_gid[r.oid_to_lid[end]]
return ilower, iupper
end
function Internals.get_proc_rows(A::Union{PSparseMatrix{<:Any,<:S}, PVector{<:Any,<:S}}) where S <: SequentialData
ilower::HYPRE_BigInt = typemax(HYPRE_BigInt)
iupper::HYPRE_BigInt = typemin(HYPRE_BigInt)
for r in A.rows.partition.parts
ilower = min(r.lid_to_gid[r.oid_to_lid[1]], ilower)
iupper = max(r.lid_to_gid[r.oid_to_lid[end]], iupper)
end
return ilower, iupper
end
function HYPREMatrix(B::PSparseMatrix)
# Use the same communicator as the matrix
comm = Internals.get_comm(B)
# Fetch rows owned by this process
ilower, iupper = Internals.get_proc_rows(B)
# Create the IJ matrix
A = Internals.init_matrix(comm, ilower, iupper)
# Set all the values
map_parts(B.values, B.rows.partition, B.cols.partition) do Bv, Br, Bc
nrows, ncols, rows, cols, values = Internals.to_hypre_data(Bv, Br, Bc)
@check HYPRE_IJMatrixSetValues(A.IJMatrix, nrows, ncols, rows, cols, values)
return nothing
end
# Finalize
HYPRE_IJVectorAssemble(b.IJVector)
# Fetch the assembled object
par_b_ref = Ref{Ptr{Cvoid}}(C_NULL)
HYPRE_IJVectorGetObject(b.IJVector, par_b_ref)
b.ParVector = convert(Ptr{HYPRE_ParVector}, par_b_ref[])
Internals.assemble_matrix(A)
return A
end
############################################
# PartitionedArrays.PVector -> HYPREVector #
############################################
#
function HYPREVector(v::PVector)
# Use the same communicator as the matrix
comm = Internals.get_comm(v)
# Fetch rows owned by this process
ilower, iupper = Internals.get_proc_rows(v)
# Create the IJ vector
b = Internals.init_vector(comm, ilower, iupper)
# Set all the values
map_parts(v.values, v.owned_values, v.rows.partition) do vv, vo, vr
ilower_part = vr.lid_to_gid[vr.oid_to_lid.start]
iupper_part = vr.lid_to_gid[vr.oid_to_lid.stop]
# Option 1: Set all values
nvalues = HYPRE_Int(iupper_part - ilower_part + 1)
indices = collect(HYPRE_BigInt, ilower_part:iupper_part)
# TODO: Could probably just pass the full vector even if it is too long
# values = convert(Vector{HYPRE_Complex}, vv)
values = collect(HYPRE_Complex, vo)
# # Option 2: Set only non-zeros
# indices = HYPRE_BigInt[]
# values = HYPRE_Complex[]
# for (i, vi) in zip(ilower_part:iupper_part, vo)
# if !iszero(vi)
# push!(indices, i)
# push!(values, vi)
# end
# end
# nvalues = length(indices)
@check HYPRE_IJVectorSetValues(b.IJVector, nvalues, indices, values)
return nothing
end
# Finalize
Internals.assemble_vector(b)
return b
end

14
src/Internals.jl

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
# SPDX-License-Identifier: MIT
module Internals
function check_n_rows end
function to_hypre_data end
function get_comm end
function get_proc_rows end
function init_matrix end
function init_vector end
function assemble_matrix end
function assemble_vector end
end # module Internals

74
src/LibHYPRE.jl

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
module LibHYPRE
using Libdl: dlsym
# Clang.jl auto-generated bindings
include("../lib/LibHYPRE.jl")
# Add manual methods for some ::Function signatures where the library wants function
# pointers. Instead of creating function pointers to the Julia wrappers we can just look
# up the pointer in the library and pass that.
# TODO: Maybe this can be done automatically as post-process pass in Clang.jl
function HYPRE_PCGSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_PCGSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_GMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_GMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_FlexGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_FlexGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_LGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_LGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_COGMRESSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_COGMRESSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_BiCGSTABSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_BiCGSTABSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_CGNRSetPrecond(solver, precond::Function, precondT::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precondT_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precondT))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_CGNRSetPrecond(solver, precond_ptr, precondT_ptr, precond_setup_ptr, precond_solver)
end
function HYPRE_LOBPCGSetPrecond(solver, precond::Function, precond_setup::Function, precond_solver)
precond_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond))
precond_setup_ptr = dlsym(HYPRE_jll.libHYPRE_handle, Symbol(precond_setup))
return HYPRE_LOBPCGSetPrecond(solver, precond_ptr, precond_setup_ptr, precond_solver)
end
# Macro for checking LibHYPRE return codes
macro check(arg)
Meta.isexpr(arg, :call) || throw(ArgumentError("wrong usage of @check"))
msg = "LibHYPRE.$(arg.args[1]) returned non-zero return code: "
return quote
r = $(esc(arg))
if r != 0
error(string($msg, r))
end
r
end
end
# Export everything with HYPRE_ prefix
for name in names(@__MODULE__; all=true)
if startswith(string(name), "HYPRE_")
@eval export $name
end
end
end

156
test/runtests.jl

@ -1,10 +1,15 @@ @@ -1,10 +1,15 @@
# SPDX-License-Identifier: MIT
using HYPRE
using HYPRE.Internals
using HYPRE.LibHYPRE
using Test
using LinearAlgebra
using MPI
using PartitionedArrays
using SparseArrays
using SparseMatricesCSR
using Test
using MPI
MPI.Init()
@testset "HYPREMatrix" begin
@ -12,6 +17,15 @@ MPI.Init() @@ -12,6 +17,15 @@ MPI.Init()
@test H.IJMatrix == HYPRE_IJMatrix(C_NULL)
@test H.ParCSRMatrix == HYPRE_ParCSRMatrix(C_NULL)
H = Internals.init_matrix(MPI.COMM_WORLD, 1, 5)
@test H.IJMatrix != HYPRE_IJMatrix(C_NULL)
@test H.ParCSRMatrix == HYPRE_ParCSRMatrix(C_NULL)
Internals.assemble_matrix(H)
@test H.IJMatrix != HYPRE_IJMatrix(C_NULL)
@test H.ParCSRMatrix != HYPRE_ParCSRMatrix(C_NULL)
end
@testset "HYPREMatrix(::SparseMatrixCS(C|R))" begin
ilower, iupper = 4, 6
CSC = convert(SparseMatrixCSC{HYPRE_Complex, HYPRE_Int}, sparse([
1 2 0 0 3
@ -20,8 +34,8 @@ MPI.Init() @@ -20,8 +34,8 @@ MPI.Init()
]))
CSR = sparsecsr(findnz(CSC)..., size(CSC)...)
@test CSC == CSR
csc = HYPRE.Internals.to_hypre_data(CSC, ilower, iupper)
csr = HYPRE.Internals.to_hypre_data(CSR, ilower, iupper)
csc = Internals.to_hypre_data(CSC, ilower, iupper)
csr = Internals.to_hypre_data(CSR, ilower, iupper)
@test csc[1]::HYPRE_Int == csr[1]::HYPRE_Int == 3 # nrows
@test csc[2]::Vector{HYPRE_Int} == csr[2]::Vector{HYPRE_Int} == [3, 2, 3] # ncols
@test csc[3]::Vector{HYPRE_BigInt} == csr[3]::Vector{HYPRE_BigInt} == [4, 5, 6] # rows
@ -34,10 +48,9 @@ MPI.Init() @@ -34,10 +48,9 @@ MPI.Init()
@test csr[5] == CSR.nzval
@test_broken csr[5]::Vector{HYPRE_Complex} === CSR.nzval
@test_throws ArgumentError HYPRE.Internals.to_hypre_data(CSC, ilower, iupper-1)
@test_throws ArgumentError HYPRE.Internals.to_hypre_data(CSR, ilower, iupper+1)
@test_throws ArgumentError Internals.to_hypre_data(CSC, ilower, iupper-1)
@test_throws ArgumentError Internals.to_hypre_data(CSR, ilower, iupper+1)
# Converting SparseMatrixCS(C|R) to HYPREMatrix
ilower, iupper = 6, 10
CSC = sprand(5, 10, 0.3)
CSR = sparsecsr(findnz(CSC)..., size(CSC)...)
@ -50,29 +63,140 @@ MPI.Init() @@ -50,29 +63,140 @@ MPI.Init()
@test H.ParCSRMatrix != HYPRE_ParCSRMatrix(C_NULL)
end
function tomain(x)
g = gather(x)
be = get_backend(g.values)
if be isa SequentialBackend
return g.values.parts[1]
else # if be isa MPIBackend
return g.values.part
end
end
@testset "HYPREMatrix(::PSparseMatrix)" begin
# Sequential backend
function diag_data(backend, parts)
is_seq = backend isa SequentialBackend
rows = PRange(parts, 10)
cols = PRange(parts, 10)
I, J, V = map_parts(parts) do p
i = Int[]
j = Int[]
v = Float64[]
if (is_seq && p == 1) || !is_seq
append!(i, [1, 2, 3, 4, 5, 6])
append!(j, [1, 2, 3, 4, 5, 6])
append!(v, [1, 2, 3, 4, 5, 6])
end
if (is_seq && p == 2) || !is_seq
append!(i, [4, 5, 6, 7, 8, 9, 10])
append!(j, [4, 5, 6, 7, 8, 9, 10])
append!(v, [4, 5, 6, 7, 8, 9, 10])
end
return i, j, v
end
add_gids!(rows, I)
assemble!(I, J, V, rows)
add_gids!(cols, J)
return I, J, V, rows, cols
end
backend = SequentialBackend()
parts = get_part_ids(backend, 2)
CSC = PSparseMatrix(diag_data(backend, parts)...; ids=:global)
CSR = PSparseMatrix(sparsecsr, diag_data(backend, parts)...; ids=:global)
@test tomain(copy(CSC)) == tomain(copy(CSR)) ==
Diagonal([1, 2, 3, 8, 10, 12, 7, 8, 9, 10])
map_parts(CSC.values, CSC.rows.partition, CSC.cols.partition,
CSR.values, CSR.rows.partition, CSR.cols.partition, parts) do args...
cscvalues, cscrows, csccols, csrvalues, csrrows, csrcols, p = args
csc = Internals.to_hypre_data(cscvalues, cscrows, csccols)
csr = Internals.to_hypre_data(csrvalues, csrrows, csrcols)
if p == 1
nrows = 5
ncols = [1, 1, 1, 1, 1]
rows = [1, 2, 3, 4, 5]
cols = [1, 2, 3, 4, 5]
values = [1, 2, 3, 8, 10]
else # if p == 1
nrows = 5
ncols = [1, 1, 1, 1, 1]
rows = [6, 7, 8, 9, 10]
cols = [6, 7, 8, 9, 10]
values = [12, 7, 8, 9, 10]
end
@test csc[1]::HYPRE_Int == csr[1]::HYPRE_Int == nrows
@test csc[2]::Vector{HYPRE_Int} == csr[2]::Vector{HYPRE_Int} == ncols
@test csc[3]::Vector{HYPRE_BigInt} == csr[3]::Vector{HYPRE_BigInt} == rows
@test csc[4]::Vector{HYPRE_BigInt} == csr[4]::Vector{HYPRE_BigInt} == cols
@test csc[5]::Vector{HYPRE_Complex} == csr[5]::Vector{HYPRE_Complex} == values
end
# MPI backend
backend = MPIBackend()
parts = MPIData(1, MPI.COMM_WORLD, (1,)) # get_part_ids duplicates the comm
CSC = PSparseMatrix(diag_data(backend, parts)...; ids=:global)
CSR = PSparseMatrix(sparsecsr, diag_data(backend, parts)...; ids=:global)
@test tomain(copy(CSC)) == tomain(copy(CSR)) ==
Diagonal([1, 2, 3, 8, 10, 12, 7, 8, 9, 10])
map_parts(CSC.values, CSC.rows.partition, CSC.cols.partition,
CSR.values, CSR.rows.partition, CSR.cols.partition, parts) do args...
cscvalues, cscrows, csccols, csrvalues, csrrows, csrcols, p = args
csc = Internals.to_hypre_data(cscvalues, cscrows, csccols)
csr = Internals.to_hypre_data(csrvalues, csrrows, csrcols)
nrows = 10
ncols = fill(1, 10)
rows = collect(1:10)
cols = collect(1:10)
values = [1, 2, 3, 8, 10, 12, 7, 8, 9, 10]
@test csc[1]::HYPRE_Int == csr[1]::HYPRE_Int == nrows
@test csc[2]::Vector{HYPRE_Int} == csr[2]::Vector{HYPRE_Int} == ncols
@test csc[3]::Vector{HYPRE_BigInt} == csr[3]::Vector{HYPRE_BigInt} == rows
@test csc[4]::Vector{HYPRE_BigInt} == csr[4]::Vector{HYPRE_BigInt} == cols
@test csc[5]::Vector{HYPRE_Complex} == csr[5]::Vector{HYPRE_Complex} == values
end
end
@testset "HYPREVector" begin
h = HYPREVector()
@test h.IJVector == HYPRE_IJVector(C_NULL)
@test h.ParVector == HYPRE_ParVector(C_NULL)
h = Internals.init_vector(MPI.COMM_WORLD, 1, 5)
@test h.IJVector != HYPRE_IJVector(C_NULL)
@test h.ParVector == HYPRE_ParVector(C_NULL)
Internals.assemble_vector(h)
@test h.IJVector != HYPRE_IJVector(C_NULL)
@test h.ParVector != HYPRE_ParVector(C_NULL)
end
@testset "HYPREVector(::Vector)" begin
ilower, iupper = 1, 10
b = rand(HYPRE_Complex, 10)
h = HYPREVector(b, ilower, iupper)
@test h.IJVector != HYPRE_IJVector(C_NULL)
@test h.ParVector != HYPRE_ParVector(C_NULL)
@test_throws ArgumentError HYPREVector([1, 2], ilower, iupper)
ilower, iupper = 1, 10
b = rand(HYPRE_Complex, 10)
nvalues, indices, values = HYPRE.Internals.to_hypre_data(b, ilower, iupper)
nvalues, indices, values = Internals.to_hypre_data(b, ilower, iupper)
@test nvalues::HYPRE_Int == 10
@test indices::Vector{HYPRE_Int} == collect(1:10)
@test values::Vector{HYPRE_Complex} === b # === for correct eltype
b = rand(1:10, 10)
nvalues, indices, values = HYPRE.Internals.to_hypre_data(b, ilower, iupper)
nvalues, indices, values = Internals.to_hypre_data(b, ilower, iupper)
@test nvalues::HYPRE_Int == 10
@test indices::Vector{HYPRE_Int} == collect(1:10)
@test values::Vector{HYPRE_Complex} == b # == for other eltype
@test_throws ArgumentError HYPRE.Internals.to_hypre_data([1, 2], ilower, iupper)
@test_throws ArgumentError Internals.to_hypre_data([1, 2], ilower, iupper)
end
# Converting Vector to HYPREVector
b = rand(HYPRE_Complex, 10)
h = HYPREVector(b, ilower, iupper)
@test h.IJVector != HYPRE_IJVector(C_NULL)
@test h.ParVector != HYPRE_ParVector(C_NULL)
@test_throws ArgumentError HYPREVector([1, 2], ilower, iupper)
@testset "HYPREMatrix(::PVector)" begin
end

Loading…
Cancel
Save