4957 lines
153 KiB
C++
4957 lines
153 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.
|
|
|
|
// Author: Stefano Zampini <stefano.zampini@gmail.com>
|
|
|
|
#include "../config/config.hpp"
|
|
|
|
#ifdef MFEM_USE_MPI
|
|
#ifdef MFEM_USE_PETSC
|
|
|
|
#include "linalg.hpp"
|
|
#include "../fem/fem.hpp"
|
|
|
|
#include "petsc.h"
|
|
#if defined(PETSC_HAVE_HYPRE)
|
|
#include "petscmathypre.h"
|
|
#endif
|
|
|
|
// Backward compatibility
|
|
#if PETSC_VERSION_LT(3,12,0)
|
|
#define MatComputeOperator(A,B,C) MatComputeExplicitOperator(A,C)
|
|
#define MatComputeOperatorTranspose(A,B,C) MatComputeExplicitOperatorTranspose(A,C)
|
|
#endif
|
|
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
|
|
// Note: there are additional #include statements below.
|
|
|
|
// Error handling
|
|
// Prints PETSc's stacktrace and then calls MFEM_ABORT
|
|
// We cannot use PETSc's CHKERRQ since it returns a PetscErrorCode
|
|
#define PCHKERRQ(obj,err) do { \
|
|
if ((err)) \
|
|
{ \
|
|
PetscError(PetscObjectComm((PetscObject)(obj)),__LINE__,_MFEM_FUNC_NAME, \
|
|
__FILE__,(err),PETSC_ERROR_REPEAT,NULL); \
|
|
MFEM_ABORT("Error in PETSc. See stacktrace above."); \
|
|
} \
|
|
} while(0);
|
|
#define CCHKERRQ(comm,err) do { \
|
|
if ((err)) \
|
|
{ \
|
|
PetscError(comm,__LINE__,_MFEM_FUNC_NAME, \
|
|
__FILE__,(err),PETSC_ERROR_REPEAT,NULL); \
|
|
MFEM_ABORT("Error in PETSc. See stacktrace above."); \
|
|
} \
|
|
} while(0);
|
|
|
|
// Callback functions: these functions will be called by PETSc
|
|
static PetscErrorCode __mfem_ts_monitor(TS,PetscInt,PetscReal,Vec,void*);
|
|
static PetscErrorCode __mfem_ts_rhsfunction(TS,PetscReal,Vec,Vec,void*);
|
|
static PetscErrorCode __mfem_ts_rhsjacobian(TS,PetscReal,Vec,Mat,Mat,
|
|
void*);
|
|
static PetscErrorCode __mfem_ts_ifunction(TS,PetscReal,Vec,Vec,Vec,void*);
|
|
static PetscErrorCode __mfem_ts_ijacobian(TS,PetscReal,Vec,Vec,
|
|
PetscReal,Mat,
|
|
Mat,void*);
|
|
static PetscErrorCode __mfem_ts_computesplits(TS,PetscReal,Vec,Vec,
|
|
Mat,Mat,Mat,Mat);
|
|
static PetscErrorCode __mfem_snes_monitor(SNES,PetscInt,PetscReal,void*);
|
|
static PetscErrorCode __mfem_snes_jacobian(SNES,Vec,Mat,Mat,void*);
|
|
static PetscErrorCode __mfem_snes_function(SNES,Vec,Vec,void*);
|
|
static PetscErrorCode __mfem_snes_objective(SNES,Vec,PetscReal*,void*);
|
|
static PetscErrorCode __mfem_snes_update(SNES,PetscInt);
|
|
static PetscErrorCode __mfem_snes_postcheck(SNESLineSearch,Vec,Vec,Vec,
|
|
PetscBool*,PetscBool*,void*);
|
|
static PetscErrorCode __mfem_ksp_monitor(KSP,PetscInt,PetscReal,void*);
|
|
static PetscErrorCode __mfem_pc_shell_apply(PC,Vec,Vec);
|
|
static PetscErrorCode __mfem_pc_shell_apply_transpose(PC,Vec,Vec);
|
|
static PetscErrorCode __mfem_pc_shell_setup(PC);
|
|
static PetscErrorCode __mfem_pc_shell_destroy(PC);
|
|
static PetscErrorCode __mfem_pc_shell_view(PC,PetscViewer);
|
|
static PetscErrorCode __mfem_mat_shell_apply(Mat,Vec,Vec);
|
|
static PetscErrorCode __mfem_mat_shell_apply_transpose(Mat,Vec,Vec);
|
|
static PetscErrorCode __mfem_mat_shell_destroy(Mat);
|
|
static PetscErrorCode __mfem_mat_shell_copy(Mat,Mat,MatStructure);
|
|
static PetscErrorCode __mfem_array_container_destroy(void*);
|
|
static PetscErrorCode __mfem_matarray_container_destroy(void*);
|
|
static PetscErrorCode __mfem_monitor_ctx_destroy(void**);
|
|
|
|
// auxiliary functions
|
|
static PetscErrorCode Convert_Array_IS(MPI_Comm,bool,const mfem::Array<int>*,
|
|
PetscInt,IS*);
|
|
static PetscErrorCode Convert_Vmarks_IS(MPI_Comm,mfem::Array<Mat>&,
|
|
const mfem::Array<int>*,PetscInt,IS*);
|
|
static PetscErrorCode MakeShellPC(PC,mfem::Solver&,bool);
|
|
static PetscErrorCode MakeShellPCWithFactory(PC,
|
|
mfem::PetscPreconditionerFactory*);
|
|
|
|
// Equivalent functions are present in PETSc source code
|
|
// if PETSc has been compiled with hypre support
|
|
// We provide them here in case PETSC_HAVE_HYPRE is not defined
|
|
#if !defined(PETSC_HAVE_HYPRE)
|
|
static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix*,Mat*);
|
|
static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix*,Mat*);
|
|
#endif
|
|
|
|
// structs used by PETSc code
|
|
typedef struct
|
|
{
|
|
mfem::Solver *op;
|
|
mfem::PetscPreconditionerFactory *factory;
|
|
bool ownsop;
|
|
unsigned long int numprec;
|
|
} __mfem_pc_shell_ctx;
|
|
|
|
typedef struct
|
|
{
|
|
mfem::Operator *op; // The nonlinear operator
|
|
mfem::PetscBCHandler *bchandler; // Handling of essential bc
|
|
mfem::Vector *work; // Work vector
|
|
mfem::Operator::Type jacType; // OperatorType for the Jacobian
|
|
// Objective for line search
|
|
void (*objective)(mfem::Operator *op, const mfem::Vector&, double*);
|
|
// PostCheck function (to be called after successful line search)
|
|
void (*postcheck)(mfem::Operator *op, const mfem::Vector&, mfem::Vector&,
|
|
mfem::Vector&, bool&, bool&);
|
|
// General purpose update function (to be called at the beginning of
|
|
// each nonlinear step)
|
|
void (*update)(mfem::Operator *op, int,
|
|
const mfem::Vector&, const mfem::Vector&,
|
|
const mfem::Vector&, const mfem::Vector&);
|
|
} __mfem_snes_ctx;
|
|
|
|
typedef struct
|
|
{
|
|
mfem::TimeDependentOperator *op; // The time-dependent operator
|
|
mfem::PetscBCHandler *bchandler; // Handling of essential bc
|
|
mfem::Vector *work; // Work vector
|
|
mfem::Vector *work2; // Work vector
|
|
mfem::Operator::Type jacType; // OperatorType for the Jacobian
|
|
enum mfem::PetscODESolver::Type type;
|
|
PetscReal cached_shift;
|
|
PetscObjectState cached_ijacstate;
|
|
PetscObjectState cached_rhsjacstate;
|
|
PetscObjectState cached_splits_xstate;
|
|
PetscObjectState cached_splits_xdotstate;
|
|
} __mfem_ts_ctx;
|
|
|
|
typedef struct
|
|
{
|
|
mfem::PetscSolver *solver; // The solver object
|
|
mfem::PetscSolverMonitor *monitor; // The user-defined monitor class
|
|
} __mfem_monitor_ctx;
|
|
|
|
// use global scope ierr to check PETSc errors inside mfem calls
|
|
static PetscErrorCode ierr;
|
|
|
|
using namespace std;
|
|
|
|
namespace mfem
|
|
{
|
|
|
|
void MFEMInitializePetsc()
|
|
{
|
|
MFEMInitializePetsc(NULL,NULL,NULL,NULL);
|
|
}
|
|
|
|
void MFEMInitializePetsc(int *argc,char*** argv)
|
|
{
|
|
MFEMInitializePetsc(argc,argv,NULL,NULL);
|
|
}
|
|
|
|
void MFEMInitializePetsc(int *argc,char ***argv,const char rc_file[],
|
|
const char help[])
|
|
{
|
|
ierr = PetscInitialize(argc,argv,rc_file,help);
|
|
MFEM_VERIFY(!ierr,"Unable to initialize PETSc");
|
|
}
|
|
|
|
void MFEMFinalizePetsc()
|
|
{
|
|
ierr = PetscFinalize();
|
|
MFEM_VERIFY(!ierr,"Unable to finalize PETSc");
|
|
}
|
|
|
|
// PetscParVector methods
|
|
|
|
void PetscParVector::_SetDataAndSize_()
|
|
{
|
|
const PetscScalar *array;
|
|
PetscInt n;
|
|
|
|
ierr = VecGetArrayRead(x,&array); PCHKERRQ(x,ierr);
|
|
ierr = VecGetLocalSize(x,&n); PCHKERRQ(x,ierr);
|
|
SetDataAndSize((PetscScalar*)array,n);
|
|
ierr = VecRestoreArrayRead(x,&array); PCHKERRQ(x,ierr);
|
|
}
|
|
|
|
PetscInt PetscParVector::GlobalSize() const
|
|
{
|
|
PetscInt N;
|
|
ierr = VecGetSize(x,&N); PCHKERRQ(x,ierr);
|
|
return N;
|
|
}
|
|
|
|
PetscParVector::PetscParVector(MPI_Comm comm, const Vector &_x,
|
|
bool copy) : Vector()
|
|
{
|
|
ierr = VecCreate(comm,&x); CCHKERRQ(comm,ierr);
|
|
ierr = VecSetSizes(x,_x.Size(),PETSC_DECIDE); PCHKERRQ(x,ierr);
|
|
ierr = VecSetType(x,VECSTANDARD); PCHKERRQ(x,ierr);
|
|
_SetDataAndSize_();
|
|
if (copy)
|
|
{
|
|
PetscScalar *array;
|
|
|
|
ierr = VecGetArray(x,&array); PCHKERRQ(x,ierr);
|
|
for (int i = 0; i < _x.Size(); i++) { array[i] = _x[i]; }
|
|
ierr = VecRestoreArray(x,&array); PCHKERRQ(x,ierr);
|
|
}
|
|
}
|
|
|
|
PetscParVector::PetscParVector(MPI_Comm comm, PetscInt glob_size,
|
|
PetscInt *col) : Vector()
|
|
{
|
|
ierr = VecCreate(comm,&x); CCHKERRQ(comm,ierr);
|
|
if (col)
|
|
{
|
|
PetscMPIInt myid;
|
|
MPI_Comm_rank(comm, &myid);
|
|
ierr = VecSetSizes(x,col[myid+1]-col[myid],PETSC_DECIDE); PCHKERRQ(x,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = VecSetSizes(x,PETSC_DECIDE,glob_size); PCHKERRQ(x,ierr);
|
|
}
|
|
ierr = VecSetType(x,VECSTANDARD); PCHKERRQ(x,ierr);
|
|
_SetDataAndSize_();
|
|
}
|
|
|
|
PetscParVector::~PetscParVector()
|
|
{
|
|
MPI_Comm comm = PetscObjectComm((PetscObject)x);
|
|
ierr = VecDestroy(&x); CCHKERRQ(comm,ierr);
|
|
}
|
|
|
|
PetscParVector::PetscParVector(MPI_Comm comm, PetscInt glob_size,
|
|
PetscScalar *_data, PetscInt *col) : Vector()
|
|
{
|
|
MFEM_VERIFY(col,"Missing distribution");
|
|
PetscMPIInt myid;
|
|
MPI_Comm_rank(comm, &myid);
|
|
ierr = VecCreateMPIWithArray(comm,1,col[myid+1]-col[myid],glob_size,_data,
|
|
&x); CCHKERRQ(comm,ierr)
|
|
_SetDataAndSize_();
|
|
}
|
|
|
|
PetscParVector::PetscParVector(const PetscParVector &y) : Vector()
|
|
{
|
|
ierr = VecDuplicate(y.x,&x); PCHKERRQ(x,ierr);
|
|
_SetDataAndSize_();
|
|
}
|
|
|
|
PetscParVector::PetscParVector(MPI_Comm comm, const Operator &op,
|
|
bool transpose, bool allocate) : Vector()
|
|
{
|
|
PetscInt loc = transpose ? op.Height() : op.Width();
|
|
if (allocate)
|
|
{
|
|
ierr = VecCreate(comm,&x);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = VecSetSizes(x,loc,PETSC_DECIDE);
|
|
PCHKERRQ(x,ierr);
|
|
ierr = VecSetType(x,VECSTANDARD);
|
|
PCHKERRQ(x,ierr);
|
|
ierr = VecSetUp(x);
|
|
PCHKERRQ(x,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = VecCreateMPIWithArray(comm,1,loc,PETSC_DECIDE,NULL,
|
|
&x); CCHKERRQ(comm,ierr);
|
|
}
|
|
_SetDataAndSize_();
|
|
}
|
|
|
|
PetscParVector::PetscParVector(const PetscParMatrix &A,
|
|
bool transpose, bool allocate) : Vector()
|
|
{
|
|
Mat pA = const_cast<PetscParMatrix&>(A);
|
|
if (!transpose)
|
|
{
|
|
ierr = MatCreateVecs(pA,&x,NULL); PCHKERRQ(pA,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = MatCreateVecs(pA,NULL,&x); PCHKERRQ(pA,ierr);
|
|
}
|
|
if (!allocate)
|
|
{
|
|
ierr = VecReplaceArray(x,NULL); PCHKERRQ(x,ierr);
|
|
}
|
|
_SetDataAndSize_();
|
|
}
|
|
|
|
PetscParVector::PetscParVector(Vec y, bool ref) : Vector()
|
|
{
|
|
if (ref)
|
|
{
|
|
ierr = PetscObjectReference((PetscObject)y); PCHKERRQ(y,ierr);
|
|
}
|
|
x = y;
|
|
_SetDataAndSize_();
|
|
}
|
|
|
|
PetscParVector::PetscParVector(ParFiniteElementSpace *pfes) : Vector()
|
|
{
|
|
|
|
HYPRE_Int* offsets = pfes->GetTrueDofOffsets();
|
|
MPI_Comm comm = pfes->GetComm();
|
|
ierr = VecCreate(comm,&x); CCHKERRQ(comm,ierr);
|
|
|
|
PetscMPIInt myid = 0;
|
|
if (!HYPRE_AssumedPartitionCheck())
|
|
{
|
|
MPI_Comm_rank(comm,&myid);
|
|
}
|
|
ierr = VecSetSizes(x,offsets[myid+1]-offsets[myid],PETSC_DECIDE);
|
|
PCHKERRQ(x,ierr);
|
|
ierr = VecSetType(x,VECSTANDARD); PCHKERRQ(x,ierr);
|
|
_SetDataAndSize_();
|
|
}
|
|
|
|
MPI_Comm PetscParVector::GetComm() const
|
|
{
|
|
return x ? PetscObjectComm((PetscObject)x) : MPI_COMM_NULL;
|
|
}
|
|
|
|
Vector * PetscParVector::GlobalVector() const
|
|
{
|
|
VecScatter scctx;
|
|
Vec vout;
|
|
PetscScalar *array;
|
|
PetscInt size;
|
|
|
|
ierr = VecScatterCreateToAll(x,&scctx,&vout); PCHKERRQ(x,ierr);
|
|
ierr = VecScatterBegin(scctx,x,vout,INSERT_VALUES,SCATTER_FORWARD);
|
|
PCHKERRQ(x,ierr);
|
|
ierr = VecScatterEnd(scctx,x,vout,INSERT_VALUES,SCATTER_FORWARD);
|
|
PCHKERRQ(x,ierr);
|
|
ierr = VecScatterDestroy(&scctx); PCHKERRQ(x,ierr);
|
|
ierr = VecGetArray(vout,&array); PCHKERRQ(x,ierr);
|
|
ierr = VecGetLocalSize(vout,&size); PCHKERRQ(x,ierr);
|
|
Array<PetscScalar> data(size);
|
|
data.Assign(array);
|
|
ierr = VecRestoreArray(vout,&array); PCHKERRQ(x,ierr);
|
|
ierr = VecDestroy(&vout); PCHKERRQ(x,ierr);
|
|
Vector *v = new Vector(data, internal::to_int(size));
|
|
v->MakeDataOwner();
|
|
data.LoseData();
|
|
return v;
|
|
}
|
|
|
|
PetscParVector& PetscParVector::operator=(PetscScalar d)
|
|
{
|
|
ierr = VecSet(x,d); PCHKERRQ(x,ierr);
|
|
return *this;
|
|
}
|
|
|
|
PetscParVector& PetscParVector::SetValues(const Array<PetscInt>& idx,
|
|
const Array<PetscScalar>& vals)
|
|
{
|
|
MFEM_VERIFY(idx.Size() == vals.Size(),
|
|
"Size mismatch between indices and values");
|
|
PetscInt n = idx.Size();
|
|
ierr = VecSetValues(x,n,idx.GetData(),vals.GetData(),INSERT_VALUES);
|
|
PCHKERRQ(x,ierr);
|
|
ierr = VecAssemblyBegin(x); PCHKERRQ(x,ierr);
|
|
ierr = VecAssemblyEnd(x); PCHKERRQ(x,ierr);
|
|
return *this;
|
|
}
|
|
|
|
PetscParVector& PetscParVector::AddValues(const Array<PetscInt>& idx,
|
|
const Array<PetscScalar>& vals)
|
|
{
|
|
MFEM_VERIFY(idx.Size() == vals.Size(),
|
|
"Size mismatch between indices and values");
|
|
PetscInt n = idx.Size();
|
|
ierr = VecSetValues(x,n,idx.GetData(),vals.GetData(),ADD_VALUES);
|
|
PCHKERRQ(x,ierr);
|
|
ierr = VecAssemblyBegin(x); PCHKERRQ(x,ierr);
|
|
ierr = VecAssemblyEnd(x); PCHKERRQ(x,ierr);
|
|
return *this;
|
|
}
|
|
|
|
PetscParVector& PetscParVector::operator=(const PetscParVector &y)
|
|
{
|
|
ierr = VecCopy(y.x,x); PCHKERRQ(x,ierr);
|
|
return *this;
|
|
}
|
|
|
|
PetscParVector& PetscParVector::operator+=(const PetscParVector &y)
|
|
{
|
|
ierr = VecAXPY(x,1.0,y.x); PCHKERRQ(x,ierr);
|
|
return *this;
|
|
}
|
|
|
|
PetscParVector& PetscParVector::operator-=(const PetscParVector &y)
|
|
{
|
|
ierr = VecAXPY(x,-1.0,y.x); PCHKERRQ(x,ierr);
|
|
return *this;
|
|
}
|
|
|
|
PetscParVector& PetscParVector::operator*=(PetscScalar s)
|
|
{
|
|
ierr = VecScale(x,s); PCHKERRQ(x,ierr);
|
|
return *this;
|
|
}
|
|
|
|
PetscParVector& PetscParVector::operator+=(PetscScalar s)
|
|
{
|
|
ierr = VecShift(x,s); PCHKERRQ(x,ierr);
|
|
return *this;
|
|
}
|
|
|
|
void PetscParVector::PlaceArray(PetscScalar *temp_data)
|
|
{
|
|
ierr = VecPlaceArray(x,temp_data); PCHKERRQ(x,ierr);
|
|
}
|
|
|
|
void PetscParVector::ResetArray()
|
|
{
|
|
ierr = VecResetArray(x); PCHKERRQ(x,ierr);
|
|
}
|
|
|
|
void PetscParVector::Randomize(PetscInt seed)
|
|
{
|
|
PetscRandom rctx = NULL;
|
|
|
|
if (seed)
|
|
{
|
|
ierr = PetscRandomCreate(PetscObjectComm((PetscObject)x),&rctx);
|
|
PCHKERRQ(x,ierr);
|
|
ierr = PetscRandomSetSeed(rctx,(unsigned long)seed); PCHKERRQ(x,ierr);
|
|
ierr = PetscRandomSeed(rctx); PCHKERRQ(x,ierr);
|
|
}
|
|
ierr = VecSetRandom(x,rctx); PCHKERRQ(x,ierr);
|
|
ierr = PetscRandomDestroy(&rctx); PCHKERRQ(x,ierr);
|
|
}
|
|
|
|
void PetscParVector::Print(const char *fname, bool binary) const
|
|
{
|
|
if (fname)
|
|
{
|
|
PetscViewer view;
|
|
|
|
if (binary)
|
|
{
|
|
ierr = PetscViewerBinaryOpen(PetscObjectComm((PetscObject)x),fname,
|
|
FILE_MODE_WRITE,&view);
|
|
}
|
|
else
|
|
{
|
|
ierr = PetscViewerASCIIOpen(PetscObjectComm((PetscObject)x),fname,&view);
|
|
}
|
|
PCHKERRQ(x,ierr);
|
|
ierr = VecView(x,view); PCHKERRQ(x,ierr);
|
|
ierr = PetscViewerDestroy(&view); PCHKERRQ(x,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = VecView(x,NULL); PCHKERRQ(x,ierr);
|
|
}
|
|
}
|
|
|
|
// PetscParMatrix methods
|
|
|
|
PetscInt PetscParMatrix::GetRowStart() const
|
|
{
|
|
PetscInt N;
|
|
ierr = MatGetOwnershipRange(A,&N,NULL); PCHKERRQ(A,ierr);
|
|
return N;
|
|
}
|
|
|
|
PetscInt PetscParMatrix::GetColStart() const
|
|
{
|
|
PetscInt N;
|
|
ierr = MatGetOwnershipRangeColumn(A,&N,NULL); PCHKERRQ(A,ierr);
|
|
return N;
|
|
}
|
|
|
|
PetscInt PetscParMatrix::GetNumRows() const
|
|
{
|
|
PetscInt N;
|
|
ierr = MatGetLocalSize(A,&N,NULL); PCHKERRQ(A,ierr);
|
|
return N;
|
|
}
|
|
|
|
PetscInt PetscParMatrix::GetNumCols() const
|
|
{
|
|
PetscInt N;
|
|
ierr = MatGetLocalSize(A,NULL,&N); PCHKERRQ(A,ierr);
|
|
return N;
|
|
}
|
|
|
|
PetscInt PetscParMatrix::M() const
|
|
{
|
|
PetscInt N;
|
|
ierr = MatGetSize(A,&N,NULL); PCHKERRQ(A,ierr);
|
|
return N;
|
|
}
|
|
|
|
PetscInt PetscParMatrix::N() const
|
|
{
|
|
PetscInt N;
|
|
ierr = MatGetSize(A,NULL,&N); PCHKERRQ(A,ierr);
|
|
return N;
|
|
}
|
|
|
|
PetscInt PetscParMatrix::NNZ() const
|
|
{
|
|
MatInfo info;
|
|
ierr = MatGetInfo(A,MAT_GLOBAL_SUM,&info); PCHKERRQ(A,ierr);
|
|
return (PetscInt)info.nz_used;
|
|
}
|
|
|
|
void PetscParMatrix::Init()
|
|
{
|
|
A = NULL;
|
|
X = Y = NULL;
|
|
height = width = 0;
|
|
}
|
|
|
|
PetscParMatrix::PetscParMatrix()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
PetscParMatrix::PetscParMatrix(const PetscParMatrix& pB,
|
|
const mfem::Array<PetscInt>& rows, const mfem::Array<PetscInt>& cols)
|
|
{
|
|
Init();
|
|
|
|
Mat B = const_cast<PetscParMatrix&>(pB);
|
|
|
|
IS isr,isc;
|
|
ierr = ISCreateGeneral(PetscObjectComm((PetscObject)B),rows.Size(),
|
|
rows.GetData(),PETSC_USE_POINTER,&isr); PCHKERRQ(B,ierr);
|
|
ierr = ISCreateGeneral(PetscObjectComm((PetscObject)B),cols.Size(),
|
|
cols.GetData(),PETSC_USE_POINTER,&isc); PCHKERRQ(B,ierr);
|
|
ierr = MatCreateSubMatrix(B,isr,isc,MAT_INITIAL_MATRIX,&A); PCHKERRQ(B,ierr);
|
|
ierr = ISDestroy(&isr); PCHKERRQ(B,ierr);
|
|
ierr = ISDestroy(&isc); PCHKERRQ(B,ierr);
|
|
|
|
height = GetNumRows();
|
|
width = GetNumCols();
|
|
}
|
|
|
|
PetscParMatrix::PetscParMatrix(const PetscParMatrix *pa, Operator::Type tid)
|
|
{
|
|
Init();
|
|
height = pa->Height();
|
|
width = pa->Width();
|
|
ConvertOperator(pa->GetComm(),*pa,&A,tid);
|
|
}
|
|
|
|
PetscParMatrix::PetscParMatrix(const HypreParMatrix *ha, Operator::Type tid)
|
|
{
|
|
Init();
|
|
height = ha->Height();
|
|
width = ha->Width();
|
|
ConvertOperator(ha->GetComm(),*ha,&A,tid);
|
|
}
|
|
|
|
PetscParMatrix::PetscParMatrix(const SparseMatrix *sa, Operator::Type tid)
|
|
{
|
|
Init();
|
|
height = sa->Height();
|
|
width = sa->Width();
|
|
ConvertOperator(PETSC_COMM_SELF,*sa,&A,tid);
|
|
}
|
|
|
|
PetscParMatrix::PetscParMatrix(MPI_Comm comm, const Operator *op,
|
|
Operator::Type tid)
|
|
{
|
|
Init();
|
|
height = op->Height();
|
|
width = op->Width();
|
|
ConvertOperator(comm,*op,&A,tid);
|
|
}
|
|
|
|
PetscParMatrix::PetscParMatrix(MPI_Comm comm, PetscInt glob_size,
|
|
PetscInt *row_starts, SparseMatrix *diag,
|
|
Operator::Type tid)
|
|
{
|
|
Init();
|
|
BlockDiagonalConstructor(comm,row_starts,row_starts,diag,
|
|
tid==PETSC_MATAIJ,&A);
|
|
// update base class
|
|
height = GetNumRows();
|
|
width = GetNumCols();
|
|
}
|
|
|
|
PetscParMatrix::PetscParMatrix(MPI_Comm comm, PetscInt global_num_rows,
|
|
PetscInt global_num_cols, PetscInt *row_starts,
|
|
PetscInt *col_starts, SparseMatrix *diag,
|
|
Operator::Type tid)
|
|
{
|
|
Init();
|
|
BlockDiagonalConstructor(comm,row_starts,col_starts,diag,
|
|
tid==PETSC_MATAIJ,&A);
|
|
// update base class
|
|
height = GetNumRows();
|
|
width = GetNumCols();
|
|
}
|
|
|
|
PetscParMatrix& PetscParMatrix::operator=(const HypreParMatrix& B)
|
|
{
|
|
if (A)
|
|
{
|
|
MPI_Comm comm = PetscObjectComm((PetscObject)A);
|
|
ierr = MatDestroy(&A); CCHKERRQ(comm,ierr);
|
|
if (X) { delete X; }
|
|
if (Y) { delete Y; }
|
|
X = Y = NULL;
|
|
}
|
|
height = B.Height();
|
|
width = B.Width();
|
|
#if defined(PETSC_HAVE_HYPRE)
|
|
ierr = MatCreateFromParCSR(B,MATAIJ,PETSC_USE_POINTER,&A);
|
|
#else
|
|
ierr = MatConvert_hypreParCSR_AIJ(B,&A); CCHKERRQ(B.GetComm(),ierr);
|
|
#endif
|
|
return *this;
|
|
}
|
|
|
|
PetscParMatrix& PetscParMatrix::operator=(const PetscParMatrix& B)
|
|
{
|
|
if (A)
|
|
{
|
|
MPI_Comm comm = PetscObjectComm((PetscObject)A);
|
|
ierr = MatDestroy(&A); CCHKERRQ(comm,ierr);
|
|
if (X) { delete X; }
|
|
if (Y) { delete Y; }
|
|
X = Y = NULL;
|
|
}
|
|
height = B.Height();
|
|
width = B.Width();
|
|
ierr = MatDuplicate(B,MAT_COPY_VALUES,&A); CCHKERRQ(B.GetComm(),ierr);
|
|
return *this;
|
|
}
|
|
|
|
PetscParMatrix& PetscParMatrix::operator+=(const PetscParMatrix& B)
|
|
{
|
|
if (!A)
|
|
{
|
|
ierr = MatDuplicate(B,MAT_COPY_VALUES,&A); CCHKERRQ(B.GetComm(),ierr);
|
|
}
|
|
else
|
|
{
|
|
MFEM_VERIFY(height == B.Height(),"Invalid number of local rows");
|
|
MFEM_VERIFY(width == B.Width(), "Invalid number of local columns");
|
|
ierr = MatAXPY(A,1.0,B,DIFFERENT_NONZERO_PATTERN); CCHKERRQ(B.GetComm(),ierr);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
PetscParMatrix& PetscParMatrix::operator-=(const PetscParMatrix& B)
|
|
{
|
|
if (!A)
|
|
{
|
|
ierr = MatDuplicate(B,MAT_COPY_VALUES,&A); CCHKERRQ(B.GetComm(),ierr);
|
|
ierr = MatScale(A,-1.0); PCHKERRQ(A,ierr);
|
|
}
|
|
else
|
|
{
|
|
MFEM_VERIFY(height == B.Height(),"Invalid number of local rows");
|
|
MFEM_VERIFY(width == B.Width(), "Invalid number of local columns");
|
|
ierr = MatAXPY(A,-1.0,B,DIFFERENT_NONZERO_PATTERN); CCHKERRQ(B.GetComm(),ierr);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void PetscParMatrix::
|
|
BlockDiagonalConstructor(MPI_Comm comm,
|
|
PetscInt *row_starts, PetscInt *col_starts,
|
|
SparseMatrix *diag, bool assembled, Mat* Ad)
|
|
{
|
|
Mat A;
|
|
PetscInt lrsize,lcsize,rstart,cstart;
|
|
PetscMPIInt myid = 0,commsize;
|
|
|
|
ierr = MPI_Comm_size(comm,&commsize); CCHKERRQ(comm,ierr);
|
|
if (!HYPRE_AssumedPartitionCheck())
|
|
{
|
|
ierr = MPI_Comm_rank(comm,&myid); CCHKERRQ(comm,ierr);
|
|
}
|
|
lrsize = row_starts[myid+1]-row_starts[myid];
|
|
rstart = row_starts[myid];
|
|
lcsize = col_starts[myid+1]-col_starts[myid];
|
|
cstart = col_starts[myid];
|
|
|
|
if (!assembled)
|
|
{
|
|
IS is;
|
|
ierr = ISCreateStride(comm,diag->Height(),rstart,1,&is); CCHKERRQ(comm,ierr);
|
|
ISLocalToGlobalMapping rl2g,cl2g;
|
|
ierr = ISLocalToGlobalMappingCreateIS(is,&rl2g); PCHKERRQ(is,ierr);
|
|
ierr = ISDestroy(&is); CCHKERRQ(comm,ierr);
|
|
if (row_starts != col_starts)
|
|
{
|
|
ierr = ISCreateStride(comm,diag->Width(),cstart,1,&is);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = ISLocalToGlobalMappingCreateIS(is,&cl2g); PCHKERRQ(is,ierr);
|
|
ierr = ISDestroy(&is); CCHKERRQ(comm,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = PetscObjectReference((PetscObject)rl2g); PCHKERRQ(rl2g,ierr);
|
|
cl2g = rl2g;
|
|
}
|
|
|
|
// Create the PETSc object (MATIS format)
|
|
ierr = MatCreate(comm,&A); CCHKERRQ(comm,ierr);
|
|
ierr = MatSetSizes(A,lrsize,lcsize,PETSC_DECIDE,PETSC_DECIDE);
|
|
PCHKERRQ(A,ierr);
|
|
ierr = MatSetType(A,MATIS); PCHKERRQ(A,ierr);
|
|
ierr = MatSetLocalToGlobalMapping(A,rl2g,cl2g); PCHKERRQ(A,ierr);
|
|
ierr = ISLocalToGlobalMappingDestroy(&rl2g); PCHKERRQ(A,ierr)
|
|
ierr = ISLocalToGlobalMappingDestroy(&cl2g); PCHKERRQ(A,ierr)
|
|
|
|
// Copy SparseMatrix into PETSc SeqAIJ format
|
|
Mat lA;
|
|
ierr = MatISGetLocalMat(A,&lA); PCHKERRQ(A,ierr);
|
|
int *II = diag->GetI();
|
|
int *JJ = diag->GetJ();
|
|
#if defined(PETSC_USE_64BIT_INDICES)
|
|
PetscInt *pII,*pJJ;
|
|
int m = diag->Height()+1, nnz = II[diag->Height()];
|
|
ierr = PetscMalloc2(m,&pII,nnz,&pJJ); PCHKERRQ(lA,ierr);
|
|
for (int i = 0; i < m; i++) { pII[i] = II[i]; }
|
|
for (int i = 0; i < nnz; i++) { pJJ[i] = JJ[i]; }
|
|
ierr = MatSeqAIJSetPreallocationCSR(lA,pII,pJJ,
|
|
diag->GetData()); PCHKERRQ(lA,ierr);
|
|
ierr = PetscFree2(pII,pJJ); PCHKERRQ(lA,ierr);
|
|
#else
|
|
ierr = MatSeqAIJSetPreallocationCSR(lA,II,JJ,
|
|
diag->GetData()); PCHKERRQ(lA,ierr);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
PetscScalar *da;
|
|
PetscInt *dii,*djj,*oii,
|
|
m = diag->Height()+1, nnz = diag->NumNonZeroElems();
|
|
|
|
diag->SortColumnIndices();
|
|
// if we can take ownership of the SparseMatrix arrays, we can avoid this
|
|
// step
|
|
ierr = PetscMalloc1(m,&dii); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = PetscMalloc1(nnz,&djj); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = PetscMalloc1(nnz,&da); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
if (sizeof(PetscInt) == sizeof(int))
|
|
{
|
|
ierr = PetscMemcpy(dii,diag->GetI(),m*sizeof(PetscInt));
|
|
CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = PetscMemcpy(djj,diag->GetJ(),nnz*sizeof(PetscInt));
|
|
CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
}
|
|
else
|
|
{
|
|
int *iii = diag->GetI();
|
|
int *jjj = diag->GetJ();
|
|
for (int i = 0; i < m; i++) { dii[i] = iii[i]; }
|
|
for (int i = 0; i < nnz; i++) { djj[i] = jjj[i]; }
|
|
}
|
|
ierr = PetscMemcpy(da,diag->GetData(),nnz*sizeof(PetscScalar));
|
|
CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = PetscCalloc1(m,&oii);
|
|
CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
if (commsize > 1)
|
|
{
|
|
ierr = MatCreateMPIAIJWithSplitArrays(comm,lrsize,lcsize,PETSC_DECIDE,
|
|
PETSC_DECIDE,
|
|
dii,djj,da,oii,NULL,NULL,&A);
|
|
CCHKERRQ(comm,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = MatCreateSeqAIJWithArrays(comm,lrsize,lcsize,dii,djj,da,&A);
|
|
CCHKERRQ(comm,ierr);
|
|
}
|
|
|
|
void *ptrs[4] = {dii,djj,da,oii};
|
|
const char *names[4] = {"_mfem_csr_dii",
|
|
"_mfem_csr_djj",
|
|
"_mfem_csr_da",
|
|
"_mfem_csr_oii",
|
|
};
|
|
for (PetscInt i=0; i<4; i++)
|
|
{
|
|
PetscContainer c;
|
|
|
|
ierr = PetscContainerCreate(comm,&c); CCHKERRQ(comm,ierr);
|
|
ierr = PetscContainerSetPointer(c,ptrs[i]); CCHKERRQ(comm,ierr);
|
|
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = PetscObjectCompose((PetscObject)A,names[i],(PetscObject)c);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = PetscContainerDestroy(&c); CCHKERRQ(comm,ierr);
|
|
}
|
|
}
|
|
|
|
// Tell PETSc the matrix is ready to be used
|
|
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); PCHKERRQ(A,ierr);
|
|
ierr = MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY); PCHKERRQ(A,ierr);
|
|
|
|
*Ad = A;
|
|
}
|
|
|
|
MPI_Comm PetscParMatrix::GetComm() const
|
|
{
|
|
return A ? PetscObjectComm((PetscObject)A) : MPI_COMM_NULL;
|
|
}
|
|
|
|
// TODO ADD THIS CONSTRUCTOR
|
|
//PetscParMatrix::PetscParMatrix(MPI_Comm comm, int nrows, PetscInt glob_nrows,
|
|
// PetscInt glob_ncols, int *I, PetscInt *J,
|
|
// double *data, PetscInt *rows, PetscInt *cols)
|
|
//{
|
|
//}
|
|
|
|
// TODO This should take a reference on op but how?
|
|
void PetscParMatrix::MakeWrapper(MPI_Comm comm, const Operator* op, Mat *A)
|
|
{
|
|
ierr = MatCreate(comm,A); CCHKERRQ(comm,ierr);
|
|
ierr = MatSetSizes(*A,op->Height(),op->Width(),
|
|
PETSC_DECIDE,PETSC_DECIDE); PCHKERRQ(A,ierr);
|
|
ierr = MatSetType(*A,MATSHELL); PCHKERRQ(A,ierr);
|
|
ierr = MatShellSetContext(*A,(void *)op); PCHKERRQ(A,ierr);
|
|
ierr = MatShellSetOperation(*A,MATOP_MULT,
|
|
(void (*)())__mfem_mat_shell_apply);
|
|
PCHKERRQ(A,ierr);
|
|
ierr = MatShellSetOperation(*A,MATOP_MULT_TRANSPOSE,
|
|
(void (*)())__mfem_mat_shell_apply_transpose);
|
|
PCHKERRQ(A,ierr);
|
|
ierr = MatShellSetOperation(*A,MATOP_COPY,
|
|
(void (*)())__mfem_mat_shell_copy);
|
|
PCHKERRQ(A,ierr);
|
|
ierr = MatShellSetOperation(*A,MATOP_DESTROY,
|
|
(void (*)())__mfem_mat_shell_destroy);
|
|
PCHKERRQ(A,ierr);
|
|
ierr = MatSetUp(*A); PCHKERRQ(*A,ierr);
|
|
}
|
|
|
|
void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
|
Operator::Type tid)
|
|
{
|
|
PetscParMatrix *pA = const_cast<PetscParMatrix *>
|
|
(dynamic_cast<const PetscParMatrix *>(&op));
|
|
HypreParMatrix *pH = const_cast<HypreParMatrix *>
|
|
(dynamic_cast<const HypreParMatrix *>(&op));
|
|
BlockOperator *pB = const_cast<BlockOperator *>
|
|
(dynamic_cast<const BlockOperator *>(&op));
|
|
IdentityOperator *pI = const_cast<IdentityOperator *>
|
|
(dynamic_cast<const IdentityOperator *>(&op));
|
|
SparseMatrix *pS = const_cast<SparseMatrix *>
|
|
(dynamic_cast<const SparseMatrix *>(&op));
|
|
|
|
if (pA && tid == ANY_TYPE) // use same object and return
|
|
{
|
|
ierr = PetscObjectReference((PetscObject)(pA->A));
|
|
CCHKERRQ(pA->GetComm(),ierr);
|
|
*A = pA->A;
|
|
return;
|
|
}
|
|
|
|
PetscBool avoidmatconvert = PETSC_FALSE;
|
|
if (pA) // we test for these types since MatConvert will fail
|
|
{
|
|
ierr = PetscObjectTypeCompareAny((PetscObject)(pA->A),&avoidmatconvert,MATMFFD,
|
|
MATSHELL,"");
|
|
CCHKERRQ(comm,ierr);
|
|
}
|
|
if (pA && !avoidmatconvert)
|
|
{
|
|
Mat At = NULL;
|
|
PetscBool istrans;
|
|
#if PETSC_VERSION_LT(3,10,0)
|
|
PetscBool ismatis;
|
|
#endif
|
|
|
|
ierr = PetscObjectTypeCompare((PetscObject)(pA->A),MATTRANSPOSEMAT,&istrans);
|
|
CCHKERRQ(pA->GetComm(),ierr);
|
|
if (!istrans)
|
|
{
|
|
if (tid == pA->GetType()) // use same object and return
|
|
{
|
|
ierr = PetscObjectReference((PetscObject)(pA->A));
|
|
CCHKERRQ(pA->GetComm(),ierr);
|
|
*A = pA->A;
|
|
return;
|
|
}
|
|
#if PETSC_VERSION_LT(3,10,0)
|
|
ierr = PetscObjectTypeCompare((PetscObject)(pA->A),MATIS,&ismatis);
|
|
CCHKERRQ(pA->GetComm(),ierr);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
ierr = MatTransposeGetMat(pA->A,&At); CCHKERRQ(pA->GetComm(),ierr);
|
|
#if PETSC_VERSION_LT(3,10,0)
|
|
ierr = PetscObjectTypeCompare((PetscObject)(At),MATIS,&ismatis);
|
|
#endif
|
|
CCHKERRQ(pA->GetComm(),ierr);
|
|
}
|
|
|
|
// Try to convert
|
|
if (tid == PETSC_MATAIJ)
|
|
{
|
|
#if PETSC_VERSION_LT(3,10,0)
|
|
if (ismatis)
|
|
{
|
|
if (istrans)
|
|
{
|
|
Mat B;
|
|
|
|
ierr = MatISGetMPIXAIJ(At,MAT_INITIAL_MATRIX,&B); PCHKERRQ(pA->A,ierr);
|
|
ierr = MatCreateTranspose(B,A); PCHKERRQ(pA->A,ierr);
|
|
ierr = MatDestroy(&B); PCHKERRQ(pA->A,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = MatISGetMPIXAIJ(pA->A,MAT_INITIAL_MATRIX,A);
|
|
PCHKERRQ(pA->A,ierr);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
PetscMPIInt size;
|
|
ierr = MPI_Comm_size(comm,&size); CCHKERRQ(comm,ierr);
|
|
|
|
// call MatConvert and see if a converter is available
|
|
if (istrans)
|
|
{
|
|
Mat B;
|
|
ierr = MatConvert(At,size > 1 ? MATMPIAIJ : MATSEQAIJ,MAT_INITIAL_MATRIX,&B);
|
|
PCHKERRQ(pA->A,ierr);
|
|
ierr = MatCreateTranspose(B,A); PCHKERRQ(pA->A,ierr);
|
|
ierr = MatDestroy(&B); PCHKERRQ(pA->A,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = MatConvert(pA->A, size > 1 ? MATMPIAIJ : MATSEQAIJ,MAT_INITIAL_MATRIX,A);
|
|
PCHKERRQ(pA->A,ierr);
|
|
}
|
|
}
|
|
}
|
|
else if (tid == PETSC_MATIS)
|
|
{
|
|
if (istrans)
|
|
{
|
|
Mat B;
|
|
ierr = MatConvert(At,MATIS,MAT_INITIAL_MATRIX,&B); PCHKERRQ(pA->A,ierr);
|
|
ierr = MatCreateTranspose(B,A); PCHKERRQ(pA->A,ierr);
|
|
ierr = MatDestroy(&B); PCHKERRQ(pA->A,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = MatConvert(pA->A,MATIS,MAT_INITIAL_MATRIX,A); PCHKERRQ(pA->A,ierr);
|
|
}
|
|
}
|
|
else if (tid == PETSC_MATHYPRE)
|
|
{
|
|
#if defined(PETSC_HAVE_HYPRE)
|
|
if (istrans)
|
|
{
|
|
Mat B;
|
|
ierr = MatConvert(At,MATHYPRE,MAT_INITIAL_MATRIX,&B); PCHKERRQ(pA->A,ierr);
|
|
ierr = MatCreateTranspose(B,A); PCHKERRQ(pA->A,ierr);
|
|
ierr = MatDestroy(&B); PCHKERRQ(pA->A,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = MatConvert(pA->A,MATHYPRE,MAT_INITIAL_MATRIX,A); PCHKERRQ(pA->A,ierr);
|
|
}
|
|
#else
|
|
MFEM_ABORT("Reconfigure PETSc with --download-hypre or --with-hypre")
|
|
#endif
|
|
}
|
|
else if (tid == PETSC_MATSHELL)
|
|
{
|
|
MakeWrapper(comm,&op,A);
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("Unsupported operator type conversion " << tid)
|
|
}
|
|
}
|
|
else if (pH)
|
|
{
|
|
if (tid == PETSC_MATAIJ)
|
|
{
|
|
#if defined(PETSC_HAVE_HYPRE)
|
|
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATAIJ,
|
|
PETSC_USE_POINTER,A);
|
|
#else
|
|
ierr = MatConvert_hypreParCSR_AIJ(const_cast<HypreParMatrix&>(*pH),A);
|
|
#endif
|
|
CCHKERRQ(pH->GetComm(),ierr);
|
|
}
|
|
else if (tid == PETSC_MATIS)
|
|
{
|
|
#if defined(PETSC_HAVE_HYPRE)
|
|
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATIS,
|
|
PETSC_USE_POINTER,A);
|
|
#else
|
|
ierr = MatConvert_hypreParCSR_IS(const_cast<HypreParMatrix&>(*pH),A);
|
|
#endif
|
|
CCHKERRQ(pH->GetComm(),ierr);
|
|
}
|
|
else if (tid == PETSC_MATHYPRE || tid == ANY_TYPE)
|
|
{
|
|
#if defined(PETSC_HAVE_HYPRE)
|
|
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATHYPRE,
|
|
PETSC_USE_POINTER,A);
|
|
CCHKERRQ(pH->GetComm(),ierr);
|
|
#else
|
|
MFEM_ABORT("Reconfigure PETSc with --download-hypre or --with-hypre")
|
|
#endif
|
|
}
|
|
else if (tid == PETSC_MATSHELL)
|
|
{
|
|
MakeWrapper(comm,&op,A);
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("Conversion from HypreParCSR to operator type = " << tid <<
|
|
" is not implemented");
|
|
}
|
|
}
|
|
else if (pB)
|
|
{
|
|
Mat *mats,*matsl2l = NULL;
|
|
PetscInt i,j,nr,nc;
|
|
|
|
nr = pB->NumRowBlocks();
|
|
nc = pB->NumColBlocks();
|
|
ierr = PetscCalloc1(nr*nc,&mats); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
if (tid == PETSC_MATIS)
|
|
{
|
|
ierr = PetscCalloc1(nr,&matsl2l); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
}
|
|
for (i=0; i<nr; i++)
|
|
{
|
|
PetscBool needl2l = PETSC_TRUE;
|
|
|
|
for (j=0; j<nc; j++)
|
|
{
|
|
if (!pB->IsZeroBlock(i,j))
|
|
{
|
|
ConvertOperator(comm,pB->GetBlock(i,j),&mats[i*nc+j],tid);
|
|
if (tid == PETSC_MATIS && needl2l)
|
|
{
|
|
PetscContainer c;
|
|
ierr = PetscObjectQuery((PetscObject)mats[i*nc+j],"_MatIS_PtAP_l2l",
|
|
(PetscObject*)&c);
|
|
PCHKERRQ(mats[i*nc+j],ierr);
|
|
// special case for block operators: the local Vdofs should be
|
|
// ordered as:
|
|
// [f1_1,...f1_N1,f2_1,...,f2_N2,...,fm_1,...,fm_Nm]
|
|
// with m fields, Ni the number of Vdofs for the i-th field
|
|
if (c)
|
|
{
|
|
Array<Mat> *l2l = NULL;
|
|
ierr = PetscContainerGetPointer(c,(void**)&l2l);
|
|
PCHKERRQ(c,ierr);
|
|
MFEM_VERIFY(l2l->Size() == 1,"Unexpected size "
|
|
<< l2l->Size() << " for block row " << i );
|
|
ierr = PetscObjectReference((PetscObject)(*l2l)[0]);
|
|
PCHKERRQ(c,ierr);
|
|
matsl2l[i] = (*l2l)[0];
|
|
needl2l = PETSC_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ierr = MatCreateNest(comm,nr,NULL,nc,NULL,mats,A); CCHKERRQ(comm,ierr);
|
|
if (tid == PETSC_MATIS)
|
|
{
|
|
ierr = MatConvert(*A,MATIS,MAT_INPLACE_MATRIX,A); CCHKERRQ(comm,ierr);
|
|
|
|
mfem::Array<Mat> *vmatsl2l = new mfem::Array<Mat>(nr);
|
|
for (int i=0; i<(int)nr; i++) { (*vmatsl2l)[i] = matsl2l[i]; }
|
|
ierr = PetscFree(matsl2l); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
|
|
PetscContainer c;
|
|
ierr = PetscContainerCreate(comm,&c); CCHKERRQ(comm,ierr);
|
|
ierr = PetscContainerSetPointer(c,vmatsl2l); PCHKERRQ(c,ierr);
|
|
ierr = PetscContainerSetUserDestroy(c,__mfem_matarray_container_destroy);
|
|
PCHKERRQ(c,ierr);
|
|
ierr = PetscObjectCompose((PetscObject)(*A),"_MatIS_PtAP_l2l",(PetscObject)c);
|
|
PCHKERRQ((*A),ierr);
|
|
ierr = PetscContainerDestroy(&c); CCHKERRQ(comm,ierr);
|
|
}
|
|
for (i=0; i<nr*nc; i++) { ierr = MatDestroy(&mats[i]); CCHKERRQ(comm,ierr); }
|
|
ierr = PetscFree(mats); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
}
|
|
else if (pI && tid == PETSC_MATAIJ)
|
|
{
|
|
PetscInt rst;
|
|
|
|
ierr = MatCreate(comm,A); CCHKERRQ(comm,ierr);
|
|
ierr = MatSetSizes(*A,pI->Height(),pI->Width(),PETSC_DECIDE,PETSC_DECIDE);
|
|
PCHKERRQ(A,ierr);
|
|
ierr = MatSetType(*A,MATAIJ); PCHKERRQ(*A,ierr);
|
|
ierr = MatMPIAIJSetPreallocation(*A,1,NULL,0,NULL); PCHKERRQ(*A,ierr);
|
|
ierr = MatSeqAIJSetPreallocation(*A,1,NULL); PCHKERRQ(*A,ierr);
|
|
ierr = MatSetOption(*A,MAT_NO_OFF_PROC_ENTRIES,PETSC_TRUE); PCHKERRQ(*A,ierr);
|
|
ierr = MatGetOwnershipRange(*A,&rst,NULL); PCHKERRQ(*A,ierr);
|
|
for (PetscInt i = rst; i < rst+pI->Height(); i++)
|
|
{
|
|
ierr = MatSetValue(*A,i,i,1.,INSERT_VALUES); PCHKERRQ(*A,ierr);
|
|
}
|
|
ierr = MatAssemblyBegin(*A,MAT_FINAL_ASSEMBLY); PCHKERRQ(*A,ierr);
|
|
ierr = MatAssemblyEnd(*A,MAT_FINAL_ASSEMBLY); PCHKERRQ(*A,ierr);
|
|
}
|
|
else if (pS)
|
|
{
|
|
if (tid == PETSC_MATSHELL)
|
|
{
|
|
MakeWrapper(comm,&op,A);
|
|
}
|
|
else
|
|
{
|
|
Mat B;
|
|
PetscScalar *pdata;
|
|
PetscInt *pii,*pjj,*oii;
|
|
PetscMPIInt size;
|
|
|
|
int m = pS->Height();
|
|
int n = pS->Width();
|
|
int *ii = pS->GetI();
|
|
int *jj = pS->GetJ();
|
|
double *data = pS->GetData();
|
|
|
|
ierr = PetscMalloc1(m+1,&pii); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = PetscMalloc1(ii[m],&pjj); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = PetscMalloc1(ii[m],&pdata); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
pii[0] = ii[0];
|
|
for (int i = 0; i < m; i++)
|
|
{
|
|
bool issorted = true;
|
|
pii[i+1] = ii[i+1];
|
|
for (int j = ii[i]; j < ii[i+1]; j++)
|
|
{
|
|
pjj[j] = jj[j];
|
|
if (j != ii[i] && issorted) { issorted = (pjj[j] > pjj[j-1]); }
|
|
pdata[j] = data[j];
|
|
}
|
|
if (!issorted)
|
|
{
|
|
ierr = PetscSortIntWithScalarArray(pii[i+1]-pii[i],pjj + pii[i],pdata + pii[i]);
|
|
CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
}
|
|
}
|
|
|
|
ierr = MPI_Comm_size(comm,&size); CCHKERRQ(comm,ierr);
|
|
if (size == 1)
|
|
{
|
|
ierr = MatCreateSeqAIJWithArrays(comm,m,n,pii,pjj,pdata,&B);
|
|
CCHKERRQ(comm,ierr);
|
|
oii = NULL;
|
|
}
|
|
else // block diagonal constructor
|
|
{
|
|
ierr = PetscCalloc1(m+1,&oii); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = MatCreateMPIAIJWithSplitArrays(comm,m,n,PETSC_DECIDE,
|
|
PETSC_DECIDE,
|
|
pii,pjj,pdata,oii,NULL,NULL,&B);
|
|
CCHKERRQ(comm,ierr);
|
|
}
|
|
void *ptrs[4] = {pii,pjj,pdata,oii};
|
|
const char *names[4] = {"_mfem_csr_pii",
|
|
"_mfem_csr_pjj",
|
|
"_mfem_csr_pdata",
|
|
"_mfem_csr_oii"
|
|
};
|
|
for (int i=0; i<4; i++)
|
|
{
|
|
PetscContainer c;
|
|
|
|
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); PCHKERRQ(B,ierr);
|
|
ierr = PetscContainerSetPointer(c,ptrs[i]); PCHKERRQ(B,ierr);
|
|
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
|
PCHKERRQ(B,ierr);
|
|
ierr = PetscObjectCompose((PetscObject)(B),names[i],(PetscObject)c);
|
|
PCHKERRQ(B,ierr);
|
|
ierr = PetscContainerDestroy(&c); PCHKERRQ(B,ierr);
|
|
}
|
|
if (tid == PETSC_MATAIJ)
|
|
{
|
|
*A = B;
|
|
}
|
|
else if (tid == PETSC_MATHYPRE)
|
|
{
|
|
ierr = MatConvert(B,MATHYPRE,MAT_INITIAL_MATRIX,A); PCHKERRQ(B,ierr);
|
|
ierr = MatDestroy(&B); PCHKERRQ(*A,ierr);
|
|
}
|
|
else if (tid == PETSC_MATIS)
|
|
{
|
|
ierr = MatConvert(B,MATIS,MAT_INITIAL_MATRIX,A); PCHKERRQ(B,ierr);
|
|
ierr = MatDestroy(&B); PCHKERRQ(*A,ierr);
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("Unsupported operator type conversion " << tid)
|
|
}
|
|
}
|
|
}
|
|
else // fallback to general operator
|
|
{
|
|
MFEM_VERIFY(tid == PETSC_MATSHELL || tid == PETSC_MATAIJ || tid == ANY_TYPE,
|
|
"Supported types are ANY_TYPE, PETSC_MATSHELL or PETSC_MATAIJ");
|
|
MakeWrapper(comm,&op,A);
|
|
if (tid == PETSC_MATAIJ)
|
|
{
|
|
Mat B;
|
|
PetscBool isaij;
|
|
|
|
ierr = MatComputeOperator(*A,MATMPIAIJ,&B); CCHKERRQ(comm,ierr);
|
|
ierr = PetscObjectTypeCompare((PetscObject)B,MATMPIAIJ,&isaij);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = MatDestroy(A); CCHKERRQ(comm,ierr);
|
|
if (!isaij)
|
|
{
|
|
ierr = MatConvert(B,MATAIJ,MAT_INITIAL_MATRIX,A); CCHKERRQ(comm,ierr);
|
|
ierr = MatDestroy(&B); CCHKERRQ(comm,ierr);
|
|
}
|
|
else
|
|
{
|
|
*A = B;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PetscParMatrix::Destroy()
|
|
{
|
|
if (A != NULL)
|
|
{
|
|
MPI_Comm comm = MPI_COMM_NULL;
|
|
ierr = PetscObjectGetComm((PetscObject)A,&comm); PCHKERRQ(A,ierr);
|
|
ierr = MatDestroy(&A); CCHKERRQ(comm,ierr);
|
|
}
|
|
delete X;
|
|
delete Y;
|
|
X = Y = NULL;
|
|
}
|
|
|
|
PetscParMatrix::PetscParMatrix(Mat a, bool ref)
|
|
{
|
|
if (ref)
|
|
{
|
|
ierr = PetscObjectReference((PetscObject)a); PCHKERRQ(a,ierr);
|
|
}
|
|
Init();
|
|
A = a;
|
|
height = GetNumRows();
|
|
width = GetNumCols();
|
|
}
|
|
|
|
void PetscParMatrix::SetMat(Mat _A)
|
|
{
|
|
if (_A == A) { return; }
|
|
Destroy();
|
|
ierr = PetscObjectReference((PetscObject)_A); PCHKERRQ(_A,ierr);
|
|
A = _A;
|
|
height = GetNumRows();
|
|
width = GetNumCols();
|
|
}
|
|
|
|
// Computes y = alpha * A * x + beta * y
|
|
// or y = alpha * A^T* x + beta * y
|
|
static void MatMultKernel(Mat A,PetscScalar a,Vec X,PetscScalar b,Vec Y,
|
|
bool transpose)
|
|
{
|
|
PetscErrorCode (*f)(Mat,Vec,Vec);
|
|
PetscErrorCode (*fadd)(Mat,Vec,Vec,Vec);
|
|
if (transpose)
|
|
{
|
|
f = MatMultTranspose;
|
|
fadd = MatMultTransposeAdd;
|
|
}
|
|
else
|
|
{
|
|
f = MatMult;
|
|
fadd = MatMultAdd;
|
|
}
|
|
if (a != 0.)
|
|
{
|
|
if (b == 1.)
|
|
{
|
|
ierr = VecScale(X,a); PCHKERRQ(A,ierr);
|
|
ierr = (*fadd)(A,X,Y,Y); PCHKERRQ(A,ierr);
|
|
ierr = VecScale(X,1./a); PCHKERRQ(A,ierr);
|
|
}
|
|
else if (b != 0.)
|
|
{
|
|
ierr = VecScale(X,a); PCHKERRQ(A,ierr);
|
|
ierr = VecScale(Y,b); PCHKERRQ(A,ierr);
|
|
ierr = (*fadd)(A,X,Y,Y); PCHKERRQ(A,ierr);
|
|
ierr = VecScale(X,1./a); PCHKERRQ(A,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = (*f)(A,X,Y); PCHKERRQ(A,ierr);
|
|
if (a != 1.)
|
|
{
|
|
ierr = VecScale(Y,a); PCHKERRQ(A,ierr);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (b == 1.)
|
|
{
|
|
// do nothing
|
|
}
|
|
else if (b != 0.)
|
|
{
|
|
ierr = VecScale(Y,b); PCHKERRQ(A,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = VecSet(Y,0.); PCHKERRQ(A,ierr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PetscParMatrix::MakeRef(const PetscParMatrix &master)
|
|
{
|
|
ierr = PetscObjectReference((PetscObject)master.A); PCHKERRQ(master.A,ierr);
|
|
Destroy();
|
|
Init();
|
|
A = master.A;
|
|
height = master.height;
|
|
width = master.width;
|
|
}
|
|
|
|
PetscParVector * PetscParMatrix::GetX() const
|
|
{
|
|
if (!X)
|
|
{
|
|
MFEM_VERIFY(A,"Mat not present");
|
|
X = new PetscParVector(*this,false); PCHKERRQ(A,ierr);
|
|
}
|
|
return X;
|
|
}
|
|
|
|
PetscParVector * PetscParMatrix::GetY() const
|
|
{
|
|
if (!Y)
|
|
{
|
|
MFEM_VERIFY(A,"Mat not present");
|
|
Y = new PetscParVector(*this,true); PCHKERRQ(A,ierr);
|
|
}
|
|
return Y;
|
|
}
|
|
|
|
PetscParMatrix * PetscParMatrix::Transpose(bool action)
|
|
{
|
|
Mat B;
|
|
if (action)
|
|
{
|
|
ierr = MatCreateTranspose(A,&B); PCHKERRQ(A,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = MatTranspose(A,MAT_INITIAL_MATRIX,&B); PCHKERRQ(A,ierr);
|
|
}
|
|
return new PetscParMatrix(B,false);
|
|
}
|
|
|
|
void PetscParMatrix::operator*=(double s)
|
|
{
|
|
ierr = MatScale(A,s); PCHKERRQ(A,ierr);
|
|
}
|
|
|
|
void PetscParMatrix::Mult(double a, const Vector &x, double b, Vector &y) const
|
|
{
|
|
MFEM_ASSERT(x.Size() == Width(), "invalid x.Size() = " << x.Size()
|
|
<< ", expected size = " << Width());
|
|
MFEM_ASSERT(y.Size() == Height(), "invalid y.Size() = " << y.Size()
|
|
<< ", expected size = " << Height());
|
|
|
|
PetscParVector *XX = GetX();
|
|
PetscParVector *YY = GetY();
|
|
XX->PlaceArray(x.GetData());
|
|
YY->PlaceArray(y.GetData());
|
|
MatMultKernel(A,a,XX->x,b,YY->x,false);
|
|
XX->ResetArray();
|
|
YY->ResetArray();
|
|
}
|
|
|
|
void PetscParMatrix::MultTranspose(double a, const Vector &x, double b,
|
|
Vector &y) const
|
|
{
|
|
MFEM_ASSERT(x.Size() == Height(), "invalid x.Size() = " << x.Size()
|
|
<< ", expected size = " << Height());
|
|
MFEM_ASSERT(y.Size() == Width(), "invalid y.Size() = " << y.Size()
|
|
<< ", expected size = " << Width());
|
|
|
|
PetscParVector *XX = GetX();
|
|
PetscParVector *YY = GetY();
|
|
YY->PlaceArray(x.GetData());
|
|
XX->PlaceArray(y.GetData());
|
|
MatMultKernel(A,a,YY->x,b,XX->x,true);
|
|
XX->ResetArray();
|
|
YY->ResetArray();
|
|
}
|
|
|
|
void PetscParMatrix::Print(const char *fname, bool binary) const
|
|
{
|
|
if (fname)
|
|
{
|
|
PetscViewer view;
|
|
|
|
if (binary)
|
|
{
|
|
ierr = PetscViewerBinaryOpen(PetscObjectComm((PetscObject)A),fname,
|
|
FILE_MODE_WRITE,&view);
|
|
}
|
|
else
|
|
{
|
|
ierr = PetscViewerASCIIOpen(PetscObjectComm((PetscObject)A),fname,&view);
|
|
}
|
|
PCHKERRQ(A,ierr);
|
|
ierr = MatView(A,view); PCHKERRQ(A,ierr);
|
|
ierr = PetscViewerDestroy(&view); PCHKERRQ(A,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = MatView(A,NULL); PCHKERRQ(A,ierr);
|
|
}
|
|
}
|
|
|
|
void PetscParMatrix::ScaleRows(const Vector & s)
|
|
{
|
|
MFEM_ASSERT(s.Size() == Height(), "invalid s.Size() = " << s.Size()
|
|
<< ", expected size = " << Height());
|
|
|
|
PetscParVector *YY = GetY();
|
|
YY->PlaceArray(s.GetData());
|
|
ierr = MatDiagonalScale(A,*YY,NULL); PCHKERRQ(A,ierr);
|
|
YY->ResetArray();
|
|
}
|
|
|
|
void PetscParMatrix::ScaleCols(const Vector & s)
|
|
{
|
|
MFEM_ASSERT(s.Size() == Width(), "invalid s.Size() = " << s.Size()
|
|
<< ", expected size = " << Width());
|
|
|
|
PetscParVector *XX = GetX();
|
|
XX->PlaceArray(s.GetData());
|
|
ierr = MatDiagonalScale(A,NULL,*XX); PCHKERRQ(A,ierr);
|
|
XX->ResetArray();
|
|
}
|
|
|
|
void PetscParMatrix::Shift(double s)
|
|
{
|
|
ierr = MatShift(A,(PetscScalar)s); PCHKERRQ(A,ierr);
|
|
}
|
|
|
|
void PetscParMatrix::Shift(const Vector & s)
|
|
{
|
|
// for matrices with square diagonal blocks only
|
|
MFEM_ASSERT(s.Size() == Height(), "invalid s.Size() = " << s.Size()
|
|
<< ", expected size = " << Height());
|
|
MFEM_ASSERT(s.Size() == Width(), "invalid s.Size() = " << s.Size()
|
|
<< ", expected size = " << Width());
|
|
|
|
PetscParVector *XX = GetX();
|
|
XX->PlaceArray(s.GetData());
|
|
ierr = MatDiagonalSet(A,*XX,ADD_VALUES); PCHKERRQ(A,ierr);
|
|
XX->ResetArray();
|
|
}
|
|
|
|
PetscParMatrix * TripleMatrixProduct(PetscParMatrix *R, PetscParMatrix *A,
|
|
PetscParMatrix *P)
|
|
{
|
|
MFEM_VERIFY(A->Width() == P->Height(),
|
|
"Petsc TripleMatrixProduct: Number of local cols of A " << A->Width() <<
|
|
" differs from number of local rows of P " << P->Height());
|
|
MFEM_VERIFY(A->Height() == R->Width(),
|
|
"Petsc TripleMatrixProduct: Number of local rows of A " << A->Height() <<
|
|
" differs from number of local cols of R " << R->Width());
|
|
Mat B;
|
|
ierr = MatMatMatMult(*R,*A,*P,MAT_INITIAL_MATRIX,PETSC_DEFAULT,&B);
|
|
PCHKERRQ(*R,ierr);
|
|
return new PetscParMatrix(B);
|
|
}
|
|
|
|
PetscParMatrix * RAP(PetscParMatrix *Rt, PetscParMatrix *A, PetscParMatrix *P)
|
|
{
|
|
Mat pA = *A,pP = *P,pRt = *Rt;
|
|
Mat B;
|
|
PetscBool Aismatis,Pismatis,Rtismatis;
|
|
|
|
MFEM_VERIFY(A->Width() == P->Height(),
|
|
"Petsc RAP: Number of local cols of A " << A->Width() <<
|
|
" differs from number of local rows of P " << P->Height());
|
|
MFEM_VERIFY(A->Height() == Rt->Height(),
|
|
"Petsc RAP: Number of local rows of A " << A->Height() <<
|
|
" differs from number of local rows of Rt " << Rt->Height());
|
|
ierr = PetscObjectTypeCompare((PetscObject)pA,MATIS,&Aismatis);
|
|
PCHKERRQ(pA,ierr);
|
|
ierr = PetscObjectTypeCompare((PetscObject)pP,MATIS,&Pismatis);
|
|
PCHKERRQ(pA,ierr);
|
|
ierr = PetscObjectTypeCompare((PetscObject)pRt,MATIS,&Rtismatis);
|
|
PCHKERRQ(pA,ierr);
|
|
if (Aismatis &&
|
|
Pismatis &&
|
|
Rtismatis) // handle special case (this code will eventually go into PETSc)
|
|
{
|
|
Mat lA,lP,lB,lRt;
|
|
ISLocalToGlobalMapping cl2gP,cl2gRt;
|
|
PetscInt rlsize,clsize,rsize,csize;
|
|
|
|
ierr = MatGetLocalToGlobalMapping(pP,NULL,&cl2gP); PCHKERRQ(pA,ierr);
|
|
ierr = MatGetLocalToGlobalMapping(pRt,NULL,&cl2gRt); PCHKERRQ(pA,ierr);
|
|
ierr = MatGetLocalSize(pP,NULL,&clsize); PCHKERRQ(pP,ierr);
|
|
ierr = MatGetLocalSize(pRt,NULL,&rlsize); PCHKERRQ(pRt,ierr);
|
|
ierr = MatGetSize(pP,NULL,&csize); PCHKERRQ(pP,ierr);
|
|
ierr = MatGetSize(pRt,NULL,&rsize); PCHKERRQ(pRt,ierr);
|
|
ierr = MatCreate(A->GetComm(),&B); PCHKERRQ(pA,ierr);
|
|
ierr = MatSetSizes(B,rlsize,clsize,rsize,csize); PCHKERRQ(B,ierr);
|
|
ierr = MatSetType(B,MATIS); PCHKERRQ(B,ierr);
|
|
ierr = MatSetLocalToGlobalMapping(B,cl2gRt,cl2gP); PCHKERRQ(B,ierr);
|
|
ierr = MatISGetLocalMat(pA,&lA); PCHKERRQ(pA,ierr);
|
|
ierr = MatISGetLocalMat(pP,&lP); PCHKERRQ(pA,ierr);
|
|
ierr = MatISGetLocalMat(pRt,&lRt); PCHKERRQ(pA,ierr);
|
|
if (lRt == lP)
|
|
{
|
|
ierr = MatPtAP(lA,lP,MAT_INITIAL_MATRIX,PETSC_DEFAULT,&lB);
|
|
PCHKERRQ(lA,ierr);
|
|
}
|
|
else
|
|
{
|
|
Mat lR;
|
|
ierr = MatTranspose(lRt,MAT_INITIAL_MATRIX,&lR); PCHKERRQ(lRt,ierr);
|
|
ierr = MatMatMatMult(lR,lA,lP,MAT_INITIAL_MATRIX,PETSC_DEFAULT,&lB);
|
|
PCHKERRQ(lRt,ierr);
|
|
ierr = MatDestroy(&lR); PCHKERRQ(lRt,ierr);
|
|
}
|
|
|
|
// attach lRt matrix to the subdomain local matrix
|
|
// it may be used if markers on vdofs have to be mapped on
|
|
// subdomain true dofs
|
|
{
|
|
mfem::Array<Mat> *vmatsl2l = new mfem::Array<Mat>(1);
|
|
ierr = PetscObjectReference((PetscObject)lRt); PCHKERRQ(lRt,ierr);
|
|
(*vmatsl2l)[0] = lRt;
|
|
|
|
PetscContainer c;
|
|
ierr = PetscContainerCreate(PetscObjectComm((PetscObject)B),&c);
|
|
PCHKERRQ(B,ierr);
|
|
ierr = PetscContainerSetPointer(c,vmatsl2l); PCHKERRQ(c,ierr);
|
|
ierr = PetscContainerSetUserDestroy(c,__mfem_matarray_container_destroy);
|
|
PCHKERRQ(c,ierr);
|
|
ierr = PetscObjectCompose((PetscObject)B,"_MatIS_PtAP_l2l",(PetscObject)c);
|
|
PCHKERRQ(B,ierr);
|
|
ierr = PetscContainerDestroy(&c); PCHKERRQ(B,ierr);
|
|
}
|
|
|
|
// Set local problem
|
|
ierr = MatISSetLocalMat(B,lB); PCHKERRQ(lB,ierr);
|
|
ierr = MatDestroy(&lB); PCHKERRQ(lA,ierr);
|
|
ierr = MatAssemblyBegin(B,MAT_FINAL_ASSEMBLY); PCHKERRQ(B,ierr);
|
|
ierr = MatAssemblyEnd(B,MAT_FINAL_ASSEMBLY); PCHKERRQ(B,ierr);
|
|
}
|
|
else // it raises an error if the PtAP is not supported in PETSc
|
|
{
|
|
if (pP == pRt)
|
|
{
|
|
ierr = MatPtAP(pA,pP,MAT_INITIAL_MATRIX,PETSC_DEFAULT,&B);
|
|
PCHKERRQ(pA,ierr);
|
|
}
|
|
else
|
|
{
|
|
Mat pR;
|
|
ierr = MatTranspose(pRt,MAT_INITIAL_MATRIX,&pR); PCHKERRQ(Rt,ierr);
|
|
ierr = MatMatMatMult(pR,pA,pP,MAT_INITIAL_MATRIX,PETSC_DEFAULT,&B);
|
|
PCHKERRQ(pRt,ierr);
|
|
ierr = MatDestroy(&pR); PCHKERRQ(pRt,ierr);
|
|
}
|
|
}
|
|
return new PetscParMatrix(B);
|
|
}
|
|
|
|
PetscParMatrix * RAP(PetscParMatrix *A, PetscParMatrix *P)
|
|
{
|
|
PetscParMatrix *out = RAP(P,A,P);
|
|
return out;
|
|
}
|
|
|
|
PetscParMatrix * RAP(HypreParMatrix *hA, PetscParMatrix *P)
|
|
{
|
|
PetscParMatrix *out,*A;
|
|
#if defined(PETSC_HAVE_HYPRE)
|
|
A = new PetscParMatrix(hA,Operator::PETSC_MATHYPRE);
|
|
#else
|
|
A = new PetscParMatrix(hA);
|
|
#endif
|
|
out = RAP(P,A,P);
|
|
delete A;
|
|
return out;
|
|
}
|
|
|
|
|
|
PetscParMatrix * ParMult(const PetscParMatrix *A, const PetscParMatrix *B)
|
|
{
|
|
Mat AB;
|
|
|
|
ierr = MatMatMult(*A,*B,MAT_INITIAL_MATRIX,PETSC_DEFAULT,&AB);
|
|
CCHKERRQ(A->GetComm(),ierr);
|
|
return new PetscParMatrix(AB);
|
|
}
|
|
|
|
PetscParMatrix* PetscParMatrix::EliminateRowsCols(const Array<int> &rows_cols)
|
|
{
|
|
Mat Ae;
|
|
|
|
PetscParVector dummy(GetComm(),0);
|
|
ierr = MatDuplicate(A,MAT_COPY_VALUES,&Ae); PCHKERRQ(A,ierr);
|
|
EliminateRowsCols(rows_cols,dummy,dummy);
|
|
ierr = MatAXPY(Ae,-1.,A,SAME_NONZERO_PATTERN); PCHKERRQ(A,ierr);
|
|
return new PetscParMatrix(Ae);
|
|
}
|
|
|
|
void PetscParMatrix::EliminateRowsCols(const Array<int> &rows_cols,
|
|
const HypreParVector &X,
|
|
HypreParVector &B,
|
|
double diag)
|
|
{
|
|
MFEM_ABORT("Missing PetscParMatrix::EliminateRowsCols() with HypreParVectors");
|
|
}
|
|
|
|
void PetscParMatrix::EliminateRowsCols(const Array<int> &rows_cols,
|
|
const PetscParVector &X,
|
|
PetscParVector &B,
|
|
double diag)
|
|
{
|
|
PetscInt M,N;
|
|
ierr = MatGetSize(A,&M,&N); PCHKERRQ(A,ierr);
|
|
MFEM_VERIFY(M == N,"Rectangular case unsupported");
|
|
|
|
// TODO: what if a diagonal term is not present?
|
|
ierr = MatSetOption(A,MAT_NO_OFF_PROC_ZERO_ROWS,PETSC_TRUE); PCHKERRQ(A,ierr);
|
|
|
|
// rows need to be in global numbering
|
|
PetscInt rst;
|
|
ierr = MatGetOwnershipRange(A,&rst,NULL); PCHKERRQ(A,ierr);
|
|
|
|
IS dir;
|
|
ierr = Convert_Array_IS(GetComm(),true,&rows_cols,rst,&dir); PCHKERRQ(A,ierr);
|
|
if (!X.GlobalSize() && !B.GlobalSize())
|
|
{
|
|
ierr = MatZeroRowsColumnsIS(A,dir,diag,NULL,NULL); PCHKERRQ(A,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = MatZeroRowsColumnsIS(A,dir,diag,X,B); PCHKERRQ(A,ierr);
|
|
}
|
|
ierr = ISDestroy(&dir); PCHKERRQ(A,ierr);
|
|
}
|
|
|
|
void PetscParMatrix::EliminateRows(const Array<int> &rows)
|
|
{
|
|
ierr = MatSetOption(A,MAT_NO_OFF_PROC_ZERO_ROWS,PETSC_TRUE); PCHKERRQ(A,ierr);
|
|
|
|
// rows need to be in global numbering
|
|
PetscInt rst;
|
|
ierr = MatGetOwnershipRange(A,&rst,NULL); PCHKERRQ(A,ierr);
|
|
|
|
IS dir;
|
|
ierr = Convert_Array_IS(GetComm(),true,&rows,rst,&dir); PCHKERRQ(A,ierr);
|
|
ierr = MatZeroRowsIS(A,dir,0.0,NULL,NULL); PCHKERRQ(A,ierr);
|
|
ierr = ISDestroy(&dir); PCHKERRQ(A,ierr);
|
|
}
|
|
|
|
Mat PetscParMatrix::ReleaseMat(bool dereference)
|
|
{
|
|
|
|
Mat B = A;
|
|
if (dereference)
|
|
{
|
|
MPI_Comm comm = GetComm();
|
|
ierr = PetscObjectDereference((PetscObject)A); CCHKERRQ(comm,ierr);
|
|
}
|
|
A = NULL;
|
|
return B;
|
|
}
|
|
|
|
Operator::Type PetscParMatrix::GetType() const
|
|
{
|
|
PetscBool ok;
|
|
MFEM_VERIFY(A, "no associated PETSc Mat object");
|
|
PetscObject oA = (PetscObject)(this->A);
|
|
// map all of MATAIJ, MATSEQAIJ, and MATMPIAIJ to -> PETSC_MATAIJ
|
|
ierr = PetscObjectTypeCompare(oA, MATAIJ, &ok); PCHKERRQ(A,ierr);
|
|
if (ok == PETSC_TRUE) { return PETSC_MATAIJ; }
|
|
ierr = PetscObjectTypeCompare(oA, MATSEQAIJ, &ok); PCHKERRQ(A,ierr);
|
|
if (ok == PETSC_TRUE) { return PETSC_MATAIJ; }
|
|
ierr = PetscObjectTypeCompare(oA, MATMPIAIJ, &ok); PCHKERRQ(A,ierr);
|
|
if (ok == PETSC_TRUE) { return PETSC_MATAIJ; }
|
|
ierr = PetscObjectTypeCompare(oA, MATIS, &ok); PCHKERRQ(A,ierr);
|
|
if (ok == PETSC_TRUE) { return PETSC_MATIS; }
|
|
ierr = PetscObjectTypeCompare(oA, MATSHELL, &ok); PCHKERRQ(A,ierr);
|
|
if (ok == PETSC_TRUE) { return PETSC_MATSHELL; }
|
|
ierr = PetscObjectTypeCompare(oA, MATNEST, &ok); PCHKERRQ(A,ierr);
|
|
if (ok == PETSC_TRUE) { return PETSC_MATNEST; }
|
|
#if defined(PETSC_HAVE_HYPRE)
|
|
ierr = PetscObjectTypeCompare(oA, MATHYPRE, &ok); PCHKERRQ(A,ierr);
|
|
if (ok == PETSC_TRUE) { return PETSC_MATHYPRE; }
|
|
#endif
|
|
return PETSC_MATGENERIC;
|
|
}
|
|
|
|
void EliminateBC(PetscParMatrix &A, PetscParMatrix &Ae,
|
|
const Array<int> &ess_dof_list,
|
|
const Vector &X, Vector &B)
|
|
{
|
|
const PetscScalar *array;
|
|
Mat pA = const_cast<PetscParMatrix&>(A);
|
|
|
|
// B -= Ae*X
|
|
Ae.Mult(-1.0, X, 1.0, B);
|
|
|
|
Vec diag = const_cast<PetscParVector&>((*A.GetX()));
|
|
ierr = MatGetDiagonal(pA,diag); PCHKERRQ(pA,ierr);
|
|
ierr = VecGetArrayRead(diag,&array); PCHKERRQ(diag,ierr);
|
|
for (int i = 0; i < ess_dof_list.Size(); i++)
|
|
{
|
|
int r = ess_dof_list[i];
|
|
B(r) = array[r] * X(r);
|
|
}
|
|
ierr = VecRestoreArrayRead(diag,&array); PCHKERRQ(diag,ierr);
|
|
}
|
|
|
|
// PetscSolver methods
|
|
|
|
PetscSolver::PetscSolver() : clcustom(false)
|
|
{
|
|
obj = NULL;
|
|
B = X = NULL;
|
|
cid = -1;
|
|
operatorset = false;
|
|
bchandler = NULL;
|
|
private_ctx = NULL;
|
|
}
|
|
|
|
PetscSolver::~PetscSolver()
|
|
{
|
|
delete B;
|
|
delete X;
|
|
FreePrivateContext();
|
|
}
|
|
|
|
void PetscSolver::SetTol(double tol)
|
|
{
|
|
SetRelTol(tol);
|
|
}
|
|
|
|
void PetscSolver::SetRelTol(double tol)
|
|
{
|
|
if (cid == KSP_CLASSID)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
ierr = KSPSetTolerances(ksp,tol,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT);
|
|
}
|
|
else if (cid == SNES_CLASSID)
|
|
{
|
|
SNES snes = (SNES)obj;
|
|
ierr = SNESSetTolerances(snes,PETSC_DEFAULT,tol,PETSC_DEFAULT,PETSC_DEFAULT,
|
|
PETSC_DEFAULT);
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
TS ts = (TS)obj;
|
|
ierr = TSSetTolerances(ts,PETSC_DECIDE,NULL,tol,NULL);
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("CLASSID = " << cid << " is not implemented!");
|
|
}
|
|
PCHKERRQ(obj,ierr);
|
|
}
|
|
|
|
void PetscSolver::SetAbsTol(double tol)
|
|
{
|
|
if (cid == KSP_CLASSID)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
ierr = KSPSetTolerances(ksp,PETSC_DEFAULT,tol,PETSC_DEFAULT,PETSC_DEFAULT);
|
|
}
|
|
else if (cid == SNES_CLASSID)
|
|
{
|
|
SNES snes = (SNES)obj;
|
|
ierr = SNESSetTolerances(snes,tol,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT,
|
|
PETSC_DEFAULT);
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
TS ts = (TS)obj;
|
|
ierr = TSSetTolerances(ts,tol,NULL,PETSC_DECIDE,NULL);
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("CLASSID = " << cid << " is not implemented!");
|
|
}
|
|
PCHKERRQ(obj,ierr);
|
|
}
|
|
|
|
void PetscSolver::SetMaxIter(int max_iter)
|
|
{
|
|
if (cid == KSP_CLASSID)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
ierr = KSPSetTolerances(ksp,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT,
|
|
max_iter);
|
|
}
|
|
else if (cid == SNES_CLASSID)
|
|
{
|
|
SNES snes = (SNES)obj;
|
|
ierr = SNESSetTolerances(snes,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT,
|
|
max_iter,PETSC_DEFAULT);
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
TS ts = (TS)obj;
|
|
ierr = TSSetMaxSteps(ts,max_iter);
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("CLASSID = " << cid << " is not implemented!");
|
|
}
|
|
PCHKERRQ(obj,ierr);
|
|
}
|
|
|
|
|
|
void PetscSolver::SetPrintLevel(int plev)
|
|
{
|
|
typedef PetscErrorCode (*myPetscFunc)(void**);
|
|
PetscViewerAndFormat *vf = NULL;
|
|
PetscViewer viewer = PETSC_VIEWER_STDOUT_(PetscObjectComm(obj));
|
|
|
|
if (plev > 0)
|
|
{
|
|
ierr = PetscViewerAndFormatCreate(viewer,PETSC_VIEWER_DEFAULT,&vf);
|
|
PCHKERRQ(obj,ierr);
|
|
}
|
|
if (cid == KSP_CLASSID)
|
|
{
|
|
// there are many other options, see the function KSPSetFromOptions() in
|
|
// src/ksp/ksp/interface/itcl.c
|
|
typedef PetscErrorCode (*myMonitor)(KSP,PetscInt,PetscReal,void*);
|
|
KSP ksp = (KSP)obj;
|
|
if (plev >= 0)
|
|
{
|
|
ierr = KSPMonitorCancel(ksp); PCHKERRQ(ksp,ierr);
|
|
}
|
|
if (plev == 1)
|
|
{
|
|
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorDefault,vf,
|
|
(myPetscFunc)PetscViewerAndFormatDestroy);
|
|
PCHKERRQ(ksp,ierr);
|
|
}
|
|
else if (plev > 1)
|
|
{
|
|
ierr = KSPSetComputeSingularValues(ksp,PETSC_TRUE); PCHKERRQ(ksp,ierr);
|
|
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorSingularValue,vf,
|
|
(myPetscFunc)PetscViewerAndFormatDestroy);
|
|
PCHKERRQ(ksp,ierr);
|
|
if (plev > 2)
|
|
{
|
|
ierr = PetscViewerAndFormatCreate(viewer,PETSC_VIEWER_DEFAULT,&vf);
|
|
PCHKERRQ(viewer,ierr);
|
|
ierr = KSPMonitorSet(ksp,(myMonitor)KSPMonitorTrueResidualNorm,vf,
|
|
(myPetscFunc)PetscViewerAndFormatDestroy);
|
|
PCHKERRQ(ksp,ierr);
|
|
}
|
|
}
|
|
}
|
|
else if (cid == SNES_CLASSID)
|
|
{
|
|
typedef PetscErrorCode (*myMonitor)(SNES,PetscInt,PetscReal,void*);
|
|
SNES snes = (SNES)obj;
|
|
if (plev >= 0)
|
|
{
|
|
ierr = SNESMonitorCancel(snes); PCHKERRQ(snes,ierr);
|
|
}
|
|
if (plev > 0)
|
|
{
|
|
ierr = SNESMonitorSet(snes,(myMonitor)SNESMonitorDefault,vf,
|
|
(myPetscFunc)PetscViewerAndFormatDestroy);
|
|
PCHKERRQ(snes,ierr);
|
|
}
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
TS ts = (TS)obj;
|
|
if (plev >= 0)
|
|
{
|
|
ierr = TSMonitorCancel(ts); PCHKERRQ(ts,ierr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("CLASSID = " << cid << " is not implemented!");
|
|
}
|
|
}
|
|
|
|
MPI_Comm PetscSolver::GetComm() const
|
|
{
|
|
return obj ? PetscObjectComm(obj) : MPI_COMM_NULL;
|
|
}
|
|
|
|
void PetscSolver::SetMonitor(PetscSolverMonitor *ctx)
|
|
{
|
|
__mfem_monitor_ctx *monctx;
|
|
ierr = PetscNew(&monctx); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
monctx->solver = this;
|
|
monctx->monitor = ctx;
|
|
if (cid == KSP_CLASSID)
|
|
{
|
|
ierr = KSPMonitorSet((KSP)obj,__mfem_ksp_monitor,monctx,
|
|
__mfem_monitor_ctx_destroy);
|
|
PCHKERRQ(obj,ierr);
|
|
}
|
|
else if (cid == SNES_CLASSID)
|
|
{
|
|
ierr = SNESMonitorSet((SNES)obj,__mfem_snes_monitor,monctx,
|
|
__mfem_monitor_ctx_destroy);
|
|
PCHKERRQ(obj,ierr);
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
ierr = TSMonitorSet((TS)obj,__mfem_ts_monitor,monctx,
|
|
__mfem_monitor_ctx_destroy);
|
|
PCHKERRQ(obj,ierr);
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("CLASSID = " << cid << " is not implemented!");
|
|
}
|
|
}
|
|
|
|
void PetscSolver::SetBCHandler(PetscBCHandler *bch)
|
|
{
|
|
bchandler = bch;
|
|
if (cid == SNES_CLASSID)
|
|
{
|
|
__mfem_snes_ctx* snes_ctx = (__mfem_snes_ctx*)private_ctx;
|
|
snes_ctx->bchandler = bchandler;
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
__mfem_ts_ctx* ts_ctx = (__mfem_ts_ctx*)private_ctx;
|
|
ts_ctx->bchandler = bchandler;
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("Handling of essential bc only implemented for nonlinear and time-dependent solvers");
|
|
}
|
|
}
|
|
|
|
void PetscSolver::SetPreconditionerFactory(PetscPreconditionerFactory *factory)
|
|
{
|
|
PC pc = NULL;
|
|
if (cid == TS_CLASSID)
|
|
{
|
|
SNES snes;
|
|
KSP ksp;
|
|
|
|
ierr = TSGetSNES((TS)obj,&snes); PCHKERRQ(obj,ierr);
|
|
ierr = SNESGetKSP(snes,&ksp); PCHKERRQ(obj,ierr);
|
|
ierr = KSPGetPC(ksp,&pc); PCHKERRQ(obj,ierr);
|
|
}
|
|
else if (cid == SNES_CLASSID)
|
|
{
|
|
KSP ksp;
|
|
|
|
ierr = SNESGetKSP((SNES)obj,&ksp); PCHKERRQ(obj,ierr);
|
|
ierr = KSPGetPC(ksp,&pc); PCHKERRQ(obj,ierr);
|
|
}
|
|
else if (cid == KSP_CLASSID)
|
|
{
|
|
ierr = KSPGetPC((KSP)obj,&pc); PCHKERRQ(obj,ierr);
|
|
}
|
|
else if (cid == PC_CLASSID)
|
|
{
|
|
pc = (PC)obj;
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("No support for PetscPreconditionerFactory for this object");
|
|
}
|
|
if (factory)
|
|
{
|
|
ierr = MakeShellPCWithFactory(pc,factory); PCHKERRQ(pc,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = PCSetType(pc, PCNONE); PCHKERRQ(pc,ierr);
|
|
}
|
|
}
|
|
|
|
void PetscSolver::Customize(bool customize) const
|
|
{
|
|
if (!customize) { clcustom = true; }
|
|
if (!clcustom)
|
|
{
|
|
if (cid == PC_CLASSID)
|
|
{
|
|
PC pc = (PC)obj;
|
|
ierr = PCSetFromOptions(pc); PCHKERRQ(pc, ierr);
|
|
}
|
|
else if (cid == KSP_CLASSID)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
ierr = KSPSetFromOptions(ksp); PCHKERRQ(ksp, ierr);
|
|
}
|
|
else if (cid == SNES_CLASSID)
|
|
{
|
|
SNES snes = (SNES)obj;
|
|
ierr = SNESSetFromOptions(snes); PCHKERRQ(snes, ierr);
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
TS ts = (TS)obj;
|
|
ierr = TSSetFromOptions(ts); PCHKERRQ(ts, ierr);
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("CLASSID = " << cid << " is not implemented!");
|
|
}
|
|
}
|
|
clcustom = true;
|
|
}
|
|
|
|
int PetscSolver::GetConverged()
|
|
{
|
|
if (cid == KSP_CLASSID)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
KSPConvergedReason reason;
|
|
ierr = KSPGetConvergedReason(ksp,&reason);
|
|
PCHKERRQ(ksp,ierr);
|
|
return reason > 0 ? 1 : 0;
|
|
}
|
|
else if (cid == SNES_CLASSID)
|
|
{
|
|
SNES snes = (SNES)obj;
|
|
SNESConvergedReason reason;
|
|
ierr = SNESGetConvergedReason(snes,&reason);
|
|
PCHKERRQ(snes,ierr);
|
|
return reason > 0 ? 1 : 0;
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
TS ts = (TS)obj;
|
|
TSConvergedReason reason;
|
|
ierr = TSGetConvergedReason(ts,&reason);
|
|
PCHKERRQ(ts,ierr);
|
|
return reason > 0 ? 1 : 0;
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("CLASSID = " << cid << " is not implemented!");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int PetscSolver::GetNumIterations()
|
|
{
|
|
if (cid == KSP_CLASSID)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
PetscInt its;
|
|
ierr = KSPGetIterationNumber(ksp,&its);
|
|
PCHKERRQ(ksp,ierr);
|
|
return its;
|
|
}
|
|
else if (cid == SNES_CLASSID)
|
|
{
|
|
SNES snes = (SNES)obj;
|
|
PetscInt its;
|
|
ierr = SNESGetIterationNumber(snes,&its);
|
|
PCHKERRQ(snes,ierr);
|
|
return its;
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
TS ts = (TS)obj;
|
|
PetscInt its;
|
|
ierr = TSGetStepNumber(ts,&its);
|
|
PCHKERRQ(ts,ierr);
|
|
return its;
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("CLASSID = " << cid << " is not implemented!");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
double PetscSolver::GetFinalNorm()
|
|
{
|
|
if (cid == KSP_CLASSID)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
PetscReal norm;
|
|
ierr = KSPGetResidualNorm(ksp,&norm);
|
|
PCHKERRQ(ksp,ierr);
|
|
return norm;
|
|
}
|
|
if (cid == SNES_CLASSID)
|
|
{
|
|
SNES snes = (SNES)obj;
|
|
PetscReal norm;
|
|
ierr = SNESGetFunctionNorm(snes,&norm);
|
|
PCHKERRQ(snes,ierr);
|
|
return norm;
|
|
}
|
|
else
|
|
{
|
|
MFEM_ABORT("CLASSID = " << cid << " is not implemented!");
|
|
return PETSC_MAX_REAL;
|
|
}
|
|
}
|
|
|
|
void PetscSolver::CreatePrivateContext()
|
|
{
|
|
FreePrivateContext();
|
|
if (cid == SNES_CLASSID)
|
|
{
|
|
__mfem_snes_ctx *snes_ctx;
|
|
ierr = PetscNew(&snes_ctx); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
snes_ctx->op = NULL;
|
|
snes_ctx->bchandler = NULL;
|
|
snes_ctx->work = NULL;
|
|
snes_ctx->jacType = Operator::PETSC_MATAIJ;
|
|
private_ctx = (void*) snes_ctx;
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
__mfem_ts_ctx *ts_ctx;
|
|
ierr = PetscNew(&ts_ctx); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ts_ctx->op = NULL;
|
|
ts_ctx->bchandler = NULL;
|
|
ts_ctx->work = NULL;
|
|
ts_ctx->work2 = NULL;
|
|
ts_ctx->cached_shift = std::numeric_limits<PetscReal>::min();
|
|
ts_ctx->cached_ijacstate = -1;
|
|
ts_ctx->cached_rhsjacstate = -1;
|
|
ts_ctx->cached_splits_xstate = -1;
|
|
ts_ctx->cached_splits_xdotstate = -1;
|
|
ts_ctx->type = PetscODESolver::ODE_SOLVER_GENERAL;
|
|
ts_ctx->jacType = Operator::PETSC_MATAIJ;
|
|
private_ctx = (void*) ts_ctx;
|
|
}
|
|
}
|
|
|
|
void PetscSolver::FreePrivateContext()
|
|
{
|
|
if (!private_ctx) { return; }
|
|
// free private context's owned objects
|
|
if (cid == SNES_CLASSID)
|
|
{
|
|
__mfem_snes_ctx *snes_ctx = (__mfem_snes_ctx *)private_ctx;
|
|
delete snes_ctx->work;
|
|
}
|
|
else if (cid == TS_CLASSID)
|
|
{
|
|
__mfem_ts_ctx *ts_ctx = (__mfem_ts_ctx *)private_ctx;
|
|
delete ts_ctx->work;
|
|
delete ts_ctx->work2;
|
|
}
|
|
ierr = PetscFree(private_ctx); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
}
|
|
|
|
// PetscBCHandler methods
|
|
|
|
PetscBCHandler::PetscBCHandler(Array<int>& ess_tdof_list,
|
|
enum PetscBCHandler::Type _type)
|
|
: bctype(_type), setup(false), eval_t(0.0),
|
|
eval_t_cached(std::numeric_limits<double>::min())
|
|
{
|
|
SetTDofs(ess_tdof_list);
|
|
}
|
|
|
|
void PetscBCHandler::SetTDofs(Array<int>& list)
|
|
{
|
|
ess_tdof_list.SetSize(list.Size());
|
|
ess_tdof_list.Assign(list);
|
|
setup = false;
|
|
}
|
|
|
|
void PetscBCHandler::SetUp(PetscInt n)
|
|
{
|
|
if (setup) { return; }
|
|
if (bctype == CONSTANT)
|
|
{
|
|
eval_g.SetSize(n);
|
|
this->Eval(eval_t,eval_g);
|
|
eval_t_cached = eval_t;
|
|
}
|
|
else if (bctype == TIME_DEPENDENT)
|
|
{
|
|
eval_g.SetSize(n);
|
|
}
|
|
setup = true;
|
|
}
|
|
|
|
void PetscBCHandler::ApplyBC(const Vector &x, Vector &y)
|
|
{
|
|
(*this).SetUp(x.Size());
|
|
y = x;
|
|
if (bctype == ZERO)
|
|
{
|
|
for (int i = 0; i < ess_tdof_list.Size(); ++i)
|
|
{
|
|
y[ess_tdof_list[i]] = 0.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bctype != CONSTANT && eval_t != eval_t_cached)
|
|
{
|
|
Eval(eval_t,eval_g);
|
|
eval_t_cached = eval_t;
|
|
}
|
|
for (int i = 0; i < ess_tdof_list.Size(); ++i)
|
|
{
|
|
y[ess_tdof_list[i]] = eval_g[ess_tdof_list[i]];
|
|
}
|
|
}
|
|
}
|
|
|
|
void PetscBCHandler::ApplyBC(Vector &x)
|
|
{
|
|
(*this).SetUp(x.Size());
|
|
if (bctype == ZERO)
|
|
{
|
|
for (int i = 0; i < ess_tdof_list.Size(); ++i)
|
|
{
|
|
x[ess_tdof_list[i]] = 0.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bctype != CONSTANT && eval_t != eval_t_cached)
|
|
{
|
|
Eval(eval_t,eval_g);
|
|
eval_t_cached = eval_t;
|
|
}
|
|
for (int i = 0; i < ess_tdof_list.Size(); ++i)
|
|
{
|
|
x[ess_tdof_list[i]] = eval_g[ess_tdof_list[i]];
|
|
}
|
|
}
|
|
}
|
|
|
|
void PetscBCHandler::FixResidualBC(const Vector& x, Vector& y)
|
|
{
|
|
(*this).SetUp(x.Size());
|
|
if (bctype == ZERO)
|
|
{
|
|
for (int i = 0; i < ess_tdof_list.Size(); ++i)
|
|
{
|
|
y[ess_tdof_list[i]] = x[ess_tdof_list[i]];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < ess_tdof_list.Size(); ++i)
|
|
{
|
|
y[ess_tdof_list[i]] = x[ess_tdof_list[i]] - eval_g[ess_tdof_list[i]];
|
|
}
|
|
}
|
|
}
|
|
|
|
void PetscBCHandler::Zero(Vector &x)
|
|
{
|
|
(*this).SetUp(x.Size());
|
|
for (int i = 0; i < ess_tdof_list.Size(); ++i)
|
|
{
|
|
x[ess_tdof_list[i]] = 0.0;
|
|
}
|
|
}
|
|
|
|
void PetscBCHandler::ZeroBC(const Vector &x, Vector &y)
|
|
{
|
|
(*this).SetUp(x.Size());
|
|
y = x;
|
|
for (int i = 0; i < ess_tdof_list.Size(); ++i)
|
|
{
|
|
y[ess_tdof_list[i]] = 0.0;
|
|
}
|
|
}
|
|
|
|
// PetscLinearSolver methods
|
|
|
|
PetscLinearSolver::PetscLinearSolver(MPI_Comm comm, const std::string &prefix,
|
|
bool wrapin)
|
|
: PetscSolver(), Solver(), wrap(wrapin)
|
|
{
|
|
KSP ksp;
|
|
ierr = KSPCreate(comm,&ksp); CCHKERRQ(comm,ierr);
|
|
obj = (PetscObject)ksp;
|
|
ierr = PetscObjectGetClassId(obj,&cid); PCHKERRQ(obj,ierr);
|
|
ierr = KSPSetOptionsPrefix(ksp, prefix.c_str()); PCHKERRQ(ksp, ierr);
|
|
}
|
|
|
|
PetscLinearSolver::PetscLinearSolver(const PetscParMatrix &A,
|
|
const std::string &prefix)
|
|
: PetscSolver(), Solver(), wrap(false)
|
|
{
|
|
KSP ksp;
|
|
ierr = KSPCreate(A.GetComm(),&ksp); CCHKERRQ(A.GetComm(),ierr);
|
|
obj = (PetscObject)ksp;
|
|
ierr = PetscObjectGetClassId(obj,&cid); PCHKERRQ(obj,ierr);
|
|
ierr = KSPSetOptionsPrefix(ksp, prefix.c_str()); PCHKERRQ(ksp, ierr);
|
|
SetOperator(A);
|
|
}
|
|
|
|
PetscLinearSolver::PetscLinearSolver(const HypreParMatrix &A, bool wrapin,
|
|
const std::string &prefix)
|
|
: PetscSolver(), Solver(), wrap(wrapin)
|
|
{
|
|
KSP ksp;
|
|
ierr = KSPCreate(A.GetComm(),&ksp); CCHKERRQ(A.GetComm(),ierr);
|
|
obj = (PetscObject)ksp;
|
|
ierr = PetscObjectGetClassId(obj, &cid); PCHKERRQ(obj, ierr);
|
|
ierr = KSPSetOptionsPrefix(ksp, prefix.c_str()); PCHKERRQ(ksp, ierr);
|
|
SetOperator(A);
|
|
}
|
|
|
|
void PetscLinearSolver::SetOperator(const Operator &op)
|
|
{
|
|
const HypreParMatrix *hA = dynamic_cast<const HypreParMatrix *>(&op);
|
|
PetscParMatrix *pA = const_cast<PetscParMatrix *>
|
|
(dynamic_cast<const PetscParMatrix *>(&op));
|
|
const Operator *oA = dynamic_cast<const Operator *>(&op);
|
|
|
|
// update base classes: Operator, Solver, PetscLinearSolver
|
|
bool delete_pA = false;
|
|
if (!pA)
|
|
{
|
|
if (hA)
|
|
{
|
|
// Create MATSHELL object or convert into a format suitable to construct preconditioners
|
|
pA = new PetscParMatrix(hA, wrap ? PETSC_MATSHELL : PETSC_MATAIJ);
|
|
delete_pA = true;
|
|
}
|
|
else if (oA) // fallback to general operator
|
|
{
|
|
// Create MATSHELL or MATNEST (if oA is a BlockOperator) object
|
|
// If oA is a BlockOperator, Operator::Type is relevant to the subblocks
|
|
pA = new PetscParMatrix(PetscObjectComm(obj),oA,
|
|
wrap ? PETSC_MATSHELL : PETSC_MATAIJ);
|
|
delete_pA = true;
|
|
}
|
|
}
|
|
MFEM_VERIFY(pA, "Unsupported operation!");
|
|
|
|
// Set operators into PETSc KSP
|
|
KSP ksp = (KSP)obj;
|
|
Mat A = pA->A;
|
|
if (operatorset)
|
|
{
|
|
Mat C;
|
|
PetscInt nheight,nwidth,oheight,owidth;
|
|
|
|
ierr = KSPGetOperators(ksp,&C,NULL); PCHKERRQ(ksp,ierr);
|
|
ierr = MatGetSize(A,&nheight,&nwidth); PCHKERRQ(A,ierr);
|
|
ierr = MatGetSize(C,&oheight,&owidth); PCHKERRQ(A,ierr);
|
|
if (nheight != oheight || nwidth != owidth)
|
|
{
|
|
// reinit without destroying the KSP
|
|
// communicator remains the same
|
|
ierr = KSPReset(ksp); PCHKERRQ(ksp,ierr);
|
|
delete X;
|
|
delete B;
|
|
X = B = NULL;
|
|
wrap = false;
|
|
}
|
|
}
|
|
ierr = KSPSetOperators(ksp,A,A); PCHKERRQ(ksp,ierr);
|
|
|
|
// Update PetscSolver
|
|
operatorset = true;
|
|
|
|
// Update the Operator fields.
|
|
height = pA->Height();
|
|
width = pA->Width();
|
|
|
|
if (delete_pA) { delete pA; }
|
|
}
|
|
|
|
void PetscLinearSolver::SetOperator(const Operator &op, const Operator &pop)
|
|
{
|
|
const HypreParMatrix *hA = dynamic_cast<const HypreParMatrix *>(&op);
|
|
PetscParMatrix *pA = const_cast<PetscParMatrix *>
|
|
(dynamic_cast<const PetscParMatrix *>(&op));
|
|
const Operator *oA = dynamic_cast<const Operator *>(&op);
|
|
|
|
PetscParMatrix *ppA = const_cast<PetscParMatrix *>
|
|
(dynamic_cast<const PetscParMatrix *>(&pop));
|
|
const Operator *poA = dynamic_cast<const Operator *>(&pop);
|
|
|
|
// Convert Operator for linear system
|
|
bool delete_pA = false;
|
|
if (!pA)
|
|
{
|
|
if (hA)
|
|
{
|
|
// Create MATSHELL object or convert into a format suitable to construct preconditioners
|
|
pA = new PetscParMatrix(hA, wrap ? PETSC_MATSHELL : PETSC_MATAIJ);
|
|
delete_pA = true;
|
|
}
|
|
else if (oA) // fallback to general operator
|
|
{
|
|
// Create MATSHELL or MATNEST (if oA is a BlockOperator) object
|
|
// If oA is a BlockOperator, Operator::Type is relevant to the subblocks
|
|
pA = new PetscParMatrix(PetscObjectComm(obj),oA,
|
|
wrap ? PETSC_MATSHELL : PETSC_MATAIJ);
|
|
delete_pA = true;
|
|
}
|
|
}
|
|
MFEM_VERIFY(pA, "Unsupported operation!");
|
|
|
|
// Convert Operator to be preconditioned
|
|
bool delete_ppA = false;
|
|
if (!ppA)
|
|
{
|
|
if (oA == poA && !wrap) // Same operator, already converted
|
|
{
|
|
ppA = pA;
|
|
}
|
|
else
|
|
{
|
|
ppA = new PetscParMatrix(PetscObjectComm(obj), poA, PETSC_MATAIJ);
|
|
delete_ppA = true;
|
|
}
|
|
}
|
|
MFEM_VERIFY(ppA, "Unsupported operation!");
|
|
|
|
// Set operators into PETSc KSP
|
|
KSP ksp = (KSP)obj;
|
|
Mat A = pA->A;
|
|
Mat P = ppA->A;
|
|
if (operatorset)
|
|
{
|
|
Mat C;
|
|
PetscInt nheight,nwidth,oheight,owidth;
|
|
|
|
ierr = KSPGetOperators(ksp,&C,NULL); PCHKERRQ(ksp,ierr);
|
|
ierr = MatGetSize(A,&nheight,&nwidth); PCHKERRQ(A,ierr);
|
|
ierr = MatGetSize(C,&oheight,&owidth); PCHKERRQ(A,ierr);
|
|
if (nheight != oheight || nwidth != owidth)
|
|
{
|
|
// reinit without destroying the KSP
|
|
// communicator remains the same
|
|
ierr = KSPReset(ksp); PCHKERRQ(ksp,ierr);
|
|
delete X;
|
|
delete B;
|
|
X = B = NULL;
|
|
wrap = false;
|
|
}
|
|
}
|
|
ierr = KSPSetOperators(ksp,A,P); PCHKERRQ(ksp,ierr);
|
|
|
|
// Update PetscSolver
|
|
operatorset = true;
|
|
|
|
// Update the Operator fields.
|
|
height = pA->Height();
|
|
width = pA->Width();
|
|
|
|
if (delete_pA) { delete pA; }
|
|
if (delete_ppA) { delete ppA; }
|
|
}
|
|
|
|
void PetscLinearSolver::SetPreconditioner(Solver &precond)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
|
|
// Preserve Amat if already set
|
|
Mat A = NULL;
|
|
PetscBool amat;
|
|
ierr = KSPGetOperatorsSet(ksp,&amat,NULL); PCHKERRQ(ksp,ierr);
|
|
if (amat)
|
|
{
|
|
ierr = KSPGetOperators(ksp,&A,NULL); PCHKERRQ(ksp,ierr);
|
|
ierr = PetscObjectReference((PetscObject)A); PCHKERRQ(ksp,ierr);
|
|
}
|
|
PetscPreconditioner *ppc = dynamic_cast<PetscPreconditioner *>(&precond);
|
|
if (ppc)
|
|
{
|
|
ierr = KSPSetPC(ksp,*ppc); PCHKERRQ(ksp,ierr);
|
|
}
|
|
else
|
|
{
|
|
// wrap the Solver action
|
|
// Solver is assumed to be already setup
|
|
// ownership of precond is not tranferred,
|
|
// consistently with other MFEM's linear solvers
|
|
PC pc;
|
|
ierr = KSPGetPC(ksp,&pc); PCHKERRQ(ksp,ierr);
|
|
ierr = MakeShellPC(pc,precond,false); PCHKERRQ(ksp,ierr);
|
|
}
|
|
if (A)
|
|
{
|
|
Mat P;
|
|
|
|
ierr = KSPGetOperators(ksp,NULL,&P); PCHKERRQ(ksp,ierr);
|
|
ierr = PetscObjectReference((PetscObject)P); PCHKERRQ(ksp,ierr);
|
|
ierr = KSPSetOperators(ksp,A,P); PCHKERRQ(ksp,ierr);
|
|
ierr = MatDestroy(&A); PCHKERRQ(ksp,ierr);
|
|
ierr = MatDestroy(&P); PCHKERRQ(ksp,ierr);
|
|
}
|
|
}
|
|
|
|
void PetscLinearSolver::MultKernel(const Vector &b, Vector &x, bool trans) const
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
|
|
if (!B || !X)
|
|
{
|
|
Mat pA = NULL;
|
|
ierr = KSPGetOperators(ksp, &pA, NULL); PCHKERRQ(obj, ierr);
|
|
if (!B)
|
|
{
|
|
PetscParMatrix A = PetscParMatrix(pA, true);
|
|
B = new PetscParVector(A, true, false);
|
|
}
|
|
if (!X)
|
|
{
|
|
PetscParMatrix A = PetscParMatrix(pA, true);
|
|
X = new PetscParVector(A, false, false);
|
|
}
|
|
}
|
|
B->PlaceArray(b.GetData());
|
|
X->PlaceArray(x.GetData());
|
|
|
|
Customize();
|
|
|
|
ierr = KSPSetInitialGuessNonzero(ksp, (PetscBool)iterative_mode);
|
|
PCHKERRQ(ksp, ierr);
|
|
|
|
// Solve the system.
|
|
if (trans)
|
|
{
|
|
ierr = KSPSolveTranspose(ksp, B->x, X->x); PCHKERRQ(ksp,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = KSPSolve(ksp, B->x, X->x); PCHKERRQ(ksp,ierr);
|
|
}
|
|
B->ResetArray();
|
|
X->ResetArray();
|
|
}
|
|
|
|
void PetscLinearSolver::Mult(const Vector &b, Vector &x) const
|
|
{
|
|
(*this).MultKernel(b,x,false);
|
|
}
|
|
|
|
void PetscLinearSolver::MultTranspose(const Vector &b, Vector &x) const
|
|
{
|
|
(*this).MultKernel(b,x,true);
|
|
}
|
|
|
|
PetscLinearSolver::~PetscLinearSolver()
|
|
{
|
|
MPI_Comm comm;
|
|
KSP ksp = (KSP)obj;
|
|
ierr = PetscObjectGetComm((PetscObject)ksp,&comm); PCHKERRQ(ksp,ierr);
|
|
ierr = KSPDestroy(&ksp); CCHKERRQ(comm,ierr);
|
|
}
|
|
|
|
// PetscPCGSolver methods
|
|
|
|
PetscPCGSolver::PetscPCGSolver(MPI_Comm comm, const std::string &prefix)
|
|
: PetscLinearSolver(comm,prefix)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
ierr = KSPSetType(ksp,KSPCG); PCHKERRQ(ksp,ierr);
|
|
// this is to obtain a textbook PCG
|
|
ierr = KSPSetNormType(ksp,KSP_NORM_NATURAL); PCHKERRQ(ksp,ierr);
|
|
}
|
|
|
|
PetscPCGSolver::PetscPCGSolver(PetscParMatrix& A, const std::string &prefix)
|
|
: PetscLinearSolver(A,prefix)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
ierr = KSPSetType(ksp,KSPCG); PCHKERRQ(ksp,ierr);
|
|
// this is to obtain a textbook PCG
|
|
ierr = KSPSetNormType(ksp,KSP_NORM_NATURAL); PCHKERRQ(ksp,ierr);
|
|
}
|
|
|
|
PetscPCGSolver::PetscPCGSolver(HypreParMatrix& A, bool wrap,
|
|
const std::string &prefix)
|
|
: PetscLinearSolver(A,wrap,prefix)
|
|
{
|
|
KSP ksp = (KSP)obj;
|
|
ierr = KSPSetType(ksp,KSPCG); PCHKERRQ(ksp,ierr);
|
|
// this is to obtain a textbook PCG
|
|
ierr = KSPSetNormType(ksp,KSP_NORM_NATURAL); PCHKERRQ(ksp,ierr);
|
|
}
|
|
|
|
// PetscPreconditioner methods
|
|
|
|
PetscPreconditioner::PetscPreconditioner(MPI_Comm comm,
|
|
const std::string &prefix)
|
|
: PetscSolver(), Solver()
|
|
{
|
|
PC pc;
|
|
ierr = PCCreate(comm,&pc); CCHKERRQ(comm,ierr);
|
|
obj = (PetscObject)pc;
|
|
ierr = PetscObjectGetClassId(obj,&cid); PCHKERRQ(obj,ierr);
|
|
ierr = PCSetOptionsPrefix(pc, prefix.c_str()); PCHKERRQ(pc, ierr);
|
|
}
|
|
|
|
PetscPreconditioner::PetscPreconditioner(PetscParMatrix &A,
|
|
const string &prefix)
|
|
: PetscSolver(), Solver()
|
|
{
|
|
PC pc;
|
|
ierr = PCCreate(A.GetComm(),&pc); CCHKERRQ(A.GetComm(),ierr);
|
|
obj = (PetscObject)pc;
|
|
ierr = PetscObjectGetClassId(obj,&cid); PCHKERRQ(obj,ierr);
|
|
ierr = PCSetOptionsPrefix(pc, prefix.c_str()); PCHKERRQ(pc, ierr);
|
|
SetOperator(A);
|
|
}
|
|
|
|
PetscPreconditioner::PetscPreconditioner(MPI_Comm comm, Operator &op,
|
|
const string &prefix)
|
|
: PetscSolver(), Solver()
|
|
{
|
|
PC pc;
|
|
ierr = PCCreate(comm,&pc); CCHKERRQ(comm,ierr);
|
|
obj = (PetscObject)pc;
|
|
ierr = PetscObjectGetClassId(obj,&cid); PCHKERRQ(obj,ierr);
|
|
ierr = PCSetOptionsPrefix(pc, prefix.c_str()); PCHKERRQ(pc, ierr);
|
|
SetOperator(op);
|
|
}
|
|
|
|
void PetscPreconditioner::SetOperator(const Operator &op)
|
|
{
|
|
bool delete_pA = false;
|
|
PetscParMatrix *pA = const_cast<PetscParMatrix *>
|
|
(dynamic_cast<const PetscParMatrix *>(&op));
|
|
|
|
if (!pA)
|
|
{
|
|
const Operator *cop = dynamic_cast<const Operator *>(&op);
|
|
pA = new PetscParMatrix(PetscObjectComm(obj),cop,PETSC_MATAIJ);
|
|
delete_pA = true;
|
|
}
|
|
|
|
// Set operators into PETSc PC
|
|
PC pc = (PC)obj;
|
|
Mat A = pA->A;
|
|
if (operatorset)
|
|
{
|
|
Mat C;
|
|
PetscInt nheight,nwidth,oheight,owidth;
|
|
|
|
ierr = PCGetOperators(pc,&C,NULL); PCHKERRQ(pc,ierr);
|
|
ierr = MatGetSize(A,&nheight,&nwidth); PCHKERRQ(A,ierr);
|
|
ierr = MatGetSize(C,&oheight,&owidth); PCHKERRQ(A,ierr);
|
|
if (nheight != oheight || nwidth != owidth)
|
|
{
|
|
// reinit without destroying the PC
|
|
// communicator remains the same
|
|
ierr = PCReset(pc); PCHKERRQ(pc,ierr);
|
|
delete X;
|
|
delete B;
|
|
X = B = NULL;
|
|
}
|
|
}
|
|
ierr = PCSetOperators(pc,pA->A,pA->A); PCHKERRQ(obj,ierr);
|
|
|
|
// Update PetscSolver
|
|
operatorset = true;
|
|
|
|
// Update the Operator fields.
|
|
height = pA->Height();
|
|
width = pA->Width();
|
|
|
|
if (delete_pA) { delete pA; };
|
|
}
|
|
|
|
void PetscPreconditioner::MultKernel(const Vector &b, Vector &x,
|
|
bool trans) const
|
|
{
|
|
PC pc = (PC)obj;
|
|
|
|
if (!B || !X)
|
|
{
|
|
Mat pA = NULL;
|
|
ierr = PCGetOperators(pc, NULL, &pA); PCHKERRQ(obj, ierr);
|
|
if (!B)
|
|
{
|
|
PetscParMatrix A(pA, true);
|
|
B = new PetscParVector(A, true, false);
|
|
}
|
|
if (!X)
|
|
{
|
|
PetscParMatrix A(pA, true);
|
|
X = new PetscParVector(A, false, false);
|
|
}
|
|
}
|
|
B->PlaceArray(b.GetData());
|
|
X->PlaceArray(x.GetData());
|
|
|
|
Customize();
|
|
|
|
// Apply the preconditioner.
|
|
if (trans)
|
|
{
|
|
ierr = PCApplyTranspose(pc, B->x, X->x); PCHKERRQ(pc, ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = PCApply(pc, B->x, X->x); PCHKERRQ(pc, ierr);
|
|
}
|
|
B->ResetArray();
|
|
X->ResetArray();
|
|
}
|
|
|
|
void PetscPreconditioner::Mult(const Vector &b, Vector &x) const
|
|
{
|
|
(*this).MultKernel(b,x,false);
|
|
}
|
|
|
|
void PetscPreconditioner::MultTranspose(const Vector &b, Vector &x) const
|
|
{
|
|
(*this).MultKernel(b,x,true);
|
|
}
|
|
|
|
PetscPreconditioner::~PetscPreconditioner()
|
|
{
|
|
MPI_Comm comm;
|
|
PC pc = (PC)obj;
|
|
ierr = PetscObjectGetComm((PetscObject)pc,&comm); PCHKERRQ(pc,ierr);
|
|
ierr = PCDestroy(&pc); CCHKERRQ(comm,ierr);
|
|
}
|
|
|
|
// PetscBDDCSolver methods
|
|
|
|
// Coordinates sampling function
|
|
static void func_coords(const Vector &x, Vector &y)
|
|
{
|
|
for (int i = 0; i < x.Size(); i++) { y(i) = x(i); }
|
|
}
|
|
|
|
void PetscBDDCSolver::BDDCSolverConstructor(const PetscBDDCSolverParams &opts)
|
|
{
|
|
MPI_Comm comm = PetscObjectComm(obj);
|
|
|
|
// get PETSc object
|
|
PC pc = (PC)obj;
|
|
Mat pA;
|
|
ierr = PCGetOperators(pc,NULL,&pA); PCHKERRQ(pc,ierr);
|
|
|
|
// matrix type should be of type MATIS
|
|
PetscBool ismatis;
|
|
ierr = PetscObjectTypeCompare((PetscObject)pA,MATIS,&ismatis);
|
|
PCHKERRQ(pA,ierr);
|
|
MFEM_VERIFY(ismatis,"PetscBDDCSolver needs the matrix in unassembled format");
|
|
|
|
// Check options
|
|
ParFiniteElementSpace *fespace = opts.fespace;
|
|
if (opts.netflux && !fespace)
|
|
{
|
|
MFEM_WARNING("Don't know how to compute an auxiliary quadrature form without a ParFiniteElementSpace");
|
|
}
|
|
|
|
// Attach default near-null space to local matrices
|
|
{
|
|
MatNullSpace nnsp;
|
|
Mat lA;
|
|
ierr = MatISGetLocalMat(pA,&lA); CCHKERRQ(comm,ierr);
|
|
ierr = MatNullSpaceCreate(PetscObjectComm((PetscObject)lA),PETSC_TRUE,0,NULL,
|
|
&nnsp); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = MatSetNearNullSpace(lA,nnsp); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = MatNullSpaceDestroy(&nnsp); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
}
|
|
|
|
// set PETSc PC type to PCBDDC
|
|
ierr = PCSetType(pc,PCBDDC); PCHKERRQ(obj,ierr);
|
|
|
|
PetscInt rst,nl;
|
|
ierr = MatGetOwnershipRange(pA,&rst,NULL); PCHKERRQ(pA,ierr);
|
|
ierr = MatGetLocalSize(pA,&nl,NULL); PCHKERRQ(pA,ierr);
|
|
|
|
// index sets for fields splitting and coordinates for nodal spaces
|
|
IS *fields = NULL;
|
|
PetscInt nf = 0;
|
|
PetscInt sdim = 0;
|
|
PetscReal *coords = NULL;
|
|
if (fespace)
|
|
{
|
|
int vdim = fespace->GetVDim();
|
|
|
|
// Ideally, the block size should be set at matrix creation
|
|
// but the MFEM assembly does not allow to do so
|
|
if (fespace->GetOrdering() == Ordering::byVDIM)
|
|
{
|
|
Mat lA;
|
|
ierr = MatSetBlockSize(pA,vdim); PCHKERRQ(pA,ierr);
|
|
ierr = MatISGetLocalMat(pA,&lA); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = MatSetBlockSize(lA,vdim); PCHKERRQ(pA,ierr);
|
|
}
|
|
|
|
// fields
|
|
if (vdim > 1)
|
|
{
|
|
PetscInt st = rst, bs, inc, nlf;
|
|
nf = vdim;
|
|
nlf = nl/nf;
|
|
ierr = PetscMalloc1(nf,&fields); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
if (fespace->GetOrdering() == Ordering::byVDIM)
|
|
{
|
|
inc = 1;
|
|
bs = vdim;
|
|
}
|
|
else
|
|
{
|
|
inc = nlf;
|
|
bs = 1;
|
|
}
|
|
for (PetscInt i = 0; i < nf; i++)
|
|
{
|
|
ierr = ISCreateStride(comm,nlf,st,bs,&fields[i]); CCHKERRQ(comm,ierr);
|
|
st += inc;
|
|
}
|
|
}
|
|
|
|
// coordinates
|
|
const FiniteElementCollection *fec = fespace->FEColl();
|
|
bool h1space = dynamic_cast<const H1_FECollection*>(fec);
|
|
if (h1space)
|
|
{
|
|
ParFiniteElementSpace *fespace_coords = fespace;
|
|
|
|
sdim = fespace->GetParMesh()->SpaceDimension();
|
|
if (vdim != sdim || fespace->GetOrdering() != Ordering::byVDIM)
|
|
{
|
|
fespace_coords = new ParFiniteElementSpace(fespace->GetParMesh(),fec,sdim,
|
|
Ordering::byVDIM);
|
|
}
|
|
VectorFunctionCoefficient coeff_coords(sdim, func_coords);
|
|
ParGridFunction gf_coords(fespace_coords);
|
|
gf_coords.ProjectCoefficient(coeff_coords);
|
|
HypreParVector *hvec_coords = gf_coords.ParallelProject();
|
|
|
|
// likely elasticity -> we attach rigid-body modes as near-null space information to the local matrices
|
|
if (vdim == sdim)
|
|
{
|
|
MatNullSpace nnsp;
|
|
Mat lA;
|
|
Vec pvec_coords,lvec_coords;
|
|
ISLocalToGlobalMapping l2g;
|
|
PetscSF sf;
|
|
PetscLayout rmap;
|
|
const PetscInt *gidxs;
|
|
PetscInt nleaves;
|
|
|
|
ierr = VecCreateMPIWithArray(comm,sdim,hvec_coords->Size(),
|
|
hvec_coords->GlobalSize(),hvec_coords->GetData(),&pvec_coords);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = MatISGetLocalMat(pA,&lA); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = MatCreateVecs(lA,&lvec_coords,NULL); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = VecSetBlockSize(lvec_coords,sdim); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = MatGetLocalToGlobalMapping(pA,&l2g,NULL); CCHKERRQ(comm,ierr);
|
|
ierr = MatGetLayouts(pA,&rmap,NULL); CCHKERRQ(comm,ierr);
|
|
ierr = PetscSFCreate(comm,&sf); CCHKERRQ(comm,ierr);
|
|
ierr = ISLocalToGlobalMappingGetIndices(l2g,&gidxs); CCHKERRQ(comm,ierr);
|
|
ierr = ISLocalToGlobalMappingGetSize(l2g,&nleaves); CCHKERRQ(comm,ierr);
|
|
ierr = PetscSFSetGraphLayout(sf,rmap,nleaves,NULL,PETSC_OWN_POINTER,gidxs);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = ISLocalToGlobalMappingRestoreIndices(l2g,&gidxs); CCHKERRQ(comm,ierr);
|
|
{
|
|
const PetscScalar *garray;
|
|
PetscScalar *larray;
|
|
|
|
ierr = VecGetArrayRead(pvec_coords,&garray); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = VecGetArray(lvec_coords,&larray); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = PetscSFBcastBegin(sf,MPIU_SCALAR,garray,larray); CCHKERRQ(comm,ierr);
|
|
ierr = PetscSFBcastEnd(sf,MPIU_SCALAR,garray,larray); CCHKERRQ(comm,ierr);
|
|
ierr = VecRestoreArrayRead(pvec_coords,&garray); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = VecRestoreArray(lvec_coords,&larray); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
}
|
|
ierr = VecDestroy(&pvec_coords); CCHKERRQ(comm,ierr);
|
|
ierr = MatNullSpaceCreateRigidBody(lvec_coords,&nnsp);
|
|
CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = VecDestroy(&lvec_coords); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = MatSetNearNullSpace(lA,nnsp); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = MatNullSpaceDestroy(&nnsp); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = PetscSFDestroy(&sf); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
}
|
|
|
|
// each single dof has associated a tuple of coordinates
|
|
ierr = PetscMalloc1(nl*sdim,&coords); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
if (nf > 0)
|
|
{
|
|
PetscScalar *data = hvec_coords->GetData();
|
|
for (PetscInt i = 0; i < nf; i++)
|
|
{
|
|
const PetscInt *idxs;
|
|
PetscInt nn;
|
|
|
|
// It also handles the case of fespace not ordered by VDIM
|
|
ierr = ISGetLocalSize(fields[i],&nn); CCHKERRQ(comm,ierr);
|
|
ierr = ISGetIndices(fields[i],&idxs); CCHKERRQ(comm,ierr);
|
|
for (PetscInt j = 0; j < nn; j++)
|
|
{
|
|
PetscInt idx = idxs[j]-rst;
|
|
for (PetscInt d = 0; d < sdim; d++)
|
|
{
|
|
coords[sdim*idx+d] = data[sdim*j+d];
|
|
}
|
|
}
|
|
ierr = ISRestoreIndices(fields[i],&idxs); CCHKERRQ(comm,ierr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ierr = PetscMemcpy(coords,hvec_coords->GetData(),nl*sdim*sizeof(PetscReal));
|
|
CCHKERRQ(comm,ierr);
|
|
}
|
|
if (fespace_coords != fespace)
|
|
{
|
|
delete fespace_coords;
|
|
}
|
|
delete hvec_coords;
|
|
}
|
|
}
|
|
|
|
// index sets for boundary dofs specification (Essential = dir, Natural = neu)
|
|
IS dir = NULL, neu = NULL;
|
|
|
|
// Extract l2l matrices
|
|
Array<Mat> *l2l = NULL;
|
|
if (opts.ess_dof_local || opts.nat_dof_local)
|
|
{
|
|
PetscContainer c;
|
|
|
|
ierr = PetscObjectQuery((PetscObject)pA,"_MatIS_PtAP_l2l",(PetscObject*)&c);
|
|
MFEM_VERIFY(c,"Local-to-local PETSc container not present");
|
|
ierr = PetscContainerGetPointer(c,(void**)&l2l); PCHKERRQ(c,ierr);
|
|
}
|
|
|
|
// check information about index sets (essential dofs, fields, etc.)
|
|
#ifdef MFEM_DEBUG
|
|
{
|
|
// make sure ess/nat_dof have been collectively set
|
|
PetscBool lpr = PETSC_FALSE,pr;
|
|
if (opts.ess_dof) { lpr = PETSC_TRUE; }
|
|
ierr = MPI_Allreduce(&lpr,&pr,1,MPIU_BOOL,MPI_LOR,comm);
|
|
PCHKERRQ(pA,ierr);
|
|
MFEM_VERIFY(lpr == pr,"ess_dof should be collectively set");
|
|
lpr = PETSC_FALSE;
|
|
if (opts.nat_dof) { lpr = PETSC_TRUE; }
|
|
ierr = MPI_Allreduce(&lpr,&pr,1,MPIU_BOOL,MPI_LOR,comm);
|
|
PCHKERRQ(pA,ierr);
|
|
MFEM_VERIFY(lpr == pr,"nat_dof should be collectively set");
|
|
// make sure fields have been collectively set
|
|
PetscInt ms[2],Ms[2];
|
|
ms[0] = -nf; ms[1] = nf;
|
|
ierr = MPI_Allreduce(&ms,&Ms,2,MPIU_INT,MPI_MAX,comm);
|
|
PCHKERRQ(pA,ierr);
|
|
MFEM_VERIFY(-Ms[0] == Ms[1],
|
|
"number of fields should be the same across processes");
|
|
}
|
|
#endif
|
|
|
|
// boundary sets
|
|
if (opts.ess_dof)
|
|
{
|
|
PetscInt st = opts.ess_dof_local ? 0 : rst;
|
|
if (!opts.ess_dof_local)
|
|
{
|
|
// need to compute the boundary dofs in global ordering
|
|
ierr = Convert_Array_IS(comm,true,opts.ess_dof,st,&dir);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = PCBDDCSetDirichletBoundaries(pc,dir); PCHKERRQ(pc,ierr);
|
|
}
|
|
else
|
|
{
|
|
// need to compute a list for the marked boundary dofs in local ordering
|
|
ierr = Convert_Vmarks_IS(comm,*l2l,opts.ess_dof,st,&dir);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = PCBDDCSetDirichletBoundariesLocal(pc,dir); PCHKERRQ(pc,ierr);
|
|
}
|
|
}
|
|
if (opts.nat_dof)
|
|
{
|
|
PetscInt st = opts.nat_dof_local ? 0 : rst;
|
|
if (!opts.nat_dof_local)
|
|
{
|
|
// need to compute the boundary dofs in global ordering
|
|
ierr = Convert_Array_IS(comm,true,opts.nat_dof,st,&neu);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = PCBDDCSetNeumannBoundaries(pc,neu); PCHKERRQ(pc,ierr);
|
|
}
|
|
else
|
|
{
|
|
// need to compute a list for the marked boundary dofs in local ordering
|
|
ierr = Convert_Vmarks_IS(comm,*l2l,opts.nat_dof,st,&neu);
|
|
CCHKERRQ(comm,ierr);
|
|
ierr = PCBDDCSetNeumannBoundariesLocal(pc,neu); PCHKERRQ(pc,ierr);
|
|
}
|
|
}
|
|
|
|
// field splitting
|
|
if (fields)
|
|
{
|
|
ierr = PCBDDCSetDofsSplitting(pc,nf,fields); PCHKERRQ(pc,ierr);
|
|
}
|
|
for (PetscInt i = 0; i < nf; i++)
|
|
{
|
|
ierr = ISDestroy(&fields[i]); CCHKERRQ(comm,ierr);
|
|
}
|
|
ierr = PetscFree(fields); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
|
|
// coordinates
|
|
if (coords)
|
|
{
|
|
ierr = PCSetCoordinates(pc,sdim,nl,coords); PCHKERRQ(pc,ierr);
|
|
}
|
|
ierr = PetscFree(coords); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
|
|
// code for block size is disabled since we cannot change the matrix
|
|
// block size after it has been setup
|
|
// int bs = 1;
|
|
|
|
// Customize using the finite element space (if any)
|
|
if (fespace)
|
|
{
|
|
const FiniteElementCollection *fec = fespace->FEColl();
|
|
bool edgespace, rtspace, h1space;
|
|
bool needint = opts.netflux;
|
|
bool tracespace, rt_tracespace, edge_tracespace;
|
|
int vdim, dim, p;
|
|
PetscBool B_is_Trans = PETSC_FALSE;
|
|
|
|
ParMesh *pmesh = (ParMesh *) fespace->GetMesh();
|
|
dim = pmesh->Dimension();
|
|
vdim = fespace->GetVDim();
|
|
h1space = dynamic_cast<const H1_FECollection*>(fec);
|
|
rtspace = dynamic_cast<const RT_FECollection*>(fec);
|
|
edgespace = dynamic_cast<const ND_FECollection*>(fec);
|
|
edge_tracespace = dynamic_cast<const ND_Trace_FECollection*>(fec);
|
|
rt_tracespace = dynamic_cast<const RT_Trace_FECollection*>(fec);
|
|
tracespace = edge_tracespace || rt_tracespace;
|
|
|
|
p = 1;
|
|
if (fespace->GetNE() > 0)
|
|
{
|
|
if (!tracespace)
|
|
{
|
|
p = fespace->GetOrder(0);
|
|
}
|
|
else
|
|
{
|
|
p = fespace->GetFaceOrder(0);
|
|
if (dim == 2) { p++; }
|
|
}
|
|
}
|
|
|
|
if (edgespace) // H(curl)
|
|
{
|
|
if (dim == 2)
|
|
{
|
|
needint = true;
|
|
if (tracespace)
|
|
{
|
|
MFEM_WARNING("Tracespace case doesn't work for H(curl) and p=2,"
|
|
" not using auxiliary quadrature");
|
|
needint = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FiniteElementCollection *vfec;
|
|
if (tracespace)
|
|
{
|
|
vfec = new H1_Trace_FECollection(p,dim);
|
|
}
|
|
else
|
|
{
|
|
vfec = new H1_FECollection(p,dim);
|
|
}
|
|
ParFiniteElementSpace *vfespace = new ParFiniteElementSpace(pmesh,vfec);
|
|
ParDiscreteLinearOperator *grad;
|
|
grad = new ParDiscreteLinearOperator(vfespace,fespace);
|
|
if (tracespace)
|
|
{
|
|
grad->AddTraceFaceInterpolator(new GradientInterpolator);
|
|
}
|
|
else
|
|
{
|
|
grad->AddDomainInterpolator(new GradientInterpolator);
|
|
}
|
|
grad->Assemble();
|
|
grad->Finalize();
|
|
HypreParMatrix *hG = grad->ParallelAssemble();
|
|
PetscParMatrix *G = new PetscParMatrix(hG,PETSC_MATAIJ);
|
|
delete hG;
|
|
delete grad;
|
|
|
|
PetscBool conforming = PETSC_TRUE;
|
|
if (pmesh->Nonconforming()) { conforming = PETSC_FALSE; }
|
|
ierr = PCBDDCSetDiscreteGradient(pc,*G,p,0,PETSC_TRUE,conforming);
|
|
PCHKERRQ(pc,ierr);
|
|
delete vfec;
|
|
delete vfespace;
|
|
delete G;
|
|
}
|
|
}
|
|
else if (rtspace) // H(div)
|
|
{
|
|
needint = true;
|
|
if (tracespace)
|
|
{
|
|
MFEM_WARNING("Tracespace case doesn't work for H(div), not using"
|
|
" auxiliary quadrature");
|
|
needint = false;
|
|
}
|
|
}
|
|
else if (h1space) // H(grad), only for the vector case
|
|
{
|
|
if (vdim != dim) { needint = false; }
|
|
}
|
|
|
|
PetscParMatrix *B = NULL;
|
|
if (needint)
|
|
{
|
|
// Generate bilinear form in unassembled format which is used to
|
|
// compute the net-flux across subdomain boundaries for H(div) and
|
|
// Elasticity/Stokes, and the line integral \int u x n of 2D H(curl) fields
|
|
FiniteElementCollection *auxcoll;
|
|
if (tracespace) { auxcoll = new RT_Trace_FECollection(p,dim); }
|
|
else
|
|
{
|
|
if (h1space)
|
|
{
|
|
auxcoll = new H1_FECollection(std::max(p-1,1),dim);
|
|
}
|
|
else
|
|
{
|
|
auxcoll = new L2_FECollection(p,dim);
|
|
}
|
|
}
|
|
ParFiniteElementSpace *pspace = new ParFiniteElementSpace(pmesh,auxcoll);
|
|
ParMixedBilinearForm *b = new ParMixedBilinearForm(fespace,pspace);
|
|
|
|
if (edgespace)
|
|
{
|
|
if (tracespace)
|
|
{
|
|
b->AddTraceFaceIntegrator(new VectorFECurlIntegrator);
|
|
}
|
|
else
|
|
{
|
|
b->AddDomainIntegrator(new VectorFECurlIntegrator);
|
|
}
|
|
}
|
|
else if (rtspace)
|
|
{
|
|
if (tracespace)
|
|
{
|
|
b->AddTraceFaceIntegrator(new VectorFEDivergenceIntegrator);
|
|
}
|
|
else
|
|
{
|
|
b->AddDomainIntegrator(new VectorFEDivergenceIntegrator);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
b->AddDomainIntegrator(new VectorDivergenceIntegrator);
|
|
}
|
|
b->Assemble();
|
|
b->Finalize();
|
|
OperatorHandle Bh(Operator::PETSC_MATIS);
|
|
b->ParallelAssemble(Bh);
|
|
Bh.Get(B);
|
|
Bh.SetOperatorOwner(false);
|
|
|
|
if (dir) // if essential dofs are present, we need to zero the columns
|
|
{
|
|
Mat pB = *B;
|
|
ierr = MatTranspose(pB,MAT_INPLACE_MATRIX,&pB); PCHKERRQ(pA,ierr);
|
|
if (!opts.ess_dof_local)
|
|
{
|
|
ierr = MatZeroRowsIS(pB,dir,0.,NULL,NULL); PCHKERRQ(pA,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = MatZeroRowsLocalIS(pB,dir,0.,NULL,NULL); PCHKERRQ(pA,ierr);
|
|
}
|
|
B_is_Trans = PETSC_TRUE;
|
|
}
|
|
delete b;
|
|
delete pspace;
|
|
delete auxcoll;
|
|
}
|
|
|
|
if (B)
|
|
{
|
|
ierr = PCBDDCSetDivergenceMat(pc,*B,B_is_Trans,NULL); PCHKERRQ(pc,ierr);
|
|
}
|
|
delete B;
|
|
}
|
|
ierr = ISDestroy(&dir); PCHKERRQ(pc,ierr);
|
|
ierr = ISDestroy(&neu); PCHKERRQ(pc,ierr);
|
|
}
|
|
|
|
PetscBDDCSolver::PetscBDDCSolver(PetscParMatrix &A,
|
|
const PetscBDDCSolverParams &opts,
|
|
const std::string &prefix)
|
|
: PetscPreconditioner(A,prefix)
|
|
{
|
|
BDDCSolverConstructor(opts);
|
|
Customize();
|
|
}
|
|
|
|
PetscBDDCSolver::PetscBDDCSolver(MPI_Comm comm, Operator &op,
|
|
const PetscBDDCSolverParams &opts,
|
|
const std::string &prefix)
|
|
: PetscPreconditioner(comm,op,prefix)
|
|
{
|
|
BDDCSolverConstructor(opts);
|
|
Customize();
|
|
}
|
|
|
|
PetscFieldSplitSolver::PetscFieldSplitSolver(MPI_Comm comm, Operator &op,
|
|
const string &prefix)
|
|
: PetscPreconditioner(comm,op,prefix)
|
|
{
|
|
PC pc = (PC)obj;
|
|
ierr = PCSetType(pc,PCFIELDSPLIT); PCHKERRQ(pc,ierr);
|
|
|
|
Mat pA;
|
|
ierr = PCGetOperators(pc,&pA,NULL); PCHKERRQ(pc,ierr);
|
|
|
|
// Check if pA is of type MATNEST
|
|
PetscBool isnest;
|
|
ierr = PetscObjectTypeCompare((PetscObject)pA,MATNEST,&isnest);
|
|
|
|
PetscInt nr = 0;
|
|
IS *isrow = NULL;
|
|
if (isnest) // we now the fields
|
|
{
|
|
ierr = MatNestGetSize(pA,&nr,NULL); PCHKERRQ(pc,ierr);
|
|
ierr = PetscCalloc1(nr,&isrow); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
ierr = MatNestGetISs(pA,isrow,NULL); PCHKERRQ(pc,ierr);
|
|
}
|
|
|
|
// We need to customize here, before setting the index sets.
|
|
// This is because PCFieldSplitSetType customizes the function
|
|
// pointers. SubSolver options will be processed during PCApply
|
|
Customize();
|
|
|
|
for (PetscInt i=0; i<nr; i++)
|
|
{
|
|
ierr = PCFieldSplitSetIS(pc,NULL,isrow[i]); PCHKERRQ(pc,ierr);
|
|
}
|
|
ierr = PetscFree(isrow); CCHKERRQ(PETSC_COMM_SELF,ierr);
|
|
}
|
|
|
|
// PetscNonlinearSolver methods
|
|
|
|
PetscNonlinearSolver::PetscNonlinearSolver(MPI_Comm comm,
|
|
const std::string &prefix)
|
|
: PetscSolver(), Solver()
|
|
{
|
|
// Create the actual solver object
|
|
SNES snes;
|
|
ierr = SNESCreate(comm, &snes); CCHKERRQ(comm, ierr);
|
|
obj = (PetscObject)snes;
|
|
ierr = PetscObjectGetClassId(obj, &cid); PCHKERRQ(obj, ierr);
|
|
ierr = SNESSetOptionsPrefix(snes, prefix.c_str()); PCHKERRQ(snes, ierr);
|
|
|
|
// Allocate private solver context
|
|
CreatePrivateContext();
|
|
}
|
|
|
|
PetscNonlinearSolver::PetscNonlinearSolver(MPI_Comm comm, Operator &op,
|
|
const std::string &prefix)
|
|
: PetscSolver(), Solver()
|
|
{
|
|
// Create the actual solver object
|
|
SNES snes;
|
|
ierr = SNESCreate(comm, &snes); CCHKERRQ(comm, ierr);
|
|
obj = (PetscObject)snes;
|
|
ierr = PetscObjectGetClassId(obj, &cid); PCHKERRQ(obj, ierr);
|
|
ierr = SNESSetOptionsPrefix(snes, prefix.c_str()); PCHKERRQ(snes, ierr);
|
|
|
|
// Allocate private solver context
|
|
CreatePrivateContext();
|
|
|
|
SetOperator(op);
|
|
}
|
|
|
|
PetscNonlinearSolver::~PetscNonlinearSolver()
|
|
{
|
|
MPI_Comm comm;
|
|
SNES snes = (SNES)obj;
|
|
ierr = PetscObjectGetComm(obj,&comm); PCHKERRQ(obj, ierr);
|
|
ierr = SNESDestroy(&snes); CCHKERRQ(comm, ierr);
|
|
}
|
|
|
|
void PetscNonlinearSolver::SetOperator(const Operator &op)
|
|
{
|
|
SNES snes = (SNES)obj;
|
|
|
|
if (operatorset)
|
|
{
|
|
PetscBool ls,gs;
|
|
void *fctx,*jctx;
|
|
|
|
ierr = SNESGetFunction(snes, NULL, NULL, &fctx);
|
|
PCHKERRQ(snes, ierr);
|
|
ierr = SNESGetJacobian(snes, NULL, NULL, NULL, &jctx);
|
|
PCHKERRQ(snes, ierr);
|
|
|
|
ls = (PetscBool)(height == op.Height() && width == op.Width() &&
|
|
(void*)&op == fctx &&
|
|
(void*)&op == jctx);
|
|
ierr = MPI_Allreduce(&ls,&gs,1,MPIU_BOOL,MPI_LAND,
|
|
PetscObjectComm((PetscObject)snes));
|
|
PCHKERRQ(snes,ierr);
|
|
if (!gs)
|
|
{
|
|
ierr = SNESReset(snes); PCHKERRQ(snes,ierr);
|
|
delete X;
|
|
delete B;
|
|
X = B = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* PETSc sets the linesearch type to basic (i.e. no linesearch) if not
|
|
yet set. We default to backtracking */
|
|
SNESLineSearch ls;
|
|
ierr = SNESGetLineSearch(snes, &ls); PCHKERRQ(snes,ierr);
|
|
ierr = SNESLineSearchSetType(ls, SNESLINESEARCHBT); PCHKERRQ(snes,ierr);
|
|
}
|
|
|
|
__mfem_snes_ctx *snes_ctx = (__mfem_snes_ctx*)private_ctx;
|
|
snes_ctx->op = (mfem::Operator*)&op;
|
|
ierr = SNESSetFunction(snes, NULL, __mfem_snes_function, (void *)snes_ctx);
|
|
PCHKERRQ(snes, ierr);
|
|
ierr = SNESSetJacobian(snes, NULL, NULL, __mfem_snes_jacobian,
|
|
(void *)snes_ctx);
|
|
PCHKERRQ(snes, ierr);
|
|
|
|
// Update PetscSolver
|
|
operatorset = true;
|
|
|
|
// Update the Operator fields.
|
|
height = op.Height();
|
|
width = op.Width();
|
|
}
|
|
|
|
void PetscNonlinearSolver::SetJacobianType(Operator::Type jacType)
|
|
{
|
|
__mfem_snes_ctx *snes_ctx = (__mfem_snes_ctx*)private_ctx;
|
|
snes_ctx->jacType = jacType;
|
|
}
|
|
|
|
void PetscNonlinearSolver::SetObjective(void (*objfn)(Operator *,const Vector&,
|
|
double*))
|
|
{
|
|
__mfem_snes_ctx *snes_ctx = (__mfem_snes_ctx*)private_ctx;
|
|
snes_ctx->objective = objfn;
|
|
|
|
SNES snes = (SNES)obj;
|
|
ierr = SNESSetObjective(snes, __mfem_snes_objective, (void *)snes_ctx);
|
|
PCHKERRQ(snes, ierr);
|
|
}
|
|
|
|
void PetscNonlinearSolver::SetPostCheck(void (*post)(Operator *,const Vector&,
|
|
Vector&, Vector&,
|
|
bool&, bool&))
|
|
{
|
|
__mfem_snes_ctx *snes_ctx = (__mfem_snes_ctx*)private_ctx;
|
|
snes_ctx->postcheck = post;
|
|
|
|
SNES snes = (SNES)obj;
|
|
SNESLineSearch ls;
|
|
ierr = SNESGetLineSearch(snes, &ls); PCHKERRQ(snes,ierr);
|
|
ierr = SNESLineSearchSetPostCheck(ls, __mfem_snes_postcheck, (void *)snes_ctx);
|
|
PCHKERRQ(ls, ierr);
|
|
}
|
|
|
|
void PetscNonlinearSolver::SetUpdate(void (*update)(Operator *,int,
|
|
const mfem::Vector&,
|
|
const mfem::Vector&,
|
|
const mfem::Vector&,
|
|
const mfem::Vector&))
|
|
{
|
|
__mfem_snes_ctx *snes_ctx = (__mfem_snes_ctx*)private_ctx;
|
|
snes_ctx->update = update;
|
|
|
|
SNES snes = (SNES)obj;
|
|
ierr = SNESSetUpdate(snes, __mfem_snes_update); PCHKERRQ(snes, ierr);
|
|
}
|
|
|
|
void PetscNonlinearSolver::Mult(const Vector &b, Vector &x) const
|
|
{
|
|
SNES snes = (SNES)obj;
|
|
|
|
bool b_nonempty = b.Size();
|
|
if (!B) { B = new PetscParVector(PetscObjectComm(obj), *this, true); }
|
|
if (!X) { X = new PetscParVector(PetscObjectComm(obj), *this, false, false); }
|
|
X->PlaceArray(x.GetData());
|
|
if (b_nonempty) { B->PlaceArray(b.GetData()); }
|
|
else { *B = 0.0; }
|
|
|
|
Customize();
|
|
|
|
if (!iterative_mode) { *X = 0.; }
|
|
|
|
// Solve the system.
|
|
ierr = SNESSolve(snes, B->x, X->x); PCHKERRQ(snes, ierr);
|
|
X->ResetArray();
|
|
if (b_nonempty) { B->ResetArray(); }
|
|
}
|
|
|
|
// PetscODESolver methods
|
|
|
|
PetscODESolver::PetscODESolver(MPI_Comm comm, const string &prefix)
|
|
: PetscSolver(), ODESolver()
|
|
{
|
|
// Create the actual solver object
|
|
TS ts;
|
|
ierr = TSCreate(comm,&ts); CCHKERRQ(comm,ierr);
|
|
obj = (PetscObject)ts;
|
|
ierr = PetscObjectGetClassId(obj,&cid); PCHKERRQ(obj,ierr);
|
|
ierr = TSSetOptionsPrefix(ts, prefix.c_str()); PCHKERRQ(ts, ierr);
|
|
|
|
// Allocate private solver context
|
|
CreatePrivateContext();
|
|
|
|
// Default options, to comply with the current interface to ODESolver.
|
|
ierr = TSSetMaxSteps(ts,PETSC_MAX_INT-1);
|
|
PCHKERRQ(ts,ierr);
|
|
ierr = TSSetExactFinalTime(ts,TS_EXACTFINALTIME_STEPOVER);
|
|
PCHKERRQ(ts,ierr);
|
|
TSAdapt tsad;
|
|
ierr = TSGetAdapt(ts,&tsad);
|
|
PCHKERRQ(ts,ierr);
|
|
ierr = TSAdaptSetType(tsad,TSADAPTNONE);
|
|
PCHKERRQ(ts,ierr);
|
|
}
|
|
|
|
PetscODESolver::~PetscODESolver()
|
|
{
|
|
MPI_Comm comm;
|
|
TS ts = (TS)obj;
|
|
ierr = PetscObjectGetComm(obj,&comm); PCHKERRQ(obj,ierr);
|
|
ierr = TSDestroy(&ts); CCHKERRQ(comm,ierr);
|
|
}
|
|
|
|
void PetscODESolver::Init(TimeDependentOperator &f_,
|
|
enum PetscODESolver::Type type)
|
|
{
|
|
TS ts = (TS)obj;
|
|
|
|
__mfem_ts_ctx *ts_ctx = (__mfem_ts_ctx*)private_ctx;
|
|
if (operatorset)
|
|
{
|
|
ierr = TSReset(ts); PCHKERRQ(ts,ierr);
|
|
delete X;
|
|
X = NULL;
|
|
ts_ctx->cached_shift = std::numeric_limits<PetscReal>::min();
|
|
ts_ctx->cached_ijacstate = -1;
|
|
ts_ctx->cached_rhsjacstate = -1;
|
|
ts_ctx->cached_splits_xstate = -1;
|
|
ts_ctx->cached_splits_xdotstate = -1;
|
|
}
|
|
f = &f_;
|
|
|
|
// Set functions in TS
|
|
ts_ctx->op = &f_;
|
|
if (f_.isImplicit())
|
|
{
|
|
ierr = TSSetIFunction(ts, NULL, __mfem_ts_ifunction, (void *)ts_ctx);
|
|
PCHKERRQ(ts, ierr);
|
|
ierr = TSSetIJacobian(ts, NULL, NULL, __mfem_ts_ijacobian, (void *)ts_ctx);
|
|
PCHKERRQ(ts, ierr);
|
|
ierr = TSSetEquationType(ts, TS_EQ_IMPLICIT);
|
|
PCHKERRQ(ts, ierr);
|
|
}
|
|
if (!f_.isHomogeneous())
|
|
{
|
|
if (!f_.isImplicit())
|
|
{
|
|
ierr = TSSetEquationType(ts, TS_EQ_EXPLICIT);
|
|
PCHKERRQ(ts, ierr);
|
|
}
|
|
ierr = TSSetRHSFunction(ts, NULL, __mfem_ts_rhsfunction, (void *)ts_ctx);
|
|
PCHKERRQ(ts, ierr);
|
|
ierr = TSSetRHSJacobian(ts, NULL, NULL, __mfem_ts_rhsjacobian, (void *)ts_ctx);
|
|
PCHKERRQ(ts, ierr);
|
|
}
|
|
operatorset = true;
|
|
|
|
SetType(type);
|
|
|
|
// Set solution vector
|
|
PetscParVector X(PetscObjectComm(obj),*f,false,true);
|
|
ierr = TSSetSolution(ts,X); PCHKERRQ(ts,ierr);
|
|
|
|
// Compose special purpose function for PDE-constrained optimization
|
|
PetscBool use = PETSC_TRUE;
|
|
ierr = PetscOptionsGetBool(NULL,NULL,"-mfem_use_splitjac",&use,NULL);
|
|
if (use && f_.isImplicit())
|
|
{
|
|
ierr = PetscObjectComposeFunction((PetscObject)ts,"TSComputeSplitJacobians_C",
|
|
__mfem_ts_computesplits);
|
|
PCHKERRQ(ts,ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = PetscObjectComposeFunction((PetscObject)ts,"TSComputeSplitJacobians_C",
|
|
NULL);
|
|
PCHKERRQ(ts,ierr);
|
|
}
|
|
}
|
|
|
|
void PetscODESolver::SetJacobianType(Operator::Type jacType)
|
|
{
|
|
__mfem_ts_ctx *ts_ctx = (__mfem_ts_ctx*)private_ctx;
|
|
ts_ctx->jacType = jacType;
|
|
}
|
|
|
|
PetscODESolver::Type PetscODESolver::GetType() const
|
|
{
|
|
__mfem_ts_ctx *ts_ctx = (__mfem_ts_ctx*)private_ctx;
|
|
return ts_ctx->type;
|
|
}
|
|
|
|
void PetscODESolver::SetType(PetscODESolver::Type type)
|
|
{
|
|
__mfem_ts_ctx *ts_ctx = (__mfem_ts_ctx*)private_ctx;
|
|
|
|
TS ts = (TS)obj;
|
|
ts_ctx->type = type;
|
|
if (type == ODE_SOLVER_LINEAR)
|
|
{
|
|
ierr = TSSetProblemType(ts, TS_LINEAR);
|
|
PCHKERRQ(ts, ierr);
|
|
}
|
|
else
|
|
{
|
|
ierr = TSSetProblemType(ts, TS_NONLINEAR);
|
|
PCHKERRQ(ts, ierr);
|
|
}
|
|
}
|
|
|
|
void PetscODESolver::Step(Vector &x, double &t, double &dt)
|
|
{
|
|
// Pass the parameters to PETSc.
|
|
TS ts = (TS)obj;
|
|
ierr = TSSetTime(ts, t); PCHKERRQ(ts, ierr);
|
|
ierr = TSSetTimeStep(ts, dt); PCHKERRQ(ts, ierr);
|
|
|
|
if (!X) { X = new PetscParVector(PetscObjectComm(obj), *f, false, false); }
|
|
X->PlaceArray(x.GetData());
|
|
|
|
Customize();
|
|
|
|
// Take the step.
|
|
ierr = TSSetSolution(ts, *X); PCHKERRQ(ts, ierr);
|
|
ierr = TSStep(ts); PCHKERRQ(ts, ierr);
|
|
X->ResetArray();
|
|
|
|
// Get back current time and the time step used to caller.
|
|
// We cannot use TSGetTimeStep() as it returns the next candidate step
|
|
PetscReal pt;
|
|
ierr = TSGetTime(ts,&pt); PCHKERRQ(ts,ierr);
|
|
dt = pt - (PetscReal)t;
|
|
t = pt;
|
|
}
|
|
|
|
void PetscODESolver::Run(Vector &x, double &t, double &dt, double t_final)
|
|
{
|
|
// Give the parameters to PETSc.
|
|
TS ts = (TS)obj;
|
|
ierr = TSSetTime(ts, t); PCHKERRQ(ts, ierr);
|
|
ierr = TSSetTimeStep(ts, dt); PCHKERRQ(ts, ierr);
|
|
ierr = TSSetMaxTime(ts, t_final); PCHKERRQ(ts, ierr);
|
|
ierr = TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP);
|
|
PCHKERRQ(ts, ierr);
|
|
|
|
if (!X) { X = new PetscParVector(PetscObjectComm(obj), *f, false, false); }
|
|
X->PlaceArray(x.GetData());
|
|
|
|
Customize();
|
|
|
|
// Reset Jacobian caching since the user may have changed
|
|
// the parameters of the solver
|
|
// We don't do this in the Step method because two consecutive
|
|
// Step() calls are done with the same operator
|
|
__mfem_ts_ctx *ts_ctx = (__mfem_ts_ctx*)private_ctx;
|
|
ts_ctx->cached_shift = std::numeric_limits<PetscReal>::min();
|
|
ts_ctx->cached_ijacstate = -1;
|
|
ts_ctx->cached_rhsjacstate = -1;
|
|
ts_ctx->cached_splits_xstate = -1;
|
|
ts_ctx->cached_splits_xdotstate = -1;
|
|
|
|
// Take the steps.
|
|
ierr = TSSolve(ts, X->x); PCHKERRQ(ts, ierr);
|
|
X->ResetArray();
|
|
|
|
// Get back final time and time step to caller.
|
|
PetscReal pt;
|
|
ierr = TSGetTime(ts, &pt); PCHKERRQ(ts,ierr);
|
|
t = pt;
|
|
ierr = TSGetTimeStep(ts,&pt); PCHKERRQ(ts,ierr);
|
|
dt = pt;
|
|
}
|
|
|
|
} // namespace mfem
|
|
|
|
#include "petsc/private/petscimpl.h"
|
|
#include "petsc/private/matimpl.h"
|
|
|
|
// auxiliary functions
|
|
static PetscErrorCode __mfem_ts_monitor(TS ts, PetscInt it, PetscReal t, Vec x,
|
|
void* ctx)
|
|
{
|
|
__mfem_monitor_ctx *monctx = (__mfem_monitor_ctx*)ctx;
|
|
|
|
PetscFunctionBeginUser;
|
|
if (!monctx)
|
|
{
|
|
SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Missing monitor context");
|
|
}
|
|
mfem::PetscSolver *solver = (mfem::PetscSolver*)(monctx->solver);
|
|
mfem::PetscSolverMonitor *user_monitor = (mfem::PetscSolverMonitor *)(
|
|
monctx->monitor);
|
|
|
|
if (user_monitor->mon_sol)
|
|
{
|
|
mfem::PetscParVector V(x,true);
|
|
user_monitor->MonitorSolution(it,t,V);
|
|
}
|
|
user_monitor->MonitorSolver(solver);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_ts_ifunction(TS ts, PetscReal t, Vec x, Vec xp,
|
|
Vec f,void *ctx)
|
|
{
|
|
__mfem_ts_ctx* ts_ctx = (__mfem_ts_ctx*)ctx;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
mfem::PetscParVector xx(x,true);
|
|
mfem::PetscParVector yy(xp,true);
|
|
mfem::PetscParVector ff(f,true);
|
|
|
|
mfem::TimeDependentOperator *op = ts_ctx->op;
|
|
op->SetTime(t);
|
|
|
|
if (ts_ctx->bchandler)
|
|
{
|
|
// we evaluate the ImplicitMult method with the correct bc
|
|
// this means the correct time derivative for essential boundary
|
|
// dofs is zero
|
|
if (!ts_ctx->work) { ts_ctx->work = new mfem::Vector(xx.Size()); }
|
|
if (!ts_ctx->work2) { ts_ctx->work2 = new mfem::Vector(xx.Size()); }
|
|
mfem::PetscBCHandler *bchandler = ts_ctx->bchandler;
|
|
mfem::Vector* txx = ts_ctx->work;
|
|
mfem::Vector* txp = ts_ctx->work2;
|
|
bchandler->SetTime(t);
|
|
bchandler->ApplyBC(xx,*txx);
|
|
bchandler->ZeroBC(yy,*txp);
|
|
op->ImplicitMult(*txx,*txp,ff);
|
|
// and fix the residual (i.e. f_\partial\Omega = u - g(t))
|
|
bchandler->FixResidualBC(xx,ff);
|
|
}
|
|
else
|
|
{
|
|
// use the ImplicitMult method of the class
|
|
op->ImplicitMult(xx,yy,ff);
|
|
}
|
|
|
|
// need to tell PETSc the Vec has been updated
|
|
ierr = PetscObjectStateIncrease((PetscObject)f); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_ts_rhsfunction(TS ts, PetscReal t, Vec x, Vec f,
|
|
void *ctx)
|
|
{
|
|
__mfem_ts_ctx* ts_ctx = (__mfem_ts_ctx*)ctx;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
if (ts_ctx->bchandler) { MFEM_ABORT("RHS evaluation with bc not implemented"); } // TODO
|
|
mfem::PetscParVector xx(x,true);
|
|
mfem::PetscParVector ff(f,true);
|
|
mfem::TimeDependentOperator *top = ts_ctx->op;
|
|
top->SetTime(t);
|
|
|
|
// use the ExplicitMult method - compute the RHS function
|
|
top->ExplicitMult(xx,ff);
|
|
|
|
// need to tell PETSc the Vec has been updated
|
|
ierr = PetscObjectStateIncrease((PetscObject)f); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_ts_ijacobian(TS ts, PetscReal t, Vec x,
|
|
Vec xp, PetscReal shift, Mat A, Mat P,
|
|
void *ctx)
|
|
{
|
|
__mfem_ts_ctx* ts_ctx = (__mfem_ts_ctx*)ctx;
|
|
mfem::Vector *xx;
|
|
PetscScalar *array;
|
|
PetscReal eps = 0.001; /* 0.1% difference */
|
|
PetscInt n;
|
|
PetscObjectState state;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
// prevent to recompute a Jacobian if we already did so
|
|
// the relative tolerance comparison should be fine given the fact
|
|
// that two consecutive shifts should have similar magnitude
|
|
ierr = PetscObjectStateGet((PetscObject)A,&state); CHKERRQ(ierr);
|
|
if (ts_ctx->type == mfem::PetscODESolver::ODE_SOLVER_LINEAR &&
|
|
std::abs(ts_ctx->cached_shift/shift - 1.0) < eps &&
|
|
state == ts_ctx->cached_ijacstate) { PetscFunctionReturn(0); }
|
|
|
|
// update time
|
|
mfem::TimeDependentOperator *op = ts_ctx->op;
|
|
op->SetTime(t);
|
|
|
|
// wrap Vecs with Vectors
|
|
ierr = VecGetLocalSize(x,&n); CHKERRQ(ierr);
|
|
ierr = VecGetArrayRead(xp,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
mfem::Vector yy(array,n);
|
|
ierr = VecRestoreArrayRead(xp,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
ierr = VecGetArrayRead(x,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
if (!ts_ctx->bchandler)
|
|
{
|
|
xx = new mfem::Vector(array,n);
|
|
}
|
|
else
|
|
{
|
|
// make sure we compute a Jacobian with the correct boundary values
|
|
if (!ts_ctx->work) { ts_ctx->work = new mfem::Vector(n); }
|
|
mfem::Vector txx(array,n);
|
|
mfem::PetscBCHandler *bchandler = ts_ctx->bchandler;
|
|
xx = ts_ctx->work;
|
|
bchandler->SetTime(t);
|
|
bchandler->ApplyBC(txx,*xx);
|
|
}
|
|
ierr = VecRestoreArrayRead(x,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
|
|
// Use TimeDependentOperator::GetImplicitGradient(x,y,s)
|
|
mfem::Operator& J = op->GetImplicitGradient(*xx,yy,shift);
|
|
if (!ts_ctx->bchandler) { delete xx; }
|
|
ts_ctx->cached_shift = shift;
|
|
|
|
// Convert to the operator type requested if needed
|
|
bool delete_pA = false;
|
|
mfem::PetscParMatrix *pA = const_cast<mfem::PetscParMatrix *>
|
|
(dynamic_cast<const mfem::PetscParMatrix *>(&J));
|
|
if (!pA || (ts_ctx->jacType != mfem::Operator::ANY_TYPE &&
|
|
pA->GetType() != ts_ctx->jacType))
|
|
{
|
|
pA = new mfem::PetscParMatrix(PetscObjectComm((PetscObject)ts),&J,
|
|
ts_ctx->jacType);
|
|
delete_pA = true;
|
|
}
|
|
|
|
// Eliminate essential dofs
|
|
if (ts_ctx->bchandler)
|
|
{
|
|
mfem::PetscBCHandler *bchandler = ts_ctx->bchandler;
|
|
mfem::PetscParVector dummy(PetscObjectComm((PetscObject)ts),0);
|
|
pA->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy);
|
|
}
|
|
|
|
// Get nonzerostate
|
|
PetscObjectState nonzerostate;
|
|
ierr = MatGetNonzeroState(P,&nonzerostate); CHKERRQ(ierr);
|
|
|
|
// Avoid unneeded copy of the matrix by hacking
|
|
Mat B;
|
|
B = pA->ReleaseMat(false);
|
|
ierr = MatHeaderReplace(P,&B); CHKERRQ(ierr);
|
|
if (delete_pA) { delete pA; }
|
|
|
|
// Matrix-free case
|
|
if (A && A != P)
|
|
{
|
|
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
ierr = MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
}
|
|
|
|
// When using MATNEST and PCFIELDSPLIT, the second setup of the
|
|
// preconditioner fails because MatCreateSubMatrix_Nest does not
|
|
// actually return a matrix. Instead, for efficiency reasons,
|
|
// it returns a reference to the submatrix. The second time it
|
|
// is called, MAT_REUSE_MATRIX is used and MatCreateSubMatrix_Nest
|
|
// aborts since the two submatrices are actually different.
|
|
// We circumvent this issue by incrementing the nonzero state
|
|
// (i.e. PETSc thinks the operator sparsity pattern has changed)
|
|
// This does not impact performances in the case of MATNEST
|
|
PetscBool isnest;
|
|
ierr = PetscObjectTypeCompare((PetscObject)P,MATNEST,&isnest);
|
|
CHKERRQ(ierr);
|
|
if (isnest) { P->nonzerostate = nonzerostate + 1; }
|
|
|
|
// Jacobian reusage
|
|
ierr = PetscObjectStateGet((PetscObject)P,&ts_ctx->cached_ijacstate);
|
|
CHKERRQ(ierr);
|
|
|
|
// Fool DM
|
|
DM dm;
|
|
MatType mtype;
|
|
ierr = MatGetType(P,&mtype); CHKERRQ(ierr);
|
|
ierr = TSGetDM(ts,&dm); CHKERRQ(ierr);
|
|
ierr = DMSetMatType(dm,mtype); CHKERRQ(ierr);
|
|
ierr = DMShellSetMatrix(dm,P); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_ts_computesplits(TS ts,PetscReal t,Vec x,Vec xp,
|
|
Mat Ax,Mat Jx,
|
|
Mat Axp,Mat Jxp)
|
|
{
|
|
__mfem_ts_ctx* ts_ctx;
|
|
mfem::Vector *xx;
|
|
PetscScalar *array;
|
|
PetscInt n;
|
|
PetscObjectState state;
|
|
PetscBool rx = PETSC_TRUE, rxp = PETSC_TRUE;
|
|
PetscBool assembled;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = TSGetIJacobian(ts,NULL,NULL,NULL,(void**)&ts_ctx); CHKERRQ(ierr);
|
|
|
|
// prevent to recompute the Jacobians if we already did so
|
|
ierr = PetscObjectStateGet((PetscObject)Jx,&state); CHKERRQ(ierr);
|
|
if (ts_ctx->type == mfem::PetscODESolver::ODE_SOLVER_LINEAR &&
|
|
state == ts_ctx->cached_splits_xstate) { rx = PETSC_FALSE; }
|
|
ierr = PetscObjectStateGet((PetscObject)Jxp,&state); CHKERRQ(ierr);
|
|
if (ts_ctx->type == mfem::PetscODESolver::ODE_SOLVER_LINEAR &&
|
|
state == ts_ctx->cached_splits_xdotstate) { rxp = PETSC_FALSE; }
|
|
if (!rx && !rxp) { PetscFunctionReturn(0); }
|
|
|
|
// update time
|
|
mfem::TimeDependentOperator *op = ts_ctx->op;
|
|
op->SetTime(t);
|
|
|
|
// wrap Vecs with Vectors
|
|
ierr = VecGetLocalSize(x,&n); CHKERRQ(ierr);
|
|
ierr = VecGetArrayRead(xp,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
mfem::Vector yy(array,n);
|
|
ierr = VecRestoreArrayRead(xp,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
ierr = VecGetArrayRead(x,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
if (!ts_ctx->bchandler)
|
|
{
|
|
xx = new mfem::Vector(array,n);
|
|
}
|
|
else
|
|
{
|
|
// make sure we compute a Jacobian with the correct boundary values
|
|
if (!ts_ctx->work) { ts_ctx->work = new mfem::Vector(n); }
|
|
mfem::Vector txx(array,n);
|
|
mfem::PetscBCHandler *bchandler = ts_ctx->bchandler;
|
|
xx = ts_ctx->work;
|
|
bchandler->SetTime(t);
|
|
bchandler->ApplyBC(txx,*xx);
|
|
}
|
|
ierr = VecRestoreArrayRead(x,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
|
|
// We don't have a specialized interface, so we just compute the split jacobians
|
|
// evaluating twice the implicit gradient method with the correct shifts
|
|
|
|
// first we do the state jacobian
|
|
mfem::Operator& oJx = op->GetImplicitGradient(*xx,yy,0.0);
|
|
|
|
// Convert to the operator type requested if needed
|
|
bool delete_mat = false;
|
|
mfem::PetscParMatrix *pJx = const_cast<mfem::PetscParMatrix *>
|
|
(dynamic_cast<const mfem::PetscParMatrix *>(&oJx));
|
|
if (!pJx || (ts_ctx->jacType != mfem::Operator::ANY_TYPE &&
|
|
pJx->GetType() != ts_ctx->jacType))
|
|
{
|
|
if (pJx)
|
|
{
|
|
Mat B = *pJx;
|
|
ierr = PetscObjectReference((PetscObject)B); CHKERRQ(ierr);
|
|
}
|
|
pJx = new mfem::PetscParMatrix(PetscObjectComm((PetscObject)ts),&oJx,
|
|
ts_ctx->jacType);
|
|
delete_mat = true;
|
|
}
|
|
if (rx)
|
|
{
|
|
ierr = MatAssembled(Jx,&assembled); CHKERRQ(ierr);
|
|
if (assembled)
|
|
{
|
|
ierr = MatCopy(*pJx,Jx,SAME_NONZERO_PATTERN); CHKERRQ(ierr);
|
|
}
|
|
else
|
|
{
|
|
Mat B;
|
|
ierr = MatDuplicate(*pJx,MAT_COPY_VALUES,&B); CHKERRQ(ierr);
|
|
ierr = MatHeaderReplace(Jx,&B); CHKERRQ(ierr);
|
|
}
|
|
}
|
|
if (delete_mat) { delete pJx; }
|
|
pJx = new mfem::PetscParMatrix(Jx,true);
|
|
|
|
// Eliminate essential dofs
|
|
if (ts_ctx->bchandler)
|
|
{
|
|
mfem::PetscBCHandler *bchandler = ts_ctx->bchandler;
|
|
mfem::PetscParVector dummy(PetscObjectComm((PetscObject)ts),0);
|
|
pJx->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy);
|
|
}
|
|
|
|
// Then we do the jacobian wrt the time derivative of the state
|
|
// Note that this is usually the mass matrix
|
|
mfem::PetscParMatrix *pJxp = NULL;
|
|
if (rxp)
|
|
{
|
|
delete_mat = false;
|
|
mfem::Operator& oJxp = op->GetImplicitGradient(*xx,yy,1.0);
|
|
pJxp = const_cast<mfem::PetscParMatrix *>
|
|
(dynamic_cast<const mfem::PetscParMatrix *>(&oJxp));
|
|
if (!pJxp || (ts_ctx->jacType != mfem::Operator::ANY_TYPE &&
|
|
pJxp->GetType() != ts_ctx->jacType))
|
|
{
|
|
if (pJxp)
|
|
{
|
|
Mat B = *pJxp;
|
|
ierr = PetscObjectReference((PetscObject)B); CHKERRQ(ierr);
|
|
}
|
|
pJxp = new mfem::PetscParMatrix(PetscObjectComm((PetscObject)ts),
|
|
&oJxp,ts_ctx->jacType);
|
|
delete_mat = true;
|
|
}
|
|
|
|
ierr = MatAssembled(Jxp,&assembled); CHKERRQ(ierr);
|
|
if (assembled)
|
|
{
|
|
ierr = MatCopy(*pJxp,Jxp,SAME_NONZERO_PATTERN); CHKERRQ(ierr);
|
|
}
|
|
else
|
|
{
|
|
Mat B;
|
|
ierr = MatDuplicate(*pJxp,MAT_COPY_VALUES,&B); CHKERRQ(ierr);
|
|
ierr = MatHeaderReplace(Jxp,&B); CHKERRQ(ierr);
|
|
}
|
|
if (delete_mat) { delete pJxp; }
|
|
pJxp = new mfem::PetscParMatrix(Jxp,true);
|
|
|
|
// Eliminate essential dofs
|
|
if (ts_ctx->bchandler)
|
|
{
|
|
mfem::PetscBCHandler *bchandler = ts_ctx->bchandler;
|
|
mfem::PetscParVector dummy(PetscObjectComm((PetscObject)ts),0);
|
|
pJxp->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy,2.0);
|
|
}
|
|
|
|
// Obtain the time dependent part of the jacobian by subtracting
|
|
// the state jacobian
|
|
// We don't do it with the class operator "-=" since we know that
|
|
// the sparsity pattern of the two matrices is the same
|
|
ierr = MatAXPY(*pJxp,-1.0,*pJx,SAME_NONZERO_PATTERN); PCHKERRQ(ts,ierr);
|
|
}
|
|
|
|
// Matrix-free cases
|
|
if (Ax && Ax != Jx)
|
|
{
|
|
ierr = MatAssemblyBegin(Ax,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
ierr = MatAssemblyEnd(Ax,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
}
|
|
if (Axp && Axp != Jxp)
|
|
{
|
|
ierr = MatAssemblyBegin(Axp,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
ierr = MatAssemblyEnd(Axp,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
}
|
|
|
|
// Jacobian reusage
|
|
ierr = PetscObjectStateGet((PetscObject)Jx,&ts_ctx->cached_splits_xstate);
|
|
CHKERRQ(ierr);
|
|
ierr = PetscObjectStateGet((PetscObject)Jxp,&ts_ctx->cached_splits_xdotstate);
|
|
CHKERRQ(ierr);
|
|
|
|
delete pJx;
|
|
delete pJxp;
|
|
if (!ts_ctx->bchandler) { delete xx; }
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_ts_rhsjacobian(TS ts, PetscReal t, Vec x,
|
|
Mat A, Mat P, void *ctx)
|
|
{
|
|
__mfem_ts_ctx* ts_ctx = (__mfem_ts_ctx*)ctx;
|
|
mfem::Vector *xx;
|
|
PetscScalar *array;
|
|
PetscInt n;
|
|
PetscObjectState state;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
// prevent to recompute a Jacobian if we already did so
|
|
ierr = PetscObjectStateGet((PetscObject)A,&state); CHKERRQ(ierr);
|
|
if (ts_ctx->type == mfem::PetscODESolver::ODE_SOLVER_LINEAR &&
|
|
state == ts_ctx->cached_rhsjacstate) { PetscFunctionReturn(0); }
|
|
|
|
// update time
|
|
mfem::TimeDependentOperator *op = ts_ctx->op;
|
|
op->SetTime(t);
|
|
|
|
// wrap Vec with Vector
|
|
ierr = VecGetLocalSize(x,&n); CHKERRQ(ierr);
|
|
ierr = VecGetArrayRead(x,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
if (!ts_ctx->bchandler)
|
|
{
|
|
xx = new mfem::Vector(array,n);
|
|
}
|
|
else
|
|
{
|
|
// make sure we compute a Jacobian with the correct boundary values
|
|
if (!ts_ctx->work) { ts_ctx->work = new mfem::Vector(n); }
|
|
mfem::Vector txx(array,n);
|
|
mfem::PetscBCHandler *bchandler = ts_ctx->bchandler;
|
|
xx = ts_ctx->work;
|
|
bchandler->SetTime(t);
|
|
bchandler->ApplyBC(txx,*xx);
|
|
}
|
|
ierr = VecRestoreArrayRead(x,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
|
|
// Use TimeDependentOperator::GetExplicitGradient(x)
|
|
mfem::Operator& J = op->GetExplicitGradient(*xx);
|
|
if (!ts_ctx->bchandler) { delete xx; }
|
|
|
|
// Convert to the operator type requested if needed
|
|
bool delete_pA = false;
|
|
mfem::PetscParMatrix *pA = const_cast<mfem::PetscParMatrix *>
|
|
(dynamic_cast<const mfem::PetscParMatrix *>(&J));
|
|
if (!pA || (ts_ctx->jacType != mfem::Operator::ANY_TYPE &&
|
|
pA->GetType() != ts_ctx->jacType))
|
|
{
|
|
pA = new mfem::PetscParMatrix(PetscObjectComm((PetscObject)ts),&J,
|
|
ts_ctx->jacType);
|
|
delete_pA = true;
|
|
}
|
|
|
|
// Eliminate essential dofs
|
|
if (ts_ctx->bchandler)
|
|
{
|
|
mfem::PetscBCHandler *bchandler = ts_ctx->bchandler;
|
|
mfem::PetscParVector dummy(PetscObjectComm((PetscObject)ts),0);
|
|
pA->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy);
|
|
}
|
|
|
|
// Get nonzerostate
|
|
PetscObjectState nonzerostate;
|
|
ierr = MatGetNonzeroState(P,&nonzerostate); CHKERRQ(ierr);
|
|
|
|
// Avoid unneeded copy of the matrix by hacking
|
|
Mat B;
|
|
B = pA->ReleaseMat(false);
|
|
ierr = MatHeaderReplace(P,&B); CHKERRQ(ierr);
|
|
if (delete_pA) { delete pA; }
|
|
|
|
// When using MATNEST and PCFIELDSPLIT, the second setup of the
|
|
// preconditioner fails because MatCreateSubMatrix_Nest does not
|
|
// actually return a matrix. Instead, for efficiency reasons,
|
|
// it returns a reference to the submatrix. The second time it
|
|
// is called, MAT_REUSE_MATRIX is used and MatCreateSubMatrix_Nest
|
|
// aborts since the two submatrices are actually different.
|
|
// We circumvent this issue by incrementing the nonzero state
|
|
// (i.e. PETSc thinks the operator sparsity pattern has changed)
|
|
// This does not impact performances in the case of MATNEST
|
|
PetscBool isnest;
|
|
ierr = PetscObjectTypeCompare((PetscObject)P,MATNEST,&isnest);
|
|
CHKERRQ(ierr);
|
|
if (isnest) { P->nonzerostate = nonzerostate + 1; }
|
|
|
|
// Matrix-free case
|
|
if (A && A != P)
|
|
{
|
|
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
ierr = MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
}
|
|
|
|
// Jacobian reusage
|
|
if (ts_ctx->type == mfem::PetscODESolver::ODE_SOLVER_LINEAR)
|
|
{
|
|
ierr = TSRHSJacobianSetReuse(ts,PETSC_TRUE); PCHKERRQ(ts,ierr);
|
|
}
|
|
ierr = PetscObjectStateGet((PetscObject)A,&ts_ctx->cached_rhsjacstate);
|
|
CHKERRQ(ierr);
|
|
|
|
// Fool DM
|
|
DM dm;
|
|
MatType mtype;
|
|
ierr = MatGetType(P,&mtype); CHKERRQ(ierr);
|
|
ierr = TSGetDM(ts,&dm); CHKERRQ(ierr);
|
|
ierr = DMSetMatType(dm,mtype); CHKERRQ(ierr);
|
|
ierr = DMShellSetMatrix(dm,P); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_snes_monitor(SNES snes, PetscInt it, PetscReal res,
|
|
void* ctx)
|
|
{
|
|
__mfem_monitor_ctx *monctx = (__mfem_monitor_ctx*)ctx;
|
|
|
|
PetscFunctionBeginUser;
|
|
if (!monctx)
|
|
{
|
|
SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Missing monitor context");
|
|
}
|
|
|
|
mfem::PetscSolver *solver = (mfem::PetscSolver*)(monctx->solver);
|
|
mfem::PetscSolverMonitor *user_monitor = (mfem::PetscSolverMonitor *)(
|
|
monctx->monitor);
|
|
if (user_monitor->mon_sol)
|
|
{
|
|
Vec x;
|
|
PetscErrorCode ierr;
|
|
|
|
ierr = SNESGetSolution(snes,&x); CHKERRQ(ierr);
|
|
mfem::PetscParVector V(x,true);
|
|
user_monitor->MonitorSolution(it,res,V);
|
|
}
|
|
if (user_monitor->mon_res)
|
|
{
|
|
Vec x;
|
|
PetscErrorCode ierr;
|
|
|
|
ierr = SNESGetFunction(snes,&x,NULL,NULL); CHKERRQ(ierr);
|
|
mfem::PetscParVector V(x,true);
|
|
user_monitor->MonitorResidual(it,res,V);
|
|
}
|
|
user_monitor->MonitorSolver(solver);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_snes_jacobian(SNES snes, Vec x, Mat A, Mat P,
|
|
void *ctx)
|
|
{
|
|
PetscScalar *array;
|
|
PetscInt n;
|
|
PetscErrorCode ierr;
|
|
mfem::Vector *xx;
|
|
__mfem_snes_ctx *snes_ctx = (__mfem_snes_ctx*)ctx;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = VecGetArrayRead(x,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
ierr = VecGetLocalSize(x,&n); CHKERRQ(ierr);
|
|
if (!snes_ctx->bchandler)
|
|
{
|
|
xx = new mfem::Vector(array,n);
|
|
}
|
|
else
|
|
{
|
|
// make sure we compute a Jacobian with the correct boundary values
|
|
if (!snes_ctx->work) { snes_ctx->work = new mfem::Vector(n); }
|
|
mfem::Vector txx(array,n);
|
|
mfem::PetscBCHandler *bchandler = snes_ctx->bchandler;
|
|
xx = snes_ctx->work;
|
|
bchandler->ApplyBC(txx,*xx);
|
|
}
|
|
|
|
// Use Operator::GetGradient(x)
|
|
mfem::Operator& J = snes_ctx->op->GetGradient(*xx);
|
|
ierr = VecRestoreArrayRead(x,(const PetscScalar**)&array); CHKERRQ(ierr);
|
|
if (!snes_ctx->bchandler) { delete xx; }
|
|
|
|
// Convert to the operator type requested if needed
|
|
bool delete_pA = false;
|
|
mfem::PetscParMatrix *pA = const_cast<mfem::PetscParMatrix *>
|
|
(dynamic_cast<const mfem::PetscParMatrix *>(&J));
|
|
if (!pA || (snes_ctx->jacType != mfem::Operator::ANY_TYPE &&
|
|
pA->GetType() != snes_ctx->jacType))
|
|
{
|
|
pA = new mfem::PetscParMatrix(PetscObjectComm((PetscObject)snes),&J,
|
|
snes_ctx->jacType);
|
|
delete_pA = true;
|
|
}
|
|
|
|
// Eliminate essential dofs
|
|
if (snes_ctx->bchandler)
|
|
{
|
|
mfem::PetscBCHandler *bchandler = snes_ctx->bchandler;
|
|
mfem::PetscParVector dummy(PetscObjectComm((PetscObject)snes),0);
|
|
pA->EliminateRowsCols(bchandler->GetTDofs(),dummy,dummy);
|
|
}
|
|
|
|
// Get nonzerostate
|
|
PetscObjectState nonzerostate;
|
|
ierr = MatGetNonzeroState(P,&nonzerostate); CHKERRQ(ierr);
|
|
|
|
// Avoid unneeded copy of the matrix by hacking
|
|
Mat B = pA->ReleaseMat(false);
|
|
ierr = MatHeaderReplace(P,&B); CHKERRQ(ierr);
|
|
if (delete_pA) { delete pA; }
|
|
|
|
// When using MATNEST and PCFIELDSPLIT, the second setup of the
|
|
// preconditioner fails because MatCreateSubMatrix_Nest does not
|
|
// actually return a matrix. Instead, for efficiency reasons,
|
|
// it returns a reference to the submatrix. The second time it
|
|
// is called, MAT_REUSE_MATRIX is used and MatCreateSubMatrix_Nest
|
|
// aborts since the two submatrices are actually different.
|
|
// We circumvent this issue by incrementing the nonzero state
|
|
// (i.e. PETSc thinks the operator sparsity pattern has changed)
|
|
// This does not impact performances in the case of MATNEST
|
|
PetscBool isnest;
|
|
ierr = PetscObjectTypeCompare((PetscObject)P,MATNEST,&isnest);
|
|
CHKERRQ(ierr);
|
|
if (isnest) { P->nonzerostate = nonzerostate + 1; }
|
|
|
|
// Matrix-free case
|
|
if (A && A != P)
|
|
{
|
|
ierr = MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
ierr = MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
}
|
|
|
|
// Fool DM
|
|
DM dm;
|
|
MatType mtype;
|
|
ierr = MatGetType(P,&mtype); CHKERRQ(ierr);
|
|
ierr = SNESGetDM(snes,&dm); CHKERRQ(ierr);
|
|
ierr = DMSetMatType(dm,mtype); CHKERRQ(ierr);
|
|
ierr = DMShellSetMatrix(dm,P); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_snes_function(SNES snes, Vec x, Vec f, void *ctx)
|
|
{
|
|
__mfem_snes_ctx* snes_ctx = (__mfem_snes_ctx*)ctx;
|
|
|
|
PetscFunctionBeginUser;
|
|
mfem::PetscParVector xx(x,true);
|
|
mfem::PetscParVector ff(f,true);
|
|
if (snes_ctx->bchandler)
|
|
{
|
|
// we evaluate the Mult method with the correct bc
|
|
if (!snes_ctx->work) { snes_ctx->work = new mfem::Vector(xx.Size()); }
|
|
mfem::PetscBCHandler *bchandler = snes_ctx->bchandler;
|
|
mfem::Vector* txx = snes_ctx->work;
|
|
bchandler->ApplyBC(xx,*txx);
|
|
snes_ctx->op->Mult(*txx,ff);
|
|
// and fix the residual (i.e. f_\partial\Omega = u - g)
|
|
bchandler->FixResidualBC(xx,ff);
|
|
}
|
|
else
|
|
{
|
|
// use the Mult method of the class
|
|
snes_ctx->op->Mult(xx,ff);
|
|
}
|
|
// need to tell PETSc the Vec has been updated
|
|
ierr = PetscObjectStateIncrease((PetscObject)f); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_snes_objective(SNES snes, Vec x, PetscReal *f,
|
|
void *ctx)
|
|
{
|
|
__mfem_snes_ctx* snes_ctx = (__mfem_snes_ctx*)ctx;
|
|
|
|
PetscFunctionBeginUser;
|
|
if (!snes_ctx->objective)
|
|
{
|
|
SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Missing objective function");
|
|
}
|
|
mfem::PetscParVector xx(x,true);
|
|
double lf;
|
|
(*snes_ctx->objective)(snes_ctx->op,xx,&lf);
|
|
*f = (PetscReal)lf;
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_snes_postcheck(SNESLineSearch ls,Vec X,Vec Y,Vec W,
|
|
PetscBool *cy,PetscBool *cw, void* ctx)
|
|
{
|
|
__mfem_snes_ctx* snes_ctx = (__mfem_snes_ctx*)ctx;
|
|
bool lcy = false,lcw = false;
|
|
|
|
PetscFunctionBeginUser;
|
|
mfem::PetscParVector x(X,true);
|
|
mfem::PetscParVector y(Y,true);
|
|
mfem::PetscParVector w(W,true);
|
|
(*snes_ctx->postcheck)(snes_ctx->op,x,y,w,lcy,lcw);
|
|
if (lcy) { *cy = PETSC_TRUE; }
|
|
if (lcw) { *cw = PETSC_TRUE; }
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_snes_update(SNES snes, PetscInt it)
|
|
{
|
|
Vec F,X,dX,pX;
|
|
__mfem_snes_ctx* snes_ctx;
|
|
|
|
PetscFunctionBeginUser;
|
|
/* Update callback does not use the context */
|
|
ierr = SNESGetFunction(snes,&F,NULL,(void **)&snes_ctx); CHKERRQ(ierr);
|
|
ierr = SNESGetSolution(snes,&X); CHKERRQ(ierr);
|
|
if (!it)
|
|
{
|
|
ierr = VecDuplicate(X,&pX); CHKERRQ(ierr);
|
|
ierr = PetscObjectCompose((PetscObject)snes,"_mfem_snes_xp",(PetscObject)pX);
|
|
CHKERRQ(ierr);
|
|
ierr = VecDestroy(&pX); CHKERRQ(ierr);
|
|
}
|
|
ierr = PetscObjectQuery((PetscObject)snes,"_mfem_snes_xp",(PetscObject*)&pX);
|
|
CHKERRQ(ierr);
|
|
if (!pX) SETERRQ(PetscObjectComm((PetscObject)snes),PETSC_ERR_USER,
|
|
"Missing previous solution");
|
|
ierr = SNESGetSolutionUpdate(snes,&dX); CHKERRQ(ierr);
|
|
mfem::PetscParVector f(F,true);
|
|
mfem::PetscParVector x(X,true);
|
|
mfem::PetscParVector dx(dX,true);
|
|
mfem::PetscParVector px(pX,true);
|
|
(*snes_ctx->update)(snes_ctx->op,it,f,x,dx,px);
|
|
/* Store previous solution */
|
|
ierr = VecCopy(X,pX); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_ksp_monitor(KSP ksp, PetscInt it, PetscReal res,
|
|
void* ctx)
|
|
{
|
|
__mfem_monitor_ctx *monctx = (__mfem_monitor_ctx*)ctx;
|
|
|
|
PetscFunctionBeginUser;
|
|
if (!monctx)
|
|
{
|
|
SETERRQ(PETSC_COMM_SELF,PETSC_ERR_USER,"Missing monitor context");
|
|
}
|
|
|
|
mfem::PetscSolver *solver = (mfem::PetscSolver*)(monctx->solver);
|
|
mfem::PetscSolverMonitor *user_monitor = (mfem::PetscSolverMonitor *)(
|
|
monctx->monitor);
|
|
if (user_monitor->mon_sol)
|
|
{
|
|
Vec x;
|
|
PetscErrorCode ierr;
|
|
|
|
ierr = KSPBuildSolution(ksp,NULL,&x); CHKERRQ(ierr);
|
|
mfem::PetscParVector V(x,true);
|
|
user_monitor->MonitorSolution(it,res,V);
|
|
}
|
|
if (user_monitor->mon_res)
|
|
{
|
|
Vec x;
|
|
PetscErrorCode ierr;
|
|
|
|
ierr = KSPBuildResidual(ksp,NULL,NULL,&x); CHKERRQ(ierr);
|
|
mfem::PetscParVector V(x,true);
|
|
user_monitor->MonitorResidual(it,res,V);
|
|
}
|
|
user_monitor->MonitorSolver(solver);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_mat_shell_apply(Mat A, Vec x, Vec y)
|
|
{
|
|
mfem::Operator *op;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = MatShellGetContext(A,(void **)&op); CHKERRQ(ierr);
|
|
if (!op) { SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_LIB,"Missing operator"); }
|
|
mfem::PetscParVector xx(x,true);
|
|
mfem::PetscParVector yy(y,true);
|
|
op->Mult(xx,yy);
|
|
// need to tell PETSc the Vec has been updated
|
|
ierr = PetscObjectStateIncrease((PetscObject)y); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_mat_shell_apply_transpose(Mat A, Vec x, Vec y)
|
|
{
|
|
mfem::Operator *op;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = MatShellGetContext(A,(void **)&op); CHKERRQ(ierr);
|
|
if (!op) { SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_LIB,"Missing operator"); }
|
|
mfem::PetscParVector xx(x,true);
|
|
mfem::PetscParVector yy(y,true);
|
|
op->MultTranspose(xx,yy);
|
|
// need to tell PETSc the Vec has been updated
|
|
ierr = PetscObjectStateIncrease((PetscObject)y); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_mat_shell_copy(Mat A, Mat B, MatStructure str)
|
|
{
|
|
mfem::Operator *op;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = MatShellGetContext(A,(void **)&op); CHKERRQ(ierr);
|
|
if (!op) { SETERRQ(PetscObjectComm((PetscObject)A),PETSC_ERR_LIB,"Missing operator"); }
|
|
ierr = MatShellSetContext(B,(void *)op); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_mat_shell_destroy(Mat A)
|
|
{
|
|
PetscFunctionBeginUser;
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_pc_shell_view(PC pc, PetscViewer viewer)
|
|
{
|
|
__mfem_pc_shell_ctx *ctx;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = PCShellGetContext(pc,(void **)&ctx); CHKERRQ(ierr);
|
|
if (ctx->op)
|
|
{
|
|
PetscBool isascii;
|
|
ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii);
|
|
CHKERRQ(ierr);
|
|
|
|
mfem::PetscPreconditioner *ppc = dynamic_cast<mfem::PetscPreconditioner *>
|
|
(ctx->op);
|
|
if (ppc)
|
|
{
|
|
ierr = PCView(*ppc,viewer); CHKERRQ(ierr);
|
|
}
|
|
else
|
|
{
|
|
if (isascii)
|
|
{
|
|
ierr = PetscViewerASCIIPrintf(viewer,
|
|
"No information available on the mfem::Solver\n");
|
|
CHKERRQ(ierr);
|
|
}
|
|
}
|
|
if (isascii && ctx->factory)
|
|
{
|
|
ierr = PetscViewerASCIIPrintf(viewer,
|
|
"Number of preconditioners created by the factory %lu\n",ctx->numprec);
|
|
CHKERRQ(ierr);
|
|
}
|
|
}
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_pc_shell_apply(PC pc, Vec x, Vec y)
|
|
{
|
|
__mfem_pc_shell_ctx *ctx;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
mfem::PetscParVector xx(x,true);
|
|
mfem::PetscParVector yy(y,true);
|
|
ierr = PCShellGetContext(pc,(void **)&ctx); CHKERRQ(ierr);
|
|
if (ctx->op)
|
|
{
|
|
ctx->op->Mult(xx,yy);
|
|
// need to tell PETSc the Vec has been updated
|
|
ierr = PetscObjectStateIncrease((PetscObject)y); CHKERRQ(ierr);
|
|
}
|
|
else // operator is not present, copy x
|
|
{
|
|
yy = xx;
|
|
}
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_pc_shell_apply_transpose(PC pc, Vec x, Vec y)
|
|
{
|
|
__mfem_pc_shell_ctx *ctx;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
mfem::PetscParVector xx(x,true);
|
|
mfem::PetscParVector yy(y,true);
|
|
ierr = PCShellGetContext(pc,(void **)&ctx); CHKERRQ(ierr);
|
|
if (ctx->op)
|
|
{
|
|
ctx->op->MultTranspose(xx,yy);
|
|
// need to tell PETSc the Vec has been updated
|
|
ierr = PetscObjectStateIncrease((PetscObject)y); CHKERRQ(ierr);
|
|
}
|
|
else // operator is not present, copy x
|
|
{
|
|
yy = xx;
|
|
}
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_pc_shell_setup(PC pc)
|
|
{
|
|
__mfem_pc_shell_ctx *ctx;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = PCShellGetContext(pc,(void **)&ctx); CHKERRQ(ierr);
|
|
if (ctx->factory)
|
|
{
|
|
// Delete any owned operator
|
|
if (ctx->ownsop)
|
|
{
|
|
delete ctx->op;
|
|
}
|
|
|
|
// Get current preconditioning Mat
|
|
Mat B;
|
|
ierr = PCGetOperators(pc,NULL,&B); CHKERRQ(ierr);
|
|
|
|
// Call user-defined setup
|
|
mfem::OperatorHandle hB(new mfem::PetscParMatrix(B,true),true);
|
|
mfem::PetscPreconditionerFactory *factory = ctx->factory;
|
|
ctx->op = factory->NewPreconditioner(hB);
|
|
ctx->ownsop = true;
|
|
ctx->numprec++;
|
|
}
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_pc_shell_destroy(PC pc)
|
|
{
|
|
__mfem_pc_shell_ctx *ctx;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = PCShellGetContext(pc,(void **)&ctx); CHKERRQ(ierr);
|
|
if (ctx->ownsop)
|
|
{
|
|
delete ctx->op;
|
|
}
|
|
delete ctx;
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_array_container_destroy(void *ptr)
|
|
{
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = PetscFree(ptr); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_matarray_container_destroy(void *ptr)
|
|
{
|
|
mfem::Array<Mat> *a = (mfem::Array<Mat>*)ptr;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
for (int i=0; i<a->Size(); i++)
|
|
{
|
|
Mat M = (*a)[i];
|
|
MPI_Comm comm = PetscObjectComm((PetscObject)M);
|
|
ierr = MatDestroy(&M); CCHKERRQ(comm,ierr);
|
|
}
|
|
delete a;
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode __mfem_monitor_ctx_destroy(void **ctx)
|
|
{
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = PetscFree(*ctx); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
// Sets the type of PC to PCSHELL and wraps the solver action
|
|
// if ownsop is true, ownership of precond is transferred to the PETSc object
|
|
PetscErrorCode MakeShellPC(PC pc, mfem::Solver &precond, bool ownsop)
|
|
{
|
|
PetscFunctionBeginUser;
|
|
__mfem_pc_shell_ctx *ctx = new __mfem_pc_shell_ctx;
|
|
ctx->op = &precond;
|
|
ctx->ownsop = ownsop;
|
|
ctx->factory = NULL;
|
|
ctx->numprec = 0;
|
|
|
|
// In case the PC was already of type SHELL, this will destroy any
|
|
// previous user-defined data structure
|
|
// We cannot call PCReset as it will wipe out any operator already set
|
|
ierr = PCSetType(pc,PCNONE); CHKERRQ(ierr);
|
|
|
|
ierr = PCSetType(pc,PCSHELL); CHKERRQ(ierr);
|
|
ierr = PCShellSetName(pc,"MFEM Solver (unknown Pmat)"); CHKERRQ(ierr);
|
|
ierr = PCShellSetContext(pc,(void *)ctx); CHKERRQ(ierr);
|
|
ierr = PCShellSetApply(pc,__mfem_pc_shell_apply); CHKERRQ(ierr);
|
|
ierr = PCShellSetApplyTranspose(pc,__mfem_pc_shell_apply_transpose);
|
|
CHKERRQ(ierr);
|
|
ierr = PCShellSetSetUp(pc,__mfem_pc_shell_setup); CHKERRQ(ierr);
|
|
ierr = PCShellSetView(pc,__mfem_pc_shell_view); CHKERRQ(ierr);
|
|
ierr = PCShellSetDestroy(pc,__mfem_pc_shell_destroy); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
// Sets the type of PC to PCSHELL. Uses a PetscPreconditionerFactory to construct the solver
|
|
// Takes ownership of the solver created by the factory
|
|
PetscErrorCode MakeShellPCWithFactory(PC pc,
|
|
mfem::PetscPreconditionerFactory *factory)
|
|
{
|
|
PetscFunctionBeginUser;
|
|
__mfem_pc_shell_ctx *ctx = new __mfem_pc_shell_ctx;
|
|
ctx->op = NULL;
|
|
ctx->ownsop = true;
|
|
ctx->factory = factory;
|
|
ctx->numprec = 0;
|
|
|
|
// In case the PC was already of type SHELL, this will destroy any
|
|
// previous user-defined data structure
|
|
// We cannot call PCReset as it will wipe out any operator already set
|
|
ierr = PCSetType(pc,PCNONE); CHKERRQ(ierr);
|
|
|
|
ierr = PCSetType(pc,PCSHELL); CHKERRQ(ierr);
|
|
ierr = PCShellSetName(pc,factory->GetName()); CHKERRQ(ierr);
|
|
ierr = PCShellSetContext(pc,(void *)ctx); CHKERRQ(ierr);
|
|
ierr = PCShellSetApply(pc,__mfem_pc_shell_apply); CHKERRQ(ierr);
|
|
ierr = PCShellSetApplyTranspose(pc,__mfem_pc_shell_apply_transpose);
|
|
CHKERRQ(ierr);
|
|
ierr = PCShellSetSetUp(pc,__mfem_pc_shell_setup); CHKERRQ(ierr);
|
|
ierr = PCShellSetView(pc,__mfem_pc_shell_view); CHKERRQ(ierr);
|
|
ierr = PCShellSetDestroy(pc,__mfem_pc_shell_destroy); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
// Converts from a list (or a marked Array if islist is false) to an IS
|
|
// st indicates the offset where to start numbering
|
|
static PetscErrorCode Convert_Array_IS(MPI_Comm comm, bool islist,
|
|
const mfem::Array<int> *list,
|
|
PetscInt st, IS* is)
|
|
{
|
|
PetscInt n = list ? list->Size() : 0,*idxs;
|
|
const int *data = list ? list->GetData() : NULL;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
ierr = PetscMalloc1(n,&idxs); CHKERRQ(ierr);
|
|
if (islist)
|
|
{
|
|
for (PetscInt i=0; i<n; i++) { idxs[i] = data[i] + st; }
|
|
}
|
|
else
|
|
{
|
|
PetscInt cum = 0;
|
|
for (PetscInt i=0; i<n; i++)
|
|
{
|
|
if (data[i]) { idxs[cum++] = i+st; }
|
|
}
|
|
n = cum;
|
|
}
|
|
ierr = ISCreateGeneral(comm,n,idxs,PETSC_OWN_POINTER,is);
|
|
CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
// Converts from a marked Array of Vdofs to an IS
|
|
// st indicates the offset where to start numbering
|
|
// l2l is a vector of matrices generated during RAP
|
|
static PetscErrorCode Convert_Vmarks_IS(MPI_Comm comm,
|
|
mfem::Array<Mat> &pl2l,
|
|
const mfem::Array<int> *mark,
|
|
PetscInt st, IS* is)
|
|
{
|
|
mfem::Array<int> sub_dof_marker;
|
|
mfem::Array<mfem::SparseMatrix*> l2l(pl2l.Size());
|
|
PetscInt nl;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
for (int i = 0; i < pl2l.Size(); i++)
|
|
{
|
|
PetscInt m,n,*ii,*jj;
|
|
PetscBool done;
|
|
ierr = MatGetRowIJ(pl2l[i],0,PETSC_FALSE,PETSC_FALSE,&m,(const PetscInt**)&ii,
|
|
(const PetscInt**)&jj,&done); CHKERRQ(ierr);
|
|
MFEM_VERIFY(done,"Unable to perform MatGetRowIJ on " << i << " l2l matrix");
|
|
ierr = MatGetSize(pl2l[i],NULL,&n); CHKERRQ(ierr);
|
|
#if defined(PETSC_USE_64BIT_INDICES)
|
|
int nnz = (int)ii[m];
|
|
int *mii = new int[m+1];
|
|
int *mjj = new int[nnz];
|
|
for (int j = 0; j < m+1; j++) { mii[j] = (int)ii[j]; }
|
|
for (int j = 0; j < nnz; j++) { mjj[j] = (int)jj[j]; }
|
|
l2l[i] = new mfem::SparseMatrix(mii,mjj,NULL,m,n,true,true,true);
|
|
#else
|
|
l2l[i] = new mfem::SparseMatrix(ii,jj,NULL,m,n,false,true,true);
|
|
#endif
|
|
ierr = MatRestoreRowIJ(pl2l[i],0,PETSC_FALSE,PETSC_FALSE,&m,
|
|
(const PetscInt**)&ii,
|
|
(const PetscInt**)&jj,&done); CHKERRQ(ierr);
|
|
MFEM_VERIFY(done,"Unable to perform MatRestoreRowIJ on "
|
|
<< i << " l2l matrix");
|
|
}
|
|
nl = 0;
|
|
for (int i = 0; i < l2l.Size(); i++) { nl += l2l[i]->Width(); }
|
|
sub_dof_marker.SetSize(nl);
|
|
const int* vdata = mark->GetData();
|
|
int* sdata = sub_dof_marker.GetData();
|
|
int cumh = 0, cumw = 0;
|
|
for (int i = 0; i < l2l.Size(); i++)
|
|
{
|
|
const mfem::Array<int> vf_marker(const_cast<int*>(vdata)+cumh,
|
|
l2l[i]->Height());
|
|
mfem::Array<int> sf_marker(sdata+cumw,l2l[i]->Width());
|
|
l2l[i]->BooleanMultTranspose(vf_marker,sf_marker);
|
|
cumh += l2l[i]->Height();
|
|
cumw += l2l[i]->Width();
|
|
}
|
|
ierr = Convert_Array_IS(comm,false,&sub_dof_marker,st,is); CCHKERRQ(comm,ierr);
|
|
for (int i = 0; i < pl2l.Size(); i++)
|
|
{
|
|
delete l2l[i];
|
|
}
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
#if !defined(PETSC_HAVE_HYPRE)
|
|
|
|
#if defined(HYPRE_MIXEDINT)
|
|
#error "HYPRE_MIXEDINT not supported"
|
|
#endif
|
|
|
|
#include "_hypre_parcsr_mv.h"
|
|
static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix* hA,Mat* pA)
|
|
{
|
|
MPI_Comm comm = hypre_ParCSRMatrixComm(hA);
|
|
hypre_CSRMatrix *hdiag,*hoffd;
|
|
PetscScalar *da,*oa,*aptr;
|
|
PetscInt *dii,*djj,*oii,*ojj,*iptr;
|
|
PetscInt i,dnnz,onnz,m,n;
|
|
PetscMPIInt size;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
hdiag = hypre_ParCSRMatrixDiag(hA);
|
|
hoffd = hypre_ParCSRMatrixOffd(hA);
|
|
m = hypre_CSRMatrixNumRows(hdiag);
|
|
n = hypre_CSRMatrixNumCols(hdiag);
|
|
dnnz = hypre_CSRMatrixNumNonzeros(hdiag);
|
|
onnz = hypre_CSRMatrixNumNonzeros(hoffd);
|
|
ierr = PetscMalloc1(m+1,&dii); CHKERRQ(ierr);
|
|
ierr = PetscMalloc1(dnnz,&djj); CHKERRQ(ierr);
|
|
ierr = PetscMalloc1(dnnz,&da); CHKERRQ(ierr);
|
|
ierr = PetscMemcpy(dii,hypre_CSRMatrixI(hdiag),(m+1)*sizeof(PetscInt));
|
|
CHKERRQ(ierr);
|
|
ierr = PetscMemcpy(djj,hypre_CSRMatrixJ(hdiag),dnnz*sizeof(PetscInt));
|
|
CHKERRQ(ierr);
|
|
ierr = PetscMemcpy(da,hypre_CSRMatrixData(hdiag),dnnz*sizeof(PetscScalar));
|
|
CHKERRQ(ierr);
|
|
iptr = djj;
|
|
aptr = da;
|
|
for (i=0; i<m; i++)
|
|
{
|
|
PetscInt nc = dii[i+1]-dii[i];
|
|
ierr = PetscSortIntWithScalarArray(nc,iptr,aptr); CHKERRQ(ierr);
|
|
iptr += nc;
|
|
aptr += nc;
|
|
}
|
|
ierr = MPI_Comm_size(comm,&size); CHKERRQ(ierr);
|
|
if (size > 1)
|
|
{
|
|
PetscInt *offdj,*coffd;
|
|
|
|
ierr = PetscMalloc1(m+1,&oii); CHKERRQ(ierr);
|
|
ierr = PetscMalloc1(onnz,&ojj); CHKERRQ(ierr);
|
|
ierr = PetscMalloc1(onnz,&oa); CHKERRQ(ierr);
|
|
ierr = PetscMemcpy(oii,hypre_CSRMatrixI(hoffd),(m+1)*sizeof(PetscInt));
|
|
CHKERRQ(ierr);
|
|
offdj = hypre_CSRMatrixJ(hoffd);
|
|
coffd = hypre_ParCSRMatrixColMapOffd(hA);
|
|
for (i=0; i<onnz; i++) { ojj[i] = coffd[offdj[i]]; }
|
|
ierr = PetscMemcpy(oa,hypre_CSRMatrixData(hoffd),onnz*sizeof(PetscScalar));
|
|
CHKERRQ(ierr);
|
|
iptr = ojj;
|
|
aptr = oa;
|
|
for (i=0; i<m; i++)
|
|
{
|
|
PetscInt nc = oii[i+1]-oii[i];
|
|
ierr = PetscSortIntWithScalarArray(nc,iptr,aptr); CHKERRQ(ierr);
|
|
iptr += nc;
|
|
aptr += nc;
|
|
}
|
|
ierr = MatCreateMPIAIJWithSplitArrays(comm,m,n,PETSC_DECIDE,PETSC_DECIDE,dii,
|
|
djj,da,oii,ojj,oa,pA); CHKERRQ(ierr);
|
|
}
|
|
else
|
|
{
|
|
oii = ojj = NULL;
|
|
oa = NULL;
|
|
ierr = MatCreateSeqAIJWithArrays(comm,m,n,dii,djj,da,pA); CHKERRQ(ierr);
|
|
}
|
|
/* We are responsible to free the CSR arrays. However, since we can take
|
|
references of a PetscParMatrix but we cannot take reference of PETSc
|
|
arrays, we need to create a PetscContainer object to take reference of
|
|
these arrays in reference objects */
|
|
void *ptrs[6] = {dii,djj,da,oii,ojj,oa};
|
|
const char *names[6] = {"_mfem_csr_dii",
|
|
"_mfem_csr_djj",
|
|
"_mfem_csr_da",
|
|
"_mfem_csr_oii",
|
|
"_mfem_csr_ojj",
|
|
"_mfem_csr_oa"
|
|
};
|
|
for (i=0; i<6; i++)
|
|
{
|
|
PetscContainer c;
|
|
|
|
ierr = PetscContainerCreate(comm,&c); CHKERRQ(ierr);
|
|
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
|
|
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
|
CHKERRQ(ierr);
|
|
ierr = PetscObjectCompose((PetscObject)(*pA),names[i],(PetscObject)c);
|
|
CHKERRQ(ierr);
|
|
ierr = PetscContainerDestroy(&c); CHKERRQ(ierr);
|
|
}
|
|
PetscFunctionReturn(0);
|
|
}
|
|
|
|
static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix* hA,Mat* pA)
|
|
{
|
|
Mat lA;
|
|
ISLocalToGlobalMapping rl2g,cl2g;
|
|
IS is;
|
|
hypre_CSRMatrix *hdiag,*hoffd;
|
|
MPI_Comm comm = hypre_ParCSRMatrixComm(hA);
|
|
void *ptrs[2];
|
|
const char *names[2] = {"_mfem_csr_aux",
|
|
"_mfem_csr_data"
|
|
};
|
|
PetscScalar *hdd,*hod,*aa,*data;
|
|
PetscInt *col_map_offd,*hdi,*hdj,*hoi,*hoj;
|
|
PetscInt *aux,*ii,*jj;
|
|
PetscInt cum,dr,dc,oc,str,stc,nnz,i,jd,jo;
|
|
PetscErrorCode ierr;
|
|
|
|
PetscFunctionBeginUser;
|
|
/* access relevant information in ParCSR */
|
|
str = hypre_ParCSRMatrixFirstRowIndex(hA);
|
|
stc = hypre_ParCSRMatrixFirstColDiag(hA);
|
|
hdiag = hypre_ParCSRMatrixDiag(hA);
|
|
hoffd = hypre_ParCSRMatrixOffd(hA);
|
|
dr = hypre_CSRMatrixNumRows(hdiag);
|
|
dc = hypre_CSRMatrixNumCols(hdiag);
|
|
nnz = hypre_CSRMatrixNumNonzeros(hdiag);
|
|
hdi = hypre_CSRMatrixI(hdiag);
|
|
hdj = hypre_CSRMatrixJ(hdiag);
|
|
hdd = hypre_CSRMatrixData(hdiag);
|
|
oc = hypre_CSRMatrixNumCols(hoffd);
|
|
nnz += hypre_CSRMatrixNumNonzeros(hoffd);
|
|
hoi = hypre_CSRMatrixI(hoffd);
|
|
hoj = hypre_CSRMatrixJ(hoffd);
|
|
hod = hypre_CSRMatrixData(hoffd);
|
|
|
|
/* generate l2g maps for rows and cols */
|
|
ierr = ISCreateStride(comm,dr,str,1,&is); CHKERRQ(ierr);
|
|
ierr = ISLocalToGlobalMappingCreateIS(is,&rl2g); CHKERRQ(ierr);
|
|
ierr = ISDestroy(&is); CHKERRQ(ierr);
|
|
col_map_offd = hypre_ParCSRMatrixColMapOffd(hA);
|
|
ierr = PetscMalloc1(dc+oc,&aux); CHKERRQ(ierr);
|
|
for (i=0; i<dc; i++) { aux[i] = i+stc; }
|
|
for (i=0; i<oc; i++) { aux[i+dc] = col_map_offd[i]; }
|
|
ierr = ISCreateGeneral(comm,dc+oc,aux,PETSC_OWN_POINTER,&is); CHKERRQ(ierr);
|
|
ierr = ISLocalToGlobalMappingCreateIS(is,&cl2g); CHKERRQ(ierr);
|
|
ierr = ISDestroy(&is); CHKERRQ(ierr);
|
|
|
|
/* create MATIS object */
|
|
ierr = MatCreate(comm,pA); CHKERRQ(ierr);
|
|
ierr = MatSetSizes(*pA,dr,dc,PETSC_DECIDE,PETSC_DECIDE); CHKERRQ(ierr);
|
|
ierr = MatSetType(*pA,MATIS); CHKERRQ(ierr);
|
|
ierr = MatSetLocalToGlobalMapping(*pA,rl2g,cl2g); CHKERRQ(ierr);
|
|
ierr = ISLocalToGlobalMappingDestroy(&rl2g); CHKERRQ(ierr);
|
|
ierr = ISLocalToGlobalMappingDestroy(&cl2g); CHKERRQ(ierr);
|
|
|
|
/* merge local matrices */
|
|
ierr = PetscMalloc1(nnz+dr+1,&aux); CHKERRQ(ierr);
|
|
ierr = PetscMalloc1(nnz,&data); CHKERRQ(ierr);
|
|
ii = aux;
|
|
jj = aux+dr+1;
|
|
aa = data;
|
|
*ii = *(hdi++) + *(hoi++);
|
|
for (jd=0,jo=0,cum=0; *ii<nnz; cum++)
|
|
{
|
|
PetscScalar *aold = aa;
|
|
PetscInt *jold = jj,nc = jd+jo;
|
|
for (; jd<*hdi; jd++) { *jj++ = *hdj++; *aa++ = *hdd++; }
|
|
for (; jo<*hoi; jo++) { *jj++ = *hoj++ + dc; *aa++ = *hod++; }
|
|
*(++ii) = *(hdi++) + *(hoi++);
|
|
ierr = PetscSortIntWithScalarArray(jd+jo-nc,jold,aold); CHKERRQ(ierr);
|
|
}
|
|
for (; cum<dr; cum++) { *(++ii) = nnz; }
|
|
ii = aux;
|
|
jj = aux+dr+1;
|
|
aa = data;
|
|
ierr = MatCreateSeqAIJWithArrays(PETSC_COMM_SELF,dr,dc+oc,ii,jj,aa,&lA);
|
|
CHKERRQ(ierr);
|
|
ptrs[0] = aux;
|
|
ptrs[1] = data;
|
|
for (i=0; i<2; i++)
|
|
{
|
|
PetscContainer c;
|
|
|
|
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); CHKERRQ(ierr);
|
|
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
|
|
ierr = PetscContainerSetUserDestroy(c,__mfem_array_container_destroy);
|
|
CHKERRQ(ierr);
|
|
ierr = PetscObjectCompose((PetscObject)lA,names[i],(PetscObject)c);
|
|
CHKERRQ(ierr);
|
|
ierr = PetscContainerDestroy(&c); CHKERRQ(ierr);
|
|
}
|
|
ierr = MatISSetLocalMat(*pA,lA); CHKERRQ(ierr);
|
|
ierr = MatDestroy(&lA); CHKERRQ(ierr);
|
|
ierr = MatAssemblyBegin(*pA,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
ierr = MatAssemblyEnd(*pA,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
|
PetscFunctionReturn(0);
|
|
}
|
|
#endif
|
|
|
|
#endif // MFEM_USE_PETSC
|
|
#endif // MFEM_USE_MPI
|