Files
mfem/linalg/superlu.cpp
T

577 lines
15 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.
#include "../config/config.hpp"
#ifdef MFEM_USE_SUPERLU
#ifdef MFEM_USE_MPI
#include "superlu.hpp"
// SuperLU headers
#include "superlu_defs.h"
#include "superlu_ddefs.h"
#if XSDK_INDEX_SIZE == 64
#error "SuperLUDist has been built with 64bit integers. This is not supported"
#endif
#if SUPERLU_DIST_MAJOR_VERSION > 6 || \
(SUPERLU_DIST_MAJOR_VERSION == 6 && SUPERLU_DIST_MINOR_VERSION > 2)
#define ScalePermstruct_t dScalePermstruct_t
#define LUstruct_t dLUstruct_t
#define SOLVEstruct_t dSOLVEstruct_t
#define ScalePermstructFree dScalePermstructFree
#define Destroy_LU dDestroy_LU
#define LUstructFree dLUstructFree
#define LUstructInit dLUstructInit
#endif
using namespace std;
namespace mfem
{
unsigned int superlu_internal::sqrti( const unsigned int & a )
{
unsigned int a_ = a;
unsigned int rem = 0;
unsigned int root = 0;
unsigned short len = sizeof(int); len <<= 2;
unsigned short shift = (unsigned short)((len<<1) - 2);
for (int i=0; i<len; i++)
{
root <<= 1;
rem = ((rem << 2) + (a_ >> shift));
a_ <<= 2;
root ++;
if (root <= rem)
{
rem -= root;
root++;
}
else
{
root--;
}
}
return (root >> 1);
}
SuperLURowLocMatrix::SuperLURowLocMatrix(MPI_Comm comm,
int num_loc_rows, int first_loc_row,
int glob_nrows, int glob_ncols,
int *I, int *J, double *data)
: comm_(comm),
rowLocPtr_(NULL)
{
// Set mfem::Operator member data
height = num_loc_rows;
width = num_loc_rows;
// Allocate SuperLU's SuperMatrix struct
rowLocPtr_ = new SuperMatrix;
SuperMatrix * A = (SuperMatrix*)rowLocPtr_;
A->Store = NULL;
int m = glob_nrows;
int n = glob_ncols;
int nnz_loc = I[num_loc_rows];
int m_loc = num_loc_rows;
int fst_row = first_loc_row;
double * nzval = NULL;
int * colind = NULL;
int * rowptr = NULL;
if ( !(nzval = doubleMalloc_dist(nnz_loc)) )
{
ABORT("Malloc fails for nzval[].");
}
for (int i=0; i<nnz_loc; i++)
{
nzval[i] = data[i];
}
if ( !(colind = intMalloc_dist(nnz_loc)) )
{
ABORT("Malloc fails for colind[].");
}
for (int i=0; i<nnz_loc; i++)
{
colind[i] = J[i];
}
if ( !(rowptr = intMalloc_dist(m_loc+1)) )
{
ABORT("Malloc fails for rowptr[].");
}
for (int i=0; i<=m_loc; i++)
{
rowptr[i] = I[i];
}
// Assign he matrix data to SuperLU's SuperMatrix structure
dCreate_CompRowLoc_Matrix_dist(A, m, n, nnz_loc, m_loc, fst_row,
nzval, colind, rowptr,
SLU_NR_loc, SLU_D, SLU_GE);
}
SuperLURowLocMatrix::SuperLURowLocMatrix( const HypreParMatrix & hypParMat )
: comm_(hypParMat.GetComm()),
rowLocPtr_(NULL)
{
rowLocPtr_ = new SuperMatrix;
SuperMatrix * A = (SuperMatrix*)rowLocPtr_;
A->Store = NULL;
// First cast the parameter to a hypre_ParCSRMatrix
hypre_ParCSRMatrix * parcsr_op =
(hypre_ParCSRMatrix *)const_cast<HypreParMatrix&>(hypParMat);
MFEM_ASSERT(parcsr_op != NULL,"SuperLU: const_cast failed in SetOperator");
// Create the SuperMatrix A by borrowing the internal data from a
// hypre_CSRMatrix.
hypre_CSRMatrix * csr_op = hypre_MergeDiagAndOffd(parcsr_op);
hypre_CSRMatrixSetDataOwner(csr_op,0);
#if MFEM_HYPRE_VERSION >= 21600
MFEM_VERIFY(csr_op->num_rows < INT_MAX,"SuperLU: number of local rows "
"is too large to store as an integer.");
hypre_CSRMatrixBigJtoJ(csr_op);
#endif
int m = parcsr_op->global_num_rows;
int n = parcsr_op->global_num_cols;
int fst_row = parcsr_op->first_row_index;
int nnz_loc = csr_op->num_nonzeros;
int m_loc = csr_op->num_rows;
height = m_loc;
width = m_loc;
double * nzval = csr_op->data;
int * colind = csr_op->j;
int * rowptr = NULL;
// The "i" array cannot be stolen from the hypre_CSRMatrix so we'll copy it
if ( !(rowptr = intMalloc_dist(m_loc+1)) )
{
ABORT("Malloc fails for rowptr[].");
}
for (int i=0; i<=m_loc; i++)
{
rowptr[i] = (csr_op->i)[i];
}
// Everything has been copied or abducted so delete the structure
hypre_CSRMatrixDestroy(csr_op);
// Assign he matrix data to SuperLU's SuperMatrix structure
dCreate_CompRowLoc_Matrix_dist(A, m, n, nnz_loc, m_loc, fst_row,
nzval, colind, rowptr,
SLU_NR_loc, SLU_D, SLU_GE);
}
SuperLURowLocMatrix::~SuperLURowLocMatrix()
{
SuperMatrix * A = (SuperMatrix*)rowLocPtr_;
// Delete the internal data
Destroy_CompRowLoc_Matrix_dist(A);
// Delete the struct
if ( A != NULL ) { delete A; }
}
SuperLUSolver::SuperLUSolver( MPI_Comm comm )
: comm_(comm),
APtr_(NULL),
optionsPtr_(NULL),
statPtr_(NULL),
ScalePermstructPtr_(NULL),
LUstructPtr_(NULL),
SOLVEstructPtr_(NULL),
gridPtr_(NULL),
berr_(NULL),
perm_r_(NULL),
nrhs_(1),
nprow_(0),
npcol_(0),
firstSolveWithThisA_(false),
gridInitialized_(false),
LUStructInitialized_(false)
{
this->Init();
}
SuperLUSolver::SuperLUSolver( SuperLURowLocMatrix & A )
: comm_(A.GetComm()),
APtr_(&A),
optionsPtr_(NULL),
statPtr_(NULL),
ScalePermstructPtr_(NULL),
LUstructPtr_(NULL),
SOLVEstructPtr_(NULL),
gridPtr_(NULL),
berr_(NULL),
perm_r_(NULL),
nrhs_(1),
nprow_(0),
npcol_(0),
firstSolveWithThisA_(true),
gridInitialized_(false),
LUStructInitialized_(false)
{
height = A.Height();
width = A.Width();
this->Init();
}
SuperLUSolver::~SuperLUSolver()
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
SuperLUStat_t * stat = (SuperLUStat_t*)statPtr_;
ScalePermstruct_t * SPstruct = (ScalePermstruct_t*)ScalePermstructPtr_;
LUstruct_t * LUstruct = (LUstruct_t*)LUstructPtr_;
SOLVEstruct_t * SOLVEstruct = (SOLVEstruct_t*)SOLVEstructPtr_;
gridinfo_t * grid = (gridinfo_t*)gridPtr_;
SUPERLU_FREE(berr_);
PStatFree(stat);
if ( LUStructInitialized_ )
{
ScalePermstructFree(SPstruct);
Destroy_LU(width, grid, LUstruct);
LUstructFree(LUstruct);
}
if ( options->SolveInitialized )
{
dSolveFinalize(options, SOLVEstruct);
}
if ( options != NULL ) { delete options; }
if ( stat != NULL ) { delete stat; }
if ( SPstruct != NULL ) { delete SPstruct; }
if ( LUstruct != NULL ) { delete LUstruct; }
if ( SOLVEstruct != NULL ) { delete SOLVEstruct; }
if ( grid != NULL ) { delete grid; }
if ( perm_r_ != NULL ) { SUPERLU_FREE(perm_r_); }
}
void SuperLUSolver::Init()
{
MPI_Comm_size(comm_, &numProcs_);
MPI_Comm_rank(comm_, &myid_);
optionsPtr_ = new superlu_dist_options_t;
statPtr_ = new SuperLUStat_t;
ScalePermstructPtr_ = new ScalePermstruct_t;
LUstructPtr_ = new LUstruct_t;
SOLVEstructPtr_ = new SOLVEstruct_t;
gridPtr_ = new gridinfo_t;
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
SuperLUStat_t * stat = (SuperLUStat_t*)statPtr_;
if ( !(berr_ = doubleMalloc_dist(nrhs_)) )
{
ABORT("Malloc fails for berr[].");
}
// Set default options
set_default_options_dist(options);
options->ParSymbFact = YES;
options->ColPerm = NATURAL;
// Choose nprow and npcol so that the process grid is as square as possible.
// If the processes cannot be divided evenly, keep the row dimension smaller
// than the column dimension.
nprow_ = (int)superlu_internal::sqrti((unsigned int)numProcs_);
while (numProcs_ % nprow_ != 0 && nprow_ > 0)
{
nprow_--;
}
npcol_ = (int)(numProcs_ / nprow_);
MFEM_ASSERT(nprow_ * npcol_ == numProcs_, "");
PStatInit(stat); // Initialize the statistics variables.
}
void SuperLUSolver::SetPrintStatistics( bool print_stat )
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
yes_no_t opt = print_stat?YES:NO;
options->PrintStat = opt;
}
void SuperLUSolver::SetEquilibriate( bool equil )
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
yes_no_t opt = equil?YES:NO;
options->Equil = opt;
}
void SuperLUSolver::SetColumnPermutation( superlu::ColPerm col_perm )
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
colperm_t opt = (colperm_t)col_perm;
options->ColPerm = opt;
}
void SuperLUSolver::SetRowPermutation( superlu::RowPerm row_perm,
Array<int> * perm )
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
rowperm_t opt = (rowperm_t)row_perm;
options->RowPerm = opt;
if ( opt == MY_PERMR )
{
if ( perm == NULL )
{
mfem_error("SuperLUSolver::SetRowPermutation :"
" permutation vector not set!");
}
if ( !(perm_r_ = intMalloc_dist(perm->Size())) )
{
ABORT("Malloc fails for perm_r[].");
}
for (int i=0; i<perm->Size(); i++)
{
perm_r_[i] = (*perm)[i];
}
}
}
void SuperLUSolver::SetTranspose( superlu::Trans trans )
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
trans_t opt = (trans_t)trans;
options->Trans = opt;
}
void SuperLUSolver::SetIterativeRefine( superlu::IterRefine iter_ref )
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
IterRefine_t opt = (IterRefine_t)iter_ref;
options->IterRefine = opt;
}
void SuperLUSolver::SetReplaceTinyPivot( bool rtp )
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
yes_no_t opt = rtp?YES:NO;
options->ReplaceTinyPivot = opt;
}
void SuperLUSolver::SetNumLookAheads( int num_lookaheads )
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
options->num_lookaheads = num_lookaheads;
}
void SuperLUSolver::SetLookAheadElimTree( bool etree )
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
yes_no_t opt = etree?YES:NO;
options->lookahead_etree = opt;
}
void SuperLUSolver::SetSymmetricPattern( bool sym )
{
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
yes_no_t opt = sym?YES:NO;
options->SymPattern = opt;
}
void SuperLUSolver::SetupGrid()
{
gridinfo_t * grid = (gridinfo_t*)gridPtr_;
// Make sure the values of nprow and npcol are reasonable
if ( ((nprow_ * npcol_) > numProcs_) || ((nprow_ * npcol_) < 1) )
{
if ( myid_ == 0 )
{
mfem::err << "Warning: User specified nprow and npcol are such that "
<< "(nprow * npcol) > numProcs or (nprow * npcol) < 1. "
<< "Using default values for nprow and npcol instead."
<< endl;
}
nprow_ = (int)superlu_internal::sqrti((unsigned int)numProcs_);
while (numProcs_ % nprow_ != 0 && nprow_ > 0)
{
nprow_--;
}
npcol_ = (int)(numProcs_ / nprow_);
MFEM_ASSERT(nprow_ * npcol_ == numProcs_, "");
}
superlu_gridinit(comm_, nprow_, npcol_, grid);
gridInitialized_ = true;
}
void SuperLUSolver::DismantleGrid()
{
if ( gridInitialized_ )
{
gridinfo_t * grid = (gridinfo_t*)gridPtr_;
superlu_gridexit(grid);
}
gridInitialized_ = false;
}
void SuperLUSolver::Mult( const Vector & x, Vector & y ) const
{
MFEM_ASSERT(APtr_ != NULL,
"SuperLU Error: The operator must be set before"
" the system can be solved.");
superlu_dist_options_t * options = (superlu_dist_options_t*)optionsPtr_;
SuperLUStat_t * stat = (SuperLUStat_t*)statPtr_;
SuperMatrix * A = (SuperMatrix*)APtr_->InternalData();
ScalePermstruct_t * SPstruct = (ScalePermstruct_t*)ScalePermstructPtr_;
LUstruct_t * LUstruct = (LUstruct_t*)LUstructPtr_;
SOLVEstruct_t * SOLVEstruct = (SOLVEstruct_t*)SOLVEstructPtr_;
gridinfo_t * grid = (gridinfo_t*)gridPtr_;
if (!firstSolveWithThisA_)
{
options->Fact = FACTORED; // Indicate the factored form of A is supplied.
}
else // This is the first solve with this A
{
firstSolveWithThisA_ = false;
// Make sure that the parameters have been initialized The only parameter
// we might have to worry about is ScalePermstruct, if the user is
// supplying a row or column permutation.
// Initialize ScalePermstruct and LUstruct.
SPstruct->DiagScale = NOEQUIL;
// Transfer ownership of the row permutations if available
if ( perm_r_ != NULL )
{
SPstruct->perm_r = perm_r_;
perm_r_ = NULL;
}
else
{
if ( !(SPstruct->perm_r = intMalloc_dist(A->nrow)) )
{
ABORT("Malloc fails for perm_r[].");
}
}
if ( !(SPstruct->perm_c = intMalloc_dist(A->ncol)) )
{
ABORT("Malloc fails for perm_c[].");
}
LUstructInit(A->ncol, LUstruct);
LUStructInitialized_ = true;
}
// SuperLU overwrites x with y, so copy x to y and pass that to the solve
// routine.
y = x;
double* yPtr = (double*)y;
int info = -1, locSize = y.Size();
// Solve the system
pdgssvx(options, A, SPstruct, yPtr, locSize, nrhs_, grid,
LUstruct, SOLVEstruct, berr_, stat, &info);
if ( info != 0 )
{
if ( info <= A->ncol )
{
MFEM_ABORT("SuperLU: Found a singular matrix, U("
<< info << "," << info << ") is exactly zero.");
}
else if ( info > A->ncol )
{
MFEM_ABORT("SuperLU: Memory allocation error with "
<< info - A->ncol << " bytes already allocated,");
}
else
{
MFEM_ABORT("Unknown SuperLU Error");
}
}
}
void SuperLUSolver::SetOperator( const Operator & op )
{
// Verify that we have a compatible operator
APtr_ = dynamic_cast<const SuperLURowLocMatrix*>(&op);
if ( APtr_ == NULL )
{
mfem_error("SuperLUSolver::SetOperator : not SuperLURowLocMatrix!");
}
// Everything is OK so finish setting the operator
firstSolveWithThisA_ = true;
// Set mfem::Operator member data
height = op.Height();
width = op.Width();
// Initialize the processor grid if necessary
if (!gridInitialized_)
{
this->SetupGrid();
}
}
} // mfem namespace
#endif // MFEM_USE_MPI
#endif // MFEM_USE_SUPERLU