deal.II version GIT relicensing-6842-g793a97d2aa 2026-10-02 14:00:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
slepc_solver.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2009 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
14
15#ifdef DEAL_II_WITH_SLEPC
19
20# include <petscversion.h>
21
22# include <slepcversion.h>
23
24# include <cmath>
25# include <vector>
26#endif
27
29
30
31#ifdef DEAL_II_WITH_SLEPC
32
33namespace SLEPcWrappers
34{
35 SolverBase::SolverBase(SolverControl &cn, const MPI_Comm mpi_communicator)
36 : solver_control(cn)
37 , mpi_communicator(mpi_communicator)
38 , reason(EPS_CONVERGED_ITERATING)
39 {
40 // create eigensolver context
41 PetscErrorCode ierr = EPSCreate(mpi_communicator, &eps);
42 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
43
44 // hand over the absolute tolerance and the maximum number of
45 // iteration steps to the SLEPc convergence criterion.
46 ierr = EPSSetTolerances(eps,
48 this->solver_control.max_steps());
49 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
50
51 // default values:
52 set_which_eigenpairs(EPS_LARGEST_MAGNITUDE);
53 set_problem_type(EPS_GNHEP);
54
55 // TODO:
56 // By default, EPS initializes the starting vector or the initial subspace
57 // randomly.
58 }
59
60
61
63 {
64 if (eps != nullptr)
65 {
66 // Destroy the solver object.
67 const PetscErrorCode ierr = EPSDestroy(&eps);
68 AssertNothrow(ierr == 0, ExcSLEPcError(ierr));
69 }
70 }
71
72
73
74 void
76 {
77 // standard eigenspectrum problem
78 const PetscErrorCode ierr = EPSSetOperators(eps, A, nullptr);
79 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
80 }
81
82
83
84 void
87 {
88 // generalized eigenspectrum problem
89 const PetscErrorCode ierr = EPSSetOperators(eps, A, B);
90 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
91 }
92
93
94
95 void
98 {
99 // set transformation type if any
100 // STSetShift is called inside
101 PetscErrorCode ierr = EPSSetST(eps, transformation.st);
102 AssertThrow(ierr == 0, SolverBase::ExcSLEPcError(ierr));
103
104# if DEAL_II_SLEPC_VERSION_GTE(3, 8, 0)
105 // see
106 // https://lists.mcs.anl.gov/mailman/htdig/petsc-users/2017-October/033649.html
107 // From 3.8.0 onward, SLEPc insists that when looking for smallest
108 // eigenvalues with shift-and-invert, users should (a) set target,
109 // (b) use EPS_TARGET_MAGNITUDE. The former, however, needs to be
110 // applied to the 'eps' object and not the spectral transformation.
113 &transformation))
114 {
115 ierr = EPSSetTarget(eps, sinv->additional_data.shift_parameter);
116 AssertThrow(ierr == 0, SolverBase::ExcSLEPcError(ierr));
117 }
118# endif
119 }
120
121
122
123 void
124 SolverBase::set_target_eigenvalue(const PetscScalar &this_target)
125 {
126 // set target eigenvalues to solve for
127 // in all transformation except STSHIFT there is a direct connection between
128 // the target and the shift, read more on p41 of SLEPc manual.
129 const PetscErrorCode ierr = EPSSetTarget(eps, this_target);
130 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
131 }
132
133
134
135 void
136 SolverBase::set_which_eigenpairs(const EPSWhich eps_which)
137 {
138 // set which portion of the eigenspectrum to solve for
139 const PetscErrorCode ierr = EPSSetWhichEigenpairs(eps, eps_which);
140 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
141 }
142
143
144
145 void
146 SolverBase::set_problem_type(const EPSProblemType eps_problem)
147 {
148 const PetscErrorCode ierr = EPSSetProblemType(eps, eps_problem);
149 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
150 }
151
152
153
154 void
155 SolverBase::solve(const unsigned int n_eigenpairs, unsigned int *n_converged)
156 {
157 // set number of eigenvectors to compute
158 PetscErrorCode ierr =
159 EPSSetDimensions(eps, n_eigenpairs, PETSC_DECIDE, PETSC_DECIDE);
160 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
161
162 // set the solve options to the eigenvalue problem solver context
163 ierr = EPSSetFromOptions(eps);
164 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
165
166 // TODO breaks @ref step_36 "step-36"
167 // force Krylov solver to use true residual instead of an estimate.
168 // EPSSetTrueResidual(solver_data->eps, PETSC_TRUE);
169 // AssertThrow (ierr == 0, ExcSLEPcError(ierr));
170
171 // Set convergence test to be absolute
172 ierr = EPSSetConvergenceTest(eps, EPS_CONV_ABS);
173 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
174
175 // TODO Set the convergence test function
176 // ierr = EPSSetConvergenceTestFunction (solver_data->eps,
177 // &convergence_test,
178 // reinterpret_cast<void *>(&solver_control));
179 // AssertThrow (ierr == 0, ExcSLEPcError(ierr));
180
181 // solve the eigensystem
182 ierr = EPSSolve(eps);
183 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
184
185 // Get number of converged eigenstates. We need to go around with a
186 // temporary variable once because the function wants to have a
187 // PetscInt as second argument whereas the `n_converged` argument
188 // to this function is just an unsigned int.
189 {
190 PetscInt petsc_n_converged = *n_converged;
191 ierr = EPSGetConverged(eps, &petsc_n_converged);
192 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
193 *n_converged = petsc_n_converged;
194 }
195
196 PetscInt n_iterations = 0;
197 PetscReal residual_norm = 0;
198
199 // @todo Investigate elaborating on some of this to act on the
200 // complete eigenspectrum
201 {
202 // get the number of solver iterations
203 ierr = EPSGetIterationNumber(eps, &n_iterations);
204 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
205
206 // get the maximum of residual norm among converged eigenvectors.
207 for (unsigned int i = 0; i < *n_converged; ++i)
208 {
209 double residual_norm_i = 0.0;
210 // EPSComputeError (or, in older versions of SLEPc,
211 // EPSComputeResidualNorm) uses an L2-norm and is not consistent
212 // with the stopping criterion used during the solution process (see
213 // the SLEPC manual, section 2.5). However, the norm that gives error
214 // bounds (Saad, 1992, ch3) is (for Hermitian problems)
215 // | \lambda - \widehat\lambda | <= ||r||_2
216 //
217 // Similarly, EPSComputeRelativeError may not be consistent with the
218 // stopping criterion used in the solution process.
219 //
220 // EPSGetErrorEstimate is (according to the SLEPc manual) consistent
221 // with the residual norm used during the solution process. However,
222 // it is not guaranteed to be derived from the residual even when
223 // EPSSetTrueResidual is set: see the discussion in the thread
224 //
225 // https://lists.mcs.anl.gov/pipermail/petsc-users/2014-November/023509.html
226 //
227 // for more information.
228# if DEAL_II_SLEPC_VERSION_GTE(3, 6, 0)
229 ierr = EPSComputeError(eps, i, EPS_ERROR_ABSOLUTE, &residual_norm_i);
230# else
231 ierr = EPSComputeResidualNorm(eps, i, &residual_norm_i);
232# endif
233
234 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
235 residual_norm = std::max(residual_norm, residual_norm_i);
236 }
237
238 // check the solver state
239 const SolverControl::State state =
240 solver_control.check(n_iterations, residual_norm);
241
242 // get the solver state according to SLEPc
243 get_solver_state(state);
244
245 // as SLEPc uses different stopping criteria, we have to omit this step.
246 // This can be checked only in conjunction with EPSGetErrorEstimate.
247 // and in case of failure: throw exception
248 // if (solver_control.last_check () != SolverControl::success)
249 // AssertThrow(false, SolverControl::NoConvergence
250 // (solver_control.last_step(),
251 // solver_control.last_value()));
252 }
253 }
254
255
256
257 void
258 SolverBase::get_eigenpair(const unsigned int index,
259 PetscScalar &eigenvalues,
261 {
262 // get converged eigenpair
263 const PetscErrorCode ierr =
264 EPSGetEigenpair(eps, index, &eigenvalues, nullptr, eigenvectors, nullptr);
265 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
266 }
267
268
269
270 void
271 SolverBase::get_eigenpair(const unsigned int index,
272 double &real_eigenvalues,
273 double &imag_eigenvalues,
274 PETScWrappers::VectorBase &real_eigenvectors,
275 PETScWrappers::VectorBase &imag_eigenvectors)
276 {
277# ifndef PETSC_USE_COMPLEX
278 // get converged eigenpair
279 const PetscErrorCode ierr = EPSGetEigenpair(eps,
280 index,
281 &real_eigenvalues,
282 &imag_eigenvalues,
283 real_eigenvectors,
284 imag_eigenvectors);
285 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
286# else
287 Assert(
288 (false),
290 "Your PETSc/SLEPc installation was configured with scalar-type complex "
291 "but this function is not defined for complex types."));
292
293 // Cast to void to silence compiler warnings
294 (void)index;
295 (void)real_eigenvalues;
296 (void)imag_eigenvalues;
297 (void)real_eigenvectors;
298 (void)imag_eigenvectors;
299# endif
300 }
301
302
303
304 void
306 {
307 switch (state)
308 {
309 case ::SolverControl::iterate:
310 reason = EPS_CONVERGED_ITERATING;
311 break;
312
313 case ::SolverControl::success:
314 reason = static_cast<EPSConvergedReason>(1);
315 break;
316
317 case ::SolverControl::failure:
319 reason = EPS_DIVERGED_ITS;
320 else
321 reason = EPS_DIVERGED_BREAKDOWN;
322 break;
323
324 default:
326 }
327 }
328
329
330
331 /* ---------------------- SolverControls ----------------------- */
334 {
335 return solver_control;
336 }
337
338
339
340 int
342 EPS /*eps */,
343 PetscScalar /*real_eigenvalue */,
344 PetscScalar /*imag_eigenvalue */,
345 PetscReal /*residual norm associated to the eigenpair */,
346 PetscReal * /*(output) computed error estimate */,
347 void * /*solver_control_x*/)
348 {
349 // If the error estimate returned by the convergence test function is less
350 // than the tolerance, then the eigenvalue is accepted as converged.
351 // This function is undefined (future reference only).
352
353 // return without failure.
354 return 0;
355 }
356
357
358
359 /* ---------------------- SolverKrylovSchur ------------------------ */
361 const MPI_Comm mpi_communicator,
362 const AdditionalData &data)
363 : SolverBase(cn, mpi_communicator)
364 , additional_data(data)
365 {
366 const PetscErrorCode ierr =
367 EPSSetType(eps, const_cast<char *>(EPSKRYLOVSCHUR));
368 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
369 }
370
371
372
373 /* ---------------------- SolverArnoldi ------------------------ */
375 const bool delayed_reorthogonalization)
376 : delayed_reorthogonalization(delayed_reorthogonalization)
377 {}
378
379
380
383 const AdditionalData &data)
386 {
387 PetscErrorCode ierr = EPSSetType(eps, const_cast<char *>(EPSARNOLDI));
388 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
389
390 // if requested, set delayed reorthogonalization in the Arnoldi
391 // iteration.
393 {
394 ierr = EPSArnoldiSetDelayed(eps, PETSC_TRUE);
395 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
396 }
397 }
398
399
400
401 /* ---------------------- Lanczos ------------------------ */
402 SolverLanczos::AdditionalData::AdditionalData(const EPSLanczosReorthogType r)
403 : reorthog(r)
404 {}
405
406
407
410 const AdditionalData &data)
413 {
414 PetscErrorCode ierr = EPSSetType(eps, const_cast<char *>(EPSLANCZOS));
415 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
416
417 ierr = EPSLanczosSetReorthog(eps, additional_data.reorthog);
418 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
419 }
420
421
422
423 /* ----------------------- Power ------------------------- */
425 const MPI_Comm mpi_communicator,
426 const AdditionalData &data)
427 : SolverBase(cn, mpi_communicator)
428 , additional_data(data)
429 {
430 PetscErrorCode ierr = EPSSetType(eps, const_cast<char *>(EPSPOWER));
431 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
432 }
433
434
435
436 /* ---------------- Generalized Davidson ----------------- */
438 bool double_expansion)
439 : double_expansion(double_expansion)
440 {}
441
442
443
445 SolverControl &cn,
447 const AdditionalData &data)
450 {
451 PetscErrorCode ierr = EPSSetType(eps, const_cast<char *>(EPSGD));
452 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
453
455 {
456 ierr = EPSGDSetDoubleExpansion(eps, PETSC_TRUE);
457 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
458 }
459 }
460
461
462
463 /* ------------------ Jacobi Davidson -------------------- */
465 const MPI_Comm mpi_communicator,
466 const AdditionalData &data)
467 : SolverBase(cn, mpi_communicator)
468 , additional_data(data)
469 {
470 const PetscErrorCode ierr = EPSSetType(eps, const_cast<char *>(EPSJD));
471 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
472 }
473
474
475
476 /* ---------------------- LAPACK ------------------------- */
478 const MPI_Comm mpi_communicator,
479 const AdditionalData &data)
480 : SolverBase(cn, mpi_communicator)
481 , additional_data(data)
482 {
483 // 'Tis overwhelmingly likely that PETSc/SLEPc *always* has
484 // BLAS/LAPACK, but let's be defensive.
485# if PETSC_HAVE_BLASLAPACK
486 const PetscErrorCode ierr = EPSSetType(eps, const_cast<char *>(EPSLAPACK));
487 AssertThrow(ierr == 0, ExcSLEPcError(ierr));
488# else
489 Assert(
490 (false),
492 "Your PETSc/SLEPc installation was not configured with BLAS/LAPACK "
493 "but this is needed to use the LAPACK solver."));
494# endif
495 }
496} // namespace SLEPcWrappers
497
498#endif // DEAL_II_WITH_SLEPC
499
SolverArnoldi(SolverControl &cn, const MPI_Comm mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
const AdditionalData additional_data
SolverControl & control() const
EPSConvergedReason reason
void set_target_eigenvalue(const PetscScalar &this_target)
const MPI_Comm mpi_communicator
void set_matrices(const PETScWrappers::MatrixBase &A)
SolverBase(SolverControl &cn, const MPI_Comm mpi_communicator)
void get_solver_state(const SolverControl::State state)
void set_problem_type(EPSProblemType set_problem)
SolverControl & solver_control
void solve(const PETScWrappers::MatrixBase &A, std::vector< PetscScalar > &eigenvalues, std::vector< OutputVector > &eigenvectors, const unsigned int n_eigenpairs=1)
void get_eigenpair(const unsigned int index, PetscScalar &eigenvalues, PETScWrappers::VectorBase &eigenvectors)
void set_transformation(SLEPcWrappers::TransformationBase &this_transformation)
static int convergence_test(EPS eps, PetscScalar real_eigenvalue, PetscScalar imag_eigenvalue, PetscReal residual_norm, PetscReal *estimated_error, void *solver_control)
void set_which_eigenpairs(EPSWhich set_which)
SolverGeneralizedDavidson(SolverControl &cn, const MPI_Comm mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
SolverJacobiDavidson(SolverControl &cn, const MPI_Comm mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
SolverKrylovSchur(SolverControl &cn, const MPI_Comm mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
SolverLAPACK(SolverControl &cn, const MPI_Comm mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
const AdditionalData additional_data
SolverLanczos(SolverControl &cn, const MPI_Comm mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
SolverPower(SolverControl &cn, const MPI_Comm mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
unsigned int last_step() const
virtual State check(const unsigned int step, const double check_value)
unsigned int max_steps() const
double tolerance() const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_NOT_IMPLEMENTED()
static ::ExceptionBase & ExcSLEPcError(int arg1)
#define Assert(cond, exc)
#define AssertNothrow(cond, exc)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
std::vector< index_type > data
Definition mpi.cc:734
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
AdditionalData(const bool delayed_reorthogonalization=false)
AdditionalData(const EPSLanczosReorthogType r=EPS_LANCZOS_REORTHOG_FULL)
std::array< Number, 1 > eigenvalues(const SymmetricTensor< 2, 1, Number > &T)
std::array< std::pair< Number, Tensor< 1, dim, Number > >, dim > eigenvectors(const SymmetricTensor< 2, dim, Number > &T, const SymmetricTensorEigenvectorMethod method=SymmetricTensorEigenvectorMethod::ql_implicit_shifts)