Reference documentation for deal.II version 9.0.0
slepc_solver.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2009 - 2018 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE at
12 // the top level of the deal.II distribution.
13 //
14 // ---------------------------------------------------------------------
15 
16 #include <deal.II/lac/slepc_solver.h>
17 
18 #ifdef DEAL_II_WITH_SLEPC
19 
20 # include <deal.II/lac/petsc_matrix_base.h>
21 # include <deal.II/lac/petsc_vector_base.h>
22 # include <deal.II/lac/slepc_spectral_transformation.h>
23 
24 # include <cmath>
25 # include <vector>
26 
27 # include <petscversion.h>
28 # include <slepcversion.h>
29 
30 DEAL_II_NAMESPACE_OPEN
31 
32 namespace SLEPcWrappers
33 {
34 
36  const MPI_Comm &mpi_communicator)
37  :
38  solver_control (cn),
39  mpi_communicator (mpi_communicator),
40  reason (EPS_CONVERGED_ITERATING)
41  {
42  // create eigensolver context
43  PetscErrorCode ierr = EPSCreate (mpi_communicator, &eps);
44  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
45 
46  // hand over the absolute tolerance and the maximum number of
47  // iteration steps to the SLEPc convergence criterion.
48  ierr = EPSSetTolerances(eps, this->solver_control.tolerance(),
49  this->solver_control.max_steps());
50  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
51 
52  // default values:
53  set_which_eigenpairs(EPS_LARGEST_MAGNITUDE);
54  set_problem_type(EPS_GNHEP);
55 
56  // TODO:
57  // By default, EPS initializes the starting vector or the initial subspace randomly.
58  }
59 
61  {
62  if (eps != nullptr)
63  {
64  // Destroy the solver object.
65  const PetscErrorCode ierr = EPSDestroy (&eps);
66 
67  (void)ierr;
68  AssertNothrow (ierr == 0, ExcSLEPcError(ierr));
69  }
70  }
71 
72  void
74  {
75  // standard eigenspectrum problem
76  const PetscErrorCode ierr = EPSSetOperators (eps, A, nullptr);
77  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
78  }
79 
80  void
83  {
84  // generalized eigenspectrum problem
85  const PetscErrorCode ierr = EPSSetOperators (eps, A, B);
86  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
87  }
88 
89  void
91  {
92  // set transformation type if any
93  // STSetShift is called inside
94  PetscErrorCode ierr = EPSSetST(eps,transformation.st);
95  AssertThrow (ierr == 0, SolverBase::ExcSLEPcError(ierr));
96 
97 #if DEAL_II_SLEPC_VERSION_GTE(3, 8, 0)
98  // see https://lists.mcs.anl.gov/mailman/htdig/petsc-users/2017-October/033649.html
99  // From 3.8.0 SLEPc insists that when looking for smallest eigenvalues with shift-and-invert
100  // users should (a) set target (b) use EPS_TARGET_MAGNITUDE
101  // The former, however, needs to be applied to eps object and not spectral transformation.
102  if (SLEPcWrappers::TransformationShiftInvert *sinv = dynamic_cast<SLEPcWrappers::TransformationShiftInvert *>(&transformation))
103  {
104  ierr = EPSSetTarget (eps, sinv->additional_data.shift_parameter);
105  AssertThrow (ierr == 0, SolverBase::ExcSLEPcError(ierr));
106  }
107 #endif
108 
109  }
110 
111  void
113  {
114  Assert(this_initial_vector.l2_norm()>0.0,
115  ExcMessage("Initial vector should be nonzero."));
116 
117  Vec vec = this_initial_vector;
118  const PetscErrorCode ierr = EPSSetInitialSpace (eps, 1, &vec);
119 
120  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
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  void
134  SolverBase::set_which_eigenpairs (const EPSWhich eps_which)
135  {
136  // set which portion of the eigenspectrum to solve for
137  const PetscErrorCode ierr = EPSSetWhichEigenpairs (eps, eps_which);
138  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
139  }
140 
141  void
142  SolverBase::set_problem_type (const EPSProblemType eps_problem)
143  {
144  const PetscErrorCode ierr = EPSSetProblemType (eps, eps_problem);
145  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
146  }
147 
148  void
149  SolverBase::solve (const unsigned int n_eigenpairs,
150  unsigned int *n_converged)
151  {
152  // set number of eigenvectors to compute
153  PetscErrorCode ierr = EPSSetDimensions (eps, n_eigenpairs,
154  PETSC_DECIDE, PETSC_DECIDE);
155  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
156 
157  // set the solve options to the eigenvalue problem solver context
158  ierr = EPSSetFromOptions (eps);
159  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
160 
161  // TODO breaks @ref step_36 "step-36"
162  // force Krylov solver to use true residual instead of an estimate.
163  //EPSSetTrueResidual(solver_data->eps, PETSC_TRUE);
164  //AssertThrow (ierr == 0, ExcSLEPcError(ierr));
165 
166  // Set convergence test to be absolute
167  ierr = EPSSetConvergenceTest (eps, EPS_CONV_ABS);
168  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
169 
170  // TODO Set the convergence test function
171  // ierr = EPSSetConvergenceTestFunction (solver_data->eps, &convergence_test,
172  // reinterpret_cast<void *>(&solver_control));
173  // AssertThrow (ierr == 0, ExcSLEPcError(ierr));
174 
175  // solve the eigensystem
176  ierr = EPSSolve (eps);
177  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
178 
179  // get number of converged eigenstates
180  ierr = EPSGetConverged (eps,
181  reinterpret_cast<PetscInt *>(n_converged));
182  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
183 
184  PetscInt n_iterations = 0;
185  PetscReal residual_norm = 0;
186 
187  // @todo Investigate elaborating on some of this to act on the
188  // complete eigenspectrum
189  {
190  // get the number of solver iterations
191  ierr = EPSGetIterationNumber (eps, &n_iterations);
192  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
193 
194  // get the maximum of residual norm among converged eigenvectors.
195  for (unsigned int i = 0; i < *n_converged; i++)
196  {
197  double residual_norm_i = 0.0;
198  // EPSComputeError (or, in older versions of SLEPc,
199  // EPSComputeResidualNorm) uses an L2-norm and is not consistent
200  // with the stopping criterion used during the solution process (see
201  // the SLEPC manual, section 2.5). However, the norm that gives error
202  // bounds (Saad, 1992, ch3) is (for Hermitian problems)
203  // | \lambda - \widehat\lambda | <= ||r||_2
204  //
205  // Similarly, EPSComputeRelativeError may not be consistent with the
206  // stopping criterion used in the solution process.
207  //
208  // EPSGetErrorEstimate is (according to the SLEPc manual) consistent
209  // with the residual norm used during the solution process. However,
210  // it is not guaranteed to be derived from the residual even when
211  // EPSSetTrueResidual is set: see the discussion in the thread
212  //
213  // https://lists.mcs.anl.gov/pipermail/petsc-users/2014-November/023509.html
214  //
215  // for more information.
216 #if DEAL_II_SLEPC_VERSION_GTE(3, 6, 0)
217  ierr = EPSComputeError(eps, i, EPS_ERROR_ABSOLUTE, &residual_norm_i);
218 #else
219  ierr = EPSComputeResidualNorm(eps, i, &residual_norm_i);
220 #endif
221 
222  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
223  residual_norm = std::max (residual_norm, residual_norm_i);
224  }
225 
226  // check the solver state
227  const SolverControl::State state
228  = solver_control.check (n_iterations, residual_norm);
229 
230  // get the solver state according to SLEPc
231  get_solver_state (state);
232 
233  // as SLEPc uses different stopping criteria, we have to omit this step.
234  // This can be checked only in conjunction with EPSGetErrorEstimate.
235  // and in case of failure: throw exception
236  // if (solver_control.last_check () != SolverControl::success)
237  // AssertThrow(false, SolverControl::NoConvergence (solver_control.last_step(),
238  // solver_control.last_value()));
239  }
240  }
241 
242  void
243  SolverBase::get_eigenpair (const unsigned int index,
244  PetscScalar &eigenvalues,
246  {
247  // get converged eigenpair
248  const PetscErrorCode ierr = EPSGetEigenpair (eps, index,
249  &eigenvalues, nullptr,
250  eigenvectors, nullptr);
251  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
252  }
253 
254 
255  void
256  SolverBase::get_eigenpair (const unsigned int index,
257  double &real_eigenvalues,
258  double &imag_eigenvalues,
259  PETScWrappers::VectorBase &real_eigenvectors,
260  PETScWrappers::VectorBase &imag_eigenvectors)
261  {
262 #ifndef PETSC_USE_COMPLEX
263  // get converged eigenpair
264  const PetscErrorCode ierr = EPSGetEigenpair (eps, index,
265  &real_eigenvalues,
266  &imag_eigenvalues,
267  real_eigenvectors,
268  imag_eigenvectors);
269  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
270 #else
271  Assert ((false),
272  ExcMessage ("Your PETSc/SLEPc installation was configured with scalar-type complex "
273  "but this function is not defined for complex types."));
274 
275  // Cast to void to silence compiler warnings
276  (void) index;
277  (void) real_eigenvalues;
278  (void) imag_eigenvalues;
279  (void) real_eigenvectors;
280  (void) imag_eigenvectors;
281 #endif
282  }
283 
284  void
286  {
287  switch (state)
288  {
289  case ::SolverControl::iterate:
290  reason = EPS_CONVERGED_ITERATING;
291  break;
292 
293  case ::SolverControl::success:
294  reason = static_cast<EPSConvergedReason>(1);
295  break;
296 
297  case ::SolverControl::failure:
299  reason = EPS_DIVERGED_ITS;
300  else
301  reason = EPS_DIVERGED_BREAKDOWN;
302  break;
303 
304  default:
305  Assert (false, ExcNotImplemented());
306  }
307  }
308 
309  /* ---------------------- SolverControls ----------------------- */
310  SolverControl &
312  {
313  return solver_control;
314  }
315 
316  int
318  PetscScalar /*real_eigenvalue */,
319  PetscScalar /*imag_eigenvalue */,
320  PetscReal /*residual norm associated to the eigenpair */,
321  PetscReal */*(output) computed error estimate */,
322  void */*solver_control_x*/)
323  {
324  // If the error estimate returned by the convergence test function is less
325  // than the tolerance, then the eigenvalue is accepted as converged.
326  // This function is undefined (future reference only).
327 
328  // return without failure.
329  return 0;
330  }
331 
332  /* ---------------------- SolverKrylovSchur ------------------------ */
334  const MPI_Comm &mpi_communicator,
335  const AdditionalData &data)
336  :
337  SolverBase (cn, mpi_communicator),
338  additional_data (data)
339  {
340  const PetscErrorCode ierr = EPSSetType (eps,
341  const_cast<char *>(EPSKRYLOVSCHUR));
342  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
343  }
344 
345  /* ---------------------- SolverArnoldi ------------------------ */
347  AdditionalData (const bool delayed_reorthogonalization)
348  :
349  delayed_reorthogonalization (delayed_reorthogonalization)
350  {}
351 
353  const MPI_Comm &mpi_communicator,
354  const AdditionalData &data)
355  :
357  additional_data (data)
358  {
359  PetscErrorCode ierr = EPSSetType (eps, const_cast<char *>(EPSARNOLDI));
360  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
361 
362  // if requested, set delayed reorthogonalization in the Arnoldi
363  // iteration.
365  {
366  ierr = EPSArnoldiSetDelayed (eps, PETSC_TRUE);
367  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
368  }
369  }
370 
371 
372  /* ---------------------- Lanczos ------------------------ */
374  AdditionalData(const EPSLanczosReorthogType r)
375  : reorthog(r)
376  {}
377 
379  const MPI_Comm &mpi_communicator,
380  const AdditionalData &data)
381  :
383  additional_data (data)
384  {
385  PetscErrorCode ierr = EPSSetType (eps, const_cast<char *>(EPSLANCZOS));
386  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
387 
388  ierr = EPSLanczosSetReorthog(eps,additional_data.reorthog);
389  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
390  }
391 
392  /* ----------------------- Power ------------------------- */
394  const MPI_Comm &mpi_communicator,
395  const AdditionalData &data)
396  :
397  SolverBase (cn, mpi_communicator),
398  additional_data (data)
399  {
400  PetscErrorCode ierr = EPSSetType (eps, const_cast<char *>(EPSPOWER));
401  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
402  }
403 
404  /* ---------------- Generalized Davidson ----------------- */
406  AdditionalData(bool double_expansion)
407  : double_expansion(double_expansion)
408  {}
409 
411  const MPI_Comm &mpi_communicator,
412  const AdditionalData &data)
413  :
415  additional_data (data)
416  {
417  PetscErrorCode ierr = EPSSetType (eps, const_cast<char *>(EPSGD));
418  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
419 
421  {
422  ierr = EPSGDSetDoubleExpansion (eps, PETSC_TRUE);
423  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
424  }
425  }
426 
427  /* ------------------ Jacobi Davidson -------------------- */
429  const MPI_Comm &mpi_communicator,
430  const AdditionalData &data)
431  :
432  SolverBase (cn, mpi_communicator),
433  additional_data (data)
434  {
435  const PetscErrorCode ierr = EPSSetType (eps, const_cast<char *>(EPSJD));
436  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
437  }
438 
439  /* ---------------------- LAPACK ------------------------- */
441  const MPI_Comm &mpi_communicator,
442  const AdditionalData &data)
443  :
444  SolverBase (cn, mpi_communicator),
445  additional_data (data)
446  {
447  // 'Tis overwhelmingly likely that PETSc/SLEPc *always* has
448  // BLAS/LAPACK, but let's be defensive.
449 #if PETSC_HAVE_BLASLAPACK
450  const PetscErrorCode ierr = EPSSetType (eps, const_cast<char *>(EPSLAPACK));
451  AssertThrow (ierr == 0, ExcSLEPcError(ierr));
452 #else
453  Assert ((false),
454  ExcMessage ("Your PETSc/SLEPc installation was not configured with BLAS/LAPACK "
455  "but this is needed to use the LAPACK solver."));
456 #endif
457  }
458 }
459 
460 DEAL_II_NAMESPACE_CLOSE
461 
462 #endif // DEAL_II_WITH_SLEPC
#define AssertNothrow(cond, exc)
Definition: exceptions.h:1183
virtual State check(const unsigned int step, const double check_value)
SolverJacobiDavidson(SolverControl &cn, const MPI_Comm &mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
std::array< std::pair< Number, Tensor< 1, dim, Number > >, std::integral_constant< int, dim >::value > eigenvectors(const SymmetricTensor< 2, dim, Number > &T, const SymmetricTensorEigenvectorMethod method=SymmetricTensorEigenvectorMethod::ql_implicit_shifts)
SolverPower(SolverControl &cn, const MPI_Comm &mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
void set_initial_vector(const PETScWrappers::VectorBase &this_initial_vector)
std::array< Number, 1 > eigenvalues(const SymmetricTensor< 2, 1, Number > &T)
#define AssertThrow(cond, exc)
Definition: exceptions.h:1221
void solve(const PETScWrappers::MatrixBase &A, std::vector< PetscScalar > &eigenvalues, std::vector< OutputVector > &eigenvectors, const unsigned int n_eigenpairs=1)
Definition: slepc_solver.h:673
const MPI_Comm mpi_communicator
Definition: slepc_solver.h:305
SolverKrylovSchur(SolverControl &cn, const MPI_Comm &mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
SolverBase(SolverControl &cn, const MPI_Comm &mpi_communicator)
Definition: slepc_solver.cc:35
AdditionalData(const bool delayed_reorthogonalization=false)
void set_problem_type(EPSProblemType set_problem)
static ::ExceptionBase & ExcMessage(std::string arg1)
void set_target_eigenvalue(const PetscScalar &this_target)
void set_matrices(const PETScWrappers::MatrixBase &A)
Definition: slepc_solver.cc:73
void set_which_eigenpairs(EPSWhich set_which)
#define Assert(cond, exc)
Definition: exceptions.h:1142
SolverGeneralizedDavidson(SolverControl &cn, const MPI_Comm &mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
EPSConvergedReason reason
Definition: slepc_solver.h:365
const AdditionalData additional_data
Definition: slepc_solver.h:509
void get_eigenpair(const unsigned int index, PetscScalar &eigenvalues, PETScWrappers::VectorBase &eigenvectors)
static int convergence_test(EPS eps, PetscScalar real_eigenvalue, PetscScalar imag_eigenvalue, PetscReal residual_norm, PetscReal *estimated_error, void *solver_control)
SolverLanczos(SolverControl &cn, const MPI_Comm &mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
double tolerance() const
unsigned int last_step() const
unsigned int max_steps() const
SolverLAPACK(SolverControl &cn, const MPI_Comm &mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
static ::ExceptionBase & ExcNotImplemented()
AdditionalData(const EPSLanczosReorthogType r=EPS_LANCZOS_REORTHOG_FULL)
const AdditionalData additional_data
Definition: slepc_solver.h:463
static ::ExceptionBase & ExcSLEPcError(int arg1)
void set_transformation(SLEPcWrappers::TransformationBase &this_transformation)
Definition: slepc_solver.cc:90
SolverControl & solver_control
Definition: slepc_solver.h:300
SolverArnoldi(SolverControl &cn, const MPI_Comm &mpi_communicator=PETSC_COMM_SELF, const AdditionalData &data=AdditionalData())
SolverControl & control() const
void get_solver_state(const SolverControl::State state)