1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 1999 - 2020 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.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 #ifndef dealii_precondition_h
17 #define dealii_precondition_h
18 
19 // This file contains simple preconditioners.
20 
21 #include <deal.II/base/config.h>
22 
23 #include <deal.II/base/cuda_size.h>
24 #include <deal.II/base/memory_space.h>
25 #include <deal.II/base/parallel.h>
26 #include <deal.II/base/smartpointer.h>
27 #include <deal.II/base/template_constraints.h>
28 #include <deal.II/base/thread_management.h>
29 #include <deal.II/base/utilities.h>
30 
31 #include <deal.II/lac/affine_constraints.h>
32 #include <deal.II/lac/diagonal_matrix.h>
33 #include <deal.II/lac/solver_cg.h>
34 #include <deal.II/lac/vector_memory.h>
35 
36 DEAL_II_NAMESPACE_OPEN
37 
38 // forward declarations
39 #ifndef DOXYGEN
40 template <typename number>
41 class Vector;
42 template <typename number>
43 class SparseMatrix;
44 namespace LinearAlgebra
45 {
46   namespace distributed
47   {
48     template <typename, typename>
49     class Vector;
50   } // namespace distributed
51 } // namespace LinearAlgebra
52 #endif
53 
54 
55 /*! @addtogroup Preconditioners
56  *@{
57  */
58 
59 
60 /**
61  * No preconditioning.  This class helps you, if you want to use a linear
62  * solver without preconditioning. All solvers in LAC require a
63  * preconditioner. Therefore, you must use the identity provided here to avoid
64  * preconditioning. It can be used in the following way:
65  *
66  * @code
67  * SolverControl           solver_control (1000, 1e-12);
68  * SolverCG<>              cg (solver_control);
69  * cg.solve (system_matrix, solution, system_rhs, PreconditionIdentity());
70  * @endcode
71  *
72  * See the step-3 tutorial program for an example and additional explanations.
73  *
74  * Alternatively, the IdentityMatrix class can be used to precondition in this
75  * way.
76  */
77 class PreconditionIdentity : public Subscriptor
78 {
79 public:
80   /**
81    * Declare type for container size.
82    */
83   using size_type = types::global_dof_index;
84 
85   /**
86    * This function is only present to provide the interface of a
87    * preconditioner to be handed to a smoother.  This does nothing.
88    */
89   struct AdditionalData
90   {
91     /**
92      * Constructor.
93      */
94     AdditionalData() = default;
95   };
96 
97   /**
98    * Constructor, sets the domain and range sizes to their defaults.
99    */
100   PreconditionIdentity();
101 
102   /**
103    * The matrix argument is ignored and here just for compatibility with more
104    * complex preconditioners.
105    */
106   template <typename MatrixType>
107   void
108   initialize(const MatrixType &    matrix,
109              const AdditionalData &additional_data = AdditionalData());
110 
111   /**
112    * Apply preconditioner.
113    */
114   template <class VectorType>
115   void
116   vmult(VectorType &, const VectorType &) const;
117 
118   /**
119    * Apply transpose preconditioner. Since this is the identity, this function
120    * is the same as vmult().
121    */
122   template <class VectorType>
123   void
124   Tvmult(VectorType &, const VectorType &) const;
125 
126   /**
127    * Apply preconditioner, adding to the previous value.
128    */
129   template <class VectorType>
130   void
131   vmult_add(VectorType &, const VectorType &) const;
132 
133   /**
134    * Apply transpose preconditioner, adding. Since this is the identity, this
135    * function is the same as vmult_add().
136    */
137   template <class VectorType>
138   void
139   Tvmult_add(VectorType &, const VectorType &) const;
140 
141   /**
142    * This function is only present to provide the interface of a
143    * preconditioner to be handed to a smoother.  This does nothing.
144    */
145   void
clear()146   clear()
147   {}
148 
149   /**
150    * Return the dimension of the codomain (or range) space. Note that the
151    * matrix is of dimension $m \times n$.
152    *
153    * @note This function should only be called if the preconditioner has been
154    * initialized.
155    */
156   size_type
157   m() const;
158 
159   /**
160    * Return the dimension of the domain space. Note that the matrix is of
161    * dimension $m \times n$.
162    *
163    * @note This function should only be called if the preconditioner has been
164    * initialized.
165    */
166   size_type
167   n() const;
168 
169 private:
170   /**
171    * The dimension of the range space.
172    */
173   size_type n_rows;
174 
175   /**
176    * The dimension of the domain space.
177    */
178   size_type n_columns;
179 };
180 
181 
182 
183 /**
184  * Preconditioning with Richardson's method. This preconditioner just scales
185  * the vector with a constant relaxation factor provided by the AdditionalData
186  * object.
187  *
188  * In Krylov-space methods, this preconditioner should not have any effect.
189  * Using SolverRichardson, the two relaxation parameters will be just
190  * multiplied. Still, this class is useful in multigrid smoother objects
191  * (MGSmootherRelaxation).
192  */
193 class PreconditionRichardson : public Subscriptor
194 {
195 public:
196   /**
197    * Declare type for container size.
198    */
199   using size_type = types::global_dof_index;
200 
201   /**
202    * Parameters for Richardson preconditioner.
203    */
204   class AdditionalData
205   {
206   public:
207     /**
208      * Constructor. Block size must be given since there is no reasonable
209      * default parameter.
210      */
211     AdditionalData(const double relaxation = 1.);
212 
213     /**
214      * Relaxation parameter.
215      */
216     double relaxation;
217   };
218 
219   /**
220    * Constructor, sets the relaxation parameter, domain and range sizes to
221    * their default.
222    */
223   PreconditionRichardson();
224 
225   /**
226    * Change the relaxation parameter.
227    */
228   void
229   initialize(const AdditionalData &parameters);
230 
231   /**
232    * Change the relaxation parameter in a way consistent with other
233    * preconditioners. The matrix argument is ignored and here just for
234    * compatibility with more complex preconditioners.
235    */
236   template <typename MatrixType>
237   void
238   initialize(const MatrixType &matrix, const AdditionalData &parameters);
239 
240   /**
241    * Apply preconditioner.
242    */
243   template <class VectorType>
244   void
245   vmult(VectorType &, const VectorType &) const;
246 
247   /**
248    * Apply transpose preconditioner. Since this is the identity, this function
249    * is the same as vmult().
250    */
251   template <class VectorType>
252   void
253   Tvmult(VectorType &, const VectorType &) const;
254   /**
255    * Apply preconditioner, adding to the previous value.
256    */
257   template <class VectorType>
258   void
259   vmult_add(VectorType &, const VectorType &) const;
260 
261   /**
262    * Apply transpose preconditioner, adding. Since this is the identity, this
263    * function is the same as vmult_add().
264    */
265   template <class VectorType>
266   void
267   Tvmult_add(VectorType &, const VectorType &) const;
268 
269   /**
270    * This function is only present to provide the interface of a
271    * preconditioner to be handed to a smoother.  This does nothing.
272    */
273   void
clear()274   clear()
275   {}
276 
277   /**
278    * Return the dimension of the codomain (or range) space. Note that the
279    * matrix is of dimension $m \times n$.
280    *
281    * @note This function should only be called if the preconditioner has been
282    * initialized.
283    */
284   size_type
285   m() const;
286 
287   /**
288    * Return the dimension of the domain space. Note that the matrix is of
289    * dimension $m \times n$.
290    *
291    * @note This function should only be called if the preconditioner has been
292    * initialized.
293    */
294   size_type
295   n() const;
296 
297 private:
298   /**
299    * The relaxation parameter multiplied with the vectors.
300    */
301   double relaxation;
302 
303   /**
304    * The dimension of the range space.
305    */
306   size_type n_rows;
307 
308   /**
309    * The dimension of the domain space.
310    */
311   size_type n_columns;
312 };
313 
314 
315 
316 /**
317  * Preconditioner using a matrix-builtin function.  This class forms a
318  * preconditioner suitable for the LAC solver classes. Since many
319  * preconditioning methods are based on matrix entries, these have to be
320  * implemented as member functions of the underlying matrix implementation.
321  * This class now is intended to allow easy access to these member functions
322  * from LAC solver classes.
323  *
324  * It seems that all builtin preconditioners have a relaxation parameter, so
325  * please use PreconditionRelaxation for these.
326  *
327  * You will usually not want to create a named object of this type, although
328  * possible. The most common use is like this:
329  * @code
330  * SolverGMRES<SparseMatrix<double> Vector<double>> gmres(control,memory,500);
331  *
332  * gmres.solve(
333  *   matrix, solution, right_hand_side,
334  *   PreconditionUseMatrix<SparseMatrix<double>,Vector<double> >(
335  *     matrix, &SparseMatrix<double>::template precondition_Jacobi<double>));
336  * @endcode
337  * This creates an unnamed object to be passed as the fourth parameter to the
338  * solver function of the SolverGMRES class. It assumes that the SparseMatrix
339  * class has a function <tt>precondition_Jacobi</tt> taking two vectors
340  * (source and destination) as parameters (Actually, there is no function like
341  * that, the existing function takes a third parameter, denoting the
342  * relaxation parameter; this example is therefore only meant to illustrate
343  * the general idea).
344  *
345  * Note that due to the default template parameters, the above example could
346  * be written shorter as follows:
347  * @code
348  * ...
349  * gmres.solve(
350  *   matrix, solution, right_hand_side,
351  *   PreconditionUseMatrix<>(
352  *     matrix,&SparseMatrix<double>::template precondition_Jacobi<double>));
353  * @endcode
354  */
355 template <typename MatrixType = SparseMatrix<double>,
356           class VectorType    = Vector<double>>
357 class PreconditionUseMatrix : public Subscriptor
358 {
359 public:
360   /**
361    * Type of the preconditioning function of the matrix.
362    */
363   using function_ptr = void (MatrixType::*)(VectorType &,
364                                             const VectorType &) const;
365 
366   /**
367    * Constructor.  This constructor stores a reference to the matrix object
368    * for later use and selects a preconditioning method, which must be a
369    * member function of that matrix.
370    */
371   PreconditionUseMatrix(const MatrixType &M, const function_ptr method);
372 
373   /**
374    * Execute preconditioning. Calls the function passed to the constructor of
375    * this object with the two arguments given here.
376    */
377   void
378   vmult(VectorType &dst, const VectorType &src) const;
379 
380 private:
381   /**
382    * Pointer to the matrix in use.
383    */
384   const MatrixType &matrix;
385 
386   /**
387    * Pointer to the preconditioning function.
388    */
389   const function_ptr precondition;
390 };
391 
392 
393 
394 /**
395  * Base class for other preconditioners. Here, only some common features
396  * Jacobi, SOR and SSOR preconditioners are implemented. For preconditioning,
397  * refer to derived classes.
398  */
399 template <typename MatrixType = SparseMatrix<double>>
400 class PreconditionRelaxation : public Subscriptor
401 {
402 public:
403   /**
404    * Declare type for container size.
405    */
406   using size_type = typename MatrixType::size_type;
407 
408   /**
409    * Class for parameters.
410    */
411   class AdditionalData
412   {
413   public:
414     /**
415      * Constructor.
416      */
417     AdditionalData(const double relaxation = 1.);
418 
419     /**
420      * Relaxation parameter.
421      */
422     double relaxation;
423   };
424 
425   /**
426    * Initialize matrix and relaxation parameter. The matrix is just stored in
427    * the preconditioner object. The relaxation parameter should be larger than
428    * zero and smaller than 2 for numerical reasons. It defaults to 1.
429    */
430   void
431   initialize(const MatrixType &    A,
432              const AdditionalData &parameters = AdditionalData());
433 
434   /**
435    * Release the matrix and reset its pointer.
436    */
437   void
438   clear();
439 
440   /**
441    * Return the dimension of the codomain (or range) space. Note that the
442    * matrix is of dimension $m \times n$.
443    */
444   size_type
445   m() const;
446 
447   /**
448    * Return the dimension of the domain space. Note that the matrix is of
449    * dimension $m \times n$.
450    */
451   size_type
452   n() const;
453 
454 protected:
455   /**
456    * Pointer to the matrix object.
457    */
458   SmartPointer<const MatrixType, PreconditionRelaxation<MatrixType>> A;
459 
460   /**
461    * Relaxation parameter.
462    */
463   double relaxation;
464 };
465 
466 
467 
468 /**
469  * Jacobi preconditioner using matrix built-in function.  The
470  * <tt>MatrixType</tt> class used is required to have a function
471  * <tt>precondition_Jacobi(VectorType&, const VectorType&, double</tt>). This
472  * class satisfies the
473  * @ref ConceptRelaxationType "relaxation concept".
474  *
475  * @code
476  * // Declare related objects
477  *
478  * SparseMatrix<double> A;
479  * Vector<double> x;
480  * Vector<double> b;
481  * SolverCG<> solver(...);
482  *
483  * //...initialize and build A
484  *
485  * // Define and initialize preconditioner:
486  *
487  * PreconditionJacobi<SparseMatrix<double> > precondition;
488  * precondition.initialize(
489  *   A, PreconditionJacobi<SparseMatrix<double>>::AdditionalData(.6));
490  *
491  * solver.solve (A, x, b, precondition);
492  * @endcode
493  */
494 template <typename MatrixType = SparseMatrix<double>>
495 class PreconditionJacobi : public PreconditionRelaxation<MatrixType>
496 {
497 public:
498   /**
499    * An alias to the base class AdditionalData.
500    */
501   using AdditionalData =
502     typename PreconditionRelaxation<MatrixType>::AdditionalData;
503 
504   /**
505    * Apply preconditioner.
506    */
507   template <class VectorType>
508   void
509   vmult(VectorType &, const VectorType &) const;
510 
511   /**
512    * Apply transpose preconditioner. Since this is a symmetric preconditioner,
513    * this function is the same as vmult().
514    */
515   template <class VectorType>
516   void
517   Tvmult(VectorType &, const VectorType &) const;
518 
519   /**
520    * Perform one step of the preconditioned Richardson iteration.
521    */
522   template <class VectorType>
523   void
524   step(VectorType &x, const VectorType &rhs) const;
525 
526   /**
527    * Perform one transposed step of the preconditioned Richardson iteration.
528    */
529   template <class VectorType>
530   void
531   Tstep(VectorType &x, const VectorType &rhs) const;
532 };
533 
534 
535 /**
536  * SOR preconditioner using matrix built-in function.
537  *
538  * Assuming the matrix <i>A = D + L + U</i> is split into its diagonal
539  * <i>D</i> as well as the strict lower and upper triangles <i>L</i> and
540  * <i>U</i>, then the SOR preconditioner with relaxation parameter <i>r</i> is
541  * @f[
542  *  P^{-1} = r (D+rL)^{-1}.
543  * @f]
544  * It is this operator <i>P<sup>-1</sup></i>, which is implemented by vmult()
545  * through forward substitution. Analogously, Tvmult() implements the
546  * operation of <i>r(D+rU)<sup>-1</sup></i>.
547  *
548  * The SOR iteration itself can be directly written as
549  * @f[
550  *  x^{k+1} = x^k - r D^{-1} \bigl(L x^{k+1} + U x^k - b\bigr).
551  * @f]
552  * Using the right hand side <i>b</i> and the previous iterate <i>x</i>, this
553  * is the operation implemented by step().
554  *
555  * The MatrixType class used is required to have functions
556  * <tt>precondition_SOR(VectorType&, const VectorType&, double)</tt> and
557  * <tt>precondition_TSOR(VectorType&, const VectorType&, double)</tt>. This
558  * class satisfies the
559  * @ref ConceptRelaxationType "relaxation concept".
560  *
561  * @code
562  * // Declare related objects
563  *
564  * SparseMatrix<double> A;
565  * Vector<double> x;
566  * Vector<double> b;
567  * SolverCG<> solver(...);
568  *
569  * //...initialize and build A
570  *
571  * // Define and initialize preconditioner
572  *
573  * PreconditionSOR<SparseMatrix<double> > precondition;
574  * precondition.initialize(
575  *   A, PreconditionSOR<SparseMatrix<double>>::AdditionalData(.6));
576  *
577  * solver.solve (A, x, b, precondition);
578  * @endcode
579  */
580 template <typename MatrixType = SparseMatrix<double>>
581 class PreconditionSOR : public PreconditionRelaxation<MatrixType>
582 {
583 public:
584   /**
585    * An alias to the base class AdditionalData.
586    */
587   using AdditionalData =
588     typename PreconditionRelaxation<MatrixType>::AdditionalData;
589 
590   /**
591    * Apply preconditioner.
592    */
593   template <class VectorType>
594   void
595   vmult(VectorType &, const VectorType &) const;
596 
597   /**
598    * Apply transpose preconditioner.
599    */
600   template <class VectorType>
601   void
602   Tvmult(VectorType &, const VectorType &) const;
603 
604   /**
605    * Perform one step of the preconditioned Richardson iteration.
606    */
607   template <class VectorType>
608   void
609   step(VectorType &x, const VectorType &rhs) const;
610 
611   /**
612    * Perform one transposed step of the preconditioned Richardson iteration.
613    */
614   template <class VectorType>
615   void
616   Tstep(VectorType &x, const VectorType &rhs) const;
617 };
618 
619 
620 
621 /**
622  * SSOR preconditioner using matrix built-in function.  The
623  * <tt>MatrixType</tt> class used is required to have a function
624  * <tt>precondition_SSOR(VectorType&, const VectorType&, double)</tt>. This
625  * class satisfies the
626  * @ref ConceptRelaxationType "relaxation concept".
627  *
628  * @code
629  * // Declare related objects
630  *
631  * SparseMatrix<double> A;
632  * Vector<double> x;
633  * Vector<double> b;
634  * SolverCG<> solver(...);
635  *
636  * //...initialize and build A
637  *
638  * // Define and initialize preconditioner
639  *
640  * PreconditionSSOR<SparseMatrix<double> > precondition;
641  * precondition.initialize(
642  *   A, PreconditionSSOR<SparseMatrix<double>>::AdditionalData(.6));
643  *
644  * solver.solve (A, x, b, precondition);
645  * @endcode
646  */
647 template <typename MatrixType = SparseMatrix<double>>
648 class PreconditionSSOR : public PreconditionRelaxation<MatrixType>
649 {
650 public:
651   /**
652    * An alias to the base class AdditionalData.
653    */
654   using AdditionalData =
655     typename PreconditionRelaxation<MatrixType>::AdditionalData;
656 
657   /**
658    * Declare type for container size.
659    */
660   using size_type = typename MatrixType::size_type;
661 
662   /**
663    * An alias to the base class.
664    */
665   using BaseClass = PreconditionRelaxation<MatrixType>;
666 
667 
668   /**
669    * Initialize matrix and relaxation parameter. The matrix is just stored in
670    * the preconditioner object. The relaxation parameter should be larger than
671    * zero and smaller than 2 for numerical reasons. It defaults to 1.
672    */
673   void
674   initialize(const MatrixType &                        A,
675              const typename BaseClass::AdditionalData &parameters =
676                typename BaseClass::AdditionalData());
677 
678   /**
679    * Apply preconditioner.
680    */
681   template <class VectorType>
682   void
683   vmult(VectorType &, const VectorType &) const;
684 
685   /**
686    * Apply transpose preconditioner. Since this is a symmetric preconditioner,
687    * this function is the same as vmult().
688    */
689   template <class VectorType>
690   void
691   Tvmult(VectorType &, const VectorType &) const;
692 
693 
694   /**
695    * Perform one step of the preconditioned Richardson iteration
696    */
697   template <class VectorType>
698   void
699   step(VectorType &x, const VectorType &rhs) const;
700 
701   /**
702    * Perform one transposed step of the preconditioned Richardson iteration.
703    */
704   template <class VectorType>
705   void
706   Tstep(VectorType &x, const VectorType &rhs) const;
707 
708 private:
709   /**
710    * An array that stores for each matrix row where the first position after
711    * the diagonal is located.
712    */
713   std::vector<std::size_t> pos_right_of_diagonal;
714 };
715 
716 
717 /**
718  * Permuted SOR preconditioner using matrix built-in function.  The
719  * <tt>MatrixType</tt> class used is required to have functions
720  * <tt>PSOR(VectorType&, const VectorType&, double)</tt> and
721  * <tt>TPSOR(VectorType&, const VectorType&, double)</tt>.
722  *
723  * @code
724  * // Declare related objects
725  *
726  * SparseMatrix<double> A;
727  * Vector<double> x;
728  * Vector<double> b;
729  * SolverCG<> solver(...);
730  *
731  * //...initialize and build A
732  *
733  * std::vector<unsigned int> permutation(x.size());
734  * std::vector<unsigned int> inverse_permutation(x.size());
735  *
736  * //...fill permutation and its inverse with reasonable values
737  *
738  * // Define and initialize preconditioner
739  *
740  * PreconditionPSOR<SparseMatrix<double> > precondition;
741  * precondition.initialize (A, permutation, inverse_permutation, .6);
742  *
743  * solver.solve (A, x, b, precondition);
744  * @endcode
745  */
746 template <typename MatrixType = SparseMatrix<double>>
747 class PreconditionPSOR : public PreconditionRelaxation<MatrixType>
748 {
749 public:
750   /**
751    * Declare type for container size.
752    */
753   using size_type = typename MatrixType::size_type;
754 
755   /**
756    * Parameters for PreconditionPSOR.
757    */
758   class AdditionalData
759   {
760   public:
761     /**
762      * Constructor. For the parameters' description, see below.
763      *
764      * The permutation vectors are stored as a reference. Therefore, it has to
765      * be assured that the lifetime of the vector exceeds the lifetime of the
766      * preconditioner.
767      *
768      * The relaxation parameter should be larger than zero and smaller than 2
769      * for numerical reasons. It defaults to 1.
770      */
771     AdditionalData(
772       const std::vector<size_type> &permutation,
773       const std::vector<size_type> &inverse_permutation,
774       const typename PreconditionRelaxation<MatrixType>::AdditionalData
775         &parameters =
776           typename PreconditionRelaxation<MatrixType>::AdditionalData());
777 
778     /**
779      * Storage for the permutation vector.
780      */
781     const std::vector<size_type> &permutation;
782     /**
783      * Storage for the inverse permutation vector.
784      */
785     const std::vector<size_type> &inverse_permutation;
786     /**
787      * Relaxation parameters
788      */
789     typename PreconditionRelaxation<MatrixType>::AdditionalData parameters;
790   };
791 
792   /**
793    * Initialize matrix and relaxation parameter. The matrix is just stored in
794    * the preconditioner object.
795    *
796    * The permutation vector is stored as a pointer. Therefore, it has to be
797    * assured that the lifetime of the vector exceeds the lifetime of the
798    * preconditioner.
799    *
800    * The relaxation parameter should be larger than zero and smaller than 2
801    * for numerical reasons. It defaults to 1.
802    */
803   void
804   initialize(const MatrixType &            A,
805              const std::vector<size_type> &permutation,
806              const std::vector<size_type> &inverse_permutation,
807              const typename PreconditionRelaxation<MatrixType>::AdditionalData
808                &parameters =
809                  typename PreconditionRelaxation<MatrixType>::AdditionalData());
810 
811   /**
812    * Initialize matrix and relaxation parameter. The matrix is just stored in
813    * the preconditioner object.
814    *
815    * For more detail about possible parameters, see the class documentation
816    * and the documentation of the PreconditionPSOR::AdditionalData class.
817    *
818    * After this function is called the preconditioner is ready to be used
819    * (using the <code>vmult</code> function of derived classes).
820    */
821   void
822   initialize(const MatrixType &A, const AdditionalData &additional_data);
823 
824   /**
825    * Apply preconditioner.
826    */
827   template <class VectorType>
828   void
829   vmult(VectorType &, const VectorType &) const;
830 
831   /**
832    * Apply transpose preconditioner.
833    */
834   template <class VectorType>
835   void
836   Tvmult(VectorType &, const VectorType &) const;
837 
838 private:
839   /**
840    * Storage for the permutation vector.
841    */
842   const std::vector<size_type> *permutation;
843   /**
844    * Storage for the inverse permutation vector.
845    */
846   const std::vector<size_type> *inverse_permutation;
847 };
848 
849 
850 
851 /**
852  * Preconditioning with a Chebyshev polynomial for symmetric positive definite
853  * matrices. This preconditioner is based on an iteration of an inner
854  * preconditioner of type @p PreconditionerType with coefficients that are
855  * adapted to optimally cover an eigenvalue range between the largest
856  * eigenvalue $\lambda_{\max{}}$ down to a given lower eigenvalue
857  * $\lambda_{\min{}}$ specified by the optional parameter
858  * @p smoothing_range. The algorithm is based on the following three-term
859  * recurrence:
860  * @f[
861  *  x^{n+1} = x^{n} + \rho_n \rho_{n-1} (x^{n} - x^{n-1}) +
862  *     \frac{\rho_n}{\lambda_{\max{}}-\lambda_{\min{}}} P^{-1} (b-Ax^n).
863  * @f]
864  * where the parameter $\rho_0$ is set to $\rho_0 = 2
865  * \frac{\lambda_{\max{}}-\lambda_{\min{}}}{\lambda_{\max{}}+\lambda_{\min{}}}$
866  * for the maximal eigenvalue $\lambda_{\max{}}$ and updated via $\rho_n =
867  * \left(2\frac{\lambda_{\max{}}+\lambda_{\min{}}}
868  * {\lambda_{\max{}}-\lambda_{\min{}}} - \rho_{n-1}\right)^{-1}$. The
869  * Chebyshev polynomial is constructed to strongly damp the eigenvalue range
870  * between $\lambda_{\min{}}$ and $\lambda_{\max{}}$ and is visualized e.g. in
871  * Utilities::LinearAlgebra::chebyshev_filter().
872  *
873  * The typical use case for the preconditioner is a Jacobi preconditioner
874  * specified through DiagonalMatrix, which is also the default value for the
875  * preconditioner. Note that if the degree variable is set to one, the
876  * Chebyshev iteration corresponds to a Jacobi preconditioner (or the
877  * underlying preconditioner type) with relaxation parameter according to the
878  * specified smoothing range.
879  *
880  * Besides the default choice of a pointwise Jacobi preconditioner, this class
881  * also allows for more advanced types of preconditioners, for example
882  * iterating block-Jacobi preconditioners in DG methods.
883  *
884  * Apart from the inner preconditioner object, this iteration does not need
885  * access to matrix entries, which makes it an ideal ingredient for
886  * matrix-free computations. In that context, this class can be used as a
887  * multigrid smoother that is trivially %parallel (assuming that matrix-vector
888  * products are %parallel and the inner preconditioner is %parallel). Its use
889  * is demonstrated in the step-37 and step-59 tutorial programs.
890  *
891  * <h4>Estimation of the eigenvalues</h4>
892  *
893  * The Chebyshev method relies on an estimate of the eigenvalues of the matrix
894  * which are computed during the first invocation of vmult(). The algorithm
895  * invokes a conjugate gradient solver (i.e., Lanczos iteration) so symmetry
896  * and positive definiteness of the (preconditioned) matrix system are
897  * required. The eigenvalue algorithm can be controlled by
898  * PreconditionChebyshev::AdditionalData::eig_cg_n_iterations specifying how
899  * many iterations should be performed. The iterations are started from an
900  * initial vector that depends on the vector type. For the classes
901  * dealii::Vector or dealii::LinearAlgebra::distributed::Vector, which have
902  * fast element access, it is a vector with entries `(-5.5, -4.5, -3.5,
903  * -2.5, ..., 3.5, 4.5, 5.5)` with appropriate epilogue and adjusted such that
904  * its mean is always zero, which works well for the Laplacian. This setup is
905  * stable in parallel in the sense that for a different number of processors
906  * but the same ordering of unknowns, the same initial vector and thus
907  * eigenvalue distribution will be computed, apart from roundoff errors. For
908  * other vector types, the initial vector contains all ones, scaled by the
909  * length of the vector, except for the very first entry that is zero,
910  * triggering high-frequency content again.
911  *
912  * The computation of eigenvalues happens the first time one of the vmult(),
913  * Tvmult(), step() or Tstep() functions is called or when
914  * estimate_eigenvalues() is called directly. In the latter case, it is
915  * necessary to provide a temporary vector of the same layout as the source
916  * and destination vectors used during application of the preconditioner.
917  *
918  * The estimates for minimum and maximum eigenvalue are taken from SolverCG
919  * (even if the solver did not converge in the requested number of
920  * iterations). Finally, the maximum eigenvalue is multiplied by a safety
921  * factor of 1.2.
922  *
923  * Due to the cost of the eigenvalue estimate, this class is most appropriate
924  * if it is applied repeatedly, e.g. in a smoother for a geometric multigrid
925  * solver, that can in turn be used to solve several linear systems.
926  *
927  * <h4>Bypassing the eigenvalue computation</h4>
928  *
929  * In some contexts, the automatic eigenvalue computation of this class may
930  * result in bad quality, or it may be unstable when used in parallel with
931  * different enumerations of the degrees of freedom, making computations
932  * strongly dependent on the parallel configuration. It is possible to bypass
933  * the automatic eigenvalue computation by setting
934  * AdditionalData::eig_cg_n_iterations to zero, and provide the variable
935  * AdditionalData::max_eigenvalue instead. The minimal eigenvalue is
936  * implicitly specified via `max_eigenvalue/smoothing_range`.
937 
938  * <h4>Using the PreconditionChebyshev as a solver</h4>
939  *
940  * If the range <tt>[max_eigenvalue/smoothing_range, max_eigenvalue]</tt>
941  * contains all eigenvalues of the preconditioned matrix system and the degree
942  * (i.e., number of iterations) is high enough, this class can also be used as
943  * a direct solver. For an error estimation of the Chebyshev iteration that
944  * can be used to determine the number of iteration, see Varga (2009).
945  *
946  * In order to use Chebyshev as a solver, set the degree to
947  * numbers::invalid_unsigned_int to force the automatic computation of the
948  * number of iterations needed to reach a given target tolerance. In this
949  * case, the target tolerance is read from the variable
950  * PreconditionChebyshev::AdditionalData::smoothing_range (it needs to be a
951  * number less than one to force any iterations obviously).
952  *
953  * For details on the algorithm, see section 5.1 of
954  * @code{.bib}
955  * @book{Varga2009,
956  *   Title     = {Matrix iterative analysis},
957  *   Author    = {Varga, R. S.},
958  *   Publisher = {Springer},
959  *   Address   = {Berlin},
960  *   Edition   = {2nd},
961  *   Year      = {2009},
962  * }
963  * @endcode
964  *
965  * <h4>Requirements on the templated classes</h4>
966  *
967  * The class MatrixType must be derived from Subscriptor because a
968  * SmartPointer to MatrixType is held in the class. In particular, this means
969  * that the matrix object needs to persist during the lifetime of
970  * PreconditionChebyshev. The preconditioner is held in a shared_ptr that is
971  * copied into the AdditionalData member variable of the class, so the
972  * variable used for initialization can safely be discarded after calling
973  * initialize(). Both the matrix and the preconditioner need to provide
974  * @p vmult() functions for the matrix-vector product and @p m() functions for
975  * accessing the number of rows in the (square) matrix. Furthermore, the
976  * matrix must provide <tt>el(i,i)</tt> methods for accessing the matrix
977  * diagonal in case the preconditioner type is DiagonalMatrix. Even though
978  * it is highly recommended to pass the inverse diagonal entries inside a
979  * separate preconditioner object for implementing the Jacobi method (which is
980  * the only possible way to operate this class when computing in %parallel
981  * with MPI because there is no knowledge about the locally stored range of
982  * entries that would be needed from the matrix alone), there is a backward
983  * compatibility function that can extract the diagonal in case of a serial
984  * computation.
985  */
986 template <typename MatrixType         = SparseMatrix<double>,
987           typename VectorType         = Vector<double>,
988           typename PreconditionerType = DiagonalMatrix<VectorType>>
989 class PreconditionChebyshev : public Subscriptor
990 {
991 public:
992   /**
993    * Declare type for container size.
994    */
995   using size_type = types::global_dof_index;
996 
997   /**
998    * Standardized data struct to pipe additional parameters to the
999    * preconditioner.
1000    */
1001   struct AdditionalData
1002   {
1003     /**
1004      * Constructor.
1005      */
1006     AdditionalData(const unsigned int degree              = 1,
1007                    const double       smoothing_range     = 0.,
1008                    const unsigned int eig_cg_n_iterations = 8,
1009                    const double       eig_cg_residual     = 1e-2,
1010                    const double       max_eigenvalue      = 1);
1011 
1012     /**
1013      *  Copy assignment operator.
1014      */
1015     AdditionalData &
1016     operator=(const AdditionalData &other_data);
1017 
1018     /**
1019      * This determines the degree of the Chebyshev polynomial. The degree of
1020      * the polynomial gives the number of matrix-vector products to be
1021      * performed for one application of the vmult() operation. Degree one
1022      * corresponds to a damped Jacobi method.
1023      *
1024      * If the degree is set to numbers::invalid_unsigned_int, the algorithm
1025      * will automatically determine the number of necessary iterations based
1026      * on the usual Chebyshev error formula as mentioned in the discussion of
1027      * the main class.
1028      */
1029     unsigned int degree;
1030 
1031     /**
1032      * This sets the range between the largest eigenvalue in the matrix and
1033      * the smallest eigenvalue to be treated. If the parameter is set to a
1034      * number less than 1, an estimate for the largest and for the smallest
1035      * eigenvalue will be calculated internally. For a smoothing range larger
1036      * than one, the Chebyshev polynomial will act in the interval
1037      * $[\lambda_\mathrm{max}/ \tt{smoothing\_range}, \lambda_\mathrm{max}]$,
1038      * where $\lambda_\mathrm{max}$ is an estimate of the maximum eigenvalue
1039      * of the matrix. A choice of <tt>smoothing_range</tt> between 5 and 20 is
1040      * useful in case the preconditioner is used as a smoother in multigrid.
1041      */
1042     double smoothing_range;
1043 
1044     /**
1045      * Maximum number of CG iterations performed for finding the maximum
1046      * eigenvalue. If set to zero, no computations are performed. Instead, the
1047      * user must supply a largest eigenvalue via the variable
1048      * PreconditionChebyshev::AdditionalData::max_eigenvalue.
1049      */
1050     unsigned int eig_cg_n_iterations;
1051 
1052     /**
1053      * Tolerance for CG iterations performed for finding the maximum
1054      * eigenvalue.
1055      */
1056     double eig_cg_residual;
1057 
1058     /**
1059      * Maximum eigenvalue to work with. Only in effect if @p
1060      * eig_cg_n_iterations is set to zero, otherwise this parameter is
1061      * ignored.
1062      */
1063     double max_eigenvalue;
1064 
1065     /**
1066      * Constraints to be used for the operator given. This variable is used to
1067      * zero out the correct entries when creating an initial guess.
1068      */
1069     AffineConstraints<double> constraints;
1070 
1071     /**
1072      * Stores the preconditioner object that the Chebyshev is wrapped around.
1073      */
1074     std::shared_ptr<PreconditionerType> preconditioner;
1075   };
1076 
1077 
1078   /**
1079    * Constructor.
1080    */
1081   PreconditionChebyshev();
1082 
1083   /**
1084    * Initialize function. Takes the matrix which is used to form the
1085    * preconditioner, and additional flags if there are any. This function
1086    * works only if the input matrix has an operator <tt>el(i,i)</tt> for
1087    * accessing all the elements in the diagonal. Alternatively, the diagonal
1088    * can be supplied with the help of the AdditionalData field.
1089    *
1090    * This function calculates an estimate of the eigenvalue range of the
1091    * matrix weighted by its diagonal using a modified CG iteration in case the
1092    * given number of iterations is positive.
1093    */
1094   void
1095   initialize(const MatrixType &    matrix,
1096              const AdditionalData &additional_data = AdditionalData());
1097 
1098   /**
1099    * Compute the action of the preconditioner on <tt>src</tt>, storing the
1100    * result in <tt>dst</tt>.
1101    */
1102   void
1103   vmult(VectorType &dst, const VectorType &src) const;
1104 
1105   /**
1106    * Compute the action of the transposed preconditioner on <tt>src</tt>,
1107    * storing the result in <tt>dst</tt>.
1108    */
1109   void
1110   Tvmult(VectorType &dst, const VectorType &src) const;
1111 
1112   /**
1113    * Perform one step of the preconditioned Richardson iteration.
1114    */
1115   void
1116   step(VectorType &dst, const VectorType &src) const;
1117 
1118   /**
1119    * Perform one transposed step of the preconditioned Richardson iteration.
1120    */
1121   void
1122   Tstep(VectorType &dst, const VectorType &src) const;
1123 
1124   /**
1125    * Resets the preconditioner.
1126    */
1127   void
1128   clear();
1129 
1130   /**
1131    * Return the dimension of the codomain (or range) space. Note that the
1132    * matrix is of dimension $m \times n$.
1133    */
1134   size_type
1135   m() const;
1136 
1137   /**
1138    * Return the dimension of the domain space. Note that the matrix is of
1139    * dimension $m \times n$.
1140    */
1141   size_type
1142   n() const;
1143 
1144   /**
1145    * A struct that contains information about the eigenvalue estimation
1146    * performed by the PreconditionChebychev class.
1147    */
1148   struct EigenvalueInformation
1149   {
1150     /**
1151      * Estimate for the smallest eigenvalue.
1152      */
1153     double min_eigenvalue_estimate;
1154     /**
1155      * Estimate for the largest eigenvalue.
1156      */
1157     double max_eigenvalue_estimate;
1158     /**
1159      * Number of CG iterations performed or 0.
1160      */
1161     unsigned int cg_iterations;
1162     /**
1163      * The degree of the Chebyshev polynomial (either as set using
1164      * AdditionalData::degree or estimated as described there).
1165      */
1166     unsigned int degree;
1167     /**
1168      * Constructor initializing with invalid values.
1169      */
EigenvalueInformationEigenvalueInformation1170     EigenvalueInformation()
1171       : min_eigenvalue_estimate{std::numeric_limits<double>::max()}
1172       , max_eigenvalue_estimate{std::numeric_limits<double>::lowest()}
1173       , cg_iterations{0}
1174       , degree{0}
1175     {}
1176   };
1177 
1178   /**
1179    * Compute eigenvalue estimates required for the preconditioner.
1180    *
1181    * This function is called automatically on first use of the preconditioner
1182    * if it is not called by the user. The layout of the vector @p src is used
1183    * to create internal temporary vectors and its content does not matter.
1184    *
1185    * Initializes the factors theta and delta based on an eigenvalue
1186    * computation. If the user set provided values for the largest eigenvalue
1187    * in AdditionalData, no computation is performed and the information given
1188    * by the user is used.
1189    */
1190   EigenvalueInformation
1191   estimate_eigenvalues(const VectorType &src) const;
1192 
1193 private:
1194   /**
1195    * A pointer to the underlying matrix.
1196    */
1197   SmartPointer<
1198     const MatrixType,
1199     PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>>
1200     matrix_ptr;
1201 
1202   /**
1203    * Internal vector used for the <tt>vmult</tt> operation.
1204    */
1205   mutable VectorType solution_old;
1206 
1207   /**
1208    * Internal vector used for the <tt>vmult</tt> operation.
1209    */
1210   mutable VectorType temp_vector1;
1211 
1212   /**
1213    * Internal vector used for the <tt>vmult</tt> operation.
1214    */
1215   mutable VectorType temp_vector2;
1216 
1217   /**
1218    * Stores the additional data passed to the initialize function, obtained
1219    * through a copy operation.
1220    */
1221   AdditionalData data;
1222 
1223   /**
1224    * Average of the largest and smallest eigenvalue under consideration.
1225    */
1226   double theta;
1227 
1228   /**
1229    * Half the interval length between the largest and smallest eigenvalue
1230    * under consideration.
1231    */
1232   double delta;
1233 
1234   /**
1235    * Stores whether the preconditioner has been set up and eigenvalues have
1236    * been computed.
1237    */
1238   bool eigenvalues_are_initialized;
1239 
1240   /**
1241    * A mutex to avoid that multiple vmult() invocations by different threads
1242    * overwrite the temporary vectors.
1243    */
1244   mutable Threads::Mutex mutex;
1245 };
1246 
1247 
1248 
1249 /*@}*/
1250 /* ---------------------------------- Inline functions ------------------- */
1251 
1252 #ifndef DOXYGEN
1253 
PreconditionIdentity()1254 inline PreconditionIdentity::PreconditionIdentity()
1255   : n_rows(0)
1256   , n_columns(0)
1257 {}
1258 
1259 template <typename MatrixType>
1260 inline void
initialize(const MatrixType & matrix,const PreconditionIdentity::AdditionalData &)1261 PreconditionIdentity::initialize(const MatrixType &matrix,
1262                                  const PreconditionIdentity::AdditionalData &)
1263 {
1264   n_rows    = matrix.m();
1265   n_columns = matrix.n();
1266 }
1267 
1268 
1269 template <class VectorType>
1270 inline void
vmult(VectorType & dst,const VectorType & src)1271 PreconditionIdentity::vmult(VectorType &dst, const VectorType &src) const
1272 {
1273   dst = src;
1274 }
1275 
1276 
1277 
1278 template <class VectorType>
1279 inline void
Tvmult(VectorType & dst,const VectorType & src)1280 PreconditionIdentity::Tvmult(VectorType &dst, const VectorType &src) const
1281 {
1282   dst = src;
1283 }
1284 
1285 template <class VectorType>
1286 inline void
vmult_add(VectorType & dst,const VectorType & src)1287 PreconditionIdentity::vmult_add(VectorType &dst, const VectorType &src) const
1288 {
1289   dst += src;
1290 }
1291 
1292 
1293 
1294 template <class VectorType>
1295 inline void
Tvmult_add(VectorType & dst,const VectorType & src)1296 PreconditionIdentity::Tvmult_add(VectorType &dst, const VectorType &src) const
1297 {
1298   dst += src;
1299 }
1300 
1301 inline PreconditionIdentity::size_type
m()1302 PreconditionIdentity::m() const
1303 {
1304   Assert(n_rows != 0, ExcNotInitialized());
1305   return n_rows;
1306 }
1307 
1308 inline PreconditionIdentity::size_type
n()1309 PreconditionIdentity::n() const
1310 {
1311   Assert(n_columns != 0, ExcNotInitialized());
1312   return n_columns;
1313 }
1314 
1315 //---------------------------------------------------------------------------
1316 
AdditionalData(const double relaxation)1317 inline PreconditionRichardson::AdditionalData::AdditionalData(
1318   const double relaxation)
1319   : relaxation(relaxation)
1320 {}
1321 
1322 
PreconditionRichardson()1323 inline PreconditionRichardson::PreconditionRichardson()
1324   : relaxation(0)
1325   , n_rows(0)
1326   , n_columns(0)
1327 {
1328   AdditionalData add_data;
1329   relaxation = add_data.relaxation;
1330 }
1331 
1332 
1333 
1334 inline void
initialize(const PreconditionRichardson::AdditionalData & parameters)1335 PreconditionRichardson::initialize(
1336   const PreconditionRichardson::AdditionalData &parameters)
1337 {
1338   relaxation = parameters.relaxation;
1339 }
1340 
1341 
1342 
1343 template <typename MatrixType>
1344 inline void
initialize(const MatrixType & matrix,const PreconditionRichardson::AdditionalData & parameters)1345 PreconditionRichardson::initialize(
1346   const MatrixType &                            matrix,
1347   const PreconditionRichardson::AdditionalData &parameters)
1348 {
1349   relaxation = parameters.relaxation;
1350   n_rows     = matrix.m();
1351   n_columns  = matrix.n();
1352 }
1353 
1354 
1355 
1356 template <class VectorType>
1357 inline void
vmult(VectorType & dst,const VectorType & src)1358 PreconditionRichardson::vmult(VectorType &dst, const VectorType &src) const
1359 {
1360   static_assert(
1361     std::is_same<size_type, typename VectorType::size_type>::value,
1362     "PreconditionRichardson and VectorType must have the same size_type.");
1363 
1364   dst.equ(relaxation, src);
1365 }
1366 
1367 
1368 
1369 template <class VectorType>
1370 inline void
Tvmult(VectorType & dst,const VectorType & src)1371 PreconditionRichardson::Tvmult(VectorType &dst, const VectorType &src) const
1372 {
1373   static_assert(
1374     std::is_same<size_type, typename VectorType::size_type>::value,
1375     "PreconditionRichardson and VectorType must have the same size_type.");
1376 
1377   dst.equ(relaxation, src);
1378 }
1379 
1380 template <class VectorType>
1381 inline void
vmult_add(VectorType & dst,const VectorType & src)1382 PreconditionRichardson::vmult_add(VectorType &dst, const VectorType &src) const
1383 {
1384   static_assert(
1385     std::is_same<size_type, typename VectorType::size_type>::value,
1386     "PreconditionRichardson and VectorType must have the same size_type.");
1387 
1388   dst.add(relaxation, src);
1389 }
1390 
1391 
1392 
1393 template <class VectorType>
1394 inline void
Tvmult_add(VectorType & dst,const VectorType & src)1395 PreconditionRichardson::Tvmult_add(VectorType &dst, const VectorType &src) const
1396 {
1397   static_assert(
1398     std::is_same<size_type, typename VectorType::size_type>::value,
1399     "PreconditionRichardson and VectorType must have the same size_type.");
1400 
1401   dst.add(relaxation, src);
1402 }
1403 
1404 inline PreconditionRichardson::size_type
m()1405 PreconditionRichardson::m() const
1406 {
1407   Assert(n_rows != 0, ExcNotInitialized());
1408   return n_rows;
1409 }
1410 
1411 inline PreconditionRichardson::size_type
n()1412 PreconditionRichardson::n() const
1413 {
1414   Assert(n_columns != 0, ExcNotInitialized());
1415   return n_columns;
1416 }
1417 
1418 //---------------------------------------------------------------------------
1419 
1420 template <typename MatrixType>
1421 inline void
initialize(const MatrixType & rA,const AdditionalData & parameters)1422 PreconditionRelaxation<MatrixType>::initialize(const MatrixType &    rA,
1423                                                const AdditionalData &parameters)
1424 {
1425   A          = &rA;
1426   relaxation = parameters.relaxation;
1427 }
1428 
1429 
1430 template <typename MatrixType>
1431 inline void
clear()1432 PreconditionRelaxation<MatrixType>::clear()
1433 {
1434   A = nullptr;
1435 }
1436 
1437 template <typename MatrixType>
1438 inline typename PreconditionRelaxation<MatrixType>::size_type
m()1439 PreconditionRelaxation<MatrixType>::m() const
1440 {
1441   Assert(A != nullptr, ExcNotInitialized());
1442   return A->m();
1443 }
1444 
1445 template <typename MatrixType>
1446 inline typename PreconditionRelaxation<MatrixType>::size_type
n()1447 PreconditionRelaxation<MatrixType>::n() const
1448 {
1449   Assert(A != nullptr, ExcNotInitialized());
1450   return A->n();
1451 }
1452 
1453 //---------------------------------------------------------------------------
1454 
1455 template <typename MatrixType>
1456 template <class VectorType>
1457 inline void
vmult(VectorType & dst,const VectorType & src)1458 PreconditionJacobi<MatrixType>::vmult(VectorType &      dst,
1459                                       const VectorType &src) const
1460 {
1461   static_assert(
1462     std::is_same<typename PreconditionJacobi<MatrixType>::size_type,
1463                  typename VectorType::size_type>::value,
1464     "PreconditionJacobi and VectorType must have the same size_type.");
1465 
1466   Assert(this->A != nullptr, ExcNotInitialized());
1467   this->A->precondition_Jacobi(dst, src, this->relaxation);
1468 }
1469 
1470 
1471 
1472 template <typename MatrixType>
1473 template <class VectorType>
1474 inline void
Tvmult(VectorType & dst,const VectorType & src)1475 PreconditionJacobi<MatrixType>::Tvmult(VectorType &      dst,
1476                                        const VectorType &src) const
1477 {
1478   static_assert(
1479     std::is_same<typename PreconditionJacobi<MatrixType>::size_type,
1480                  typename VectorType::size_type>::value,
1481     "PreconditionJacobi and VectorType must have the same size_type.");
1482 
1483   Assert(this->A != nullptr, ExcNotInitialized());
1484   this->A->precondition_Jacobi(dst, src, this->relaxation);
1485 }
1486 
1487 
1488 
1489 template <typename MatrixType>
1490 template <class VectorType>
1491 inline void
step(VectorType & dst,const VectorType & src)1492 PreconditionJacobi<MatrixType>::step(VectorType &      dst,
1493                                      const VectorType &src) const
1494 {
1495   static_assert(
1496     std::is_same<typename PreconditionJacobi<MatrixType>::size_type,
1497                  typename VectorType::size_type>::value,
1498     "PreconditionJacobi and VectorType must have the same size_type.");
1499 
1500   Assert(this->A != nullptr, ExcNotInitialized());
1501   this->A->Jacobi_step(dst, src, this->relaxation);
1502 }
1503 
1504 
1505 
1506 template <typename MatrixType>
1507 template <class VectorType>
1508 inline void
Tstep(VectorType & dst,const VectorType & src)1509 PreconditionJacobi<MatrixType>::Tstep(VectorType &      dst,
1510                                       const VectorType &src) const
1511 {
1512   static_assert(
1513     std::is_same<typename PreconditionJacobi<MatrixType>::size_type,
1514                  typename VectorType::size_type>::value,
1515     "PreconditionJacobi and VectorType must have the same size_type.");
1516 
1517   step(dst, src);
1518 }
1519 
1520 
1521 
1522 //---------------------------------------------------------------------------
1523 
1524 template <typename MatrixType>
1525 template <class VectorType>
1526 inline void
vmult(VectorType & dst,const VectorType & src)1527 PreconditionSOR<MatrixType>::vmult(VectorType &dst, const VectorType &src) const
1528 {
1529   static_assert(std::is_same<typename PreconditionSOR<MatrixType>::size_type,
1530                              typename VectorType::size_type>::value,
1531                 "PreconditionSOR and VectorType must have the same size_type.");
1532 
1533   Assert(this->A != nullptr, ExcNotInitialized());
1534   this->A->precondition_SOR(dst, src, this->relaxation);
1535 }
1536 
1537 
1538 
1539 template <typename MatrixType>
1540 template <class VectorType>
1541 inline void
Tvmult(VectorType & dst,const VectorType & src)1542 PreconditionSOR<MatrixType>::Tvmult(VectorType &      dst,
1543                                     const VectorType &src) const
1544 {
1545   static_assert(std::is_same<typename PreconditionSOR<MatrixType>::size_type,
1546                              typename VectorType::size_type>::value,
1547                 "PreconditionSOR and VectorType must have the same size_type.");
1548 
1549   Assert(this->A != nullptr, ExcNotInitialized());
1550   this->A->precondition_TSOR(dst, src, this->relaxation);
1551 }
1552 
1553 
1554 
1555 template <typename MatrixType>
1556 template <class VectorType>
1557 inline void
step(VectorType & dst,const VectorType & src)1558 PreconditionSOR<MatrixType>::step(VectorType &dst, const VectorType &src) const
1559 {
1560   static_assert(std::is_same<typename PreconditionSOR<MatrixType>::size_type,
1561                              typename VectorType::size_type>::value,
1562                 "PreconditionSOR and VectorType must have the same size_type.");
1563 
1564   Assert(this->A != nullptr, ExcNotInitialized());
1565   this->A->SOR_step(dst, src, this->relaxation);
1566 }
1567 
1568 
1569 
1570 template <typename MatrixType>
1571 template <class VectorType>
1572 inline void
Tstep(VectorType & dst,const VectorType & src)1573 PreconditionSOR<MatrixType>::Tstep(VectorType &dst, const VectorType &src) const
1574 {
1575   static_assert(std::is_same<typename PreconditionSOR<MatrixType>::size_type,
1576                              typename VectorType::size_type>::value,
1577                 "PreconditionSOR and VectorType must have the same size_type.");
1578 
1579   Assert(this->A != nullptr, ExcNotInitialized());
1580   this->A->TSOR_step(dst, src, this->relaxation);
1581 }
1582 
1583 
1584 
1585 //---------------------------------------------------------------------------
1586 
1587 template <typename MatrixType>
1588 inline void
initialize(const MatrixType & rA,const typename BaseClass::AdditionalData & parameters)1589 PreconditionSSOR<MatrixType>::initialize(
1590   const MatrixType &                        rA,
1591   const typename BaseClass::AdditionalData &parameters)
1592 {
1593   this->PreconditionRelaxation<MatrixType>::initialize(rA, parameters);
1594 
1595   // in case we have a SparseMatrix class, we can extract information about
1596   // the diagonal.
1597   const SparseMatrix<typename MatrixType::value_type> *mat =
1598     dynamic_cast<const SparseMatrix<typename MatrixType::value_type> *>(
1599       &*this->A);
1600 
1601   // calculate the positions first after the diagonal.
1602   if (mat != nullptr)
1603     {
1604       const size_type n = this->A->n();
1605       pos_right_of_diagonal.resize(n, static_cast<std::size_t>(-1));
1606       for (size_type row = 0; row < n; ++row)
1607         {
1608           // find the first element in this line which is on the right of the
1609           // diagonal.  we need to precondition with the elements on the left
1610           // only. note: the first entry in each line denotes the diagonal
1611           // element, which we need not check.
1612           typename SparseMatrix<typename MatrixType::value_type>::const_iterator
1613             it = mat->begin(row) + 1;
1614           for (; it < mat->end(row); ++it)
1615             if (it->column() > row)
1616               break;
1617           pos_right_of_diagonal[row] = it - mat->begin();
1618         }
1619     }
1620 }
1621 
1622 
1623 template <typename MatrixType>
1624 template <class VectorType>
1625 inline void
vmult(VectorType & dst,const VectorType & src)1626 PreconditionSSOR<MatrixType>::vmult(VectorType &      dst,
1627                                     const VectorType &src) const
1628 {
1629   static_assert(
1630     std::is_same<typename PreconditionSSOR<MatrixType>::size_type,
1631                  typename VectorType::size_type>::value,
1632     "PreconditionSSOR and VectorType must have the same size_type.");
1633 
1634   Assert(this->A != nullptr, ExcNotInitialized());
1635   this->A->precondition_SSOR(dst, src, this->relaxation, pos_right_of_diagonal);
1636 }
1637 
1638 
1639 
1640 template <typename MatrixType>
1641 template <class VectorType>
1642 inline void
Tvmult(VectorType & dst,const VectorType & src)1643 PreconditionSSOR<MatrixType>::Tvmult(VectorType &      dst,
1644                                      const VectorType &src) const
1645 {
1646   static_assert(
1647     std::is_same<typename PreconditionSSOR<MatrixType>::size_type,
1648                  typename VectorType::size_type>::value,
1649     "PreconditionSSOR and VectorType must have the same size_type.");
1650 
1651   Assert(this->A != nullptr, ExcNotInitialized());
1652   this->A->precondition_SSOR(dst, src, this->relaxation, pos_right_of_diagonal);
1653 }
1654 
1655 
1656 
1657 template <typename MatrixType>
1658 template <class VectorType>
1659 inline void
step(VectorType & dst,const VectorType & src)1660 PreconditionSSOR<MatrixType>::step(VectorType &dst, const VectorType &src) const
1661 {
1662   static_assert(
1663     std::is_same<typename PreconditionSSOR<MatrixType>::size_type,
1664                  typename VectorType::size_type>::value,
1665     "PreconditionSSOR and VectorType must have the same size_type.");
1666 
1667   Assert(this->A != nullptr, ExcNotInitialized());
1668   this->A->SSOR_step(dst, src, this->relaxation);
1669 }
1670 
1671 
1672 
1673 template <typename MatrixType>
1674 template <class VectorType>
1675 inline void
Tstep(VectorType & dst,const VectorType & src)1676 PreconditionSSOR<MatrixType>::Tstep(VectorType &      dst,
1677                                     const VectorType &src) const
1678 {
1679   static_assert(
1680     std::is_same<typename PreconditionSSOR<MatrixType>::size_type,
1681                  typename VectorType::size_type>::value,
1682     "PreconditionSSOR and VectorType must have the same size_type.");
1683 
1684   step(dst, src);
1685 }
1686 
1687 
1688 
1689 //---------------------------------------------------------------------------
1690 
1691 template <typename MatrixType>
1692 inline void
initialize(const MatrixType & rA,const std::vector<size_type> & p,const std::vector<size_type> & ip,const typename PreconditionRelaxation<MatrixType>::AdditionalData & parameters)1693 PreconditionPSOR<MatrixType>::initialize(
1694   const MatrixType &                                                 rA,
1695   const std::vector<size_type> &                                     p,
1696   const std::vector<size_type> &                                     ip,
1697   const typename PreconditionRelaxation<MatrixType>::AdditionalData &parameters)
1698 {
1699   permutation         = &p;
1700   inverse_permutation = &ip;
1701   PreconditionRelaxation<MatrixType>::initialize(rA, parameters);
1702 }
1703 
1704 
1705 template <typename MatrixType>
1706 inline void
initialize(const MatrixType & A,const AdditionalData & additional_data)1707 PreconditionPSOR<MatrixType>::initialize(const MatrixType &    A,
1708                                          const AdditionalData &additional_data)
1709 {
1710   initialize(A,
1711              additional_data.permutation,
1712              additional_data.inverse_permutation,
1713              additional_data.parameters);
1714 }
1715 
1716 
1717 template <typename MatrixType>
1718 template <typename VectorType>
1719 inline void
vmult(VectorType & dst,const VectorType & src)1720 PreconditionPSOR<MatrixType>::vmult(VectorType &      dst,
1721                                     const VectorType &src) const
1722 {
1723   static_assert(
1724     std::is_same<typename PreconditionPSOR<MatrixType>::size_type,
1725                  typename VectorType::size_type>::value,
1726     "PreconditionPSOR and VectorType must have the same size_type.");
1727 
1728   Assert(this->A != nullptr, ExcNotInitialized());
1729   dst = src;
1730   this->A->PSOR(dst, *permutation, *inverse_permutation, this->relaxation);
1731 }
1732 
1733 
1734 
1735 template <typename MatrixType>
1736 template <class VectorType>
1737 inline void
Tvmult(VectorType & dst,const VectorType & src)1738 PreconditionPSOR<MatrixType>::Tvmult(VectorType &      dst,
1739                                      const VectorType &src) const
1740 {
1741   static_assert(
1742     std::is_same<typename PreconditionPSOR<MatrixType>::size_type,
1743                  typename VectorType::size_type>::value,
1744     "PreconditionPSOR and VectorType must have the same size_type.");
1745 
1746   Assert(this->A != nullptr, ExcNotInitialized());
1747   dst = src;
1748   this->A->TPSOR(dst, *permutation, *inverse_permutation, this->relaxation);
1749 }
1750 
1751 template <typename MatrixType>
AdditionalData(const std::vector<size_type> & permutation,const std::vector<size_type> & inverse_permutation,const typename PreconditionRelaxation<MatrixType>::AdditionalData & parameters)1752 PreconditionPSOR<MatrixType>::AdditionalData::AdditionalData(
1753   const std::vector<size_type> &permutation,
1754   const std::vector<size_type> &inverse_permutation,
1755   const typename PreconditionRelaxation<MatrixType>::AdditionalData &parameters)
1756   : permutation(permutation)
1757   , inverse_permutation(inverse_permutation)
1758   , parameters(parameters)
1759 {}
1760 
1761 
1762 //---------------------------------------------------------------------------
1763 
1764 
1765 template <typename MatrixType, class VectorType>
PreconditionUseMatrix(const MatrixType & M,const function_ptr method)1766 PreconditionUseMatrix<MatrixType, VectorType>::PreconditionUseMatrix(
1767   const MatrixType & M,
1768   const function_ptr method)
1769   : matrix(M)
1770   , precondition(method)
1771 {}
1772 
1773 
1774 
1775 template <typename MatrixType, class VectorType>
1776 void
vmult(VectorType & dst,const VectorType & src)1777 PreconditionUseMatrix<MatrixType, VectorType>::vmult(
1778   VectorType &      dst,
1779   const VectorType &src) const
1780 {
1781   (matrix.*precondition)(dst, src);
1782 }
1783 
1784 //---------------------------------------------------------------------------
1785 
1786 template <typename MatrixType>
AdditionalData(const double relaxation)1787 inline PreconditionRelaxation<MatrixType>::AdditionalData::AdditionalData(
1788   const double relaxation)
1789   : relaxation(relaxation)
1790 {}
1791 
1792 
1793 
1794 //---------------------------------------------------------------------------
1795 
1796 namespace internal
1797 {
1798   namespace PreconditionChebyshevImplementation
1799   {
1800     // for deal.II vectors, perform updates for Chebyshev preconditioner all
1801     // at once to reduce memory transfer. Here, we select between general
1802     // vectors and deal.II vectors where we expand the loop over the (local)
1803     // size of the vector
1804 
1805     // generic part for non-deal.II vectors
1806     template <typename VectorType, typename PreconditionerType>
1807     inline void
vector_updates(const VectorType & rhs,const PreconditionerType & preconditioner,const unsigned int iteration_index,const double factor1,const double factor2,VectorType & solution_old,VectorType & temp_vector1,VectorType & temp_vector2,VectorType & solution)1808     vector_updates(const VectorType &        rhs,
1809                    const PreconditionerType &preconditioner,
1810                    const unsigned int        iteration_index,
1811                    const double              factor1,
1812                    const double              factor2,
1813                    VectorType &              solution_old,
1814                    VectorType &              temp_vector1,
1815                    VectorType &              temp_vector2,
1816                    VectorType &              solution)
1817     {
1818       if (iteration_index == 0)
1819         {
1820           solution.equ(factor2, rhs);
1821           preconditioner.vmult(solution_old, solution);
1822         }
1823       else if (iteration_index == 1)
1824         {
1825           // compute t = P^{-1} * (b-A*x^{n})
1826           temp_vector1.sadd(-1.0, 1.0, rhs);
1827           preconditioner.vmult(solution_old, temp_vector1);
1828 
1829           // compute x^{n+1} = x^{n} + f_1 * x^{n} + f_2 * t
1830           solution_old.sadd(factor2, 1 + factor1, solution);
1831         }
1832       else
1833         {
1834           // compute t = P^{-1} * (b-A*x^{n})
1835           temp_vector1.sadd(-1.0, 1.0, rhs);
1836           preconditioner.vmult(temp_vector2, temp_vector1);
1837 
1838           // compute x^{n+1} = x^{n} + f_1 * (x^{n}-x^{n-1}) + f_2 * t
1839           solution_old.sadd(-factor1, factor2, temp_vector2);
1840           solution_old.add(1 + factor1, solution);
1841         }
1842 
1843       solution.swap(solution_old);
1844     }
1845 
1846     // worker routine for deal.II vectors. Because of vectorization, we need
1847     // to put the loop into an extra structure because the virtual function of
1848     // VectorUpdatesRange prevents the compiler from applying vectorization.
1849     template <typename Number>
1850     struct VectorUpdater
1851     {
VectorUpdaterVectorUpdater1852       VectorUpdater(const Number *     rhs,
1853                     const Number *     matrix_diagonal_inverse,
1854                     const unsigned int iteration_index,
1855                     const Number       factor1,
1856                     const Number       factor2,
1857                     Number *           solution_old,
1858                     Number *           tmp_vector,
1859                     Number *           solution)
1860         : rhs(rhs)
1861         , matrix_diagonal_inverse(matrix_diagonal_inverse)
1862         , iteration_index(iteration_index)
1863         , factor1(factor1)
1864         , factor2(factor2)
1865         , solution_old(solution_old)
1866         , tmp_vector(tmp_vector)
1867         , solution(solution)
1868       {}
1869 
1870       void
apply_to_subrangeVectorUpdater1871       apply_to_subrange(const std::size_t begin, const std::size_t end) const
1872       {
1873         // To circumvent a bug in gcc
1874         // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63945), we create
1875         // copies of the variables factor1 and factor2 and do not check based on
1876         // factor1.
1877         const Number factor1        = this->factor1;
1878         const Number factor1_plus_1 = 1. + this->factor1;
1879         const Number factor2        = this->factor2;
1880         if (iteration_index == 0)
1881           {
1882             DEAL_II_OPENMP_SIMD_PRAGMA
1883             for (std::size_t i = begin; i < end; ++i)
1884               solution[i] = factor2 * matrix_diagonal_inverse[i] * rhs[i];
1885           }
1886         else if (iteration_index == 1)
1887           {
1888             // x^{n+1} = x^{n} + f_1 * x^{n} + f_2 * P^{-1} * (b-A*x^{n})
1889             DEAL_II_OPENMP_SIMD_PRAGMA
1890             for (std::size_t i = begin; i < end; ++i)
1891               // for efficiency reason, write back to temp_vector that is
1892               // already read (avoid read-for-ownership)
1893               tmp_vector[i] =
1894                 factor1_plus_1 * solution[i] +
1895                 factor2 * matrix_diagonal_inverse[i] * (rhs[i] - tmp_vector[i]);
1896           }
1897         else
1898           {
1899             // x^{n+1} = x^{n} + f_1 * (x^{n}-x^{n-1})
1900             //           + f_2 * P^{-1} * (b-A*x^{n})
1901             DEAL_II_OPENMP_SIMD_PRAGMA
1902             for (std::size_t i = begin; i < end; ++i)
1903               solution_old[i] =
1904                 factor1_plus_1 * solution[i] - factor1 * solution_old[i] +
1905                 factor2 * matrix_diagonal_inverse[i] * (rhs[i] - tmp_vector[i]);
1906           }
1907       }
1908 
1909       const Number *     rhs;
1910       const Number *     matrix_diagonal_inverse;
1911       const unsigned int iteration_index;
1912       const Number       factor1;
1913       const Number       factor2;
1914       mutable Number *   solution_old;
1915       mutable Number *   tmp_vector;
1916       mutable Number *   solution;
1917     };
1918 
1919     template <typename Number>
1920     struct VectorUpdatesRange : public ::dealii::parallel::ParallelForInteger
1921     {
VectorUpdatesRangeVectorUpdatesRange1922       VectorUpdatesRange(const VectorUpdater<Number> &updater,
1923                          const std::size_t            size)
1924         : updater(updater)
1925       {
1926         if (size < internal::VectorImplementation::minimum_parallel_grain_size)
1927           VectorUpdatesRange::apply_to_subrange(0, size);
1928         else
1929           apply_parallel(
1930             0,
1931             size,
1932             internal::VectorImplementation::minimum_parallel_grain_size);
1933       }
1934 
1935       ~VectorUpdatesRange() override = default;
1936 
1937       virtual void
apply_to_subrangeVectorUpdatesRange1938       apply_to_subrange(const std::size_t begin,
1939                         const std::size_t end) const override
1940       {
1941         updater.apply_to_subrange(begin, end);
1942       }
1943 
1944       const VectorUpdater<Number> &updater;
1945     };
1946 
1947     // selection for diagonal matrix around deal.II vector
1948     template <typename Number>
1949     inline void
vector_updates(const::dealii::Vector<Number> & rhs,const DiagonalMatrix<::dealii::Vector<Number>> & jacobi,const unsigned int iteration_index,const double factor1,const double factor2,::dealii::Vector<Number> & solution_old,::dealii::Vector<Number> & temp_vector1,::dealii::Vector<Number> &,::dealii::Vector<Number> & solution)1950     vector_updates(const ::dealii::Vector<Number> &                rhs,
1951                    const DiagonalMatrix<::dealii::Vector<Number>> &jacobi,
1952                    const unsigned int        iteration_index,
1953                    const double              factor1,
1954                    const double              factor2,
1955                    ::dealii::Vector<Number> &solution_old,
1956                    ::dealii::Vector<Number> &temp_vector1,
1957                    ::dealii::Vector<Number> &,
1958                    ::dealii::Vector<Number> &solution)
1959     {
1960       VectorUpdater<Number> upd(rhs.begin(),
1961                                 jacobi.get_vector().begin(),
1962                                 iteration_index,
1963                                 factor1,
1964                                 factor2,
1965                                 solution_old.begin(),
1966                                 temp_vector1.begin(),
1967                                 solution.begin());
1968       VectorUpdatesRange<Number>(upd, rhs.size());
1969 
1970       // swap vectors x^{n+1}->x^{n}, given the updates in the function above
1971       if (iteration_index == 0)
1972         {
1973           // nothing to do here because we can immediately write into the
1974           // solution vector without remembering any of the other vectors
1975         }
1976       else if (iteration_index == 1)
1977         {
1978           solution.swap(temp_vector1);
1979           solution_old.swap(temp_vector1);
1980         }
1981       else
1982         solution.swap(solution_old);
1983     }
1984 
1985     // selection for diagonal matrix around parallel deal.II vector
1986     template <typename Number>
1987     inline void
vector_updates(const LinearAlgebra::distributed::Vector<Number,MemorySpace::Host> & rhs,const DiagonalMatrix<LinearAlgebra::distributed::Vector<Number,MemorySpace::Host>> & jacobi,const unsigned int iteration_index,const double factor1,const double factor2,LinearAlgebra::distributed::Vector<Number,MemorySpace::Host> & solution_old,LinearAlgebra::distributed::Vector<Number,MemorySpace::Host> & temp_vector1,LinearAlgebra::distributed::Vector<Number,MemorySpace::Host> &,LinearAlgebra::distributed::Vector<Number,MemorySpace::Host> & solution)1988     vector_updates(
1989       const LinearAlgebra::distributed::Vector<Number, MemorySpace::Host> &rhs,
1990       const DiagonalMatrix<
1991         LinearAlgebra::distributed::Vector<Number, MemorySpace::Host>> &jacobi,
1992       const unsigned int iteration_index,
1993       const double       factor1,
1994       const double       factor2,
1995       LinearAlgebra::distributed::Vector<Number, MemorySpace::Host>
1996         &solution_old,
1997       LinearAlgebra::distributed::Vector<Number, MemorySpace::Host>
1998         &temp_vector1,
1999       LinearAlgebra::distributed::Vector<Number, MemorySpace::Host> &,
2000       LinearAlgebra::distributed::Vector<Number, MemorySpace::Host> &solution)
2001     {
2002       VectorUpdater<Number> upd(rhs.begin(),
2003                                 jacobi.get_vector().begin(),
2004                                 iteration_index,
2005                                 factor1,
2006                                 factor2,
2007                                 solution_old.begin(),
2008                                 temp_vector1.begin(),
2009                                 solution.begin());
2010       VectorUpdatesRange<Number>(upd, rhs.local_size());
2011 
2012       // swap vectors x^{n+1}->x^{n}, given the updates in the function above
2013       if (iteration_index == 0)
2014         {
2015           // nothing to do here because we can immediately write into the
2016           // solution vector without remembering any of the other vectors
2017         }
2018       else if (iteration_index == 1)
2019         {
2020           solution.swap(temp_vector1);
2021           solution_old.swap(temp_vector1);
2022         }
2023       else
2024         solution.swap(solution_old);
2025     }
2026 
2027     template <typename MatrixType, typename PreconditionerType>
2028     inline void
initialize_preconditioner(const MatrixType & matrix,std::shared_ptr<PreconditionerType> & preconditioner)2029     initialize_preconditioner(
2030       const MatrixType &                   matrix,
2031       std::shared_ptr<PreconditionerType> &preconditioner)
2032     {
2033       (void)matrix;
2034       (void)preconditioner;
2035       AssertThrow(preconditioner.get() != nullptr, ExcNotInitialized());
2036     }
2037 
2038     template <typename MatrixType, typename VectorType>
2039     inline void
initialize_preconditioner(const MatrixType & matrix,std::shared_ptr<DiagonalMatrix<VectorType>> & preconditioner)2040     initialize_preconditioner(
2041       const MatrixType &                           matrix,
2042       std::shared_ptr<DiagonalMatrix<VectorType>> &preconditioner)
2043     {
2044       if (preconditioner.get() == nullptr || preconditioner->m() != matrix.m())
2045         {
2046           if (preconditioner.get() == nullptr)
2047             preconditioner = std::make_shared<DiagonalMatrix<VectorType>>();
2048 
2049           Assert(
2050             preconditioner->m() == 0,
2051             ExcMessage(
2052               "Preconditioner appears to be initialized but not sized correctly"));
2053 
2054           // This part only works in serial
2055           if (preconditioner->m() != matrix.m())
2056             {
2057               preconditioner->get_vector().reinit(matrix.m());
2058               for (typename VectorType::size_type i = 0; i < matrix.m(); ++i)
2059                 preconditioner->get_vector()(i) = 1. / matrix.el(i, i);
2060             }
2061         }
2062     }
2063 
2064     template <typename VectorType>
2065     void
set_initial_guess(VectorType & vector)2066     set_initial_guess(VectorType &vector)
2067     {
2068       vector = 1. / std::sqrt(static_cast<double>(vector.size()));
2069       if (vector.locally_owned_elements().is_element(0))
2070         vector(0) = 0.;
2071     }
2072 
2073     template <typename Number>
2074     void
set_initial_guess(::dealii::Vector<Number> & vector)2075     set_initial_guess(::dealii::Vector<Number> &vector)
2076     {
2077       // Choose a high-frequency mode consisting of numbers between 0 and 1
2078       // that is cheap to compute (cheaper than random numbers) but avoids
2079       // obviously re-occurring numbers in multi-component systems by choosing
2080       // a period of 11
2081       for (unsigned int i = 0; i < vector.size(); ++i)
2082         vector(i) = i % 11;
2083 
2084       const Number mean_value = vector.mean_value();
2085       vector.add(-mean_value);
2086     }
2087 
2088     template <typename Number>
2089     void
set_initial_guess(::dealii::LinearAlgebra::distributed::Vector<Number,MemorySpace::Host> & vector)2090     set_initial_guess(
2091       ::dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace::Host>
2092         &vector)
2093     {
2094       // Choose a high-frequency mode consisting of numbers between 0 and 1
2095       // that is cheap to compute (cheaper than random numbers) but avoids
2096       // obviously re-occurring numbers in multi-component systems by choosing
2097       // a period of 11.
2098       // Make initial guess robust with respect to number of processors
2099       // by operating on the global index.
2100       types::global_dof_index first_local_range = 0;
2101       if (!vector.locally_owned_elements().is_empty())
2102         first_local_range = vector.locally_owned_elements().nth_index_in_set(0);
2103       for (unsigned int i = 0; i < vector.local_size(); ++i)
2104         vector.local_element(i) = (i + first_local_range) % 11;
2105 
2106       const Number mean_value = vector.mean_value();
2107       vector.add(-mean_value);
2108     }
2109 
2110 
2111 #  ifdef DEAL_II_COMPILER_CUDA_AWARE
2112     template <typename Number>
2113     __global__ void
set_initial_guess_kernel(const types::global_dof_index offset,const unsigned int local_size,Number * values)2114     set_initial_guess_kernel(const types::global_dof_index offset,
2115                              const unsigned int            local_size,
2116                              Number *                      values)
2117 
2118     {
2119       const unsigned int index = threadIdx.x + blockDim.x * blockIdx.x;
2120       if (index < local_size)
2121         values[index] = (index + offset) % 11;
2122     }
2123 
2124     template <typename Number>
2125     void
set_initial_guess(::dealii::LinearAlgebra::distributed::Vector<Number,MemorySpace::CUDA> & vector)2126     set_initial_guess(
2127       ::dealii::LinearAlgebra::distributed::Vector<Number, MemorySpace::CUDA>
2128         &vector)
2129     {
2130       // Choose a high-frequency mode consisting of numbers between 0 and 1
2131       // that is cheap to compute (cheaper than random numbers) but avoids
2132       // obviously re-occurring numbers in multi-component systems by choosing
2133       // a period of 11.
2134       // Make initial guess robust with respect to number of processors
2135       // by operating on the global index.
2136       types::global_dof_index first_local_range = 0;
2137       if (!vector.locally_owned_elements().is_empty())
2138         first_local_range = vector.locally_owned_elements().nth_index_in_set(0);
2139 
2140       const auto n_local_elements = vector.local_size();
2141       const int  n_blocks =
2142         1 + (n_local_elements - 1) / CUDAWrappers::block_size;
2143       set_initial_guess_kernel<<<n_blocks, CUDAWrappers::block_size>>>(
2144         first_local_range, n_local_elements, vector.get_values());
2145       AssertCudaKernel();
2146 
2147       const Number mean_value = vector.mean_value();
2148       vector.add(-mean_value);
2149     }
2150 #  endif // DEAL_II_COMPILER_CUDA_AWARE
2151 
2152     struct EigenvalueTracker
2153     {
2154     public:
2155       void
slotEigenvalueTracker2156       slot(const std::vector<double> &eigenvalues)
2157       {
2158         values = eigenvalues;
2159       }
2160 
2161       std::vector<double> values;
2162     };
2163   } // namespace PreconditionChebyshevImplementation
2164 } // namespace internal
2165 
2166 
2167 
2168 template <typename MatrixType, class VectorType, typename PreconditionerType>
2169 inline PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::
AdditionalData(const unsigned int degree,const double smoothing_range,const unsigned int eig_cg_n_iterations,const double eig_cg_residual,const double max_eigenvalue)2170   AdditionalData::AdditionalData(const unsigned int degree,
2171                                  const double       smoothing_range,
2172                                  const unsigned int eig_cg_n_iterations,
2173                                  const double       eig_cg_residual,
2174                                  const double       max_eigenvalue)
2175   : degree(degree)
2176   , smoothing_range(smoothing_range)
2177   , eig_cg_n_iterations(eig_cg_n_iterations)
2178   , eig_cg_residual(eig_cg_residual)
2179   , max_eigenvalue(max_eigenvalue)
2180 {}
2181 
2182 
2183 
2184 template <typename MatrixType, class VectorType, typename PreconditionerType>
2185 inline typename PreconditionChebyshev<MatrixType,
2186                                       VectorType,
2187                                       PreconditionerType>::AdditionalData &
2188                   PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::
2189   AdditionalData::operator=(const AdditionalData &other_data)
2190 {
2191   degree              = other_data.degree;
2192   smoothing_range     = other_data.smoothing_range;
2193   eig_cg_n_iterations = other_data.eig_cg_n_iterations;
2194   eig_cg_residual     = other_data.eig_cg_residual;
2195   max_eigenvalue      = other_data.max_eigenvalue;
2196   preconditioner      = other_data.preconditioner;
2197   constraints.copy_from(other_data.constraints);
2198 
2199   return *this;
2200 }
2201 
2202 
2203 
2204 template <typename MatrixType, typename VectorType, typename PreconditionerType>
2205 inline PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::
PreconditionChebyshev()2206   PreconditionChebyshev()
2207   : theta(1.)
2208   , delta(1.)
2209   , eigenvalues_are_initialized(false)
2210 {
2211   static_assert(
2212     std::is_same<size_type, typename VectorType::size_type>::value,
2213     "PreconditionChebyshev and VectorType must have the same size_type.");
2214 }
2215 
2216 
2217 
2218 template <typename MatrixType, typename VectorType, typename PreconditionerType>
2219 inline void
initialize(const MatrixType & matrix,const AdditionalData & additional_data)2220 PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::initialize(
2221   const MatrixType &    matrix,
2222   const AdditionalData &additional_data)
2223 {
2224   matrix_ptr = &matrix;
2225   data       = additional_data;
2226   Assert(data.degree > 0,
2227          ExcMessage("The degree of the Chebyshev method must be positive."));
2228   internal::PreconditionChebyshevImplementation::initialize_preconditioner(
2229     matrix, data.preconditioner);
2230   eigenvalues_are_initialized = false;
2231 }
2232 
2233 
2234 
2235 template <typename MatrixType, typename VectorType, typename PreconditionerType>
2236 inline void
clear()2237 PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::clear()
2238 {
2239   eigenvalues_are_initialized = false;
2240   theta = delta = 1.0;
2241   matrix_ptr    = nullptr;
2242   {
2243     VectorType empty_vector;
2244     solution_old.reinit(empty_vector);
2245     temp_vector1.reinit(empty_vector);
2246     temp_vector2.reinit(empty_vector);
2247   }
2248   data.preconditioner.reset();
2249 }
2250 
2251 
2252 
2253 template <typename MatrixType, typename VectorType, typename PreconditionerType>
2254 inline typename PreconditionChebyshev<MatrixType,
2255                                       VectorType,
2256                                       PreconditionerType>::EigenvalueInformation
2257 PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::
estimate_eigenvalues(const VectorType & src)2258   estimate_eigenvalues(const VectorType &src) const
2259 {
2260   Assert(eigenvalues_are_initialized == false, ExcInternalError());
2261   Assert(data.preconditioner.get() != nullptr, ExcNotInitialized());
2262 
2263   PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::
2264     EigenvalueInformation info{};
2265 
2266   solution_old.reinit(src);
2267   temp_vector1.reinit(src, true);
2268 
2269   // calculate largest eigenvalue using a hand-tuned CG iteration on the
2270   // matrix weighted by its diagonal. we start with a vector that consists of
2271   // ones only, weighted by the length.
2272   if (data.eig_cg_n_iterations > 0)
2273     {
2274       Assert(data.eig_cg_n_iterations > 2,
2275              ExcMessage(
2276                "Need to set at least two iterations to find eigenvalues."));
2277 
2278       // set a very strict tolerance to force at least two iterations
2279       ReductionControl control(
2280         data.eig_cg_n_iterations,
2281         std::sqrt(
2282           std::numeric_limits<typename VectorType::value_type>::epsilon()),
2283         1e-10,
2284         false,
2285         false);
2286 
2287       internal::PreconditionChebyshevImplementation::EigenvalueTracker
2288                            eigenvalue_tracker;
2289       SolverCG<VectorType> solver(control);
2290       solver.connect_eigenvalues_slot(
2291         [&eigenvalue_tracker](const std::vector<double> &eigenvalues) {
2292           eigenvalue_tracker.slot(eigenvalues);
2293         });
2294 
2295       // set an initial guess which is close to the constant vector but where
2296       // one entry is different to trigger high frequencies
2297       internal::PreconditionChebyshevImplementation::set_initial_guess(
2298         temp_vector1);
2299       data.constraints.set_zero(temp_vector1);
2300 
2301       try
2302         {
2303           solver.solve(*matrix_ptr,
2304                        solution_old,
2305                        temp_vector1,
2306                        *data.preconditioner);
2307         }
2308       catch (SolverControl::NoConvergence &)
2309         {}
2310 
2311       // read the eigenvalues from the attached eigenvalue tracker
2312       if (eigenvalue_tracker.values.empty())
2313         info.min_eigenvalue_estimate = info.max_eigenvalue_estimate = 1.;
2314       else
2315         {
2316           info.min_eigenvalue_estimate = eigenvalue_tracker.values.front();
2317 
2318           // include a safety factor since the CG method will in general not
2319           // be converged
2320           info.max_eigenvalue_estimate = 1.2 * eigenvalue_tracker.values.back();
2321         }
2322 
2323       info.cg_iterations = control.last_step();
2324     }
2325   else
2326     {
2327       info.max_eigenvalue_estimate = data.max_eigenvalue;
2328       info.min_eigenvalue_estimate = data.max_eigenvalue / data.smoothing_range;
2329     }
2330 
2331   const double alpha = (data.smoothing_range > 1. ?
2332                           info.max_eigenvalue_estimate / data.smoothing_range :
2333                           std::min(0.9 * info.max_eigenvalue_estimate,
2334                                    info.min_eigenvalue_estimate));
2335 
2336   // in case the user set the degree to invalid unsigned int, we have to
2337   // determine the number of necessary iterations from the Chebyshev error
2338   // estimate, given the target tolerance specified by smoothing_range. This
2339   // estimate is based on the error formula given in section 5.1 of
2340   // R. S. Varga, Matrix iterative analysis, 2nd ed., Springer, 2009
2341   if (data.degree == numbers::invalid_unsigned_int)
2342     {
2343       const double actual_range = info.max_eigenvalue_estimate / alpha;
2344       const double sigma        = (1. - std::sqrt(1. / actual_range)) /
2345                            (1. + std::sqrt(1. / actual_range));
2346       const double eps = data.smoothing_range;
2347       const_cast<
2348         PreconditionChebyshev<MatrixType, VectorType, PreconditionerType> *>(
2349         this)
2350         ->data.degree =
2351         1 + static_cast<unsigned int>(
2352               std::log(1. / eps + std::sqrt(1. / eps / eps - 1.)) /
2353               std::log(1. / sigma));
2354 
2355       info.degree = data.degree;
2356     }
2357 
2358   const_cast<
2359     PreconditionChebyshev<MatrixType, VectorType, PreconditionerType> *>(this)
2360     ->delta = (info.max_eigenvalue_estimate - alpha) * 0.5;
2361   const_cast<
2362     PreconditionChebyshev<MatrixType, VectorType, PreconditionerType> *>(this)
2363     ->theta = (info.max_eigenvalue_estimate + alpha) * 0.5;
2364 
2365   // We do not need the second temporary vector in case we have a
2366   // DiagonalMatrix as preconditioner and use deal.II's own vectors
2367   using NumberType = typename VectorType::value_type;
2368   if (std::is_same<PreconditionerType, DiagonalMatrix<VectorType>>::value ==
2369         false ||
2370       (std::is_same<VectorType, dealii::Vector<NumberType>>::value == false &&
2371        ((std::is_same<VectorType,
2372                       LinearAlgebra::distributed::
2373                         Vector<NumberType, MemorySpace::Host>>::value ==
2374          false) ||
2375         (std::is_same<VectorType,
2376                       LinearAlgebra::distributed::
2377                         Vector<NumberType, MemorySpace::CUDA>>::value ==
2378          false))))
2379     temp_vector2.reinit(src, true);
2380   else
2381     {
2382       VectorType empty_vector;
2383       temp_vector2.reinit(empty_vector);
2384     }
2385 
2386   const_cast<
2387     PreconditionChebyshev<MatrixType, VectorType, PreconditionerType> *>(this)
2388     ->eigenvalues_are_initialized = true;
2389 
2390   return info;
2391 }
2392 
2393 
2394 
2395 template <typename MatrixType, typename VectorType, typename PreconditionerType>
2396 inline void
vmult(VectorType & solution,const VectorType & rhs)2397 PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::vmult(
2398   VectorType &      solution,
2399   const VectorType &rhs) const
2400 {
2401   std::lock_guard<std::mutex> lock(mutex);
2402   if (eigenvalues_are_initialized == false)
2403     estimate_eigenvalues(rhs);
2404 
2405   internal::PreconditionChebyshevImplementation::vector_updates(
2406     rhs,
2407     *data.preconditioner,
2408     0,
2409     0.,
2410     1. / theta,
2411     solution_old,
2412     temp_vector1,
2413     temp_vector2,
2414     solution);
2415 
2416   // if delta is zero, we do not need to iterate because the updates will be
2417   // zero
2418   if (data.degree < 2 || std::abs(delta) < 1e-40)
2419     return;
2420 
2421   double rhok = delta / theta, sigma = theta / delta;
2422   for (unsigned int k = 0; k < data.degree - 1; ++k)
2423     {
2424       matrix_ptr->vmult(temp_vector1, solution);
2425       const double rhokp   = 1. / (2. * sigma - rhok);
2426       const double factor1 = rhokp * rhok, factor2 = 2. * rhokp / delta;
2427       rhok = rhokp;
2428       internal::PreconditionChebyshevImplementation::vector_updates(
2429         rhs,
2430         *data.preconditioner,
2431         k + 1,
2432         factor1,
2433         factor2,
2434         solution_old,
2435         temp_vector1,
2436         temp_vector2,
2437         solution);
2438     }
2439 }
2440 
2441 
2442 
2443 template <typename MatrixType, typename VectorType, typename PreconditionerType>
2444 inline void
Tvmult(VectorType & solution,const VectorType & rhs)2445 PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::Tvmult(
2446   VectorType &      solution,
2447   const VectorType &rhs) const
2448 {
2449   std::lock_guard<std::mutex> lock(mutex);
2450   if (eigenvalues_are_initialized == false)
2451     estimate_eigenvalues(rhs);
2452 
2453   internal::PreconditionChebyshevImplementation::vector_updates(
2454     rhs,
2455     *data.preconditioner,
2456     0,
2457     0.,
2458     1. / theta,
2459     solution_old,
2460     temp_vector1,
2461     temp_vector2,
2462     solution);
2463 
2464   if (data.degree < 2 || std::abs(delta) < 1e-40)
2465     return;
2466 
2467   double rhok = delta / theta, sigma = theta / delta;
2468   for (unsigned int k = 0; k < data.degree - 1; ++k)
2469     {
2470       matrix_ptr->Tvmult(temp_vector1, solution);
2471       const double rhokp   = 1. / (2. * sigma - rhok);
2472       const double factor1 = rhokp * rhok, factor2 = 2. * rhokp / delta;
2473       rhok = rhokp;
2474       internal::PreconditionChebyshevImplementation::vector_updates(
2475         rhs,
2476         *data.preconditioner,
2477         k + 1,
2478         factor1,
2479         factor2,
2480         solution_old,
2481         temp_vector1,
2482         temp_vector2,
2483         solution);
2484     }
2485 }
2486 
2487 
2488 
2489 template <typename MatrixType, typename VectorType, typename PreconditionerType>
2490 inline void
step(VectorType & solution,const VectorType & rhs)2491 PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::step(
2492   VectorType &      solution,
2493   const VectorType &rhs) const
2494 {
2495   std::lock_guard<std::mutex> lock(mutex);
2496   if (eigenvalues_are_initialized == false)
2497     estimate_eigenvalues(rhs);
2498 
2499   matrix_ptr->vmult(temp_vector1, solution);
2500   internal::PreconditionChebyshevImplementation::vector_updates(
2501     rhs,
2502     *data.preconditioner,
2503     1,
2504     0.,
2505     1. / theta,
2506     solution_old,
2507     temp_vector1,
2508     temp_vector2,
2509     solution);
2510 
2511   if (data.degree < 2 || std::abs(delta) < 1e-40)
2512     return;
2513 
2514   double rhok = delta / theta, sigma = theta / delta;
2515   for (unsigned int k = 0; k < data.degree - 1; ++k)
2516     {
2517       matrix_ptr->vmult(temp_vector1, solution);
2518       const double rhokp   = 1. / (2. * sigma - rhok);
2519       const double factor1 = rhokp * rhok, factor2 = 2. * rhokp / delta;
2520       rhok = rhokp;
2521       internal::PreconditionChebyshevImplementation::vector_updates(
2522         rhs,
2523         *data.preconditioner,
2524         k + 2,
2525         factor1,
2526         factor2,
2527         solution_old,
2528         temp_vector1,
2529         temp_vector2,
2530         solution);
2531     }
2532 }
2533 
2534 
2535 
2536 template <typename MatrixType, typename VectorType, typename PreconditionerType>
2537 inline void
Tstep(VectorType & solution,const VectorType & rhs)2538 PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::Tstep(
2539   VectorType &      solution,
2540   const VectorType &rhs) const
2541 {
2542   std::lock_guard<std::mutex> lock(mutex);
2543   if (eigenvalues_are_initialized == false)
2544     estimate_eigenvalues(rhs);
2545 
2546   matrix_ptr->Tvmult(temp_vector1, solution);
2547   internal::PreconditionChebyshevImplementation::vector_updates(
2548     rhs,
2549     *data.preconditioner,
2550     1,
2551     0.,
2552     1. / theta,
2553     solution_old,
2554     temp_vector1,
2555     temp_vector2,
2556     solution);
2557 
2558   if (data.degree < 2 || std::abs(delta) < 1e-40)
2559     return;
2560 
2561   double rhok = delta / theta, sigma = theta / delta;
2562   for (unsigned int k = 0; k < data.degree - 1; ++k)
2563     {
2564       matrix_ptr->Tvmult(temp_vector1, solution);
2565       const double rhokp   = 1. / (2. * sigma - rhok);
2566       const double factor1 = rhokp * rhok, factor2 = 2. * rhokp / delta;
2567       rhok = rhokp;
2568       internal::PreconditionChebyshevImplementation::vector_updates(
2569         rhs,
2570         *data.preconditioner,
2571         k + 2,
2572         factor1,
2573         factor2,
2574         solution_old,
2575         temp_vector1,
2576         temp_vector2,
2577         solution);
2578     }
2579 }
2580 
2581 
2582 
2583 template <typename MatrixType, typename VectorType, typename PreconditionerType>
2584 inline typename PreconditionChebyshev<MatrixType,
2585                                       VectorType,
2586                                       PreconditionerType>::size_type
m()2587 PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::m() const
2588 {
2589   Assert(matrix_ptr != nullptr, ExcNotInitialized());
2590   return matrix_ptr->m();
2591 }
2592 
2593 
2594 
2595 template <typename MatrixType, typename VectorType, typename PreconditionerType>
2596 inline typename PreconditionChebyshev<MatrixType,
2597                                       VectorType,
2598                                       PreconditionerType>::size_type
n()2599 PreconditionChebyshev<MatrixType, VectorType, PreconditionerType>::n() const
2600 {
2601   Assert(matrix_ptr != nullptr, ExcNotInitialized());
2602   return matrix_ptr->n();
2603 }
2604 
2605 #endif // DOXYGEN
2606 
2607 DEAL_II_NAMESPACE_CLOSE
2608 
2609 #endif
2610