20#include "libmesh/libmesh_common.h"
22#if defined(LIBMESH_HAVE_SLEPC) && defined(LIBMESH_HAVE_PETSC)
25#include "libmesh/libmesh_logging.h"
26#include "libmesh/petsc_matrix_base.h"
27#include "libmesh/petsc_vector.h"
28#include "libmesh/slepc_eigen_solver.h"
29#include "libmesh/shell_matrix.h"
30#include "libmesh/enum_to_string.h"
31#include "libmesh/solver_configuration.h"
32#include "libmesh/enum_eigen_solver_type.h"
33#include "libmesh/petsc_shell_matrix.h"
39#if SLEPC_VERSION_LESS_THAN(3,15,0) && !SLEPC_VERSION_LESS_THAN(3,14,2)
40#include "libmesh/ignore_warnings.h"
51 _initial_space(nullptr)
72 this->_is_initialized =
false;
74 PetscErrorCode ierr = LibMeshEPSDestroy(&_eps);
76 libmesh_warning(
"Warning: EPSDestroy returned a non-zero error code which we ignored.");
91 this->_is_initialized =
true;
94 LibmeshPetscCall(EPSCreate (this->comm().get(), &_eps));
97 set_slepc_solver_type();
104std::pair<unsigned int, unsigned int>
109 const unsigned int m_its)
111 LOG_SCOPE(
"solve_standard()",
"SlepcEigenSolver");
118 auto *
const matrix_A = cast_ptr<PetscMatrixBase<T> *>(&matrix_A_in);
120 libmesh_error_msg_if(!matrix_A,
"Error: input matrix to solve_standard() must be a PetscMatrixBase.");
123 if (this->_close_matrix_before_solve)
126 return _solve_standard_helper(matrix_A->mat(),
nullptr, nev, ncv, tol, m_its);
131std::pair<unsigned int, unsigned int>
136 const unsigned int m_its)
147 LibmeshPetscCall(MatCreateShell(this->comm().get(),
152 const_cast<void *
>(
static_cast<const void *
>(&shell_matrix)),
155 LibmeshPetscCall(MatShellSetOperation(mat,MATOP_MULT,
reinterpret_cast<void(*)(
void)
>(_petsc_shell_matrix_mult)));
156 LibmeshPetscCall(MatShellSetOperation(mat,MATOP_GET_DIAGONAL,
reinterpret_cast<void(*)(
void)
>(_petsc_shell_matrix_get_diagonal)));
158 return _solve_standard_helper(mat,
nullptr, nev, ncv, tol, m_its);
162std::pair<unsigned int, unsigned int>
168 const unsigned int m_its)
175 auto *
const precond = cast_ptr<PetscMatrixBase<T> *>(&precond_in);
177 libmesh_error_msg_if(!precond,
"Error: input preconditioning matrix to solve_standard() must be a PetscMatrixBase.");
179 auto *
const matrix = cast_ptr<PetscShellMatrix<T> *> (&shell_matrix);
181 libmesh_error_msg_if(!matrix,
"Error: input operator matrix to solve_standard() must be a PetscShellMatrix.");
183 return _solve_standard_helper(matrix->mat(), precond->mat(), nev, ncv, tol, m_its);
187std::pair<unsigned int, unsigned int>
193 const unsigned int m_its)
200 auto *
const precond = cast_ptr<PetscShellMatrix<T> *>(&precond_in);
202 libmesh_error_msg_if(!precond,
"Error: input preconditioning matrix to solve_standard() must be a PetscShellMatrix.");
204 auto *
const matrix = cast_ptr<PetscShellMatrix<T> *> (&shell_matrix);
206 libmesh_error_msg_if(!matrix,
"Error: input operator matrix to solve_standard() must be a PetscShellMatrix.");
208 return _solve_standard_helper(matrix->mat(), precond->mat(), nev, ncv, tol, m_its);
214std::pair<unsigned int, unsigned int>
220 const unsigned int m_its)
222 LOG_SCOPE(
"solve_standard()",
"SlepcEigenSolver");
225 LibmeshPetscCall(EPSSetOperators (_eps, mat, LIBMESH_PETSC_NULLPTR));
227 return this->_solve_helper(precond, nev, ncv, tol, m_its);
233std::pair<unsigned int, unsigned int>
239 const unsigned int m_its)
246 auto *
const matrix_A = cast_ptr<PetscMatrixBase<T> *>(&matrix_A_in);
247 auto *
const matrix_B = cast_ptr<PetscMatrixBase<T> *>(&matrix_B_in);
249 libmesh_error_msg_if(!matrix_A || !matrix_B,
250 "Error: inputs to solve_generalized() must be of type PetscMatrixBase.");
253 if (this->_close_matrix_before_solve)
259 return _solve_generalized_helper (matrix_A->mat(), matrix_B->mat(),
nullptr, nev, ncv, tol, m_its);
263std::pair<unsigned int, unsigned int>
269 const unsigned int m_its)
280 LibmeshPetscCall(MatCreateShell(this->comm().get(),
285 const_cast<void *
>(
static_cast<const void *
>(&shell_matrix_A)),
288 auto *
const matrix_B = cast_ptr<PetscMatrixBase<T> *>(&matrix_B_in);
290 libmesh_error_msg_if(!matrix_B,
"Error: inputs to solve_generalized() must be of type PetscMatrixBase.");
293 if (this->_close_matrix_before_solve)
296 LibmeshPetscCall(MatShellSetOperation(mat_A,MATOP_MULT,
reinterpret_cast<void(*)(
void)
>(_petsc_shell_matrix_mult)));
297 LibmeshPetscCall(MatShellSetOperation(mat_A,MATOP_GET_DIAGONAL,
reinterpret_cast<void(*)(
void)
>(_petsc_shell_matrix_get_diagonal)));
299 return _solve_generalized_helper (mat_A, matrix_B->mat(),
nullptr, nev, ncv, tol, m_its);
303std::pair<unsigned int, unsigned int>
309 const unsigned int m_its)
315 auto *
const matrix_A = cast_ptr<PetscMatrixBase<T> *>(&matrix_A_in);
317 libmesh_error_msg_if(!matrix_A,
"Error: inputs to solve_generalized() must be of type PetscMatrixBase.");
320 if (this->_close_matrix_before_solve)
328 LibmeshPetscCall(MatCreateShell(this->comm().get(),
333 const_cast<void *
>(
static_cast<const void *
>(&shell_matrix_B)),
336 LibmeshPetscCall(MatShellSetOperation(mat_B,MATOP_MULT,
reinterpret_cast<void(*)(
void)
>(_petsc_shell_matrix_mult)));
337 LibmeshPetscCall(MatShellSetOperation(mat_B,MATOP_GET_DIAGONAL,
reinterpret_cast<void(*)(
void)
>(_petsc_shell_matrix_get_diagonal)));
339 return _solve_generalized_helper (matrix_A->mat(), mat_B,
nullptr, nev, ncv, tol, m_its);
343std::pair<unsigned int, unsigned int>
349 const unsigned int m_its)
360 LibmeshPetscCall(MatCreateShell(this->comm().get(),
365 const_cast<void *
>(
static_cast<const void *
>(&shell_matrix_A)),
369 LibmeshPetscCall(MatCreateShell(this->comm().get(),
374 const_cast<void *
>(
static_cast<const void *
>(&shell_matrix_B)),
377 LibmeshPetscCall(MatShellSetOperation(mat_A,MATOP_MULT,
reinterpret_cast<void(*)(
void)
>(_petsc_shell_matrix_mult)));
378 LibmeshPetscCall(MatShellSetOperation(mat_A,MATOP_GET_DIAGONAL,
reinterpret_cast<void(*)(
void)
>(_petsc_shell_matrix_get_diagonal)));
380 LibmeshPetscCall(MatShellSetOperation(mat_B,MATOP_MULT,
reinterpret_cast<void(*)(
void)
>(_petsc_shell_matrix_mult)));
381 LibmeshPetscCall(MatShellSetOperation(mat_B,MATOP_GET_DIAGONAL,
reinterpret_cast<void(*)(
void)
>(_petsc_shell_matrix_get_diagonal)));
383 return _solve_generalized_helper (mat_A, mat_B,
nullptr, nev, ncv, tol, m_its);
387std::pair<unsigned int, unsigned int>
394 const unsigned int m_its)
401 auto *
const precond = cast_ptr<PetscMatrixBase<T> *>(&precond_in);
403 libmesh_error_msg_if(!precond,
"Error: input preconditioning matrix to solve_generalized() must be of type PetscMatrixBase.");
405 auto *
const matrix_A = cast_ptr<PetscShellMatrix<T> *> (&shell_matrix_A);
407 libmesh_error_msg_if(!matrix_A,
"Error: input operator A to solve_generalized() must be of type PetscShellMatrix.");
409 auto *
const matrix_B = cast_ptr<PetscShellMatrix<T> *> (&shell_matrix_B);
411 libmesh_error_msg_if(!matrix_B,
"Error: input operator B to solve_generalized() must be of type PetscShellMatrix.");
413 return _solve_generalized_helper (matrix_A->mat(), matrix_B->mat(), precond->mat(), nev, ncv, tol, m_its);
417std::pair<unsigned int, unsigned int>
424 const unsigned int m_its)
431 auto *
const precond = cast_ptr<PetscShellMatrix<T> *>(&precond_in);
433 libmesh_error_msg_if(!precond,
"Error: input preconditioning matrix to solve_generalized() must be of type PetscShellMatrix.");
435 auto *
const matrix_A = cast_ptr<PetscShellMatrix<T> *> (&shell_matrix_A);
437 libmesh_error_msg_if(!matrix_A,
"Error: input operator A to solve_generalized() must be of type PetscShellMatrix.");
439 auto *
const matrix_B = cast_ptr<PetscShellMatrix<T> *> (&shell_matrix_B);
441 libmesh_error_msg_if(!matrix_B,
"Error: input operator B to solve_generalized() must be of type PetscShellMatrix.");
443 return _solve_generalized_helper (matrix_A->mat(), matrix_B->mat(), precond->mat(), nev, ncv, tol, m_its);
447std::pair<unsigned int, unsigned int>
454 const unsigned int m_its)
456 LOG_SCOPE(
"solve_generalized()",
"SlepcEigenSolver");
459 LibmeshPetscCall(EPSSetOperators (_eps, mat_A, mat_B));
461 return this->_solve_helper(precond, nev, ncv, tol, m_its);
467std::pair<unsigned int, unsigned int>
472 const unsigned int m_its)
480 set_slepc_problem_type();
481 set_slepc_position_of_spectrum();
484#if SLEPC_VERSION_LESS_THAN(3,0,0)
485 LibmeshPetscCall(EPSSetDimensions (_eps, nev, ncv));
487 LibmeshPetscCall(EPSSetDimensions (_eps, nev, ncv, PETSC_DECIDE));
490 LibmeshPetscCall(EPSSetTolerances (_eps, tol, m_its));
497 LibmeshPetscCall(EPSSetFromOptions (_eps));
501 LibmeshPetscCall(EPSGetST(_eps,&st));
502#if SLEPC_VERSION_LESS_THAN(3,15,0)
503 LibmeshPetscCall(STPrecondSetMatForPC(st, precond));
505 LibmeshPetscCall(STSetPreconditionerMat(st, precond));
511 if (this->_solver_configuration)
513 this->_solver_configuration->configure_solver();
517 if (_initial_space) {
519 Vec initial_vector = _initial_space->vec();
521 LibmeshPetscCall(EPSSetInitialSpace(_eps, 1, &initial_vector));
525 LibmeshPetscCall(EPSSolve (_eps));
528 LibmeshPetscCall(EPSGetIterationNumber (_eps, &its));
531 LibmeshPetscCall(EPSGetConverged(_eps,&nconv));
535 return std::make_pair(nconv, its);
546 PetscReal error, re, im;
552 LibmeshPetscCall(EPSGetConverged(_eps,&nconv));
555 LibmeshPetscCall(PetscPrintf(this->comm().get(),
556 " k ||Ax-kx||/|kx|\n"
557 " ----------------- -----------------\n" ));
559 for (PetscInt i=0; i<nconv; i++ )
561 LibmeshPetscCall(EPSGetEigenpair(_eps, i, &kr, &ki, LIBMESH_PETSC_NULLPTR,
562 LIBMESH_PETSC_NULLPTR));
564#if SLEPC_VERSION_LESS_THAN(3,6,0)
565 LibmeshPetscCall(EPSComputeRelativeError(_eps, i, &error));
567 LibmeshPetscCall(EPSComputeError(_eps, i, EPS_ERROR_RELATIVE, &error));
570#ifdef LIBMESH_USE_COMPLEX_NUMBERS
571 re = PetscRealPart(kr);
572 im = PetscImaginaryPart(kr);
579 LibmeshPetscCall(PetscPrintf(this->comm().get(),
" %9f%+9f i %12f\n",
double(re),
double(im),
double(error)));
581 LibmeshPetscCall(PetscPrintf(this->comm().get(),
" %12f %12f\n",
double(re),
double(error)));
584 LibmeshPetscCall(PetscPrintf(this->comm().get(),
"\n" ));
591 switch (this->_eigen_solver_type)
594 LibmeshPetscCall(EPSSetType (_eps, EPSPOWER));
return;
596 LibmeshPetscCall(EPSSetType (_eps, EPSSUBSPACE));
return;
598 LibmeshPetscCall(EPSSetType (_eps, EPSLAPACK));
return;
600 LibmeshPetscCall(EPSSetType (_eps, EPSARNOLDI));
return;
602 LibmeshPetscCall(EPSSetType (_eps, EPSLANCZOS));
return;
604 LibmeshPetscCall(EPSSetType (_eps, EPSKRYLOVSCHUR));
return;
609 libMesh::err <<
"ERROR: Unsupported SLEPc Eigen Solver: "
611 <<
"Continuing with SLEPc defaults" << std::endl;
621 switch (this->_eigen_problem_type)
624 LibmeshPetscCall(EPSSetProblemType (_eps, EPS_NHEP));
return;
626 LibmeshPetscCall(EPSSetProblemType (_eps, EPS_GNHEP));
return;
628 LibmeshPetscCall(EPSSetProblemType (_eps, EPS_HEP));
return;
630 LibmeshPetscCall(EPSSetProblemType (_eps, EPS_GHEP));
return;
631#if !SLEPC_VERSION_LESS_THAN(3,3,0)
634 LibmeshPetscCall(EPSSetProblemType (_eps, EPS_GHIEP));
return;
638 libMesh::err <<
"ERROR: Unsupported SLEPc Eigen Problem: "
639 << this->_eigen_problem_type << std::endl
640 <<
"Continuing with SLEPc defaults" << std::endl;
649 switch (this->_position_of_spectrum)
653 LibmeshPetscCall(EPSSetWhichEigenpairs (_eps, EPS_LARGEST_MAGNITUDE));
658 LibmeshPetscCall(EPSSetWhichEigenpairs (_eps, EPS_SMALLEST_MAGNITUDE));
663 LibmeshPetscCall(EPSSetWhichEigenpairs (_eps, EPS_LARGEST_REAL));
668 LibmeshPetscCall(EPSSetWhichEigenpairs (_eps, EPS_SMALLEST_REAL));
673 LibmeshPetscCall(EPSSetWhichEigenpairs (_eps, EPS_LARGEST_IMAGINARY));
678 LibmeshPetscCall(EPSSetWhichEigenpairs (_eps, EPS_SMALLEST_IMAGINARY));
683#if !SLEPC_VERSION_LESS_THAN(3,1,0)
686 LibmeshPetscCall(EPSSetTarget(_eps,
PS(this->_target_val)));
687 LibmeshPetscCall(EPSSetWhichEigenpairs (_eps, EPS_TARGET_MAGNITUDE));
692 LibmeshPetscCall(EPSSetTarget(_eps,
PS(this->_target_val)));
693 LibmeshPetscCall(EPSSetWhichEigenpairs (_eps, EPS_TARGET_REAL));
698 LibmeshPetscCall(EPSSetTarget(_eps,
PS(this->_target_val)));
699 LibmeshPetscCall(EPSSetWhichEigenpairs (_eps, EPS_TARGET_IMAGINARY));
705 libmesh_error_msg(
"ERROR: Unsupported SLEPc position of spectrum: " << this->_position_of_spectrum);
723 libmesh_error_msg_if(!solution,
"Error getting eigenvector: input vector must be a PetscVector.");
730 LibmeshPetscCall(EPSGetEigenpair(_eps, i, &kr, &ki, solution->
vec(),
731 LIBMESH_PETSC_NULLPTR));
733#ifdef LIBMESH_USE_COMPLEX_NUMBERS
734 re = PetscRealPart(kr);
735 im = PetscImaginaryPart(kr);
741 return std::make_pair(re, im);
753 LibmeshPetscCall(EPSGetEigenvalue(_eps, i, &kr, &ki));
755#ifdef LIBMESH_USE_COMPLEX_NUMBERS
756 re = PetscRealPart(kr);
757 im = PetscImaginaryPart(kr);
763 return std::make_pair(re, im);
772#if SLEPC_VERSION_LESS_THAN(3,6,0)
773 LibmeshPetscCall(EPSComputeRelativeError(_eps, i, &error));
775 LibmeshPetscCall(EPSComputeError(_eps, i, EPS_ERROR_RELATIVE, &error));
791 libmesh_error_msg_if(!deflation_vector_petsc_vec,
"Error attaching deflation space: input vector must be a PetscVector.");
794 Vec deflation_vector = deflation_vector_petsc_vec->
vec();
796#if SLEPC_VERSION_LESS_THAN(3,1,0)
797 LibmeshPetscCall(EPSAttachDeflationSpace(_eps, 1, &deflation_vector, PETSC_FALSE));
799 LibmeshPetscCall(EPSSetDeflationSpace(_eps, 1, &deflation_vector));
806#if SLEPC_VERSION_LESS_THAN(3,1,0)
807 libmesh_error_msg(
"SLEPc 3.1 is required to call EigenSolver::set_initial_space()");
811 _initial_space = cast_ptr<PetscVector<T> *>(&initial_space_in);
866#if SLEPC_VERSION_LESS_THAN(3,15,0) && !SLEPC_VERSION_LESS_THAN(3,14,2)
867#include "libmesh/restore_warnings.h"
This class provides an interface to solvers for eigenvalue problems.
EigenSolverType _eigen_solver_type
Enum stating which type of eigensolver to use.
EigenProblemType _eigen_problem_type
Enum stating which type of eigen problem we deal with.
Provides a uniform interface to vector storage schemes for different linear algebra libraries.
const Parallel::Communicator & comm() const
This class provides a nice interface to PETSc's Vec object.
virtual void close() override
Calls the NumericVector's internal assembly routines, ensuring that the values are consistent across ...
Generic shell matrix, i.e.
virtual numeric_index_type n() const =0
virtual void vector_mult(NumericVector< T > &dest, const NumericVector< T > &arg) const =0
Multiplies the matrix with arg and stores the result in dest.
virtual void get_diagonal(NumericVector< T > &dest) const =0
Copies the diagonal part of the matrix into dest.
virtual numeric_index_type m() const =0
This class provides an interface to the SLEPc eigenvalue solver library from http://slepc....
void set_slepc_solver_type()
Tells Slepc to use the user-specified solver stored in _eigen_solver_type.
std::pair< unsigned int, unsigned int > _solve_generalized_helper(Mat mat_A, Mat mat_B, Mat precond, int nev, int ncv, const double tol, const unsigned int m_its)
Helper function that actually performs the generalized eigensolve.
virtual std::pair< unsigned int, unsigned int > solve_standard(SparseMatrix< T > &matrix_A, int nev, int ncv, const double tol, const unsigned int m_its) override
This function calls the SLEPc solver to compute the eigenpairs of the SparseMatrix matrix_A.
void set_slepc_problem_type()
Tells Slepc to deal with the type of problem stored in _eigen_problem_type.
virtual std::pair< unsigned int, unsigned int > solve_generalized(SparseMatrix< T > &matrix_A, SparseMatrix< T > &matrix_B, int nev, int ncv, const double tol, const unsigned int m_its) override
This function calls the SLEPc solver to compute the eigenpairs for the generalized eigenproblem defin...
virtual std::pair< Real, Real > get_eigenvalue(dof_id_type i) override
Same as above, but does not copy the eigenvector.
virtual std::pair< Real, Real > get_eigenpair(dof_id_type i, NumericVector< T > &solution_in) override
std::pair< unsigned int, unsigned int > _solve_helper(Mat precond, int nev, int ncv, const double tol, const unsigned int m_its)
Helper function that actually performs either eigensolve.
void set_slepc_position_of_spectrum()
Tells Slepc to compute the spectrum at the position stored in _position_of_spectrum.
Real get_relative_error(unsigned int i)
SlepcEigenSolver(const Parallel::Communicator &comm_in)
Constructor.
~SlepcEigenSolver()
Destructor.
virtual void attach_deflation_space(NumericVector< T > &deflation_vector) override
Attach a deflation space defined by a single vector.
void print_eigenvalues() const
Print the eigenvalues and associated error.
static PetscErrorCode _petsc_shell_matrix_get_diagonal(Mat mat, Vec dest)
Internal function if shell matrix mode is used, this just calls the shell matrix's get_diagonal funct...
virtual void set_initial_space(NumericVector< T > &initial_space_in) override
Use initial_space_in as the initial guess.
static PetscErrorCode _petsc_shell_matrix_mult(Mat mat, Vec arg, Vec dest)
Internal function if shell matrix mode is used, this just calls the shell matrix's matrix multiplicat...
std::pair< unsigned int, unsigned int > _solve_standard_helper(Mat mat, Mat precond, int nev, int ncv, const double tol, const unsigned int m_its)
Helper function that actually performs the standard eigensolve.
virtual void clear() noexcept override
Release all memory and clear data structures.
virtual void init() override
Initialize data structures if not done so already.
std::string enum_to_string(const T e)
The libMesh namespace provides an interface to certain functionality in the library.
PetscFunctionReturn(LIBMESH_PETSC_SUCCESS)
bool initialized()
Checks that library initialization has been done.
LibmeshPetscCallQ(DMShellGetContext(dm, &ctx))
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real