LCOV - code coverage report
Current view: top level - linalg - chebyshev.cpp (source / functions) Coverage Total Hit
Test: Palace Coverage Report Lines: 0.0 % 124 0
Test Date: 2025-10-23 22:45:05 Functions: 0.0 % 20 0
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
       2              : // SPDX-License-Identifier: Apache-2.0
       3              : 
       4              : #include "chebyshev.hpp"
       5              : 
       6              : #include <mfem/general/forall.hpp>
       7              : 
       8              : namespace palace
       9              : {
      10              : 
      11              : namespace
      12              : {
      13              : 
      14            0 : double GetLambdaMax(MPI_Comm comm, const Operator &A, const Vector &dinv)
      15              : {
      16              :   // Assumes A SPD (diag(A) > 0) to use Hermitian eigenvalue solver.
      17              :   DiagonalOperator Dinv(dinv);
      18            0 :   ProductOperator DinvA(Dinv, A);
      19            0 :   return linalg::SpectralNorm(comm, DinvA, true);
      20              : }
      21              : 
      22            0 : double GetLambdaMax(MPI_Comm comm, const ComplexOperator &A, const ComplexVector &dinv)
      23              : {
      24              :   // Assumes A SPD (diag(A) > 0) to use Hermitian eigenvalue solver.
      25              :   ComplexDiagonalOperator Dinv(dinv);
      26            0 :   ComplexProductOperator DinvA(Dinv, A);
      27            0 :   return linalg::SpectralNorm(comm, DinvA, A.IsReal());
      28              : }
      29              : 
      30              : template <bool Transpose = false>
      31              : inline void ApplyOp(const Operator &A, const Vector &x, Vector &y)
      32              : {
      33            0 :   A.Mult(x, y);
      34              : }
      35              : 
      36              : template <bool Transpose = false>
      37              : inline void ApplyOp(const ComplexOperator &A, const ComplexVector &x, ComplexVector &y)
      38              : {
      39              :   if constexpr (!Transpose)
      40              :   {
      41            0 :     A.Mult(x, y);
      42              :   }
      43              :   else
      44              :   {
      45              :     A.MultHermitianTranspose(x, y);
      46              :   }
      47              : }
      48              : 
      49              : template <bool Transpose = false>
      50              : inline void ApplyOp(const Operator &A, const Vector &x, Vector &y, const double a)
      51              : {
      52            0 :   A.AddMult(x, y, a);
      53              : }
      54              : 
      55              : template <bool Transpose = false>
      56              : inline void ApplyOp(const ComplexOperator &A, const ComplexVector &x, ComplexVector &y,
      57              :                     const double a)
      58              : {
      59              :   if constexpr (!Transpose)
      60              :   {
      61            0 :     A.AddMult(x, y, a);
      62              :   }
      63              :   else
      64              :   {
      65              :     A.AddMultHermitianTranspose(x, y, a);
      66              :   }
      67              : }
      68              : 
      69              : template <bool Transpose = false>
      70            0 : inline void ApplyOrder0(double sr, const Vector &dinv, const Vector &r, Vector &d)
      71              : {
      72            0 :   const bool use_dev = dinv.UseDevice() || r.UseDevice() || d.UseDevice();
      73              :   const int N = d.Size();
      74            0 :   const auto *DI = dinv.Read(use_dev);
      75            0 :   const auto *R = r.Read(use_dev);
      76            0 :   auto *D = d.Write(use_dev);
      77              :   mfem::forall_switch(use_dev, N,
      78            0 :                       [=] MFEM_HOST_DEVICE(int i) { D[i] = sr * DI[i] * R[i]; });
      79            0 : }
      80              : 
      81              : template <bool Transpose = false>
      82            0 : inline void ApplyOrder0(const double sr, const ComplexVector &dinv, const ComplexVector &r,
      83              :                         ComplexVector &d)
      84              : {
      85            0 :   const bool use_dev = dinv.UseDevice() || r.UseDevice() || d.UseDevice();
      86              :   const int N = dinv.Size();
      87            0 :   const auto *DIR = dinv.Real().Read(use_dev);
      88            0 :   const auto *DII = dinv.Imag().Read(use_dev);
      89            0 :   const auto *RR = r.Real().Read(use_dev);
      90            0 :   const auto *RI = r.Imag().Read(use_dev);
      91            0 :   auto *DR = d.Real().Write(use_dev);
      92            0 :   auto *DI = d.Imag().Write(use_dev);
      93              :   if constexpr (!Transpose)
      94              :   {
      95              :     mfem::forall_switch(use_dev, N,
      96            0 :                         [=] MFEM_HOST_DEVICE(int i)
      97              :                         {
      98            0 :                           DR[i] = sr * (DIR[i] * RR[i] - DII[i] * RI[i]);
      99            0 :                           DI[i] = sr * (DII[i] * RR[i] + DIR[i] * RI[i]);
     100              :                         });
     101              :   }
     102              :   else
     103              :   {
     104              :     mfem::forall_switch(use_dev, N,
     105              :                         [=] MFEM_HOST_DEVICE(int i)
     106              :                         {
     107              :                           DR[i] = sr * (DIR[i] * RR[i] + DII[i] * RI[i]);
     108              :                           DI[i] = sr * (-DII[i] * RR[i] + DIR[i] * RI[i]);
     109              :                         });
     110              :   }
     111            0 : }
     112              : 
     113              : template <bool Transpose = false>
     114            0 : inline void ApplyOrderK(const double sd, const double sr, const Vector &dinv,
     115              :                         const Vector &r, Vector &d)
     116              : {
     117            0 :   const bool use_dev = dinv.UseDevice() || r.UseDevice() || d.UseDevice();
     118              :   const int N = dinv.Size();
     119            0 :   const auto *DI = dinv.Read(use_dev);
     120            0 :   const auto *R = r.Read(use_dev);
     121            0 :   auto *D = d.ReadWrite(use_dev);
     122            0 :   mfem::forall_switch(use_dev, N, [=] MFEM_HOST_DEVICE(int i)
     123            0 :                       { D[i] = sd * D[i] + sr * DI[i] * R[i]; });
     124            0 : }
     125              : 
     126              : template <bool Transpose = false>
     127            0 : inline void ApplyOrderK(const double sd, const double sr, const ComplexVector &dinv,
     128              :                         const ComplexVector &r, ComplexVector &d)
     129              : {
     130            0 :   const bool use_dev = dinv.UseDevice() || r.UseDevice() || d.UseDevice();
     131              :   const int N = dinv.Size();
     132            0 :   const auto *DIR = dinv.Real().Read(use_dev);
     133            0 :   const auto *DII = dinv.Imag().Read(use_dev);
     134            0 :   const auto *RR = r.Real().Read(use_dev);
     135            0 :   const auto *RI = r.Imag().Read(use_dev);
     136            0 :   auto *DR = d.Real().ReadWrite(use_dev);
     137            0 :   auto *DI = d.Imag().ReadWrite(use_dev);
     138              :   if constexpr (!Transpose)
     139              :   {
     140              :     mfem::forall_switch(use_dev, N,
     141            0 :                         [=] MFEM_HOST_DEVICE(int i)
     142              :                         {
     143            0 :                           DR[i] = sd * DR[i] + sr * (DIR[i] * RR[i] - DII[i] * RI[i]);
     144            0 :                           DI[i] = sd * DI[i] + sr * (DII[i] * RR[i] + DIR[i] * RI[i]);
     145              :                         });
     146              :   }
     147              :   else
     148              :   {
     149              :     mfem::forall_switch(use_dev, N,
     150              :                         [=] MFEM_HOST_DEVICE(int i)
     151              :                         {
     152              :                           DR[i] = sd * DR[i] + sr * (DIR[i] * RR[i] + DII[i] * RI[i]);
     153              :                           DI[i] = sd * DI[i] + sr * (-DII[i] * RR[i] + DIR[i] * RI[i]);
     154              :                         });
     155              :   }
     156            0 : }
     157              : 
     158              : }  // namespace
     159              : 
     160              : template <typename OperType>
     161            0 : ChebyshevSmoother<OperType>::ChebyshevSmoother(MPI_Comm comm, int smooth_it, int poly_order,
     162              :                                                double sf_max)
     163            0 :   : Solver<OperType>(), comm(comm), pc_it(smooth_it), order(poly_order), A(nullptr),
     164            0 :     lambda_max(0.0), sf_max(sf_max)
     165              : {
     166            0 :   MFEM_VERIFY(order > 0, "Polynomial order for Chebyshev smoothing must be positive!");
     167            0 : }
     168              : 
     169              : template <typename OperType>
     170            0 : void ChebyshevSmoother<OperType>::SetOperator(const OperType &op)
     171              : {
     172            0 :   A = &op;
     173            0 :   d.SetSize(op.Height());
     174            0 :   dinv.SetSize(op.Height());
     175            0 :   d.UseDevice(true);
     176            0 :   dinv.UseDevice(true);
     177            0 :   op.AssembleDiagonal(dinv);
     178            0 :   dinv.Reciprocal();
     179              : 
     180              :   // Set up Chebyshev coefficients using the computed maximum eigenvalue estimate. See
     181              :   // mfem::OperatorChebyshevSmoother or Adams et al. (2003).
     182            0 :   lambda_max = sf_max * GetLambdaMax(comm, *A, dinv);
     183            0 :   MFEM_VERIFY(lambda_max > 0.0,
     184              :               "Encountered zero maximum eigenvalue in Chebyshev smoother!");
     185              : 
     186            0 :   this->height = op.Height();
     187            0 :   this->width = op.Width();
     188            0 : }
     189              : 
     190              : template <typename OperType>
     191            0 : void ChebyshevSmoother<OperType>::Mult2(const VecType &x, VecType &y, VecType &r) const
     192              : {
     193              :   // Apply smoother: y = y + p(A) (x - A y) .
     194            0 :   for (int it = 0; it < pc_it; it++)
     195              :   {
     196            0 :     if (this->initial_guess || it > 0)
     197              :     {
     198            0 :       ApplyOp(*A, y, r);
     199            0 :       linalg::AXPBY(1.0, x, -1.0, r);
     200              :     }
     201              :     else
     202              :     {
     203            0 :       r = x;
     204            0 :       y = 0.0;
     205              :     }
     206              : 
     207              :     // 4th-kind Chebyshev smoother, from Phillips and Fischer or Lottes (with k -> k + 1
     208              :     // shift due to 1-based indexing).
     209            0 :     ApplyOrder0(4.0 / (3.0 * lambda_max), dinv, r, d);
     210            0 :     for (int k = 1; k < order; k++)
     211              :     {
     212            0 :       y += d;
     213            0 :       ApplyOp(*A, d, r, -1.0);
     214            0 :       const double sd = (2.0 * k - 1.0) / (2.0 * k + 3.0);
     215            0 :       const double sr = (8.0 * k + 4.0) / ((2.0 * k + 3.0) * lambda_max);
     216            0 :       ApplyOrderK(sd, sr, dinv, r, d);
     217              :     }
     218            0 :     y += d;
     219              :   }
     220            0 : }
     221              : 
     222              : template <typename OperType>
     223            0 : ChebyshevSmoother1stKind<OperType>::ChebyshevSmoother1stKind(MPI_Comm comm, int smooth_it,
     224              :                                                              int poly_order, double sf_max,
     225              :                                                              double sf_min)
     226            0 :   : Solver<OperType>(), comm(comm), pc_it(smooth_it), order(poly_order), A(nullptr),
     227            0 :     theta(0.0), sf_max(sf_max), sf_min(sf_min)
     228              : {
     229            0 :   MFEM_VERIFY(order > 0, "Polynomial order for Chebyshev smoothing must be positive!");
     230            0 : }
     231              : 
     232              : template <typename OperType>
     233            0 : void ChebyshevSmoother1stKind<OperType>::SetOperator(const OperType &op)
     234              : {
     235            0 :   A = &op;
     236            0 :   d.SetSize(op.Height());
     237            0 :   dinv.SetSize(op.Height());
     238            0 :   d.UseDevice(true);
     239            0 :   dinv.UseDevice(true);
     240            0 :   op.AssembleDiagonal(dinv);
     241            0 :   dinv.Reciprocal();
     242              : 
     243              :   // Set up Chebyshev coefficients using the computed maximum eigenvalue estimate. The
     244              :   // optimized estimate of lambda_min comes from (2.24) of Phillips and Fischer (2022).
     245            0 :   if (sf_min <= 0.0)
     246              :   {
     247            0 :     sf_min = 1.69 / (std::pow(order, 1.68) + 2.11 * order + 1.98);
     248              :   }
     249            0 :   const double lambda_max = sf_max * GetLambdaMax(comm, *A, dinv);
     250            0 :   MFEM_VERIFY(lambda_max > 0.0,
     251              :               "Encountered zero maximum eigenvalue in Chebyshev smoother!");
     252            0 :   const double lambda_min = sf_min * lambda_max;
     253            0 :   theta = 0.5 * (lambda_max + lambda_min);
     254            0 :   delta = 0.5 * (lambda_max - lambda_min);
     255              : 
     256            0 :   this->height = op.Height();
     257            0 :   this->width = op.Width();
     258            0 : }
     259              : 
     260              : template <typename OperType>
     261            0 : void ChebyshevSmoother1stKind<OperType>::Mult2(const VecType &x, VecType &y,
     262              :                                                VecType &r) const
     263              : {
     264              :   // Apply smoother: y = y + p(A) (x - A y) .
     265            0 :   for (int it = 0; it < pc_it; it++)
     266              :   {
     267            0 :     if (this->initial_guess || it > 0)
     268              :     {
     269            0 :       ApplyOp(*A, y, r);
     270            0 :       linalg::AXPBY(1.0, x, -1.0, r);
     271              :     }
     272              :     else
     273              :     {
     274            0 :       r = x;
     275            0 :       y = 0.0;
     276              :     }
     277              : 
     278              :     // 1th-kind Chebyshev smoother, from Phillips and Fischer or Adams.
     279            0 :     ApplyOrder0(1.0 / theta, dinv, r, d);
     280            0 :     double rhop = delta / theta;
     281            0 :     for (int k = 1; k < order; k++)
     282              :     {
     283            0 :       y += d;
     284            0 :       ApplyOp(*A, d, r, -1.0);
     285            0 :       const double rho = 1.0 / (2.0 * theta / delta - rhop);
     286            0 :       const double sd = rho * rhop;
     287            0 :       const double sr = 2.0 * rho / delta;
     288            0 :       ApplyOrderK(sd, sr, dinv, r, d);
     289              :       rhop = rho;
     290              :     }
     291            0 :     y += d;
     292              :   }
     293            0 : }
     294              : 
     295              : template class ChebyshevSmoother<Operator>;
     296              : template class ChebyshevSmoother<ComplexOperator>;
     297              : 
     298              : template class ChebyshevSmoother1stKind<Operator>;
     299              : template class ChebyshevSmoother1stKind<ComplexOperator>;
     300              : 
     301              : }  // namespace palace
        

Generated by: LCOV version 2.0-1