LCOV - code coverage report
Current view: top level - src/LinearSolvers - Preconditioner.h (source / functions) Coverage Total Hit
Test: final_report.info Lines: 0.0 % 201 0
Test Date: 2025-08-18 19:39:12 Functions: 0.0 % 98 0

            Line data    Source code
       1              : //
       2              : // General pre-conditioners for the pre-conditioned Conjugate Gradient Solver
       3              : // such as Jacobi, Polynomial Newton, Polynomial Chebyshev, Richardson, Gauss-Seidel
       4              : // provides a function operator() that returns the preconditioned field
       5              : //
       6              : 
       7              : #ifndef IPPL_PRECONDITIONER_H
       8              : #define IPPL_PRECONDITIONER_H
       9              : 
      10              : #include "Expression/IpplOperations.h"  // get the function apply()
      11              : 
      12              : // Expands to a lambda that acts as a wrapper for a differential operator
      13              : // fun: the function for which to create the wrapper, such as ippl::laplace
      14              : // type: the argument type, which should match the LHS type for the solver
      15              : #define IPPL_SOLVER_OPERATOR_WRAPPER(fun, type) \
      16              :     [](type arg) {                              \
      17              :         return fun(arg);                        \
      18              :     }
      19              : 
      20              : namespace ippl {
      21              :     template <typename Field>
      22              :     struct preconditioner {
      23              :         constexpr static unsigned Dim = Field::dim;
      24              :         using mesh_type               = typename Field::Mesh_t;
      25              :         using layout_type             = typename Field::Layout_t;
      26              : 
      27            0 :         preconditioner()
      28            0 :             : type_m("Identity") {}
      29              : 
      30            0 :         preconditioner(std::string name)
      31            0 :             : type_m(name) {}
      32              : 
      33            0 :         virtual ~preconditioner() = default;
      34              : 
      35              :         // Placeholder for the function operator, actually implemented in the derived classes
      36            0 :         virtual Field operator()(Field& u) {
      37            0 :             Field res = u.deepCopy();
      38            0 :             return res;
      39              :         }
      40              : 
      41              :         // Placeholder for setting additional fields, actually implemented in the derived classes
      42            0 :         virtual void init_fields(Field& b) {
      43            0 :             Field res = b.deepCopy();
      44            0 :             return;
      45            0 :         }
      46              : 
      47              :         std::string get_type() { return type_m; };
      48              : 
      49              :     protected:
      50              :         std::string type_m;
      51              :     };
      52              : 
      53              :     /*!
      54              :      * Jacobi preconditioner
      55              :      * M = w*diag{A} // w is a damping factor
      56              :      */
      57              :     template <typename Field, typename InvDiagF>
      58              :     struct jacobi_preconditioner : public preconditioner<Field> {
      59              :         constexpr static unsigned Dim = Field::dim;
      60              :         using mesh_type               = typename Field::Mesh_t;
      61              :         using layout_type             = typename Field::Layout_t;
      62              : 
      63            0 :         jacobi_preconditioner(InvDiagF&& inverse_diagonal, double w = 1.0)
      64              :             : preconditioner<Field>("jacobi")
      65            0 :             , w_m(w) {
      66            0 :             inverse_diagonal_m = std::move(inverse_diagonal);
      67            0 :         }
      68              : 
      69            0 :         Field operator()(Field& u) override {
      70            0 :             mesh_type& mesh     = u.get_mesh();
      71            0 :             layout_type& layout = u.getLayout();
      72            0 :             Field res(mesh, layout);
      73              : 
      74            0 :             res = inverse_diagonal_m(u);
      75            0 :             res = w_m * res;
      76            0 :             return res;
      77            0 :         }
      78              : 
      79              :     protected:
      80              :         InvDiagF inverse_diagonal_m;
      81              :         double w_m;  // Damping factor
      82              :     };
      83              : 
      84              :     /*!
      85              :      * Polynomial Newton Preconditioner
      86              :      * Computes iteratively approximations for A^-1
      87              :      */
      88              :     template <typename Field, typename OperatorF>
      89              :     struct polynomial_newton_preconditioner : public preconditioner<Field> {
      90              :         constexpr static unsigned Dim = Field::dim;
      91              :         using mesh_type               = typename Field::Mesh_t;
      92              :         using layout_type             = typename Field::Layout_t;
      93              : 
      94            0 :         polynomial_newton_preconditioner(OperatorF&& op, double alpha, double beta,
      95              :                                          unsigned int max_level = 6, double zeta = 1e-3,
      96              :                                          double* eta = nullptr)
      97              :             : preconditioner<Field>("polynomial_newton")
      98            0 :             , alpha_m(alpha)
      99            0 :             , beta_m(beta)
     100            0 :             , level_m(max_level)
     101            0 :             , zeta_m(zeta)
     102            0 :             , eta_m(eta) {
     103            0 :             op_m = std::move(op);
     104            0 :         }
     105              : 
     106              :         // Memory management is needed because of runtime defined eta_m
     107            0 :         ~polynomial_newton_preconditioner() {
     108            0 :             if (eta_m != nullptr) {
     109            0 :                 delete[] eta_m;
     110            0 :                 eta_m = nullptr;
     111              :             }
     112            0 :         }
     113              : 
     114              :         polynomial_newton_preconditioner(const polynomial_newton_preconditioner& other)
     115              :             : preconditioner<Field>("polynomial_newton")
     116              :             , level_m(other.level_m)
     117              :             , alpha_m(other.alpha_m)
     118              :             , beta_m(other.beta_m)
     119              :             , zeta_m(other.zeta_m)
     120              :             , eta_m(other.eta_m) {
     121              :             op_m = std::move(other.op_m);
     122              :         }
     123              : 
     124              :         polynomial_newton_preconditioner& operator=(const polynomial_newton_preconditioner& other) {
     125              :             return *this = polynomial_newton_preconditioner(other);
     126              :         }
     127              : 
     128            0 :         Field recursive_preconditioner(Field& u, unsigned int level) {
     129            0 :             mesh_type& mesh     = u.get_mesh();
     130            0 :             layout_type& layout = u.getLayout();
     131              :             // Define etas if not defined yet
     132            0 :             if (eta_m == nullptr) {
     133              :                 // Precompute the etas for later use
     134            0 :                 eta_m    = new double[level_m + 1];
     135            0 :                 eta_m[0] = 2.0 / ((alpha_m + beta_m) * (1.0 + zeta_m));
     136            0 :                 if (level_m > 0) {
     137            0 :                     eta_m[1] =
     138              :                         2.0
     139            0 :                         / (1.0 + 2 * alpha_m * eta_m[0] - alpha_m * eta_m[0] * alpha_m * eta_m[0]);
     140              :                 }
     141            0 :                 for (unsigned int i = 2; i < level_m + 1; ++i) {
     142            0 :                     eta_m[i] = 2.0 / (1.0 + 2 * eta_m[i - 1] - eta_m[i - 1] * eta_m[i - 1]);
     143              :                 }
     144              :             }
     145              : 
     146            0 :             Field res(mesh, layout);
     147              :             // Base case
     148            0 :             if (level == 0) {
     149            0 :                 res = eta_m[0] * u;
     150            0 :                 return res;
     151              :             }
     152              :             // Recursive case
     153            0 :             Field PAPr(mesh, layout);
     154            0 :             Field Pr(mesh, layout);
     155              : 
     156            0 :             Pr   = recursive_preconditioner(u, level - 1);
     157            0 :             PAPr = op_m(Pr);
     158            0 :             PAPr = recursive_preconditioner(PAPr, level - 1);
     159            0 :             res  = eta_m[level] * (2.0 * Pr - PAPr);
     160            0 :             return res;
     161            0 :         }
     162              : 
     163            0 :         Field operator()(Field& u) override { return recursive_preconditioner(u, level_m); }
     164              : 
     165              :     protected:
     166              :         OperatorF op_m;        // Operator to be preconditioned
     167              :         double alpha_m;        // Smallest Eigenvalue
     168              :         double beta_m;         // Largest Eigenvalue
     169              :         unsigned int level_m;  // Number of recursive calls
     170              :         double zeta_m;  // smallest (alpha + beta) is multiplied by (1+zeta) to avoid clustering of
     171              :                         // Eigenvalues
     172              :         double* eta_m = nullptr;  // Size is determined at runtime
     173              :     };
     174              : 
     175              :     /*!
     176              :      * Polynomial Chebyshev Preconditioner
     177              :      * Computes iteratively approximations for A^-1
     178              :      */
     179              :     template <typename Field, typename OperatorF>
     180              :     struct polynomial_chebyshev_preconditioner : public preconditioner<Field> {
     181              :         constexpr static unsigned Dim = Field::dim;
     182              :         using mesh_type               = typename Field::Mesh_t;
     183              :         using layout_type             = typename Field::Layout_t;
     184              : 
     185            0 :         polynomial_chebyshev_preconditioner(OperatorF&& op, double alpha, double beta,
     186              :                                             unsigned int degree = 63, double zeta = 1e-3)
     187              :             : preconditioner<Field>("polynomial_chebyshev")
     188            0 :             , alpha_m(alpha)
     189            0 :             , beta_m(beta)
     190            0 :             , degree_m(degree)
     191            0 :             , zeta_m(zeta)
     192            0 :             , rho_m(nullptr) {
     193            0 :             op_m = std::move(op);
     194            0 :         }
     195              : 
     196              :         // Memory management is needed because of runtime defined rho_m
     197            0 :         ~polynomial_chebyshev_preconditioner() {
     198            0 :             if (rho_m != nullptr) {
     199            0 :                 delete[] rho_m;
     200            0 :                 rho_m = nullptr;
     201              :             }
     202            0 :         }
     203              : 
     204              :         polynomial_chebyshev_preconditioner(const polynomial_chebyshev_preconditioner& other)
     205              :             : preconditioner<Field>("polynomial_chebyshev")
     206              :             , degree_m(other.degree_m)
     207              :             , theta_m(other.theta_m)
     208              :             , sigma_m(other.sigma_m)
     209              :             , delta_m(other.delta_m)
     210              :             , alpha_m(other.delta_m)
     211              :             , beta_m(other.delta_m)
     212              :             , zeta_m(other.zeta_m)
     213              :             , rho_m(other.rho_m) {
     214              :             op_m = std::move(other.op_m);
     215              :         }
     216              : 
     217              :         polynomial_chebyshev_preconditioner& operator=(
     218              :             const polynomial_chebyshev_preconditioner& other) {
     219              :             return *this = polynomial_chebyshev_preconditioner(other);
     220              :         }
     221              : 
     222            0 :         Field operator()(Field& r) override {
     223            0 :             mesh_type& mesh     = r.get_mesh();
     224            0 :             layout_type& layout = r.getLayout();
     225              : 
     226            0 :             Field res(mesh, layout);
     227            0 :             Field x(mesh, layout);
     228            0 :             Field x_old(mesh, layout);
     229            0 :             Field A(mesh, layout);
     230            0 :             Field z(mesh, layout);
     231              : 
     232              :             // Precompute the coefficients if not done yet
     233            0 :             if (rho_m == nullptr) {
     234              :                 // Start precomputing the coefficients
     235            0 :                 theta_m = (beta_m + alpha_m) / 2.0 * (1.0 + zeta_m);
     236            0 :                 delta_m = (beta_m - alpha_m) / 2.0;
     237            0 :                 sigma_m = theta_m / delta_m;
     238              : 
     239            0 :                 rho_m    = new double[degree_m + 1];
     240            0 :                 rho_m[0] = 1.0 / sigma_m;
     241            0 :                 for (unsigned int i = 1; i < degree_m + 1; ++i) {
     242            0 :                     rho_m[i] = 1.0 / (2.0 * sigma_m - rho_m[i - 1]);
     243              :                 }
     244              :             }  // End of precomputing the coefficients
     245              : 
     246            0 :             res = r.deepCopy();
     247              : 
     248            0 :             x_old = r / theta_m;
     249            0 :             A     = op_m(r);
     250            0 :             x     = 2.0 * rho_m[1] / delta_m * (2.0 * r - A / theta_m);
     251              : 
     252            0 :             if (degree_m == 0) {
     253            0 :                 return x_old;
     254              :             }
     255              : 
     256            0 :             if (degree_m == 1) {
     257            0 :                 return x;
     258              :             }
     259            0 :             for (unsigned int i = 2; i < degree_m + 1; ++i) {
     260            0 :                 A     = op_m(x);
     261            0 :                 z     = 2.0 / delta_m * (r - A);
     262            0 :                 res   = rho_m[i] * (2 * sigma_m * x - rho_m[i - 1] * x_old + z);
     263            0 :                 x_old = x.deepCopy();
     264            0 :                 x     = res.deepCopy();
     265              :             }
     266            0 :             return res;
     267            0 :         }
     268              : 
     269              :     protected:
     270              :         OperatorF op_m;
     271              :         double alpha_m;
     272              :         double beta_m;
     273              :         double delta_m;
     274              :         double theta_m;
     275              :         double sigma_m;
     276              :         unsigned degree_m;
     277              :         double zeta_m;
     278              :         double* rho_m = nullptr;  // Size is determined at runtime
     279              :     };
     280              : 
     281              :     /*!
     282              :      * Richardson preconditioner
     283              :      */
     284              :     template <typename Field, typename UpperAndLowerF, typename InvDiagF>
     285              :     struct richardson_preconditioner : public preconditioner<Field> {
     286              :         constexpr static unsigned Dim = Field::dim;
     287              :         using mesh_type               = typename Field::Mesh_t;
     288              :         using layout_type             = typename Field::Layout_t;
     289              : 
     290            0 :         richardson_preconditioner(UpperAndLowerF&& upper_and_lower, InvDiagF&& inverse_diagonal,
     291              :                                   unsigned innerloops = 5)
     292              :             : preconditioner<Field>("Richardson")
     293            0 :             , innerloops_m(innerloops) {
     294            0 :             upper_and_lower_m  = std::move(upper_and_lower);
     295            0 :             inverse_diagonal_m = std::move(inverse_diagonal);
     296            0 :         }
     297              : 
     298            0 :         Field operator()(Field& r) override {
     299            0 :             mesh_type& mesh     = r.get_mesh();
     300            0 :             layout_type& layout = r.getLayout();
     301            0 :             Field g(mesh, layout);
     302              : 
     303            0 :             g = 0;
     304            0 :             for (unsigned int j = 0; j < innerloops_m; ++j) {
     305            0 :                 ULg_m = upper_and_lower_m(g);
     306            0 :                 g     = r - ULg_m;
     307              :          
     308              :                 // The inverse diagonal is applied to the
     309              :                 // vector itself to return the result usually.
     310              :                 // However, the operator for FEM already
     311              :                 // returns the result of inv_diag * itself
     312              :                 // due to the matrix-free evaluation.
     313              :                 // Therefore, we need this if to differentiate
     314              :                 // the two cases.
     315              :                 if constexpr (std::is_same_v<InvDiagF, double>) {
     316              :                     g = inverse_diagonal_m(g) * g;
     317              :                 } else {
     318            0 :                     g = inverse_diagonal_m(g);
     319              :                 }
     320              :             }
     321            0 :             return g;
     322            0 :         }
     323              : 
     324            0 :         void init_fields(Field& b) override {
     325            0 :             layout_type& layout = b.getLayout();
     326            0 :             mesh_type& mesh     = b.get_mesh();
     327              : 
     328            0 :             ULg_m = Field(mesh, layout);
     329            0 :         }
     330              : 
     331              :     protected:
     332              :         UpperAndLowerF upper_and_lower_m;
     333              :         InvDiagF inverse_diagonal_m;
     334              :         unsigned innerloops_m;
     335              :         Field ULg_m;
     336              :     };
     337              : 
     338              :     /*!
     339              :      * 2-step Gauss-Seidel preconditioner
     340              :      */
     341              :     template <typename Field, typename LowerF, typename UpperF, typename InvDiagF>
     342              :     struct gs_preconditioner : public preconditioner<Field> {
     343              :         constexpr static unsigned Dim = Field::dim;
     344              :         using mesh_type               = typename Field::Mesh_t;
     345              :         using layout_type             = typename Field::Layout_t;
     346              : 
     347            0 :         gs_preconditioner(LowerF&& lower, UpperF&& upper, InvDiagF&& inverse_diagonal,
     348              :                           unsigned innerloops, unsigned outerloops)
     349              :             : preconditioner<Field>("Gauss-Seidel")
     350            0 :             , innerloops_m(innerloops)
     351            0 :             , outerloops_m(outerloops) {
     352            0 :             lower_m            = std::move(lower);
     353            0 :             upper_m            = std::move(upper);
     354            0 :             inverse_diagonal_m = std::move(inverse_diagonal);
     355            0 :         }
     356              : 
     357            0 :         Field operator()(Field& b) override {
     358            0 :             layout_type& layout = b.getLayout();
     359            0 :             mesh_type& mesh     = b.get_mesh();
     360              : 
     361            0 :             Field x(mesh, layout);
     362              : 
     363            0 :             x = 0;  // Initial guess
     364              : 
     365            0 :             for (unsigned int k = 0; k < outerloops_m; ++k) {
     366            0 :                 UL_m = upper_m(x);
     367            0 :                 r_m  = b - UL_m;
     368            0 :                 for (unsigned int j = 0; j < innerloops_m; ++j) {
     369            0 :                     UL_m = lower_m(x);
     370            0 :                     x    = r_m - UL_m;
     371              :                     // The inverse diagonal is applied to the
     372              :                     // vector itself to return the result usually.
     373              :                     // However, the operator for FEM already
     374              :                     // returns the result of inv_diag * itself
     375              :                     // due to the matrix-free evaluation.
     376              :                     // Therefore, we need this if to differentiate
     377              :                     // the two cases.
     378              :                     if constexpr (std::is_same_v<InvDiagF, double>) {
     379              :                         x = inverse_diagonal_m(x) * x;
     380              :                     } else {
     381            0 :                         x = inverse_diagonal_m(x);
     382              :                     }
     383              :                 }
     384            0 :                 UL_m = lower_m(x);
     385            0 :                 r_m  = b - UL_m;
     386            0 :                 for (unsigned int j = 0; j < innerloops_m; ++j) {
     387            0 :                     UL_m = upper_m(x);
     388            0 :                     x    = r_m - UL_m;
     389              :                     // The inverse diagonal is applied to the
     390              :                     // vector itself to return the result usually.
     391              :                     // However, the operator for FEM already
     392              :                     // returns the result of inv_diag * itself
     393              :                     // due to the matrix-free evaluation.
     394              :                     // Therefore, we need this if to differentiate
     395              :                     // the two cases.
     396              :                     if constexpr (std::is_same_v<InvDiagF, double>) {
     397              :                         x = inverse_diagonal_m(x) * x;
     398              :                     } else {
     399            0 :                         x = inverse_diagonal_m(x);
     400              :                     }
     401              :                 }
     402              :             }
     403            0 :             return x;
     404            0 :         }
     405              : 
     406            0 :         void init_fields(Field& b) override {
     407            0 :             layout_type& layout = b.getLayout();
     408            0 :             mesh_type& mesh     = b.get_mesh();
     409              : 
     410            0 :             UL_m = Field(mesh, layout);
     411            0 :             r_m  = Field(mesh, layout);
     412            0 :         }
     413              : 
     414              :     protected:
     415              :         LowerF lower_m;
     416              :         UpperF upper_m;
     417              :         InvDiagF inverse_diagonal_m;
     418              :         unsigned innerloops_m;
     419              :         unsigned outerloops_m;
     420              :         Field UL_m;
     421              :         Field r_m;
     422              :     };
     423              : 
     424              :     /*!
     425              :      * Symmetric successive over-relaxation
     426              :      */
     427              :     template <typename Field, typename LowerF, typename UpperF, typename InvDiagF, typename DiagF>
     428              :     struct ssor_preconditioner : public preconditioner<Field> {
     429              :         constexpr static unsigned Dim = Field::dim;
     430              :         using mesh_type               = typename Field::Mesh_t;
     431              :         using layout_type             = typename Field::Layout_t;
     432              : 
     433            0 :         ssor_preconditioner(LowerF&& lower, UpperF&& upper, InvDiagF&& inverse_diagonal,
     434              :                             DiagF&& diagonal, unsigned innerloops, unsigned outerloops,
     435              :                             double omega)
     436              :             : preconditioner<Field>("ssor")
     437            0 :             , innerloops_m(innerloops)
     438            0 :             , outerloops_m(outerloops)
     439            0 :             , omega_m(omega) {
     440            0 :             lower_m            = std::move(lower);
     441            0 :             upper_m            = std::move(upper);
     442            0 :             inverse_diagonal_m = std::move(inverse_diagonal);
     443            0 :             diagonal_m         = std::move(diagonal);
     444            0 :         }
     445              : 
     446            0 :         Field operator()(Field& b) override {
     447            0 :             static IpplTimings::TimerRef initTimer = IpplTimings::getTimer("SSOR Init");
     448            0 :             IpplTimings::startTimer(initTimer);
     449              : 
     450              :             double D;
     451              : 
     452            0 :             layout_type& layout = b.getLayout();
     453            0 :             mesh_type& mesh     = b.get_mesh();
     454              : 
     455            0 :             Field x(mesh, layout);
     456              : 
     457            0 :             x = 0;  // Initial guess
     458              : 
     459            0 :             IpplTimings::stopTimer(initTimer);
     460              : 
     461            0 :             static IpplTimings::TimerRef loopTimer = IpplTimings::getTimer("SSOR loop");
     462            0 :             IpplTimings::startTimer(loopTimer);
     463              : 
     464              :             // The inverse diagonal is applied to the
     465              :             // vector itself to return the result usually.
     466              :             // However, the operator for FEM already
     467              :             // returns the result of inv_diag * itself
     468              :             // due to the matrix-free evaluation.
     469              :             // Therefore, we need this if to differentiate
     470              :             // the two cases.
     471            0 :             for (unsigned int k = 0; k < outerloops_m; ++k) {
     472              :                 if constexpr (std::is_same_v<DiagF, double>) {
     473              :                     UL_m = upper_m(x);
     474              :                     D    = diagonal_m(x);
     475              :                     r_m  = omega_m * (b - UL_m) + (1.0 - omega_m) * D * x;
     476              : 
     477              :                     for (unsigned int j = 0; j < innerloops_m; ++j) {
     478              :                         UL_m = lower_m(x);
     479              :                         x    = r_m - omega_m * UL_m;
     480              :                         x    = inverse_diagonal_m(x) * x;
     481              :                     }
     482              :                     UL_m = lower_m(x);
     483              :                     D    = diagonal_m(x);
     484              :                     r_m  = omega_m * (b - UL_m) + (1.0 - omega_m) * D * x;
     485              :                     for (unsigned int j = 0; j < innerloops_m; ++j) {
     486              :                         UL_m = upper_m(x);
     487              :                         x    = r_m - omega_m * UL_m;
     488              :                         x    = inverse_diagonal_m(x) * x;
     489              :                     }
     490              :                 } else {
     491            0 :                     UL_m = upper_m(x);
     492            0 :                     r_m  = omega_m * (b - UL_m) + (1.0 - omega_m) * diagonal_m(x);
     493              : 
     494            0 :                     for (unsigned int j = 0; j < innerloops_m; ++j) {
     495            0 :                         UL_m = lower_m(x);
     496            0 :                         x    = r_m - omega_m * UL_m;
     497            0 :                         x    = inverse_diagonal_m(x);
     498              :                     }
     499            0 :                     UL_m = lower_m(x);
     500            0 :                     r_m  = omega_m * (b - UL_m) + (1.0 - omega_m) * diagonal_m(x);
     501            0 :                     for (unsigned int j = 0; j < innerloops_m; ++j) {
     502            0 :                         UL_m = upper_m(x);
     503            0 :                         x    = r_m - omega_m * UL_m;
     504            0 :                         x    = inverse_diagonal_m(x);
     505              :                     }
     506              :                 }
     507              :             }
     508            0 :             IpplTimings::stopTimer(loopTimer);
     509            0 :             return x;
     510            0 :         }
     511              : 
     512            0 :         void init_fields(Field& b) override {
     513            0 :             layout_type& layout = b.getLayout();
     514            0 :             mesh_type& mesh     = b.get_mesh();
     515              : 
     516            0 :             UL_m = Field(mesh, layout);
     517            0 :             r_m  = Field(mesh, layout);
     518            0 :         }
     519              : 
     520              :     protected:
     521              :         LowerF lower_m;
     522              :         UpperF upper_m;
     523              :         InvDiagF inverse_diagonal_m;
     524              :         DiagF diagonal_m;
     525              :         unsigned innerloops_m;
     526              :         unsigned outerloops_m;
     527              :         double omega_m;
     528              :         Field UL_m;
     529              :         Field r_m;
     530              :     };
     531              : 
     532              :     /*!
     533              :      * Computes the largest Eigenvalue of the Functor f
     534              :      * @param f Functor
     535              :      * @param x_0 initial guess
     536              :      * @param max_iter maximum number of iterations
     537              :      * @param tol tolerance
     538              :      */
     539              :     template <typename Field, typename Functor>
     540              :     double powermethod(Functor&& f, Field& x_0, unsigned int max_iter = 5000, double tol = 1e-3) {
     541              :         unsigned int i      = 0;
     542              :         using mesh_type     = typename Field::Mesh_t;
     543              :         using layout_type   = typename Field::Layout_t;
     544              :         mesh_type& mesh     = x_0.get_mesh();
     545              :         layout_type& layout = x_0.getLayout();
     546              :         Field x_new(mesh, layout);
     547              :         Field x_diff(mesh, layout);
     548              :         double error  = 1.0;
     549              :         double lambda = 1.0;
     550              :         while (error > tol && i < max_iter) {
     551              :             x_new  = f(x_0);
     552              :             lambda = norm(x_new);
     553              :             x_diff = x_new - lambda * x_0;
     554              :             error  = norm(x_diff);
     555              :             x_new  = x_new / lambda;
     556              :             x_0    = x_new.deepCopy();
     557              :             ++i;
     558              :         }
     559              :         if (i == max_iter) {
     560              :             std::cerr << "Powermethod did not converge, lambda_max : " << lambda
     561              :                       << ", error : " << error << std::endl;
     562              :         }
     563              :         return lambda;
     564              :     }
     565              : 
     566              :     /*!
     567              :      * Computes the smallest Eigenvalue of the Functor f (f must be symmetric positive definite)
     568              :      * @param f Functor
     569              :      * @param x_0 initial guess
     570              :      * @param lambda_max largest Eigenvalue
     571              :      * @param max_iter maximum number of iterations
     572              :      * @param tol tolerance
     573              :      */
     574              :     template <typename Field, typename Functor>
     575              :     double adapted_powermethod(Functor&& f, Field& x_0, double lambda_max,
     576              :                                unsigned int max_iter = 5000, double tol = 1e-3) {
     577              :         unsigned int i      = 0;
     578              :         using mesh_type     = typename Field::Mesh_t;
     579              :         using layout_type   = typename Field::Layout_t;
     580              :         mesh_type& mesh     = x_0.get_mesh();
     581              :         layout_type& layout = x_0.getLayout();
     582              :         Field x_new(mesh, layout);
     583              :         Field x_diff(mesh, layout);
     584              :         double error  = 1.0;
     585              :         double lambda = 1.0;
     586              :         while (error > tol && i < max_iter) {
     587              :             x_new  = f(x_0);
     588              :             x_new  = x_new - lambda_max * x_0;
     589              :             lambda = -norm(x_new);  // We know that lambda < 0;
     590              :             x_diff = x_new - lambda * x_0;
     591              :             error  = norm(x_diff);
     592              :             x_new  = x_new / -lambda;
     593              :             x_0    = x_new.deepCopy();
     594              :             ++i;
     595              :         }
     596              :         lambda = lambda + lambda_max;
     597              :         if (i == max_iter) {
     598              :             std::cerr << "Powermethod did not converge, lambda_min : " << lambda
     599              :                       << ", error : " << error << std::endl;
     600              :         }
     601              :         return lambda;
     602              :     }
     603              : 
     604              :     /*
     605              :     // Use the powermethod to compute the eigenvalues if no analytical solution is known
     606              :     beta = powermethod(std::move(op_m), x_0);
     607              :     // Trick for computing the smallest Eigenvalue of SPD Matrix
     608              :     alpha = adapted_powermethod(std::move(op_m), x_0, beta);
     609              :      */
     610              : 
     611              : }  // namespace ippl
     612              : 
     613              : #endif  // IPPL_PRECONDITIONER_H
        

Generated by: LCOV version 2.0-1