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-2025 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
44namespace 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(
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 /// NOTE: Not required in Trilinos 13.4.0
125 static Teuchos::RCP<oomph::DoubleMultiVector> CloneView(
126 oomph::DoubleMultiVector& mv, const std::vector<int>& index)
127 {
128 return Teuchos::rcp(new oomph::DoubleMultiVector(mv, index, false));
129 }
130
131 /// \brief Creates a new oomph::DoubleMultiVector that
132 /// contains shallow copies
133 /// of selected entries of the oomph::DoubleMultiVector mv
134 /// return Reference-counted pointer to the new oomph::DoubleMultiVector
135 /// (const version)
136 static Teuchos::RCP<oomph::DoubleMultiVector> CloneView(
137 oomph::DoubleMultiVector& mv, const Teuchos::Range1D& index)
138 {
139 return Teuchos::rcp(new oomph::DoubleMultiVector(mv, index, false));
140 }
141
142 /// Obtain the global length of the vector
144 {
145 return static_cast<int>(mv.nrow());
146 }
147
148 /// Return the number of rows in the given multivector.
149 /// NOTE: Trilinos 13.4.0 uses GetGlobalLength instead of GetVecLength
150 static ptrdiff_t GetGlobalLength(const oomph::DoubleMultiVector& mv)
151 {
152 return static_cast<ptrdiff_t>(mv.nrow());
153 }
154
155 /// Obtain the number of vectors in the multivector
157 {
158 return static_cast<int>(mv.nvector());
159 }
160
161 /// \brief Update \c mv with \f$ \alpha AB + \beta mv \f$.
162 static void MvTimesMatAddMv(
163 const double alpha,
165 const Teuchos::SerialDenseMatrix<int, double>& B,
166 const double beta,
168 {
169 // For safety let's (deep) copy A
171 // First pre-multiply by the scalars
172 mv *= beta;
173 C *= alpha;
174
175 // Find the number of vectors in each multivector
176 int mv_n_vector = mv.nvector();
177 int A_n_vector = A.nvector();
178 // Find the number of rows
179 int n_row_local = A.nrow_local();
180 // Now need to multiply by the matrix and add the result
181 // to the appropriate entry in the multivector
182 for (int i = 0; i < n_row_local; ++i)
183 {
184 for (int j = 0; j < mv_n_vector; j++)
185 {
186 double res = 0.0;
187 for (int k = 0; k < A_n_vector; k++)
188 {
189 res += C(k, i) * B(k, j);
190 }
191 mv(j, i) += res;
192 }
193 }
194 }
195
196 /// \brief Replace \c mv with \f$\alpha A + \beta B\f$.
197 static void MvAddMv(const double alpha,
199 const double beta,
202 {
203 const unsigned n_vector = A.nvector();
204 const unsigned n_row_local = A.nrow_local();
205 for (unsigned v = 0; v < n_vector; v++)
206 {
207 for (unsigned i = 0; i < n_row_local; i++)
208 {
209 mv(v, i) = alpha * A(v, i) + beta * B(v, i);
210 }
211 }
212 }
213
214 /*! \brief Scale each element of the vectors in \c mv with \c alpha.
215 */
216 static void MvScale(oomph::DoubleMultiVector& mv, const double alpha)
217 {
218 mv *= alpha;
219 }
220
221 /*! \brief Scale each element of the \c i-th vector in \c mv with \c
222 * alpha[i].
223 */
225 const std::vector<double>& alpha)
226 {
227 const unsigned n_vector = mv.nvector();
228 const unsigned n_row_local = mv.nrow_local();
229 for (unsigned v = 0; v < n_vector; v++)
230 {
231 for (unsigned i = 0; i < n_row_local; i++)
232 {
233 mv(v, i) *= alpha[v];
234 }
235 }
236 }
237
238 /*! \brief Compute a dense matrix \c B through the matrix-matrix multiply
239 * \f$ \alpha A^Hmv \f$.
240 */
241 static void MvTransMv(const double alpha,
243 const oomph::DoubleMultiVector& mv,
244 Teuchos::SerialDenseMatrix<int, double>& B)
245 {
246 const unsigned A_nvec = A.nvector();
247 const unsigned A_nrow_local = A.nrow_local();
248 const unsigned mv_nvec = mv.nvector();
249 // const unsigned mv_nrow_local = mv.nrow_local();
250
251 for (unsigned v1 = 0; v1 < A_nvec; v1++)
252 {
253 for (unsigned v2 = 0; v2 < mv_nvec; v2++)
254 {
255 B(v1, v2) = 0.0;
256 for (unsigned i = 0; i < A_nrow_local; i++)
257 {
258 B(v1, v2) += A(v1, i) * mv(v2, i);
259 }
260 B(v1, v2) *= alpha;
261 }
262 }
263
264 // This will need to be communicated if we're in parallel and distributed
265#ifdef OOMPH_HAS_MPI
266 if (A.distributed() &&
267 A.distribution_pt()->communicator_pt()->nproc() > 1)
268 {
269 const int n_total_val = A_nvec * mv_nvec;
270 // Pack entries into a vector
271 double b[n_total_val];
272 double b2[n_total_val];
273 unsigned count = 0;
274 for (unsigned v1 = 0; v1 < A_nvec; v1++)
275 {
276 for (unsigned v2 = 0; v2 < mv_nvec; v2++)
277 {
278 b[count] = B(v1, v2);
279 b2[count] = b[count];
280 ++count;
281 }
282 }
283
284
285 MPI_Allreduce(&b,
286 &b2,
287 n_total_val,
288 MPI_DOUBLE,
289 MPI_SUM,
290 A.distribution_pt()->communicator_pt()->mpi_comm());
291
292 count = 0;
293 for (unsigned v1 = 0; v1 < A_nvec; v1++)
294 {
295 for (unsigned v2 = 0; v2 < mv_nvec; v2++)
296 {
297 B(v1, v2) = b2[count];
298 ++count;
299 }
300 }
301 }
302#endif
303 }
304
305
306 /*! \brief Compute a vector \c b where the components are the individual
307 * dot-products of the \c i-th columns of \c A and \c mv, i.e.
308 * \f$ b[i] = * A[i]^Hmv[i] \f$.
309 */
310 static void MvDot(const oomph::DoubleMultiVector& mv,
312 std::vector<double>& b)
313 {
314 mv.dot(A, b);
315 }
316
317
318 /*! \brief Compute the 2-norm of each individual vector of \c mv.
319 Upon return, \c normvec[i] holds the value of \f$||mv_i||_2\f$, the \c
320 i-th column of \c mv.
321 */
322 static void MvNorm(const oomph::DoubleMultiVector& mv,
323 std::vector<double>& normvec)
324 {
325 mv.norm(normvec);
326 }
327
328 //@}
329
330 //! @name Initialization methods
331 //@{
332 /*! \brief Copy the vectors in \c A to a set of vectors in \c mv indicated
333 by the indices given in \c index.
334
335 The \c numvecs vectors in \c A are copied to a subset of vectors in \c mv
336 indicated by the indices given in \c index, i.e.<tt> mv[index[i]] =
337 A[i]</tt>.
338 */
339 static void SetBlock(const oomph::DoubleMultiVector& A,
340 const std::vector<int>& index,
342 {
343 // Check some stuff
344 const unsigned n_index = index.size();
345 if (n_index == 0)
346 {
347 return;
348 }
349 const unsigned n_row_local = mv.nrow_local();
350 for (unsigned v = 0; v < n_index; v++)
351 {
352 for (unsigned i = 0; i < n_row_local; i++)
353 {
354 mv(index[v], i) = A(v, i);
355 }
356 }
357 }
358
359 /// \brief Deep copy of A into specified columns of mv
360 ///
361 /// (Deeply) copy the first <tt>index.size()</tt> columns of \c A
362 /// into the columns of \c mv specified by the given index range.
363 ///
364 /// Postcondition: <tt>mv[i] = A[i - index.lbound()]</tt>
365 /// for all <tt>i</tt> in <tt>[index.lbound(), index.ubound()]</tt>
366 ///
367 /// \param A [in] Source multivector
368 /// \param index [in] Inclusive index range of columns of mv;
369 /// index set of the target
370 /// \param mv [out] Target multivector
371 static void SetBlock(const oomph::DoubleMultiVector& A,
372 const Teuchos::Range1D& index,
374 {
375 // Check some stuff
376 const unsigned n_index = index.size();
377 if (n_index == 0)
378 {
379 return;
380 }
381 const unsigned n_row_local = mv.nrow_local();
382 unsigned range_index = index.lbound();
383 for (unsigned v = 0; v < n_index; v++)
384 {
385 for (unsigned i = 0; i < n_row_local; i++)
386 {
387 mv(range_index, i) = A(v, i);
388 }
389 ++range_index;
390 }
391 }
392
393 /// \brief mv := A
394 ///
395 /// Assign (deep copy) A into mv.
396 static void Assign(const oomph::DoubleMultiVector& A,
398 {
399 mv = A;
400 }
401
402 /*! \brief Replace the vectors in \c mv with random vectors.
403 */
405 {
406 const unsigned n_vector = mv.nvector();
407 const unsigned n_row_local = mv.nrow_local();
408 for (unsigned v = 0; v < n_vector; v++)
409 {
410 for (unsigned i = 0; i < n_row_local; i++)
411 {
412 mv(v, i) = Teuchos::ScalarTraits<double>::random();
413 }
414 }
415 }
416
417 /*! \brief Replace each element of the vectors in \c mv with \c alpha.
418 */
419 static void MvInit(
421 const double alpha = Teuchos::ScalarTraits<double>::zero())
422 {
423 mv.initialise(alpha);
424 }
425
426 //@}
427
428 //! @name Print method
429 //@{
430
431 /*! \brief Print the \c mv multi-vector to the \c os output stream.
432 */
433 static void MvPrint(const oomph::DoubleMultiVector& mv, std::ostream& os)
434 {
435 mv.output(os);
436 }
437
438 //@}
439
440#ifdef HAVE_ANASAZI_TSQR
441 /// \typedef tsqr_adaptor_type
442 /// \brief TsqrAdaptor specialization for the multivector type MV.
443 ///
444 /// By default, we provide a "stub" implementation. It has the
445 /// right methods and typedefs, but its constructors and methods
446 /// all throw std::logic_error. If you plan to use TSQR in
447 /// Anasazi (e.g., through TsqrOrthoManager), and if your
448 /// multivector type MV is neither Epetra_MultiVector nor
449 /// Tpetra::MultiVector, you must implement a functional TSQR
450 /// adapter. Please refer to Epetra::TsqrAdapter (for
451 /// Epetra_MultiVector) or Tpetra::TsqrAdaptor (for
452 /// Tpetra::MultiVector) for examples.
453 typedef Anasazi::details::StubTsqrAdapter<MV> tsqr_adaptor_type;
454#endif // HAVE_ANASAZI_TSQR
455 };
456
457
458} // namespace Anasazi
459
460namespace oomph
461{
462 //===================================================================
463 /// Base class for Oomph-lib's Vector Operator classes that will be
464 /// used with the DoubleMultiVector
465 //==================================================================
467 {
468 public:
469 /// Empty constructor
471
472 /// virtual destructor
474
475 /// The apply interface
476 virtual void apply(const DoubleMultiVector& x,
477 DoubleMultiVector& y) const = 0;
478 };
479
480} // namespace oomph
481
482namespace Anasazi
483{
484 //======================================================================
485 /// Anasazi Traits class that specialises the apply operator based on
486 /// oomph-lib's DoubleVector class and double as the primitive type
487 //======================================================================
488 template<>
489 class OperatorTraits<double,
490 oomph::DoubleMultiVector,
492 {
493 public:
494 /// Matrix operator application method
498 {
499 Op.apply(x, y);
500 }
501
502 }; // End of the specialised traits class
503
504} // namespace Anasazi
505
506
507namespace oomph
508{
509 //====================================================================
510 /// Class for the shift invert operation
511 //====================================================================
513 {
514 private:
515 // Pointer to the problem
517
518 // Pointer to the linear solver used
520
521 // Storage for the shift
522 double Sigma;
523
524 // Storage for the matrices
526
527 public:
529 LinearSolver* const& linear_solver_pt,
530 const double& sigma = 0.0)
531 : Problem_pt(problem_pt), Linear_solver_pt(linear_solver_pt), Sigma(sigma)
532 {
533 // Before we get into the Arnoldi loop solve the shifted matrix problem
534 // Allocated Row compressed matrices for the mass matrix and shifted main
535 // matrix
536 // No need for a distribution; gets automatically set up by the Problem
537 M_pt = new CRDoubleMatrix;
539
540
541 // Assemble the matrices
543
544 // Do not report the time taken
546 }
547
548
549 // Now specify how to apply the operator
551 {
552 const unsigned n_vec = x.nvector();
553 const unsigned n_row_local = x.nrow_local();
554 if (n_vec > 1)
555 {
557 }
558
559 // Solve for the first vector (no need for a distribution)
560 DoubleVector X;
561
562 // Premultiply by mass matrix
563 M_pt->multiply(x.doublevector(0), X);
564
565 // Create output vector
566 DoubleVector Y;
568
569 // Need to synchronise
570 // #ifdef OOMPH_HAS_MPI
571 // Problem_pt->synchronise_all_dofs();
572 // #endif
573
574 for (unsigned i = 0; i < n_row_local; i++)
575 {
576 y(0, i) = Y[i];
577 }
578
579 // Now loop over the others and resolve
580 for (unsigned v = 1; v < n_vec; ++v)
581 {
582 M_pt->multiply(x.doublevector(v), X);
584
585 // #ifdef OOMPH_HAS_MPI
586 // Problem_pt->synchronise_all_dofs();
587 // #endif
588
589 for (unsigned i = 0; i < n_row_local; i++)
590 {
591 y(v, i) = Y[i];
592 }
593 }
594 }
595 };
596
597
598 //====================================================================
599 /// Class for the adjoing problem shift invert operation
600 //====================================================================
603 {
604 private:
605 /// Pointer to the problem
607
608 /// Pointer to the linear solver used
610
611 /// Storage for the shift
612 double Sigma;
613
614 /// Storage for the matrices
616
617 public:
619 Problem* const& problem_pt,
620 LinearSolver* const& linear_solver_pt,
621 const double& sigma = 0.0)
622 : Problem_pt(problem_pt), Linear_solver_pt(linear_solver_pt), Sigma(sigma)
623 {
624 /// Before we get into the Arnoldi loop solve the shifted matrix problem
625 /// Allocated Row compressed matrices for the mass matrix and shifted main
626 /// matrix
627 M_pt = new CRDoubleMatrix(problem_pt->dof_distribution_pt());
628 AsigmaM_pt = new CRDoubleMatrix(problem_pt->dof_distribution_pt());
629
630 /// Assemble the matrices
632
633 /// Get the transpose of the mass and jacobian
634 /// NB Only difference to ProblemBasedShiftInvertOperator
637
638 /// Do not report the time taken
640 }
641
642
643 /// Now specify how to apply the operator
645 {
646 const unsigned n_vec = x.nvector();
647 const unsigned n_row_local = x.nrow_local();
648 if (n_vec > 1)
649 {
651 }
652
653 /// Solve the first vector
655
656 /// Premultiply by mass matrix
657 M_pt->multiply(x.doublevector(0), X);
658
659 /// Create output vector
662
663 for (unsigned i = 0; i < n_row_local; i++)
664 {
665 y(0, i) = Y[i];
666 }
667
668 /// Now loop over the others and resolve
669 for (unsigned v = 1; v < n_vec; ++v)
670 {
671 M_pt->multiply(x.doublevector(v), X);
673
674 for (unsigned i = 0; i < n_row_local; i++)
675 {
676 y(v, i) = Y[i];
677 }
678 }
679 }
680 };
681
682
683 //=====================================================================
684 /// Class for the Anasazi eigensolver
685 //=====================================================================
686 class ANASAZI : public EigenSolver
687 {
688 private:
689 typedef double ST;
690 typedef Teuchos::ScalarTraits<ST> SCT;
691 typedef SCT::magnitudeType MT;
692 typedef Anasazi::MultiVec<ST> MV;
693 typedef Anasazi::Operator<ST> OP;
694 typedef Anasazi::MultiVecTraits<ST, MV> MVT;
695 typedef Anasazi::OperatorTraits<ST, MV, OP> OPT;
696
697 /// Pointer to output manager
698 Anasazi::OutputManager<ST>* Output_manager_pt;
699
700 /// Pointer to a linear solver
702
703 /// Pointer to a default linear solver
705
706 // /// Integer to set whether the real, imaginary or magnitude is
707 // /// required
708 // /// to be small or large.
709 // int Spectrum;
710
711 // /// Number of Arnoldi vectors to compute
712 // int NArnoldi;
713
714 // /// Boolean to set which part of the spectrum left (default) or right
715 // /// of the shifted value.
716 // bool Small;
717
718 // /// Boolean to indicate whether or not to compute the eigenvectors
719 // bool Compute_eigenvectors;
720
721
722 public:
723 /// Constructor
725 // Spectrum(0),
726 // NArnoldi(10),
727 // Compute_eigenvectors(true)
728 {
729 Output_manager_pt = new Anasazi::BasicOutputManager<ST>();
730 // Set verbosity level
731 int verbosity = 0;
732 verbosity += Anasazi::Warnings;
733 verbosity += Anasazi::FinalSummary;
734 verbosity += Anasazi::TimingDetails;
735 Output_manager_pt->setVerbosity(verbosity);
736
737 // print greeting
738 Output_manager_pt->stream(Anasazi::Warnings)
739 << Anasazi::Anasazi_Version() << std::endl
740 << std::endl;
741 }
742
743 /// Empty copy constructor
744 ANASAZI(const ANASAZI&) {}
745
746 /// Destructor, delete the linear solver
747 virtual ~ANASAZI() {}
748
749 // /// Access function for the number of Arnoldi vectors
750 // int& narnoldi()
751 // {
752 // return NArnoldi;
753 // }
754
755 // /// Access function for the number of Arnoldi vectors (const version)
756 // const int& narnoldi() const
757 // {
758 // return NArnoldi;
759 // }
760
761 // /// Set to enable the computation of the eigenvectors (default)
762 // void enable_compute_eigenvectors()
763 // {
764 // Compute_eigenvectors = true;
765 // }
766
767 // /// Set to disable the computation of the eigenvectors
768 // void disable_compute_eigenvectors()
769 // {
770 // Compute_eigenvectors = false;
771 // }
772
773 /// Solve the real eigenproblem that is assembled by elements in
774 /// a mesh in a Problem object. Note that the assembled matrices include the
775 /// shift and are real. The eigenvalues and eigenvectors are,
776 /// in general, complex. Eigenvalues may be infinite and are therefore
777 /// returned as
778 /// \f$ \lambda_i = \alpha_i / \beta_i \f$ where \f$ \alpha_i \f$ is complex
779 /// while \f$ \beta_i \f$ is real. The actual eigenvalues may then be
780 /// computed by doing the division, checking for zero betas to avoid NaNs.
781 /// There's a convenience wrapper to this function that simply computes
782 /// these eigenvalues regardless. That version may die in NaN checking is
783 /// enabled (via the fenv.h header and the associated feenable function).
784 /// NOTE: While the above statement is true, the implementation of this
785 /// function is actually everse engineered -- trilinos actually computes the
786 /// eigenvalues directly (and being an Arnoldi method it wouldn't be able to
787 /// obtain any infinite/NaN eigenvalues anyway
788 void solve_eigenproblem(Problem* const& problem_pt,
789 const int& n_eval,
790 Vector<std::complex<double>>& alpha,
791 Vector<double>& beta,
794 const bool& do_adjoint_problem)
795 {
796 // Reverse engineer the "safe" version of the eigenvalues
797 Vector<std::complex<double>> eigenvalue;
798 solve_eigenproblem(problem_pt,
799 n_eval,
800 eigenvalue,
804 unsigned n = eigenvalue.size();
805 alpha.resize(n);
806 beta.resize(n);
807 for (unsigned i = 0; i < n; i++)
808 {
809 alpha[i] = eigenvalue[i];
810 beta[i] = 1.0;
811 }
812 }
813
814 /// Solve the eigen problem
815 void solve_eigenproblem(Problem* const& problem_pt,
816 const int& n_eval,
817 Vector<std::complex<double>>& eigenvalue,
820 const bool& do_adjoint_problem)
821 {
822 // Initially be dumb here
823 Linear_solver_pt = problem_pt->linear_solver_pt();
824
825 // Get a shiny new linear algebra distribution from the problem
828
829 // Let's make the initial vector
830 Teuchos::RCP<DoubleMultiVector> initial =
831 Teuchos::rcp(new DoubleMultiVector(1, dist_pt));
832 Anasazi::MultiVecTraits<double, DoubleMultiVector>::MvRandom(*initial);
833
834 // Make the operator
835 Teuchos::RCP<DoubleMultiVectorOperator> Op_pt;
837 {
839 problem_pt, this->linear_solver_pt(), Sigma_real));
840 }
841 else
842 {
843 Op_pt = Teuchos::rcp(new ProblemBasedShiftInvertOperator(
844 problem_pt, this->linear_solver_pt(), Sigma_real));
845 }
846
847 // Create the basic problem
848 Teuchos::RCP<Anasazi::BasicEigenproblem<double,
851 anasazi_pt = Teuchos::rcp(
852 new Anasazi::BasicEigenproblem<double,
855 initial));
856
857 // No assumptions about niceness of problem matrices
858 anasazi_pt->setHermitian(false);
859
860 // set the number of eigenvalues requested
861 anasazi_pt->setNEV(n_eval);
862
863 // Inform the eigenproblem that you are done passing it information
864 if (anasazi_pt->setProblem() != true)
865 {
866 oomph_info << "Anasazi::BasicEigenproblem::setProblem() had an error."
867 << std::endl
868 << "End Result: TEST FAILED" << std::endl;
869 }
870
871 // Create the solver manager
872 // No need to have ncv specificed, Triliinos has a sensible default
873 // int ncv = 10;
874 MT tol = 1.0e-10;
875 int verbosity = 0;
876 verbosity += Anasazi::Warnings;
877 // MH has switched off the (overly) verbose output
878 // Could introduce handle to switch it back on.
879 // verbosity+=Anasazi::FinalSummary;
880 // verbosity+=Anasazi::TimingDetails;
881
882
883 Teuchos::ParameterList MyPL;
884 MyPL.set("Which", "LM");
885 MyPL.set("Block Size", 1);
886 // MyPL.set( "Num Blocks", ncv );
887 MyPL.set("Maximum Restarts", 500);
888 MyPL.set("Orthogonalization", "DGKS");
889 MyPL.set("Verbosity", verbosity);
890 MyPL.set("Convergence Tolerance", tol);
891 Anasazi::BlockKrylovSchurSolMgr<double,
895
896 // Solve the problem to the specified tolerances or length
897 Anasazi::ReturnType ret = BKS.solve();
898 if (ret != Anasazi::Converged)
899 {
900 std::string error_message = "Eigensolver did not converge!\n";
901
902 throw OomphLibError(
904 }
905
906
907 // Get the eigenvalues and eigenvectors from the eigenproblem
908 Anasazi::Eigensolution<double, DoubleMultiVector> sol =
909 anasazi_pt->getSolution();
910 std::vector<Anasazi::Value<double>> evals = sol.Evals;
911 Teuchos::RCP<DoubleMultiVector> evecs = sol.Evecs;
912
913 // Here's the vector that contains the information
914 // about how to translate the vectors that are returned
915 // by anasazi into real and imag parts of the actual
916 // eigenvectors
917 // std::vector<int> Anasazi::Eigensolution< ScalarType, MV >::index
918 std::vector<int> index_vector = sol.index;
919
920 // Here's what we're actually going to return.
921 unsigned n_eval_avail = evals.size();
922 eigenvalue.resize(n_eval_avail);
925
926 // Extract these from the raw data returned by trilinos
927 for (unsigned i = 0; i < n_eval_avail; i++)
928 {
929 // Undo shift and invert
930 double a = evals[i].realpart;
931 double b = evals[i].imagpart;
932 double det = a * a + b * b;
933 eigenvalue[i] = std::complex<double>(a / det + Sigma_real, -b / det);
934
935 // Now set the eigenvectors
936 unsigned nrow_local = evecs->nrow_local();
937 eigenvector_real[i].build(evecs->distribution_pt(), 0.0);
938 eigenvector_imag[i].build(evecs->distribution_pt(), 0.0);
939
940 // std::vector<int> Anasazi::Eigensolution< ScalarType, MV >::index
941 // to translate into proper complex vector; see
942 // https://docs.trilinos.org/dev/packages/anasazi/doc/html/structAnasazi_1_1Eigensolution.html#ac9d141d98adcba85fbad011a7b7bda6e
943
944 // An index into Evecs to allow compressed storage of eigenvectors for
945 // real, non-Hermitian problems.
946
947 // index has length numVecs, where each entry is 0, +1, or -1. These
948 // have the following interpretation:
949
950 // index[i] == 0: signifies that the corresponding eigenvector is
951 // stored as the i column of Evecs. This will usually be the case
952 // when ScalarType is complex, an eigenproblem is Hermitian, or a
953 // real, non-Hermitian eigenproblem has a real eigenvector. index[i]
954 // == +1: signifies that the corresponding eigenvector is stored in
955 // two vectors: the real part in the i column of Evecs and the
956 // positive imaginary part in the i+1 column of Evecs. index[i] ==
957 // -1: signifies that the corresponding eigenvector is stored in two
958 // vectors: the real part in the i-1 column of Evecs and the
959 // negative imaginary part in the i column of Evecs
960
961 // Real eigenvector
962 if (index_vector[i] == 0)
963 {
964 for (unsigned j = 0; j < nrow_local; j++)
965 {
966 eigenvector_real[i][j] = (*evecs)(i, j);
967 eigenvector_imag[i][j] = 0.0;
968 }
969 }
970 else if (index_vector[i] == 1)
971 {
972 for (unsigned j = 0; j < nrow_local; j++)
973 {
974 eigenvector_real[i][j] = (*evecs)(i, j);
975 eigenvector_imag[i][j] = (*evecs)(i + 1, j);
976 }
977 }
978 else if (index_vector[i] == -1)
979 {
980 for (unsigned j = 0; j < nrow_local; j++)
981 {
982 eigenvector_real[i][j] = (*evecs)(i - 1, j);
983 eigenvector_imag[i][j] = -(*evecs)(i, j);
984 }
985 }
986 else
987 {
988 oomph_info << "Never get here: index_vector[ " << i
989 << " ] = " << index_vector[i] << std::endl;
990 abort();
991 }
992 }
993
994 // Tidy up
995 delete dist_pt;
996 }
997
998 /// Return a pointer to the linear solver object
1000 {
1001 return Linear_solver_pt;
1002 }
1003
1004 /// Return a pointer to the linear solver object (const version)
1006 {
1007 return Linear_solver_pt;
1008 }
1009 };
1010
1011} // namespace oomph
1012
1013
1014#endif
cstr elem_len * i
Definition cfortran.h:603
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 int GetVecLength(const oomph::DoubleMultiVector &mv)
Obtain the global length of the vector.
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 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 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 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 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.
Anasazi::details::StubTsqrAdapter< MV > tsqr_adaptor_type
TsqrAdaptor specialization for the multivector type MV.
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 SetBlock(const oomph::DoubleMultiVector &A, const Teuchos::Range1D &index, oomph::DoubleMultiVector &mv)
Deep copy of A into specified columns of mv.
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 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 MvScale(oomph::DoubleMultiVector &mv, const double alpha)
Scale each element of the vectors in mv with alpha.
static ptrdiff_t GetGlobalLength(const oomph::DoubleMultiVector &mv)
Return the number of rows in the given multivector. NOTE: Trilinos 13.4.0 uses GetGlobalLength instea...
static void MvAddMv(const double alpha, const oomph::DoubleMultiVector &A, const double beta, const oomph::DoubleMultiVector &B, oomph::DoubleMultiVector &mv)
Replace mv with .
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 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 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 > 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 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 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.
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.
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.
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 * Linear_solver_pt
Pointer to a linear solver.
LinearSolver *& linear_solver_pt()
Return a pointer to the linear solver object.
SCT::magnitudeType MT
Anasazi::MultiVec< ST > MV
Anasazi::MultiVecTraits< ST, MV > MVT
ANASAZI(const ANASAZI &)
Empty copy constructor.
LinearSolver *const & linear_solver_pt() const
Return a pointer to the linear solver object (const version)
Anasazi::Operator< ST > OP
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
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.
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
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
unsigned nvector() const
Return the number of vectors.
DoubleVector & doublevector(const unsigned &i)
access to the DoubleVector representatoin
A vector in the mathematical sense, initially developed for linear algebra type applications....
Base class for all EigenProblem solves. This simply defines standard interfaces so that different sol...
double Sigma_real
Double value that represents the real part of the shift in shifted eigensolvers.
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
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,...
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
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:8442
LinearAlgebraDistribution *const & dof_distribution_pt() const
Return the pointer to the dof distribution (read-only)
Definition problem.h:1698
void create_new_linear_algebra_distribution(LinearAlgebraDistribution *&dist_pt)
Get new linear algebra distribution (you're in charge of deleting it!)
Definition problem.cc:309
LinearSolver *& linear_solver_pt()
Return a pointer to the linear solver object.
Definition problem.h:1486
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition Vector.h:58
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...