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 ¶meters);
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 ¶meters);
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 ¶meters = 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 ¶meters =
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 ¶meters =
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 ¶meters =
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 ¶meters)
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 ¶meters)
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 ¶meters)
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 ¶meters)
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 ¶meters)
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 ¶meters)
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