trilinos_eigen_solver.h
Go to the documentation of this file.
1 // LIC// ====================================================================
2 // LIC// This file forms part of oomph-lib, the object-oriented,
3 // LIC// multi-physics finite-element library, available
4 // LIC// at http://www.oomph-lib.org.
5 // LIC//
6 // LIC// Copyright (C) 2006-2024 Matthias Heil and Andrew Hazel
7 // LIC//
8 // LIC// This library is free software; you can redistribute it and/or
9 // LIC// modify it under the terms of the GNU Lesser General Public
10 // LIC// License as published by the Free Software Foundation; either
11 // LIC// version 2.1 of the License, or (at your option) any later version.
12 // LIC//
13 // LIC// This library is distributed in the hope that it will be useful,
14 // LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // LIC// Lesser General Public License for more details.
17 // LIC//
18 // LIC// You should have received a copy of the GNU Lesser General Public
19 // LIC// License along with this library; if not, write to the Free Software
20 // LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 // LIC// 02110-1301 USA.
22 // LIC//
23 // LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24 // LIC//
25 // LIC//====================================================================
26 #ifndef OOMPH_TRILINOS_EIGEN_SOLVER_HEADER
27 #define OOMPH_TRILINOS_EIGEN_SOLVER_HEADER
28 
29 // oomph-lib includes
30 #include "double_multi_vector.h"
31 #include "problem.h"
32 #include "linear_solver.h"
33 #include "eigen_solver.h"
34 
35 // Trilinos includes
36 #include "AnasaziOutputManager.hpp"
37 #include "AnasaziBasicOutputManager.hpp"
38 #include "AnasaziConfigDefs.hpp"
39 #include "AnasaziOperator.hpp"
40 #include "AnasaziMultiVec.hpp"
41 #include "AnasaziBasicEigenproblem.hpp"
42 #include "AnasaziBlockKrylovSchurSolMgr.hpp"
43 
44 namespace Anasazi
45 {
46  //========================================================================
47  /// Specialize the Anasazi traits class for the oomph-lib DoubleMultiVector.
48  /// This provides the interfaces required by the Anasazi eigensolvers.
49  //========================================================================
50  template<>
51  class MultiVecTraits<double, oomph::DoubleMultiVector>
52  {
53  public:
54  /// \brief Creates a new empty DoubleMultiVector containing numvecs columns.
55  /// Return a reference-counted pointer to the new multivector
56  static Teuchos::RCP<oomph::DoubleMultiVector> Clone(
57  const oomph::DoubleMultiVector& mv, const int numvecs)
58  {
59  return Teuchos::rcp(new oomph::DoubleMultiVector(numvecs, mv));
60  }
61 
62  /// \brief Creates a deep copy of the DoubleMultiVector mv
63  /// return Reference-counted pointer to the new DoubleMultiVector
64  static Teuchos::RCP<oomph::DoubleMultiVector> CloneCopy(
65  const oomph::DoubleMultiVector& mv)
66  {
67  return Teuchos::rcp(new oomph::DoubleMultiVector(mv));
68  }
69 
70  /// \brief Creates a new oomph::DoubleMultiVector and (deep)
71  /// copies the contents of
72  /// the vectors in index into the new vector
73  /// return Reference-counted pointer to the new oomph::DoubleMultiVector
74  static Teuchos::RCP<oomph::DoubleMultiVector> CloneCopy(
75  const oomph::DoubleMultiVector& mv, const std::vector<int>& index)
76  {
77  return Teuchos::rcp(new oomph::DoubleMultiVector(mv, index));
78  }
79 
80  /// \brief Deep copy of specified columns of oomph::DoubleMultiVector
81  /// return Reference-counted pointer to the new oomph::DoubleMultiVector
82  static Teuchos::RCP<oomph::DoubleMultiVector> CloneCopy(
83  const oomph::DoubleMultiVector& mv, const Teuchos::Range1D& index)
84  {
85  return Teuchos::rcp(new oomph::DoubleMultiVector(mv, index));
86  }
87 
88  /// \brief Creates a new oomph::DoubleMultiVector that contains
89  /// shallow copies
90  /// of selected entries of the oomph::DoubleMultiVector mv
91  /// return Reference-counted pointer to the new oomph::DoubleMultiVector
92  static Teuchos::RCP<oomph::DoubleMultiVector> CloneViewNonConst(
93  oomph::DoubleMultiVector& mv, const std::vector<int>& index)
94  {
95  return Teuchos::rcp(new oomph::DoubleMultiVector(mv, index, false));
96  }
97 
98  /// \brief Creates a new oomph::DoubleMultiVector that
99  /// contains shallow copies
100  /// of selected entries of the oomph::DoubleMultiVector mv
101  /// return Reference-counted pointer to the new oomph::DoubleMultiVector
102  static Teuchos::RCP<oomph::DoubleMultiVector> CloneViewNonConst(
103  oomph::DoubleMultiVector& mv, const Teuchos::Range1D& index)
104  {
105  return Teuchos::rcp(new oomph::DoubleMultiVector(mv, index, false));
106  }
107 
108  /// \brief Creates a new oomph::DoubleMultiVector that contains
109  /// shallow copies
110  /// of selected entries of the oomph::DoubleMultiVector mv
111  /// return Reference-counted pointer to the new oomph::DoubleMultiVector
112  /// (const version)
113  static Teuchos::RCP<const oomph::DoubleMultiVector> CloneView(
114  const oomph::DoubleMultiVector& mv, const std::vector<int>& index)
115  {
116  return Teuchos::rcp(new oomph::DoubleMultiVector(mv, index, false));
117  }
118 
119  /// \brief Creates a new oomph::DoubleMultiVector that contains
120  /// shallow copies
121  /// of selected entries of the oomph::DoubleMultiVector mv
122  /// return Reference-counted pointer to the new oomph::DoubleMultiVector
123  /// (Non-const version for Trilinos 9 interface)
124  static Teuchos::RCP<oomph::DoubleMultiVector> CloneView(
125  oomph::DoubleMultiVector& mv, const std::vector<int>& index)
126  {
127  return Teuchos::rcp(new oomph::DoubleMultiVector(mv, index, false));
128  }
129 
130  /// \brief Creates a new oomph::DoubleMultiVector that
131  /// contains shallow copies
132  /// of selected entries of the oomph::DoubleMultiVector mv
133  /// return Reference-counted pointer to the new oomph::DoubleMultiVector
134  /// (const version)
135  static Teuchos::RCP<oomph::DoubleMultiVector> CloneView(
136  oomph::DoubleMultiVector& mv, const Teuchos::Range1D& index)
137  {
138  return Teuchos::rcp(new oomph::DoubleMultiVector(mv, index, false));
139  }
140 
141  /// Obtain the global length of the vector
143  {
144  return static_cast<int>(mv.nrow());
145  }
146 
147  /// Obtain the number of vectors in the multivector
149  {
150  return static_cast<int>(mv.nvector());
151  }
152 
153  /// \brief Update \c mv with \f$ \alpha AB + \beta mv \f$.
154  static void MvTimesMatAddMv(
155  const double alpha,
156  const oomph::DoubleMultiVector& A,
157  const Teuchos::SerialDenseMatrix<int, double>& B,
158  const double beta,
160  {
161  // For safety let's (deep) copy A
163  // First pre-multiply by the scalars
164  mv *= beta;
165  C *= alpha;
166 
167  // Find the number of vectors in each multivector
168  int mv_n_vector = mv.nvector();
169  int A_n_vector = A.nvector();
170  // Find the number of rows
171  int n_row_local = A.nrow_local();
172  // Now need to multiply by the matrix and add the result
173  // to the appropriate entry in the multivector
174  for (int i = 0; i < n_row_local; ++i)
175  {
176  for (int j = 0; j < mv_n_vector; j++)
177  {
178  double res = 0.0;
179  for (int k = 0; k < A_n_vector; k++)
180  {
181  res += C(k, i) * B(k, j);
182  }
183  mv(j, i) += res;
184  }
185  }
186  }
187 
188  /// \brief Replace \c mv with \f$\alpha A + \beta B\f$.
189  static void MvAddMv(const double alpha,
190  const oomph::DoubleMultiVector& A,
191  const double beta,
194  {
195  const unsigned n_vector = A.nvector();
196  const unsigned n_row_local = A.nrow_local();
197  for (unsigned v = 0; v < n_vector; v++)
198  {
199  for (unsigned i = 0; i < n_row_local; i++)
200  {
201  mv(v, i) = alpha * A(v, i) + beta * B(v, i);
202  }
203  }
204  }
205 
206  /*! \brief Scale each element of the vectors in \c mv with \c alpha.
207  */
208  static void MvScale(oomph::DoubleMultiVector& mv, const double alpha)
209  {
210  mv *= alpha;
211  }
212 
213  /*! \brief Scale each element of the \c i-th vector in \c mv with \c
214  * alpha[i].
215  */
217  const std::vector<double>& alpha)
218  {
219  const unsigned n_vector = mv.nvector();
220  const unsigned n_row_local = mv.nrow_local();
221  for (unsigned v = 0; v < n_vector; v++)
222  {
223  for (unsigned i = 0; i < n_row_local; i++)
224  {
225  mv(v, i) *= alpha[v];
226  }
227  }
228  }
229 
230  /*! \brief Compute a dense matrix \c B through the matrix-matrix multiply
231  * \f$ \alpha A^Hmv \f$.
232  */
233  static void MvTransMv(const double alpha,
234  const oomph::DoubleMultiVector& A,
235  const oomph::DoubleMultiVector& mv,
236  Teuchos::SerialDenseMatrix<int, double>& B)
237  {
238  const unsigned A_nvec = A.nvector();
239  const unsigned A_nrow_local = A.nrow_local();
240  const unsigned mv_nvec = mv.nvector();
241  // const unsigned mv_nrow_local = mv.nrow_local();
242 
243  for (unsigned v1 = 0; v1 < A_nvec; v1++)
244  {
245  for (unsigned v2 = 0; v2 < mv_nvec; v2++)
246  {
247  B(v1, v2) = 0.0;
248  for (unsigned i = 0; i < A_nrow_local; i++)
249  {
250  B(v1, v2) += A(v1, i) * mv(v2, i);
251  }
252  B(v1, v2) *= alpha;
253  }
254  }
255 
256  // This will need to be communicated if we're in parallel and distributed
257 #ifdef OOMPH_HAS_MPI
258  if (A.distributed() &&
259  A.distribution_pt()->communicator_pt()->nproc() > 1)
260  {
261  const int n_total_val = A_nvec * mv_nvec;
262  // Pack entries into a vector
263  double b[n_total_val];
264  double b2[n_total_val];
265  unsigned count = 0;
266  for (unsigned v1 = 0; v1 < A_nvec; v1++)
267  {
268  for (unsigned v2 = 0; v2 < mv_nvec; v2++)
269  {
270  b[count] = B(v1, v2);
271  b2[count] = b[count];
272  ++count;
273  }
274  }
275 
276 
277  MPI_Allreduce(&b,
278  &b2,
279  n_total_val,
280  MPI_DOUBLE,
281  MPI_SUM,
282  A.distribution_pt()->communicator_pt()->mpi_comm());
283 
284  count = 0;
285  for (unsigned v1 = 0; v1 < A_nvec; v1++)
286  {
287  for (unsigned v2 = 0; v2 < mv_nvec; v2++)
288  {
289  B(v1, v2) = b2[count];
290  ++count;
291  }
292  }
293  }
294 #endif
295  }
296 
297 
298  /*! \brief Compute a vector \c b where the components are the individual
299  * dot-products of the \c i-th columns of \c A and \c mv, i.e.
300  * \f$ b[i] = * A[i]^Hmv[i] \f$.
301  */
302  static void MvDot(const oomph::DoubleMultiVector& mv,
303  const oomph::DoubleMultiVector& A,
304  std::vector<double>& b)
305  {
306  mv.dot(A, b);
307  }
308 
309 
310  /*! \brief Compute the 2-norm of each individual vector of \c mv.
311  Upon return, \c normvec[i] holds the value of \f$||mv_i||_2\f$, the \c
312  i-th column of \c mv.
313  */
314  static void MvNorm(const oomph::DoubleMultiVector& mv,
315  std::vector<double>& normvec)
316  {
317  mv.norm(normvec);
318  }
319 
320  //@}
321 
322  //! @name Initialization methods
323  //@{
324  /*! \brief Copy the vectors in \c A to a set of vectors in \c mv indicated
325  by the indices given in \c index.
326 
327  The \c numvecs vectors in \c A are copied to a subset of vectors in \c mv
328  indicated by the indices given in \c index, i.e.<tt> mv[index[i]] =
329  A[i]</tt>.
330  */
331  static void SetBlock(const oomph::DoubleMultiVector& A,
332  const std::vector<int>& index,
334  {
335  // Check some stuff
336  const unsigned n_index = index.size();
337  if (n_index == 0)
338  {
339  return;
340  }
341  const unsigned n_row_local = mv.nrow_local();
342  for (unsigned v = 0; v < n_index; v++)
343  {
344  for (unsigned i = 0; i < n_row_local; i++)
345  {
346  mv(index[v], i) = A(v, i);
347  }
348  }
349  }
350 
351  /// \brief Deep copy of A into specified columns of mv
352  ///
353  /// (Deeply) copy the first <tt>index.size()</tt> columns of \c A
354  /// into the columns of \c mv specified by the given index range.
355  ///
356  /// Postcondition: <tt>mv[i] = A[i - index.lbound()]</tt>
357  /// for all <tt>i</tt> in <tt>[index.lbound(), index.ubound()]</tt>
358  ///
359  /// \param A [in] Source multivector
360  /// \param index [in] Inclusive index range of columns of mv;
361  /// index set of the target
362  /// \param mv [out] Target multivector
363  static void SetBlock(const oomph::DoubleMultiVector& A,
364  const Teuchos::Range1D& index,
366  {
367  // Check some stuff
368  const unsigned n_index = index.size();
369  if (n_index == 0)
370  {
371  return;
372  }
373  const unsigned n_row_local = mv.nrow_local();
374  unsigned range_index = index.lbound();
375  for (unsigned v = 0; v < n_index; v++)
376  {
377  for (unsigned i = 0; i < n_row_local; i++)
378  {
379  mv(range_index, i) = A(v, i);
380  }
381  ++range_index;
382  }
383  }
384 
385  /// \brief mv := A
386  ///
387  /// Assign (deep copy) A into mv.
388  static void Assign(const oomph::DoubleMultiVector& A,
390  {
391  mv = A;
392  }
393 
394  /*! \brief Replace the vectors in \c mv with random vectors.
395  */
397  {
398  const unsigned n_vector = mv.nvector();
399  const unsigned n_row_local = mv.nrow_local();
400  for (unsigned v = 0; v < n_vector; v++)
401  {
402  for (unsigned i = 0; i < n_row_local; i++)
403  {
404  mv(v, i) = Teuchos::ScalarTraits<double>::random();
405  }
406  }
407  }
408 
409  /*! \brief Replace each element of the vectors in \c mv with \c alpha.
410  */
411  static void MvInit(
413  const double alpha = Teuchos::ScalarTraits<double>::zero())
414  {
415  mv.initialise(alpha);
416  }
417 
418  //@}
419 
420  //! @name Print method
421  //@{
422 
423  /*! \brief Print the \c mv multi-vector to the \c os output stream.
424  */
425  static void MvPrint(const oomph::DoubleMultiVector& mv, std::ostream& os)
426  {
427  mv.output(os);
428  }
429 
430  //@}
431  };
432 
433 
434 } // namespace Anasazi
435 
436 namespace oomph
437 {
438  //===================================================================
439  /// Base class for Oomph-lib's Vector Operator classes that will be
440  /// used with the DoubleMultiVector
441  //==================================================================
443  {
444  public:
445  /// Empty constructor
447 
448  /// virtual destructor
450 
451  /// The apply interface
452  virtual void apply(const DoubleMultiVector& x,
453  DoubleMultiVector& y) const = 0;
454  };
455 
456 } // namespace oomph
457 
458 namespace Anasazi
459 {
460  //======================================================================
461  /// Anasazi Traits class that specialises the apply operator based on
462  /// oomph-lib's DoubleVector class and double as the primitive type
463  //======================================================================
464  template<>
465  class OperatorTraits<double,
466  oomph::DoubleMultiVector,
468  {
469  public:
470  /// Matrix operator application method
472  const oomph::DoubleMultiVector& x,
474  {
475  Op.apply(x, y);
476  }
477 
478  }; // End of the specialised traits class
479 
480 } // namespace Anasazi
481 
482 
483 namespace oomph
484 {
485  //====================================================================
486  /// Class for the shift invert operation
487  //====================================================================
489  {
490  private:
491  // Pointer to the problem
493 
494  // Pointer to the linear solver used
496 
497  // Storage for the shift
498  double Sigma;
499 
500  // Storage for the matrices
502 
503  public:
505  LinearSolver* const& linear_solver_pt,
506  const double& sigma = 0.0)
507  : Problem_pt(problem_pt), Linear_solver_pt(linear_solver_pt), Sigma(sigma)
508  {
509  // Before we get into the Arnoldi loop solve the shifted matrix problem
510  // Allocated Row compressed matrices for the mass matrix and shifted main
511  // matrix
512  // No need for a distribution; gets automatically set up by the Problem
513  M_pt = new CRDoubleMatrix;
515 
516 
517  // Assemble the matrices
519 
520  // Do not report the time taken
522  }
523 
524 
525  // Now specify how to apply the operator
526  void apply(const DoubleMultiVector& x, DoubleMultiVector& y) const
527  {
528  const unsigned n_vec = x.nvector();
529  const unsigned n_row_local = x.nrow_local();
530  if (n_vec > 1)
531  {
533  }
534 
535  // Solve for the first vector (no need for a distribution)
536  DoubleVector X;
537 
538  // Premultiply by mass matrix
539  M_pt->multiply(x.doublevector(0), X);
540 
541  // Create output vector
542  DoubleVector Y;
544 
545  // Need to synchronise
546  //#ifdef OOMPH_HAS_MPI
547  // Problem_pt->synchronise_all_dofs();
548  //#endif
549 
550  for (unsigned i = 0; i < n_row_local; i++)
551  {
552  y(0, i) = Y[i];
553  }
554 
555  // Now loop over the others and resolve
556  for (unsigned v = 1; v < n_vec; ++v)
557  {
558  M_pt->multiply(x.doublevector(v), X);
559  Linear_solver_pt->resolve(X, Y);
560 
561  //#ifdef OOMPH_HAS_MPI
562  // Problem_pt->synchronise_all_dofs();
563  //#endif
564 
565  for (unsigned i = 0; i < n_row_local; i++)
566  {
567  y(v, i) = Y[i];
568  }
569  }
570  }
571  };
572 
573 
574  //====================================================================
575  /// Class for the adjoing problem shift invert operation
576  //====================================================================
579  {
580  private:
581  /// Pointer to the problem
583 
584  /// Pointer to the linear solver used
586 
587  /// Storage for the shift
588  double Sigma;
589 
590  /// Storage for the matrices
592 
593  public:
595  Problem* const& problem_pt,
596  LinearSolver* const& linear_solver_pt,
597  const double& sigma = 0.0)
598  : Problem_pt(problem_pt), Linear_solver_pt(linear_solver_pt), Sigma(sigma)
599  {
600  /// Before we get into the Arnoldi loop solve the shifted matrix problem
601  /// Allocated Row compressed matrices for the mass matrix and shifted main
602  /// matrix
603  M_pt = new CRDoubleMatrix(problem_pt->dof_distribution_pt());
604  AsigmaM_pt = new CRDoubleMatrix(problem_pt->dof_distribution_pt());
605 
606  /// Assemble the matrices
608 
609  /// Get the transpose of the mass and jacobian
610  /// NB Only difference to ProblemBasedShiftInvertOperator
613 
614  /// Do not report the time taken
616  }
617 
618 
619  /// Now specify how to apply the operator
620  void apply(const DoubleMultiVector& x, DoubleMultiVector& y) const
621  {
622  const unsigned n_vec = x.nvector();
623  const unsigned n_row_local = x.nrow_local();
624  if (n_vec > 1)
625  {
627  }
628 
629  /// Solve the first vector
631 
632  /// Premultiply by mass matrix
633  M_pt->multiply(x.doublevector(0), X);
634 
635  /// Create output vector
638 
639  for (unsigned i = 0; i < n_row_local; i++)
640  {
641  y(0, i) = Y[i];
642  }
643 
644  /// Now loop over the others and resolve
645  for (unsigned v = 1; v < n_vec; ++v)
646  {
647  M_pt->multiply(x.doublevector(v), X);
648  Linear_solver_pt->resolve(X, Y);
649 
650  for (unsigned i = 0; i < n_row_local; i++)
651  {
652  y(v, i) = Y[i];
653  }
654  }
655  }
656  };
657 
658 
659  //=====================================================================
660  /// Class for the Anasazi eigensolver
661  //=====================================================================
662  class ANASAZI : public EigenSolver
663  {
664  private:
665  typedef double ST;
666  typedef Teuchos::ScalarTraits<ST> SCT;
667  typedef SCT::magnitudeType MT;
668  typedef Anasazi::MultiVec<ST> MV;
669  typedef Anasazi::Operator<ST> OP;
670  typedef Anasazi::MultiVecTraits<ST, MV> MVT;
671  typedef Anasazi::OperatorTraits<ST, MV, OP> OPT;
672 
673  /// Pointer to output manager
674  Anasazi::OutputManager<ST>* Output_manager_pt;
675 
676  /// Pointer to a linear solver
678 
679  /// Pointer to a default linear solver
681 
682  // /// Integer to set whether the real, imaginary or magnitude is
683  // /// required
684  // /// to be small or large.
685  // int Spectrum;
686 
687  // /// Number of Arnoldi vectors to compute
688  // int NArnoldi;
689 
690  // /// Boolean to set which part of the spectrum left (default) or right
691  // /// of the shifted value.
692  // bool Small;
693 
694  // /// Boolean to indicate whether or not to compute the eigenvectors
695  // bool Compute_eigenvectors;
696 
697 
698  public:
699  /// Constructor
701  // Spectrum(0),
702  // NArnoldi(10),
703  // Compute_eigenvectors(true)
704  {
705  Output_manager_pt = new Anasazi::BasicOutputManager<ST>();
706  // Set verbosity level
707  int verbosity = 0;
708  verbosity += Anasazi::Warnings;
709  verbosity += Anasazi::FinalSummary;
710  verbosity += Anasazi::TimingDetails;
711  Output_manager_pt->setVerbosity(verbosity);
712 
713  // print greeting
714  Output_manager_pt->stream(Anasazi::Warnings)
715  << Anasazi::Anasazi_Version() << std::endl
716  << std::endl;
717  }
718 
719  /// Empty copy constructor
720  ANASAZI(const ANASAZI&) {}
721 
722  /// Destructor, delete the linear solver
723  virtual ~ANASAZI() {}
724 
725  // /// Access function for the number of Arnoldi vectors
726  // int& narnoldi()
727  // {
728  // return NArnoldi;
729  // }
730 
731  // /// Access function for the number of Arnoldi vectors (const version)
732  // const int& narnoldi() const
733  // {
734  // return NArnoldi;
735  // }
736 
737  // /// Set to enable the computation of the eigenvectors (default)
738  // void enable_compute_eigenvectors()
739  // {
740  // Compute_eigenvectors = true;
741  // }
742 
743  // /// Set to disable the computation of the eigenvectors
744  // void disable_compute_eigenvectors()
745  // {
746  // Compute_eigenvectors = false;
747  // }
748 
749  /// Solve the real eigenproblem that is assembled by elements in
750  /// a mesh in a Problem object. Note that the assembled matrices include the
751  /// shift and are real. The eigenvalues and eigenvectors are,
752  /// in general, complex. Eigenvalues may be infinite and are therefore
753  /// returned as
754  /// \f$ \lambda_i = \alpha_i / \beta_i \f$ where \f$ \alpha_i \f$ is complex
755  /// while \f$ \beta_i \f$ is real. The actual eigenvalues may then be
756  /// computed by doing the division, checking for zero betas to avoid NaNs.
757  /// There's a convenience wrapper to this function that simply computes
758  /// these eigenvalues regardless. That version may die in NaN checking is
759  /// enabled (via the fenv.h header and the associated feenable function).
760  /// NOTE: While the above statement is true, the implementation of this
761  /// function is actually everse engineered -- trilinos actually computes the
762  /// eigenvalues directly (and being an Arnoldi method it wouldn't be able to
763  /// obtain any infinite/NaN eigenvalues anyway
764  void solve_eigenproblem(Problem* const& problem_pt,
765  const int& n_eval,
766  Vector<std::complex<double>>& alpha,
767  Vector<double>& beta,
768  Vector<DoubleVector>& eigenvector_real,
769  Vector<DoubleVector>& eigenvector_imag,
770  const bool& do_adjoint_problem)
771  {
772  // Reverse engineer the "safe" version of the eigenvalues
773  Vector<std::complex<double>> eigenvalue;
774  solve_eigenproblem(problem_pt,
775  n_eval,
776  eigenvalue,
777  eigenvector_real,
778  eigenvector_imag,
779  do_adjoint_problem);
780  unsigned n = eigenvalue.size();
781  alpha.resize(n);
782  beta.resize(n);
783  for (unsigned i = 0; i < n; i++)
784  {
785  alpha[i] = eigenvalue[i];
786  beta[i] = 1.0;
787  }
788  }
789 
790  /// Solve the eigen problem
791  void solve_eigenproblem(Problem* const& problem_pt,
792  const int& n_eval,
793  Vector<std::complex<double>>& eigenvalue,
794  Vector<DoubleVector>& eigenvector_real,
795  Vector<DoubleVector>& eigenvector_imag,
796  const bool& do_adjoint_problem)
797  {
798  // Initially be dumb here
799  Linear_solver_pt = problem_pt->linear_solver_pt();
800 
801  // Get a shiny new linear algebra distribution from the problem
802  LinearAlgebraDistribution* dist_pt = 0;
803  problem_pt->create_new_linear_algebra_distribution(dist_pt);
804 
805  // Let's make the initial vector
806  Teuchos::RCP<DoubleMultiVector> initial =
807  Teuchos::rcp(new DoubleMultiVector(1, dist_pt));
808  Anasazi::MultiVecTraits<double, DoubleMultiVector>::MvRandom(*initial);
809 
810  // Make the operator
811  Teuchos::RCP<DoubleMultiVectorOperator> Op_pt;
812  if (do_adjoint_problem)
813  {
814  Op_pt = Teuchos::rcp(new AdjointProblemBasedShiftInvertOperator(
815  problem_pt, this->linear_solver_pt(), Sigma_real));
816  }
817  else
818  {
819  Op_pt = Teuchos::rcp(new ProblemBasedShiftInvertOperator(
820  problem_pt, this->linear_solver_pt(), Sigma_real));
821  }
822 
823  // Create the basic problem
824  Teuchos::RCP<Anasazi::BasicEigenproblem<double,
827  anasazi_pt = Teuchos::rcp(
828  new Anasazi::BasicEigenproblem<double,
831  initial));
832 
833  // No assumptions about niceness of problem matrices
834  anasazi_pt->setHermitian(false);
835 
836  // set the number of eigenvalues requested
837  anasazi_pt->setNEV(n_eval);
838 
839  // Inform the eigenproblem that you are done passing it information
840  if (anasazi_pt->setProblem() != true)
841  {
842  oomph_info << "Anasazi::BasicEigenproblem::setProblem() had an error."
843  << std::endl
844  << "End Result: TEST FAILED" << std::endl;
845  }
846 
847  // Create the solver manager
848  // No need to have ncv specificed, Triliinos has a sensible default
849  // int ncv = 10;
850  MT tol = 1.0e-10;
851  int verbosity = 0;
852  verbosity += Anasazi::Warnings;
853  // MH has switched off the (overly) verbose output
854  // Could introduce handle to switch it back on.
855  // verbosity+=Anasazi::FinalSummary;
856  // verbosity+=Anasazi::TimingDetails;
857 
858 
859  Teuchos::ParameterList MyPL;
860  MyPL.set("Which", "LM");
861  MyPL.set("Block Size", 1);
862  // MyPL.set( "Num Blocks", ncv );
863  MyPL.set("Maximum Restarts", 500);
864  MyPL.set("Orthogonalization", "DGKS");
865  MyPL.set("Verbosity", verbosity);
866  MyPL.set("Convergence Tolerance", tol);
867  Anasazi::BlockKrylovSchurSolMgr<double,
870  BKS(anasazi_pt, MyPL);
871 
872  // Solve the problem to the specified tolerances or length
873  Anasazi::ReturnType ret = BKS.solve();
874  if (ret != Anasazi::Converged)
875  {
876  std::string error_message = "Eigensolver did not converge!\n";
877 
878  throw OomphLibError(
879  error_message, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
880  }
881 
882 
883  // Get the eigenvalues and eigenvectors from the eigenproblem
884  Anasazi::Eigensolution<double, DoubleMultiVector> sol =
885  anasazi_pt->getSolution();
886  std::vector<Anasazi::Value<double>> evals = sol.Evals;
887  Teuchos::RCP<DoubleMultiVector> evecs = sol.Evecs;
888 
889  // Here's the vector that contains the information
890  // about how to translate the vectors that are returned
891  // by anasazi into real and imag parts of the actual
892  // eigenvectors
893  // std::vector<int> Anasazi::Eigensolution< ScalarType, MV >::index
894  std::vector<int> index_vector = sol.index;
895 
896  // Here's what we're actually going to return.
897  unsigned n_eval_avail = evals.size();
898  eigenvalue.resize(n_eval_avail);
899  eigenvector_real.resize(n_eval_avail);
900  eigenvector_imag.resize(n_eval_avail);
901 
902  // Extract these from the raw data returned by trilinos
903  for (unsigned i = 0; i < n_eval_avail; i++)
904  {
905  // Undo shift and invert
906  double a = evals[i].realpart;
907  double b = evals[i].imagpart;
908  double det = a * a + b * b;
909  eigenvalue[i] = std::complex<double>(a / det + Sigma_real, -b / det);
910 
911  // Now set the eigenvectors
912  unsigned nrow_local = evecs->nrow_local();
913  eigenvector_real[i].build(evecs->distribution_pt(), 0.0);
914  eigenvector_imag[i].build(evecs->distribution_pt(), 0.0);
915 
916  // std::vector<int> Anasazi::Eigensolution< ScalarType, MV >::index
917  // to translate into proper complex vector; see
918  // https://docs.trilinos.org/dev/packages/anasazi/doc/html/structAnasazi_1_1Eigensolution.html#ac9d141d98adcba85fbad011a7b7bda6e
919 
920  // An index into Evecs to allow compressed storage of eigenvectors for
921  // real, non-Hermitian problems.
922 
923  // index has length numVecs, where each entry is 0, +1, or -1. These
924  // have the following interpretation:
925 
926  // index[i] == 0: signifies that the corresponding eigenvector is
927  // stored as the i column of Evecs. This will usually be the case
928  // when ScalarType is complex, an eigenproblem is Hermitian, or a
929  // real, non-Hermitian eigenproblem has a real eigenvector. index[i]
930  // == +1: signifies that the corresponding eigenvector is stored in
931  // two vectors: the real part in the i column of Evecs and the
932  // positive imaginary part in the i+1 column of Evecs. index[i] ==
933  // -1: signifies that the corresponding eigenvector is stored in two
934  // vectors: the real part in the i-1 column of Evecs and the
935  // negative imaginary part in the i column of Evecs
936 
937  // Real eigenvector
938  if (index_vector[i] == 0)
939  {
940  for (unsigned j = 0; j < nrow_local; j++)
941  {
942  eigenvector_real[i][j] = (*evecs)(i, j);
943  eigenvector_imag[i][j] = 0.0;
944  }
945  }
946  else if (index_vector[i] == 1)
947  {
948  for (unsigned j = 0; j < nrow_local; j++)
949  {
950  eigenvector_real[i][j] = (*evecs)(i, j);
951  eigenvector_imag[i][j] = (*evecs)(i + 1, j);
952  }
953  }
954  else if (index_vector[i] == -1)
955  {
956  for (unsigned j = 0; j < nrow_local; j++)
957  {
958  eigenvector_real[i][j] = (*evecs)(i - 1, j);
959  eigenvector_imag[i][j] = -(*evecs)(i, j);
960  }
961  }
962  else
963  {
964  oomph_info << "Never get here: index_vector[ " << i
965  << " ] = " << index_vector[i] << std::endl;
966  abort();
967  }
968  }
969 
970  // Tidy up
971  delete dist_pt;
972  }
973 
974  /// Return a pointer to the linear solver object
976  {
977  return Linear_solver_pt;
978  }
979 
980  /// Return a pointer to the linear solver object (const version)
982  {
983  return Linear_solver_pt;
984  }
985  };
986 
987 } // namespace oomph
988 
989 
990 #endif
cstr elem_len * i
Definition: cfortran.h:603
static Teuchos::RCP< oomph::DoubleMultiVector > CloneCopy(const oomph::DoubleMultiVector &mv)
Creates a deep copy of the DoubleMultiVector mv return Reference-counted pointer to the new DoubleMul...
static int GetVecLength(const oomph::DoubleMultiVector &mv)
Obtain the global length of the vector.
static Teuchos::RCP< oomph::DoubleMultiVector > CloneCopy(const oomph::DoubleMultiVector &mv, const std::vector< int > &index)
Creates a new oomph::DoubleMultiVector and (deep) copies the contents of the vectors in index into th...
static void MvTransMv(const double alpha, const oomph::DoubleMultiVector &A, const oomph::DoubleMultiVector &mv, Teuchos::SerialDenseMatrix< int, double > &B)
Compute a dense matrix B through the matrix-matrix multiply .
static void MvTimesMatAddMv(const double alpha, const oomph::DoubleMultiVector &A, const Teuchos::SerialDenseMatrix< int, double > &B, const double beta, oomph::DoubleMultiVector &mv)
Update mv with .
static void MvPrint(const oomph::DoubleMultiVector &mv, std::ostream &os)
Print the mv multi-vector to the os output stream.
static Teuchos::RCP< oomph::DoubleMultiVector > CloneView(oomph::DoubleMultiVector &mv, const Teuchos::Range1D &index)
Creates a new oomph::DoubleMultiVector that contains shallow copies of selected entries of the oomph:...
static void SetBlock(const oomph::DoubleMultiVector &A, const Teuchos::Range1D &index, oomph::DoubleMultiVector &mv)
Deep copy of A into specified columns of mv.
static Teuchos::RCP< oomph::DoubleMultiVector > CloneViewNonConst(oomph::DoubleMultiVector &mv, const std::vector< int > &index)
Creates a new oomph::DoubleMultiVector that contains shallow copies of selected entries of the oomph:...
static void MvScale(oomph::DoubleMultiVector &mv, const std::vector< double > &alpha)
Scale each element of the i-th vector in mv with alpha[i].
static void MvScale(oomph::DoubleMultiVector &mv, const double alpha)
Scale each element of the vectors in mv with alpha.
static Teuchos::RCP< const oomph::DoubleMultiVector > CloneView(const oomph::DoubleMultiVector &mv, const std::vector< int > &index)
Creates a new oomph::DoubleMultiVector that contains shallow copies of selected entries of the oomph:...
static void MvAddMv(const double alpha, const oomph::DoubleMultiVector &A, const double beta, const oomph::DoubleMultiVector &B, oomph::DoubleMultiVector &mv)
Replace mv with .
static Teuchos::RCP< oomph::DoubleMultiVector > Clone(const oomph::DoubleMultiVector &mv, const int numvecs)
Creates a new empty DoubleMultiVector containing numvecs columns. Return a reference-counted pointer ...
static int GetNumberVecs(const oomph::DoubleMultiVector &mv)
Obtain the number of vectors in the multivector.
static void SetBlock(const oomph::DoubleMultiVector &A, const std::vector< int > &index, oomph::DoubleMultiVector &mv)
Copy the vectors in A to a set of vectors in mv indicated by the indices given in index.
static Teuchos::RCP< oomph::DoubleMultiVector > CloneViewNonConst(oomph::DoubleMultiVector &mv, const Teuchos::Range1D &index)
Creates a new oomph::DoubleMultiVector that contains shallow copies of selected entries of the oomph:...
static void MvNorm(const oomph::DoubleMultiVector &mv, std::vector< double > &normvec)
Compute the 2-norm of each individual vector of mv. Upon return, normvec[i] holds the value of ,...
static Teuchos::RCP< oomph::DoubleMultiVector > CloneView(oomph::DoubleMultiVector &mv, const std::vector< int > &index)
Creates a new oomph::DoubleMultiVector that contains shallow copies of selected entries of the oomph:...
static void MvDot(const oomph::DoubleMultiVector &mv, const oomph::DoubleMultiVector &A, std::vector< double > &b)
Compute a vector b where the components are the individual dot-products of the i-th columns of A and ...
static void MvRandom(oomph::DoubleMultiVector &mv)
Replace the vectors in mv with random vectors.
static Teuchos::RCP< oomph::DoubleMultiVector > CloneCopy(const oomph::DoubleMultiVector &mv, const Teuchos::Range1D &index)
Deep copy of specified columns of oomph::DoubleMultiVector return Reference-counted pointer to the ne...
static void MvInit(oomph::DoubleMultiVector &mv, const double alpha=Teuchos::ScalarTraits< double >::zero())
Replace each element of the vectors in mv with alpha.
static void Assign(const oomph::DoubleMultiVector &A, oomph::DoubleMultiVector &mv)
mv := A
static void Apply(const oomph::DoubleMultiVectorOperator &Op, const oomph::DoubleMultiVector &x, oomph::DoubleMultiVector &y)
Matrix operator application method.
Class for the Anasazi eigensolver.
void solve_eigenproblem(Problem *const &problem_pt, const int &n_eval, Vector< std::complex< double >> &alpha, Vector< double > &beta, Vector< DoubleVector > &eigenvector_real, Vector< DoubleVector > &eigenvector_imag, const bool &do_adjoint_problem)
Solve the real eigenproblem that is assembled by elements in a mesh in a Problem object....
LinearSolver * Default_linear_solver_pt
Pointer to a default linear solver.
Anasazi::OutputManager< ST > * Output_manager_pt
Pointer to output manager.
Teuchos::ScalarTraits< ST > SCT
Anasazi::OperatorTraits< ST, MV, OP > OPT
virtual ~ANASAZI()
Destructor, delete the linear solver.
LinearSolver * Linear_solver_pt
Pointer to a linear solver.
void solve_eigenproblem(Problem *const &problem_pt, const int &n_eval, Vector< std::complex< double >> &eigenvalue, Vector< DoubleVector > &eigenvector_real, Vector< DoubleVector > &eigenvector_imag, const bool &do_adjoint_problem)
Solve the eigen problem.
LinearSolver *const & linear_solver_pt() const
Return a pointer to the linear solver object (const version)
SCT::magnitudeType MT
Anasazi::MultiVec< ST > MV
Anasazi::MultiVecTraits< ST, MV > MVT
ANASAZI(const ANASAZI &)
Empty copy constructor.
Anasazi::Operator< ST > OP
LinearSolver *& linear_solver_pt()
Return a pointer to the linear solver object.
Class for the adjoing problem shift invert operation.
AdjointProblemBasedShiftInvertOperator(Problem *const &problem_pt, LinearSolver *const &linear_solver_pt, const double &sigma=0.0)
void apply(const DoubleMultiVector &x, DoubleMultiVector &y) const
Now specify how to apply the operator.
LinearSolver * Linear_solver_pt
Pointer to the linear solver used.
CRDoubleMatrix * M_pt
Storage for the matrices.
A class for compressed row matrices. This is a distributable object.
Definition: matrices.h:888
void multiply(const DoubleVector &x, DoubleVector &soln) const
Multiply the matrix by the vector x: soln=Ax.
Definition: matrices.cc:1782
void get_matrix_transpose(CRDoubleMatrix *result) const
Returns the transpose of this matrix.
Definition: matrices.cc:3271
bool distributed() const
distribution is serial or distributed
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
unsigned nrow() const
access function to the number of global rows.
unsigned nrow_local() const
access function for the num of local rows on this processor.
Base class for Oomph-lib's Vector Operator classes that will be used with the DoubleMultiVector.
virtual ~DoubleMultiVectorOperator()
virtual destructor
virtual void apply(const DoubleMultiVector &x, DoubleMultiVector &y) const =0
The apply interface.
A multi vector in the mathematical sense, initially developed for linear algebra type applications....
void output(std::ostream &outfile) const
output the contents of the vector
void initialise(const double &initial_value)
initialise the whole vector with value v
void dot(const DoubleMultiVector &vec, std::vector< double > &result) const
compute the 2 norm of this vector
void norm(std::vector< double > &result) const
compute the 2 norm of this vector
DoubleVector & doublevector(const unsigned &i)
access to the DoubleVector representatoin
unsigned nvector() const
Return the number of vectors.
A vector in the mathematical sense, initially developed for linear algebra type applications....
Definition: double_vector.h:58
Base class for all EigenProblem solves. This simply defines standard interfaces so that different sol...
Definition: eigen_solver.h:61
double Sigma_real
Double value that represents the real part of the shift in shifted eigensolvers.
Definition: eigen_solver.h:65
Describes the distribution of a distributable linear algebra type object. Typically this is a contain...
OomphCommunicator * communicator_pt() const
const access to the communicator pointer
Base class for all linear solvers. This merely defines standard interfaces for linear solvers,...
Definition: linear_solver.h:68
virtual void solve(Problem *const &problem_pt, DoubleVector &result)=0
Solver: Takes pointer to problem and returns the results vector which contains the solution of the li...
virtual void enable_resolve()
Enable resolve (i.e. store matrix and/or LU decomposition, say) Virtual so it can be overloaded to pe...
virtual void resolve(const DoubleVector &rhs, DoubleVector &result)
Resolve the system defined by the last assembled jacobian and the rhs vector. Solution is returned in...
void disable_doc_time()
Disable documentation of solve times.
An OomphLibError object which should be thrown when an run-time error is encountered....
Class for the shift invert operation.
void apply(const DoubleMultiVector &x, DoubleMultiVector &y) const
The apply interface.
ProblemBasedShiftInvertOperator(Problem *const &problem_pt, LinearSolver *const &linear_solver_pt, const double &sigma=0.0)
////////////////////////////////////////////////////////////////// //////////////////////////////////...
Definition: problem.h:154
LinearAlgebraDistribution *const & dof_distribution_pt() const
Return the pointer to the dof distribution (read-only)
Definition: problem.h:1698
virtual void get_eigenproblem_matrices(CRDoubleMatrix &mass_matrix, CRDoubleMatrix &main_matrix, const double &shift=0.0)
Get the matrices required by a eigensolver. If the shift parameter is non-zero the second matrix will...
Definition: problem.cc:8429
LinearSolver *& linear_solver_pt()
Return a pointer to the linear solver object.
Definition: problem.h:1486
void create_new_linear_algebra_distribution(LinearAlgebraDistribution *&dist_pt)
Get new linear algebra distribution (you're in charge of deleting it!)
Definition: problem.cc:300
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition: Vector.h:58
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn't been defined.
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...