Files
mfem/linalg/hypre.hpp
T

1327 lines
48 KiB
C++

// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_HYPRE
#define MFEM_HYPRE
#include "../config/config.hpp"
#ifdef MFEM_USE_MPI
#include <mpi.h>
// Enable internal hypre timing routines
#define HYPRE_TIMING
// hypre header files
#include "seq_mv.h"
#include "_hypre_parcsr_mv.h"
#include "_hypre_parcsr_ls.h"
#include "temp_multivector.h"
#include "../general/globals.hpp"
#ifdef HYPRE_COMPLEX
#error "MFEM does not work with HYPRE's complex numbers support"
#endif
#include "sparsemat.hpp"
#include "hypre_parcsr.hpp"
#ifdef MFEM_USE_SUNDIALS
#include <nvector/nvector_parhyp.h>
#endif
namespace mfem
{
class ParFiniteElementSpace;
class HypreParMatrix;
namespace internal
{
template <typename int_type>
inline int to_int(int_type i)
{
MFEM_ASSERT(int_type(int(i)) == i, "overflow converting int_type to int");
return int(i);
}
// Specialization for to_int(int)
template <> inline int to_int(int i) { return i; }
// Convert a HYPRE_Int to int
#ifdef HYPRE_BIGINT
template <>
inline int to_int(HYPRE_Int i)
{
MFEM_ASSERT(HYPRE_Int(int(i)) == i, "overflow converting HYPRE_Int to int");
return int(i);
}
#endif
}
/// Wrapper for hypre's parallel vector class
class HypreParVector : public Vector
{
private:
int own_ParVector;
/// The actual object
hypre_ParVector *x;
friend class HypreParMatrix;
// Set Vector::data and Vector::size from *x
inline void _SetDataAndSize_();
public:
/** @brief Creates vector with given global size and parallel partitioning of
the rows/columns given by @a col. */
/** @anchor hypre_partitioning_descr
The partitioning is defined in one of two ways depending on the
configuration of HYPRE:
1. If HYPRE_AssumedPartitionCheck() returns true (the default),
then col is of length 2 and the local processor owns columns
[col[0],col[1]).
2. If HYPRE_AssumedPartitionCheck() returns false, then col is of
length (number of processors + 1) and processor P owns columns
[col[P],col[P+1]) i.e. each processor has a copy of the same col
array. */
HypreParVector(MPI_Comm comm, HYPRE_Int glob_size, HYPRE_Int *col);
/** @brief Creates vector with given global size, partitioning of the
columns, and data. */
/** The data must be allocated and destroyed outside. If @a _data is NULL, a
dummy vector without a valid data array will be created. See @ref
hypre_partitioning_descr "here" for a description of the @a col array. */
HypreParVector(MPI_Comm comm, HYPRE_Int glob_size, double *_data,
HYPRE_Int *col);
/// Creates vector compatible with y
HypreParVector(const HypreParVector &y);
/// Creates vector compatible with (i.e. in the domain of) A or A^T
explicit HypreParVector(const HypreParMatrix &A, int transpose = 0);
/// Creates vector wrapping y
explicit HypreParVector(HYPRE_ParVector y);
/// Create a true dof parallel vector on a given ParFiniteElementSpace
explicit HypreParVector(ParFiniteElementSpace *pfes);
/// MPI communicator
MPI_Comm GetComm() { return x->comm; }
/// Returns the parallel row/column partitioning
/** See @ref hypre_partitioning_descr "here" for a description of the
partitioning array. */
inline HYPRE_Int *Partitioning() { return x->partitioning; }
/// Returns the global number of rows
inline HYPRE_Int GlobalSize() { return x->global_size; }
/// Typecasting to hypre's hypre_ParVector*
operator hypre_ParVector*() const { return x; }
#ifndef HYPRE_PAR_VECTOR_STRUCT
/// Typecasting to hypre's HYPRE_ParVector, a.k.a. void *
operator HYPRE_ParVector() const { return (HYPRE_ParVector) x; }
#endif
/// Changes the ownership of the the vector
hypre_ParVector *StealParVector() { own_ParVector = 0; return x; }
/// Sets ownership of the internal hypre_ParVector
void SetOwnership(int own) { own_ParVector = own; }
/// Gets ownership of the internal hypre_ParVector
int GetOwnership() const { return own_ParVector; }
/// Returns the global vector in each processor
Vector* GlobalVector() const;
/// Set constant values
HypreParVector& operator= (double d);
/// Define '=' for hypre vectors.
HypreParVector& operator= (const HypreParVector &y);
/// Sets the data of the Vector and the hypre_ParVector to @a _data.
/** Must be used only for HypreParVector%s that do not own the data,
e.g. created with the constructor:
HypreParVector(MPI_Comm, HYPRE_Int, double *, HYPRE_Int *). */
void SetData(double *_data);
/// Set random values
HYPRE_Int Randomize(HYPRE_Int seed);
/// Prints the locally owned rows in parallel
void Print(const char *fname) const;
/// Calls hypre's destroy function
~HypreParVector();
#ifdef MFEM_USE_SUNDIALS
/// Return a new wrapper SUNDIALS N_Vector of type SUNDIALS_NVEC_PARHYP.
/** The returned N_Vector must be destroyed by the caller. */
virtual N_Vector ToNVector() { return N_VMake_ParHyp(x); }
/** @brief Update an existing wrapper SUNDIALS N_Vector of type
SUNDIALS_NVEC_PARHYP to point to this Vector. */
virtual void ToNVector(N_Vector &nv);
#endif
};
/// Returns the inner product of x and y
double InnerProduct(HypreParVector &x, HypreParVector &y);
double InnerProduct(HypreParVector *x, HypreParVector *y);
/** @brief Compute the l_p norm of the Vector which is split without overlap
across the given communicator. */
double ParNormlp(const Vector &vec, double p, MPI_Comm comm);
/// Wrapper for hypre's ParCSR matrix class
class HypreParMatrix : public Operator
{
private:
/// The actual object
hypre_ParCSRMatrix *A;
/// Auxiliary vectors for typecasting
mutable HypreParVector *X, *Y;
// Flags indicating ownership of A->diag->{i,j,data}, A->offd->{i,j,data},
// and A->col_map_offd.
// The possible values for diagOwner are:
// -1: no special treatment of A->diag (default)
// 0: prevent hypre from destroying A->diag->{i,j,data}
// 1: same as 0, plus take ownership of A->diag->{i,j}
// 2: same as 0, plus take ownership of A->diag->data
// 3: same as 0, plus take ownership of A->diag->{i,j,data}
// The same values and rules apply to offdOwner and A->offd.
// The possible values for colMapOwner are:
// -1: no special treatment of A->col_map_offd (default)
// 0: prevent hypre from destroying A->col_map_offd
// 1: same as 0, plus take ownership of A->col_map_offd
// All owned arrays are destroyed with 'delete []'.
signed char diagOwner, offdOwner, colMapOwner;
// Does the object own the pointer A?
signed char ParCSROwner;
// Initialize with defaults. Does not initialize inherited members.
void Init();
// Delete all owned data. Does not perform re-initialization with defaults.
void Destroy();
// Copy (shallow/deep, based on HYPRE_BIGINT) the I and J arrays from csr to
// hypre_csr. Shallow copy the data. Return the appropriate ownership flag.
static char CopyCSR(SparseMatrix *csr, hypre_CSRMatrix *hypre_csr);
// Copy (shallow or deep, based on HYPRE_BIGINT) the I and J arrays from
// bool_csr to hypre_csr. Allocate the data array and set it to all ones.
// Return the appropriate ownership flag.
static char CopyBoolCSR(Table *bool_csr, hypre_CSRMatrix *hypre_csr);
// Copy the j array of a hypre_CSRMatrix to the given J array, converting
// the indices from HYPRE_Int to int.
static void CopyCSR_J(hypre_CSRMatrix *hypre_csr, int *J);
public:
/// An empty matrix to be used as a reference to an existing matrix
HypreParMatrix();
/// Converts hypre's format to HypreParMatrix
/** If @a owner is false, ownership of @a a is not transferred */
explicit HypreParMatrix(hypre_ParCSRMatrix *a, bool owner = true)
{
Init();
A = a;
if (!owner) { ParCSROwner = 0; }
height = GetNumRows();
width = GetNumCols();
}
/// Creates block-diagonal square parallel matrix.
/** Diagonal is given by @a diag which must be in CSR format (finalized). The
new HypreParMatrix does not take ownership of any of the input arrays.
See @ref hypre_partitioning_descr "here" for a description of the row
partitioning array @a row_starts.
@warning The ordering of the columns in each row in @a *diag may be
changed by this constructor to ensure that the first entry in each row is
the diagonal one. This is expected by most hypre functions. */
HypreParMatrix(MPI_Comm comm, HYPRE_Int glob_size, HYPRE_Int *row_starts,
SparseMatrix *diag); // constructor with 4 arguments, v1
/// Creates block-diagonal rectangular parallel matrix.
/** Diagonal is given by @a diag which must be in CSR format (finalized). The
new HypreParMatrix does not take ownership of any of the input arrays.
See @ref hypre_partitioning_descr "here" for a description of the
partitioning arrays @a row_starts and @a col_starts. */
HypreParMatrix(MPI_Comm comm, HYPRE_Int global_num_rows,
HYPRE_Int global_num_cols, HYPRE_Int *row_starts,
HYPRE_Int *col_starts,
SparseMatrix *diag); // constructor with 6 arguments, v1
/// Creates general (rectangular) parallel matrix.
/** The new HypreParMatrix does not take ownership of any of the input
arrays. See @ref hypre_partitioning_descr "here" for a description of the
partitioning arrays @a row_starts and @a col_starts. */
HypreParMatrix(MPI_Comm comm, HYPRE_Int global_num_rows,
HYPRE_Int global_num_cols, HYPRE_Int *row_starts,
HYPRE_Int *col_starts, SparseMatrix *diag, SparseMatrix *offd,
HYPRE_Int *cmap); // constructor with 8 arguments
/// Creates general (rectangular) parallel matrix.
/** The new HypreParMatrix takes ownership of all input arrays, except
@a col_starts and @a row_starts. See @ref hypre_partitioning_descr "here"
for a description of the partitioning arrays @a row_starts and @a
col_starts. */
HypreParMatrix(MPI_Comm comm,
HYPRE_Int global_num_rows, HYPRE_Int global_num_cols,
HYPRE_Int *row_starts, HYPRE_Int *col_starts,
HYPRE_Int *diag_i, HYPRE_Int *diag_j, double *diag_data,
HYPRE_Int *offd_i, HYPRE_Int *offd_j, double *offd_data,
HYPRE_Int offd_num_cols,
HYPRE_Int *offd_col_map); // constructor with 13 arguments
/// Creates a parallel matrix from SparseMatrix on processor 0.
/** See @ref hypre_partitioning_descr "here" for a description of the
partitioning arrays @a row_starts and @a col_starts. */
HypreParMatrix(MPI_Comm comm, HYPRE_Int *row_starts, HYPRE_Int *col_starts,
SparseMatrix *a); // constructor with 4 arguments, v2
/// Creates boolean block-diagonal rectangular parallel matrix.
/** The new HypreParMatrix does not take ownership of any of the input
arrays. See @ref hypre_partitioning_descr "here" for a description of the
partitioning arrays @a row_starts and @a col_starts. */
HypreParMatrix(MPI_Comm comm, HYPRE_Int global_num_rows,
HYPRE_Int global_num_cols, HYPRE_Int *row_starts,
HYPRE_Int *col_starts,
Table *diag); // constructor with 6 arguments, v2
/// Creates boolean rectangular parallel matrix.
/** The new HypreParMatrix takes ownership of the arrays @a i_diag,
@a j_diag, @a i_offd, @a j_offd, and @a cmap; does not take ownership of
the arrays @a row and @a col. See @ref hypre_partitioning_descr "here"
for a description of the partitioning arrays @a row and @a col. */
HypreParMatrix(MPI_Comm comm, int id, int np, HYPRE_Int *row, HYPRE_Int *col,
HYPRE_Int *i_diag, HYPRE_Int *j_diag, HYPRE_Int *i_offd,
HYPRE_Int *j_offd, HYPRE_Int *cmap,
HYPRE_Int cmap_size); // constructor with 11 arguments
/** @brief Creates a general parallel matrix from a local CSR matrix on each
processor described by the @a I, @a J and @a data arrays. */
/** The local matrix should be of size (local) @a nrows by (global)
@a glob_ncols. The new parallel matrix contains copies of all input
arrays (so they can be deleted). See @ref hypre_partitioning_descr "here"
for a description of the partitioning arrays @a rows and @a cols. */
HypreParMatrix(MPI_Comm comm, int nrows, HYPRE_Int glob_nrows,
HYPRE_Int glob_ncols, int *I, HYPRE_Int *J,
double *data, HYPRE_Int *rows,
HYPRE_Int *cols); // constructor with 9 arguments
/** @brief Copy constructor for a ParCSR matrix which creates a deep copy of
structure and data from @a P. */
HypreParMatrix(const HypreParMatrix &P);
/// Make this HypreParMatrix a reference to 'master'
void MakeRef(const HypreParMatrix &master);
/// MPI communicator
MPI_Comm GetComm() const { return A->comm; }
/// Typecasting to hypre's hypre_ParCSRMatrix*
operator hypre_ParCSRMatrix*() const { return A; }
#ifndef HYPRE_PAR_CSR_MATRIX_STRUCT
/// Typecasting to hypre's HYPRE_ParCSRMatrix, a.k.a. void *
operator HYPRE_ParCSRMatrix() { return (HYPRE_ParCSRMatrix) A; }
#endif
/// Changes the ownership of the the matrix
hypre_ParCSRMatrix* StealData();
/// Explicitly set the three ownership flags, see docs for diagOwner etc.
void SetOwnerFlags(signed char diag, signed char offd, signed char colmap)
{ diagOwner = diag, offdOwner = offd, colMapOwner = colmap; }
/// Get diag ownership flag
signed char OwnsDiag() const { return diagOwner; }
/// Get offd ownership flag
signed char OwnsOffd() const { return offdOwner; }
/// Get colmap ownership flag
signed char OwnsColMap() const { return colMapOwner; }
/** If the HypreParMatrix does not own the row-starts array, make a copy of
it that the HypreParMatrix will own. If the col-starts array is the same
as the row-starts array, col-starts is also replaced. */
void CopyRowStarts();
/** If the HypreParMatrix does not own the col-starts array, make a copy of
it that the HypreParMatrix will own. If the row-starts array is the same
as the col-starts array, row-starts is also replaced. */
void CopyColStarts();
/// Returns the global number of nonzeros
inline HYPRE_Int NNZ() const { return A->num_nonzeros; }
/// Returns the row partitioning
/** See @ref hypre_partitioning_descr "here" for a description of the
partitioning array. */
inline HYPRE_Int *RowPart() { return A->row_starts; }
/// Returns the column partitioning
/** See @ref hypre_partitioning_descr "here" for a description of the
partitioning array. */
inline HYPRE_Int *ColPart() { return A->col_starts; }
/// Returns the row partitioning (const version)
/** See @ref hypre_partitioning_descr "here" for a description of the
partitioning array. */
inline const HYPRE_Int *RowPart() const { return A->row_starts; }
/// Returns the column partitioning (const version)
/** See @ref hypre_partitioning_descr "here" for a description of the
partitioning array. */
inline const HYPRE_Int *ColPart() const { return A->col_starts; }
/// Returns the global number of rows
inline HYPRE_Int M() const { return A->global_num_rows; }
/// Returns the global number of columns
inline HYPRE_Int N() const { return A->global_num_cols; }
/// Get the local diagonal of the matrix.
void GetDiag(Vector &diag) const;
/// Get the local diagonal block. NOTE: 'diag' will not own any data.
void GetDiag(SparseMatrix &diag) const;
/// Get the local off-diagonal block. NOTE: 'offd' will not own any data.
void GetOffd(SparseMatrix &offd, HYPRE_Int* &cmap) const;
/** Split the matrix into M x N equally sized blocks of parallel matrices.
The size of 'blocks' must already be set to M x N. */
void GetBlocks(Array2D<HypreParMatrix*> &blocks,
bool interleaved_rows = false,
bool interleaved_cols = false) const;
/// Returns the transpose of *this
HypreParMatrix * Transpose() const;
/// Returns the number of rows in the diagonal block of the ParCSRMatrix
int GetNumRows() const
{
return internal::to_int(
hypre_CSRMatrixNumRows(hypre_ParCSRMatrixDiag(A)));
}
/// Returns the number of columns in the diagonal block of the ParCSRMatrix
int GetNumCols() const
{
return internal::to_int(
hypre_CSRMatrixNumCols(hypre_ParCSRMatrixDiag(A)));
}
/// Return the global number of rows
HYPRE_Int GetGlobalNumRows() const
{ return hypre_ParCSRMatrixGlobalNumRows(A); }
/// Return the global number of columns
HYPRE_Int GetGlobalNumCols() const
{ return hypre_ParCSRMatrixGlobalNumCols(A); }
/// Return the parallel row partitioning array.
/** See @ref hypre_partitioning_descr "here" for a description of the
partitioning array. */
HYPRE_Int *GetRowStarts() const { return hypre_ParCSRMatrixRowStarts(A); }
/// Return the parallel column partitioning array.
/** See @ref hypre_partitioning_descr "here" for a description of the
partitioning array. */
HYPRE_Int *GetColStarts() const { return hypre_ParCSRMatrixColStarts(A); }
/// Computes y = alpha * A * x + beta * y
HYPRE_Int Mult(HypreParVector &x, HypreParVector &y,
double alpha = 1.0, double beta = 0.0);
/// Computes y = alpha * A * x + beta * y
HYPRE_Int Mult(HYPRE_ParVector x, HYPRE_ParVector y,
double alpha = 1.0, double beta = 0.0);
/// Computes y = alpha * A^t * x + beta * y
HYPRE_Int MultTranspose(HypreParVector &x, HypreParVector &y,
double alpha = 1.0, double beta = 0.0);
void Mult(double a, const Vector &x, double b, Vector &y) const;
void MultTranspose(double a, const Vector &x, double b, Vector &y) const;
virtual void Mult(const Vector &x, Vector &y) const
{ Mult(1.0, x, 0.0, y); }
virtual void MultTranspose(const Vector &x, Vector &y) const
{ MultTranspose(1.0, x, 0.0, y); }
/** The "Boolean" analog of y = alpha * A * x + beta * y, where elements in
the sparsity pattern of the matrix are treated as "true". */
void BooleanMult(int alpha, const int *x, int beta, int *y)
{
internal::hypre_ParCSRMatrixBooleanMatvec(A, alpha, const_cast<int*>(x),
beta, y);
}
/** The "Boolean" analog of y = alpha * A^T * x + beta * y, where elements in
the sparsity pattern of the matrix are treated as "true". */
void BooleanMultTranspose(int alpha, const int *x, int beta, int *y)
{
internal::hypre_ParCSRMatrixBooleanMatvecT(A, alpha, const_cast<int*>(x),
beta, y);
}
/// Initialize all entries with value.
HypreParMatrix &operator=(double value)
{ internal::hypre_ParCSRMatrixSetConstantValues(A, value); return *this; }
/** Perform the operation `*this += B`, assuming that both matrices use the
same row and column partitions and the same col_map_offd arrays, or B has
an empty off-diagonal block. We also assume that the sparsity pattern of
`*this` contains that of `B`. */
HypreParMatrix &operator+=(const HypreParMatrix &B) { return Add(1.0, B); }
/** Perform the operation `*this += beta*B`, assuming that both matrices use
the same row and column partitions and the same col_map_offd arrays, or
B has an empty off-diagonal block. We also assume that the sparsity
pattern of `*this` contains that of `B`. For a more general case consider
the stand-alone function ParAdd described below. */
HypreParMatrix &Add(const double beta, const HypreParMatrix &B)
{
MFEM_VERIFY(internal::hypre_ParCSRMatrixSum(A, beta, B.A) == 0,
"error in hypre_ParCSRMatrixSum");
return *this;
}
/** @brief Multiply the HypreParMatrix on the left by a block-diagonal
parallel matrix @a D and return the result as a new HypreParMatrix. */
/** If @a D has a different number of rows than @a A (this matrix), @a D's
row starts array needs to be given (as returned by the methods
GetDofOffsets/GetTrueDofOffsets of ParFiniteElementSpace). The new
matrix @a D*A uses copies of the row- and column-starts arrays, so "this"
matrix and @a row_starts can be deleted.
@note This operation is local and does not require communication. */
HypreParMatrix* LeftDiagMult(const SparseMatrix &D,
HYPRE_Int* row_starts = NULL) const;
/// Scale the local row i by s(i).
void ScaleRows(const Vector & s);
/// Scale the local row i by 1./s(i)
void InvScaleRows(const Vector & s);
/// Scale all entries by s: A_scaled = s*A.
void operator*=(double s);
/// Remove values smaller in absolute value than some threshold
void Threshold(double threshold = 0.0);
/// If a row contains only zeros, set its diagonal to 1.
void EliminateZeroRows() { hypre_ParCSRMatrixFixZeroRows(A); }
/** Eliminate rows and columns from the matrix, and rows from the vector B.
Modify B with the BC values in X. */
void EliminateRowsCols(const Array<int> &rows_cols, const HypreParVector &X,
HypreParVector &B);
/** Eliminate rows and columns from the matrix and store the eliminated
elements in a new matrix Ae (returned), so that the modified matrix and
Ae sum to the original matrix. */
HypreParMatrix* EliminateRowsCols(const Array<int> &rows_cols);
/** Eliminate columns from the matrix and store the eliminated elements in a
new matrix Ae (returned) so that the modified matrix and Ae sum to the
original matrix. */
HypreParMatrix* EliminateCols(const Array<int> &cols);
/// Eliminate rows from the diagonal and off-diagonal blocks of the matrix.
void EliminateRows(const Array<int> &rows);
/// Prints the locally owned rows in parallel
void Print(const char *fname, HYPRE_Int offi = 0, HYPRE_Int offj = 0);
/// Reads the matrix from a file
void Read(MPI_Comm comm, const char *fname);
/// Read a matrix saved as a HYPRE_IJMatrix
void Read_IJMatrix(MPI_Comm comm, const char *fname);
/// Print information about the hypre_ParCSRCommPkg of the HypreParMatrix.
void PrintCommPkg(std::ostream &out = mfem::out) const;
/// Calls hypre's destroy function
virtual ~HypreParMatrix() { Destroy(); }
Type GetType() const { return Hypre_ParCSR; }
};
/** @brief Return a new matrix `C = alpha*A + beta*B`, assuming that both `A`
and `B` use the same row and column partitions and the same `col_map_offd`
arrays. */
HypreParMatrix *Add(double alpha, const HypreParMatrix &A,
double beta, const HypreParMatrix &B);
/** Returns the matrix @a A * @a B. Returned matrix does not necessarily own
row or column starts unless the bool @a own_matrix is set to true. */
HypreParMatrix * ParMult(const HypreParMatrix *A, const HypreParMatrix *B,
bool own_matrix = false);
/// Returns the matrix A + B
/** It is assumed that both matrices use the same row and column partitions and
the same col_map_offd arrays. */
HypreParMatrix * ParAdd(const HypreParMatrix *A, const HypreParMatrix *B);
/// Returns the matrix P^t * A * P
HypreParMatrix * RAP(const HypreParMatrix *A, const HypreParMatrix *P);
/// Returns the matrix Rt^t * A * P
HypreParMatrix * RAP(const HypreParMatrix * Rt, const HypreParMatrix *A,
const HypreParMatrix *P);
/// Returns a merged hypre matrix constructed from hypre matrix blocks.
/** It is assumed that all block matrices use the same communicator, and the
block sizes are consistent in rows and columns. Rows and columns are
renumbered but not redistributed in parallel, e.g. the block rows owned by
each process remain on that process in the resulting matrix. Some blocks can
be NULL. Each block and the entire system can be rectangular. Scalability to
extremely large processor counts is limited by global MPI communication, see
GatherBlockOffsetData in hypre.cpp. */
HypreParMatrix * HypreParMatrixFromBlocks(Array2D<HypreParMatrix*> &blocks,
Array2D<double> *blockCoeff=NULL);
/** Eliminate essential BC specified by 'ess_dof_list' from the solution X to
the r.h.s. B. Here A is a matrix with eliminated BC, while Ae is such that
(A+Ae) is the original (Neumann) matrix before elimination. */
void EliminateBC(HypreParMatrix &A, HypreParMatrix &Ae,
const Array<int> &ess_dof_list, const Vector &X, Vector &B);
/// Parallel smoothers in hypre
class HypreSmoother : public Solver
{
protected:
/// The linear system matrix
HypreParMatrix *A;
/// Right-hand side and solution vectors
mutable HypreParVector *B, *X;
/// Temporary vectors
mutable HypreParVector *V, *Z;
/// FIR Filter Temporary Vectors
mutable HypreParVector *X0, *X1;
/** Smoother type from hypre_ParCSRRelax() in ams.c plus extensions, see the
enumeration Type below. */
int type;
/// Number of relaxation sweeps
int relax_times;
/// Damping coefficient (usually <= 1)
double relax_weight;
/// SOR parameter (usually in (0,2))
double omega;
/// Order of the smoothing polynomial
int poly_order;
/// Fraction of spectrum to smooth for polynomial relaxation
double poly_fraction;
/// Apply the polynomial smoother to A or D^{-1/2} A D^{-1/2}
int poly_scale;
/// Taubin's lambda-mu method parameters
double lambda;
double mu;
int taubin_iter;
/// l1 norms of the rows of A
double *l1_norms;
/// If set, take absolute values of the computed l1_norms
bool pos_l1_norms;
/// Number of CG iterations to determine eigenvalue estimates
int eig_est_cg_iter;
/// Maximal eigenvalue estimate for polynomial smoothing
double max_eig_est;
/// Minimal eigenvalue estimate for polynomial smoothing
double min_eig_est;
/// Parameters for windowing function of FIR filter
double window_params[3];
/// Combined coefficients for windowing and Chebyshev polynomials.
double* fir_coeffs;
public:
/** Hypre smoother types:
0 = Jacobi
1 = l1-scaled Jacobi
2 = l1-scaled block Gauss-Seidel/SSOR
4 = truncated l1-scaled block Gauss-Seidel/SSOR
5 = lumped Jacobi
6 = Gauss-Seidel
16 = Chebyshev
1001 = Taubin polynomial smoother
1002 = FIR polynomial smoother. */
enum Type { Jacobi = 0, l1Jacobi = 1, l1GS = 2, l1GStr = 4, lumpedJacobi = 5,
GS = 6, Chebyshev = 16, Taubin = 1001, FIR = 1002
};
HypreSmoother();
HypreSmoother(HypreParMatrix &_A, int type = l1GS,
int relax_times = 1, double relax_weight = 1.0,
double omega = 1.0, int poly_order = 2,
double poly_fraction = .3, int eig_est_cg_iter = 10);
/// Set the relaxation type and number of sweeps
void SetType(HypreSmoother::Type type, int relax_times = 1);
/// Set SOR-related parameters
void SetSOROptions(double relax_weight, double omega);
/// Set parameters for polynomial smoothing
/** By default, 10 iterations of CG are used to estimate the eigenvalues.
Setting eig_est_cg_iter = 0 uses hypre's hypre_ParCSRMaxEigEstimate() instead. */
void SetPolyOptions(int poly_order, double poly_fraction,
int eig_est_cg_iter = 10);
/// Set parameters for Taubin's lambda-mu method
void SetTaubinOptions(double lambda, double mu, int iter);
/// Convenience function for setting canonical windowing parameters
void SetWindowByName(const char* window_name);
/// Set parameters for windowing function for FIR smoother.
void SetWindowParameters(double a, double b, double c);
/// Compute window and Chebyshev coefficients for given polynomial order.
void SetFIRCoefficients(double max_eig);
/// After computing l1-norms, replace them with their absolute values.
/** By default, the l1-norms take their sign from the corresponding diagonal
entries in the associated matrix. */
void SetPositiveDiagonal(bool pos = true) { pos_l1_norms = pos; }
/** Set/update the associated operator. Must be called after setting the
HypreSmoother type and options. */
virtual void SetOperator(const Operator &op);
/// Relax the linear system Ax=b
virtual void Mult(const HypreParVector &b, HypreParVector &x) const;
virtual void Mult(const Vector &b, Vector &x) const;
virtual ~HypreSmoother();
};
/// Abstract class for hypre's solvers and preconditioners
class HypreSolver : public Solver
{
public:
/// How to treat errors returned by hypre function calls.
enum ErrorMode
{
IGNORE_HYPRE_ERRORS, ///< Ignore hypre errors (see e.g. HypreADS)
WARN_HYPRE_ERRORS, ///< Issue warnings on hypre errors
ABORT_HYPRE_ERRORS ///< Abort on hypre errors (default in base class)
};
protected:
/// The linear system matrix
HypreParMatrix *A;
/// Right-hand side and solution vector
mutable HypreParVector *B, *X;
/// Was hypre's Setup function called already?
mutable int setup_called;
/// How to treat hypre errors.
mutable ErrorMode error_mode;
public:
HypreSolver();
HypreSolver(HypreParMatrix *_A);
/// Typecast to HYPRE_Solver -- return the solver
virtual operator HYPRE_Solver() const = 0;
/// hypre's internal Setup function
virtual HYPRE_PtrToParSolverFcn SetupFcn() const = 0;
/// hypre's internal Solve function
virtual HYPRE_PtrToParSolverFcn SolveFcn() const = 0;
virtual void SetOperator(const Operator &op)
{ mfem_error("HypreSolvers do not support SetOperator!"); }
/// Solve the linear system Ax=b
virtual void Mult(const HypreParVector &b, HypreParVector &x) const;
virtual void Mult(const Vector &b, Vector &x) const;
/** @brief Set the behavior for treating hypre errors, see the ErrorMode
enum. The default mode in the base class is ABORT_HYPRE_ERRORS. */
/** Currently, there are three cases in derived classes where the error flag
is set to IGNORE_HYPRE_ERRORS:
* in the method HypreBoomerAMG::SetElasticityOptions(), and
* in the constructor of classes HypreAMS and HypreADS.
The reason for this is that a nonzero hypre error is returned) when
hypre_ParCSRComputeL1Norms() encounters zero row in a matrix, which is
expected in some cases with the above solvers. */
void SetErrorMode(ErrorMode err_mode) const { error_mode = err_mode; }
virtual ~HypreSolver();
};
/// PCG solver in hypre
class HyprePCG : public HypreSolver
{
private:
HYPRE_Solver pcg_solver;
HypreSolver * precond;
public:
HyprePCG(MPI_Comm comm);
HyprePCG(HypreParMatrix &_A);
virtual void SetOperator(const Operator &op);
void SetTol(double tol);
void SetMaxIter(int max_iter);
void SetLogging(int logging);
void SetPrintLevel(int print_lvl);
/// Set the hypre solver to be used as a preconditioner
void SetPreconditioner(HypreSolver &precond);
/** Use the L2 norm of the residual for measuring PCG convergence, plus
(optionally) 1) periodically recompute true residuals from scratch; and
2) enable residual-based stopping criteria. */
void SetResidualConvergenceOptions(int res_frequency=-1, double rtol=0.0);
/// non-hypre setting
void SetZeroInintialIterate() { iterative_mode = false; }
void GetNumIterations(int &num_iterations)
{
HYPRE_Int num_it;
HYPRE_ParCSRPCGGetNumIterations(pcg_solver, &num_it);
num_iterations = internal::to_int(num_it);
}
/// The typecast to HYPRE_Solver returns the internal pcg_solver
virtual operator HYPRE_Solver() const { return pcg_solver; }
/// PCG Setup function
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSRPCGSetup; }
/// PCG Solve function
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSRPCGSolve; }
/// Solve Ax=b with hypre's PCG
virtual void Mult(const HypreParVector &b, HypreParVector &x) const;
using HypreSolver::Mult;
virtual ~HyprePCG();
};
/// GMRES solver in hypre
class HypreGMRES : public HypreSolver
{
private:
HYPRE_Solver gmres_solver;
HypreSolver * precond;
/// Default, generally robust, GMRES options
void SetDefaultOptions();
public:
HypreGMRES(MPI_Comm comm);
HypreGMRES(HypreParMatrix &_A);
virtual void SetOperator(const Operator &op);
void SetTol(double tol);
void SetMaxIter(int max_iter);
void SetKDim(int dim);
void SetLogging(int logging);
void SetPrintLevel(int print_lvl);
/// Set the hypre solver to be used as a preconditioner
void SetPreconditioner(HypreSolver &precond);
/// non-hypre setting
void SetZeroInintialIterate() { iterative_mode = false; }
/// The typecast to HYPRE_Solver returns the internal gmres_solver
virtual operator HYPRE_Solver() const { return gmres_solver; }
/// GMRES Setup function
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSRGMRESSetup; }
/// GMRES Solve function
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSRGMRESSolve; }
/// Solve Ax=b with hypre's GMRES
virtual void Mult (const HypreParVector &b, HypreParVector &x) const;
using HypreSolver::Mult;
virtual ~HypreGMRES();
};
/// The identity operator as a hypre solver
class HypreIdentity : public HypreSolver
{
public:
virtual operator HYPRE_Solver() const { return NULL; }
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) hypre_ParKrylovIdentitySetup; }
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) hypre_ParKrylovIdentity; }
virtual ~HypreIdentity() { }
};
/// Jacobi preconditioner in hypre
class HypreDiagScale : public HypreSolver
{
public:
HypreDiagScale() : HypreSolver() { }
explicit HypreDiagScale(HypreParMatrix &A) : HypreSolver(&A) { }
virtual operator HYPRE_Solver() const { return NULL; }
virtual void SetOperator(const Operator &op);
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSRDiagScaleSetup; }
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSRDiagScale; }
HypreParMatrix* GetData() { return A; }
virtual ~HypreDiagScale() { }
};
/// The ParaSails preconditioner in hypre
class HypreParaSails : public HypreSolver
{
private:
HYPRE_Solver sai_precond;
/// Default, generally robust, ParaSails options
void SetDefaultOptions();
// If sai_precond is NULL, this method allocates it and sets default options.
// Otherwise the method saves the options from sai_precond, destroys it,
// allocates a new object, and sets its options to the saved values.
void ResetSAIPrecond(MPI_Comm comm);
public:
HypreParaSails(MPI_Comm comm);
HypreParaSails(HypreParMatrix &A);
virtual void SetOperator(const Operator &op);
void SetSymmetry(int sym);
/// The typecast to HYPRE_Solver returns the internal sai_precond
virtual operator HYPRE_Solver() const { return sai_precond; }
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParaSailsSetup; }
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParaSailsSolve; }
virtual ~HypreParaSails();
};
/** The Euclid preconditioner in Hypre
Euclid implements the Parallel Incomplete LU factorization technique. For
more information see:
"A Scalable Parallel Algorithm for Incomplete Factor Preconditioning" by
David Hysom and Alex Pothen, https://doi.org/10.1137/S1064827500376193
*/
class HypreEuclid : public HypreSolver
{
private:
HYPRE_Solver euc_precond;
/// Default, generally robust, Euclid options
void SetDefaultOptions();
// If euc_precond is NULL, this method allocates it and sets default options.
// Otherwise the method saves the options from euc_precond, destroys it,
// allocates a new object, and sets its options to the saved values.
void ResetEuclidPrecond(MPI_Comm comm);
public:
HypreEuclid(MPI_Comm comm);
HypreEuclid(HypreParMatrix &A);
virtual void SetOperator(const Operator &op);
/// The typecast to HYPRE_Solver returns the internal euc_precond
virtual operator HYPRE_Solver() const { return euc_precond; }
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_EuclidSetup; }
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_EuclidSolve; }
virtual ~HypreEuclid();
};
/// The BoomerAMG solver in hypre
class HypreBoomerAMG : public HypreSolver
{
private:
HYPRE_Solver amg_precond;
/// Rigid body modes
Array<HYPRE_ParVector> rbms;
/// Finite element space for elasticity problems, see SetElasticityOptions()
ParFiniteElementSpace *fespace;
/// Recompute the rigid-body modes vectors (in the rbms array)
void RecomputeRBMs();
/// Default, generally robust, BoomerAMG options
void SetDefaultOptions();
// If amg_precond is NULL, allocates it and sets default options.
// Otherwise saves the options from amg_precond, destroys it, allocates a new
// one, and sets its options to the saved values.
void ResetAMGPrecond();
public:
HypreBoomerAMG();
HypreBoomerAMG(HypreParMatrix &A);
virtual void SetOperator(const Operator &op);
/** More robust options for systems, such as elasticity. Note that BoomerAMG
assumes Ordering::byVDIM in the finite element space used to generate the
matrix A. */
void SetSystemsOptions(int dim);
/** A special elasticity version of BoomerAMG that takes advantage of
geometric rigid body modes and could perform better on some problems, see
"Improving algebraic multigrid interpolation operators for linear
elasticity problems", Baker, Kolev, Yang, NLAA 2009, DOI:10.1002/nla.688.
As with SetSystemsOptions(), this solver assumes Ordering::byVDIM. */
void SetElasticityOptions(ParFiniteElementSpace *fespace);
void SetPrintLevel(int print_level)
{ HYPRE_BoomerAMGSetPrintLevel(amg_precond, print_level); }
/// The typecast to HYPRE_Solver returns the internal amg_precond
virtual operator HYPRE_Solver() const { return amg_precond; }
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_BoomerAMGSetup; }
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_BoomerAMGSolve; }
virtual ~HypreBoomerAMG();
};
/// Compute the discrete gradient matrix between the nodal linear and ND1 spaces
HypreParMatrix* DiscreteGrad(ParFiniteElementSpace *edge_fespace,
ParFiniteElementSpace *vert_fespace);
/// Compute the discrete curl matrix between the ND1 and RT0 spaces
HypreParMatrix* DiscreteCurl(ParFiniteElementSpace *face_fespace,
ParFiniteElementSpace *edge_fespace);
/// The Auxiliary-space Maxwell Solver in hypre
class HypreAMS : public HypreSolver
{
private:
/// Constuct AMS solver from finite element space
void Init(ParFiniteElementSpace *edge_space);
HYPRE_Solver ams;
/// Vertex coordinates
HypreParVector *x, *y, *z;
/// Discrete gradient matrix
HypreParMatrix *G;
/// Nedelec interpolation matrix and its components
HypreParMatrix *Pi, *Pix, *Piy, *Piz;
public:
HypreAMS(ParFiniteElementSpace *edge_fespace);
HypreAMS(HypreParMatrix &A, ParFiniteElementSpace *edge_fespace);
virtual void SetOperator(const Operator &op);
void SetPrintLevel(int print_lvl);
/// Set this option when solving a curl-curl problem with zero mass term
void SetSingularProblem() { HYPRE_AMSSetBetaPoissonMatrix(ams, NULL); }
/// The typecast to HYPRE_Solver returns the internal ams object
virtual operator HYPRE_Solver() const { return ams; }
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_AMSSetup; }
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_AMSSolve; }
virtual ~HypreAMS();
};
/// The Auxiliary-space Divergence Solver in hypre
class HypreADS : public HypreSolver
{
private:
/// Constuct ADS solver from finite element space
void Init(ParFiniteElementSpace *face_fespace);
HYPRE_Solver ads;
/// Vertex coordinates
HypreParVector *x, *y, *z;
/// Discrete gradient matrix
HypreParMatrix *G;
/// Discrete curl matrix
HypreParMatrix *C;
/// Nedelec interpolation matrix and its components
HypreParMatrix *ND_Pi, *ND_Pix, *ND_Piy, *ND_Piz;
/// Raviart-Thomas interpolation matrix and its components
HypreParMatrix *RT_Pi, *RT_Pix, *RT_Piy, *RT_Piz;
public:
HypreADS(ParFiniteElementSpace *face_fespace);
HypreADS(HypreParMatrix &A, ParFiniteElementSpace *face_fespace);
virtual void SetOperator(const Operator &op);
void SetPrintLevel(int print_lvl);
/// The typecast to HYPRE_Solver returns the internal ads object
virtual operator HYPRE_Solver() const { return ads; }
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ADSSetup; }
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ADSSolve; }
virtual ~HypreADS();
};
/** LOBPCG eigenvalue solver in hypre
The Locally Optimal Block Preconditioned Conjugate Gradient (LOBPCG)
eigenvalue solver is designed to find the lowest eigenmodes of the
generalized eigenvalue problem:
A x = lambda M x
where A is symmetric, potentially indefinite and M is symmetric positive
definite. The eigenvectors are M-orthonormal, meaning that
x^T M x = 1 and x^T M y = 0,
if x and y are distinct eigenvectors. The matrix M is optional and is
assumed to be the identity if left unset.
The efficiency of LOBPCG relies on the availability of a suitable
preconditioner for the matrix A. The preconditioner is supplied through the
SetPreconditioner() method. It should be noted that the operator used with
the preconditioner need not be A itself.
For more information regarding LOBPCG see "Block Locally Optimal
Preconditioned Eigenvalue Xolvers (BLOPEX) in Hypre and PETSc" by
A. Knyazev, M. Argentati, I. Lashuk, and E. Ovtchinnikov, SISC, 29(5),
2224-2239, 2007.
*/
class HypreLOBPCG
{
private:
MPI_Comm comm;
int myid;
int numProcs;
int nev; // Number of desired eigenmodes
int seed; // Random seed used for initial vectors
HYPRE_Int glbSize; // Global number of DoFs in the linear system
HYPRE_Int * part; // Row partitioning of the linear system
// Pointer to HYPRE's solver struct
HYPRE_Solver lobpcg_solver;
// Interface for matrix storage type
mv_InterfaceInterpreter interpreter;
// Interface for setting up and performing matrix-vector products
HYPRE_MatvecFunctions matvec_fn;
// Eigenvalues
Array<double> eigenvalues;
// Forward declaration
class HypreMultiVector;
// MultiVector to store eigenvectors
HypreMultiVector * multi_vec;
// Empty vectors used to setup the matrices and preconditioner
HypreParVector * x;
// An optional operator which projects vectors into a desired subspace
Operator * subSpaceProj;
/// Internal class to represent a set of eigenvectors
class HypreMultiVector
{
private:
// Pointer to hypre's multi-vector object
mv_MultiVectorPtr mv_ptr;
// Wrappers for each member of the multivector
HypreParVector ** hpv;
// Number of vectors in the multivector
int nv;
public:
HypreMultiVector(int n, HypreParVector & v,
mv_InterfaceInterpreter & interpreter);
~HypreMultiVector();
/// Set random values
void Randomize(HYPRE_Int seed);
/// Extract a single HypreParVector object
HypreParVector & GetVector(unsigned int i);
/// Transfers ownership of data to returned array of vectors
HypreParVector ** StealVectors();
operator mv_MultiVectorPtr() const { return mv_ptr; }
mv_MultiVectorPtr & GetMultiVector() { return mv_ptr; }
};
static void * OperatorMatvecCreate( void *A, void *x );
static HYPRE_Int OperatorMatvec( void *matvec_data,
HYPRE_Complex alpha,
void *A,
void *x,
HYPRE_Complex beta,
void *y );
static HYPRE_Int OperatorMatvecDestroy( void *matvec_data );
static HYPRE_Int PrecondSolve(void *solver,
void *A,
void *b,
void *x);
static HYPRE_Int PrecondSetup(void *solver,
void *A,
void *b,
void *x);
public:
HypreLOBPCG(MPI_Comm comm);
~HypreLOBPCG();
void SetTol(double tol);
void SetRelTol(double rel_tol);
void SetMaxIter(int max_iter);
void SetPrintLevel(int logging);
void SetNumModes(int num_eigs) { nev = num_eigs; }
void SetPrecondUsageMode(int pcg_mode);
void SetRandomSeed(int s) { seed = s; }
void SetInitialVectors(int num_vecs, HypreParVector ** vecs);
// The following four methods support general operators
void SetPreconditioner(Solver & precond);
void SetOperator(Operator & A);
void SetMassMatrix(Operator & M);
void SetSubSpaceProjector(Operator & proj) { subSpaceProj = &proj; }
/// Solve the eigenproblem
void Solve();
/// Collect the converged eigenvalues
void GetEigenvalues(Array<double> & eigenvalues);
/// Extract a single eigenvector
HypreParVector & GetEigenvector(unsigned int i);
/// Transfer ownership of the converged eigenvectors
HypreParVector ** StealEigenvectors() { return multi_vec->StealVectors(); }
};
/** AME eigenvalue solver in hypre
The Auxiliary space Maxwell Eigensolver (AME) is designed to find
the lowest eigenmodes of the generalized eigenvalue problem:
Curl Curl x = lambda M x
where the Curl Curl operator is discretized using Nedelec finite element
basis functions. Properties of this discretization are essential to
eliminating the large null space of the Curl Curl operator.
This eigensolver relies upon the LOBPCG eigensolver internally. It is also
expected that the preconditioner supplied to this method will be the
HypreAMS preconditioner defined above.
As with LOBPCG, the operator set in the preconditioner need not be the same
as A. This flexibility may be useful in solving eigenproblems which bare a
strong resemblance to the Curl Curl problems for which AME is designed.
Unlike LOBPCG, this eigensolver requires that the mass matrix be set.
It is possible to circumvent this by passing an identity operator as the
mass matrix but it seems unlikely that this would be useful so it is not the
default behavior.
*/
class HypreAME
{
private:
int myid;
int numProcs;
int nev; // Number of desired eigenmodes
bool setT;
// Pointer to HYPRE's AME solver struct
HYPRE_Solver ame_solver;
// Pointer to HYPRE's AMS solver struct
HypreSolver * ams_precond;
// Eigenvalues
HYPRE_Real * eigenvalues;
// MultiVector to store eigenvectors
HYPRE_ParVector * multi_vec;
HypreParVector ** eigenvectors;
void createDummyVectors();
public:
HypreAME(MPI_Comm comm);
~HypreAME();
void SetTol(double tol);
void SetRelTol(double rel_tol);
void SetMaxIter(int max_iter);
void SetPrintLevel(int logging);
void SetNumModes(int num_eigs);
// The following four methods support operators of type HypreParMatrix.
void SetPreconditioner(HypreSolver & precond);
void SetOperator(HypreParMatrix & A);
void SetMassMatrix(HypreParMatrix & M);
/// Solve the eigenproblem
void Solve();
/// Collect the converged eigenvalues
void GetEigenvalues(Array<double> & eigenvalues);
/// Extract a single eigenvector
HypreParVector & GetEigenvector(unsigned int i);
/// Transfer ownership of the converged eigenvectors
HypreParVector ** StealEigenvectors();
};
}
#endif // MFEM_USE_MPI
#endif