LCOV - code coverage report
Current view: top level - linalg - gmg.cpp (source / functions) Coverage Total Hit
Test: Palace Coverage Report Lines: 0.0 % 70 0
Test Date: 2025-10-23 22:45:05 Functions: 0.0 % 8 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 "gmg.hpp"
       5              : 
       6              : #include <mfem.hpp>
       7              : #include "linalg/chebyshev.hpp"
       8              : #include "linalg/distrelaxation.hpp"
       9              : #include "linalg/rap.hpp"
      10              : #include "utils/timer.hpp"
      11              : 
      12              : namespace palace
      13              : {
      14              : 
      15              : template <typename OperType>
      16            0 : GeometricMultigridSolver<OperType>::GeometricMultigridSolver(
      17              :     MPI_Comm comm, std::unique_ptr<Solver<OperType>> &&coarse_solver,
      18              :     const std::vector<const Operator *> &P, const std::vector<const Operator *> *G,
      19              :     int cycle_it, int smooth_it, int cheby_order, double cheby_sf_max, double cheby_sf_min,
      20              :     bool cheby_4th_kind)
      21            0 :   : Solver<OperType>(), pc_it(cycle_it), P(P.begin(), P.end()), A(P.size() + 1),
      22            0 :     dbc_tdof_lists(P.size()), B(P.size() + 1), X(P.size() + 1), Y(P.size() + 1),
      23            0 :     R(P.size() + 1), use_timer(false)
      24              : {
      25              :   // Configure levels of geometric coarsening. Multigrid vectors will be configured at first
      26              :   // call to Mult. The multigrid operator size is set based on the finest space dimension.
      27            0 :   const auto n_levels = P.size() + 1;
      28            0 :   MFEM_VERIFY(n_levels > 0,
      29              :               "Empty finite element space hierarchy during multigrid solver setup!");
      30            0 :   MFEM_VERIFY(!G || G->size() == n_levels,
      31              :               "Invalid input for distributive relaxation smoother auxiliary space transfer "
      32              :               "operators (mismatch in number of levels)!");
      33              : 
      34              :   // Use the supplied level 0 (coarse) solver.
      35              :   B[0] = std::move(coarse_solver);
      36              : 
      37              :   // Configure level smoothers. Use distributive relaxation smoothing if an auxiliary
      38              :   // finite element space was provided.
      39            0 :   for (std::size_t l = 1; l < n_levels; l++)
      40              :   {
      41            0 :     if (G)
      42              :     {
      43            0 :       const int cheby_smooth_it = 1;
      44            0 :       B[l] = std::make_unique<DistRelaxationSmoother<OperType>>(
      45              :           comm, *(*G)[l], smooth_it, cheby_smooth_it, cheby_order, cheby_sf_max,
      46              :           cheby_sf_min, cheby_4th_kind);
      47              :     }
      48              :     else
      49              :     {
      50            0 :       const int cheby_smooth_it = smooth_it;
      51            0 :       if (cheby_4th_kind)
      52              :       {
      53            0 :         B[l] = std::make_unique<ChebyshevSmoother<OperType>>(comm, cheby_smooth_it,
      54              :                                                              cheby_order, cheby_sf_max);
      55              :       }
      56              :       else
      57              :       {
      58            0 :         B[l] = std::make_unique<ChebyshevSmoother1stKind<OperType>>(
      59              :             comm, cheby_smooth_it, cheby_order, cheby_sf_max, cheby_sf_min);
      60              :       }
      61              :     }
      62              :   }
      63            0 : }
      64              : 
      65              : template <typename OperType>
      66            0 : void GeometricMultigridSolver<OperType>::SetOperator(const OperType &op)
      67              : {
      68              :   using ParOperType =
      69              :       typename std::conditional<std::is_same<OperType, ComplexOperator>::value,
      70              :                                 ComplexParOperator, ParOperator>::type;
      71              : 
      72            0 :   const auto *mg_op = dynamic_cast<const BaseMultigridOperator<OperType> *>(&op);
      73            0 :   MFEM_VERIFY(mg_op, "GeometricMultigridSolver requires a MultigridOperator or "
      74              :                      "ComplexMultigridOperator argument provided to SetOperator!");
      75              : 
      76              :   const auto n_levels = A.size();
      77            0 :   MFEM_VERIFY(
      78              :       mg_op->GetNumLevels() == n_levels &&
      79              :           (!mg_op->HasAuxiliaryOperators() || mg_op->GetNumAuxiliaryLevels() == n_levels),
      80              :       "Invalid number of levels for operators in multigrid solver setup!");
      81            0 :   for (std::size_t l = 0; l < n_levels; l++)
      82              :   {
      83            0 :     A[l] = &mg_op->GetOperatorAtLevel(l);
      84            0 :     MFEM_VERIFY(
      85              :         A[l]->Width() == A[l]->Height() &&
      86              :             (n_levels == 1 ||
      87              :              (A[l]->Height() == ((l < n_levels - 1) ? P[l]->Width() : P[l - 1]->Height()))),
      88              :         "Invalid operator sizes for GeometricMultigridSolver!");
      89              : 
      90            0 :     const auto *PtAP_l = dynamic_cast<const ParOperType *>(&mg_op->GetOperatorAtLevel(l));
      91            0 :     MFEM_VERIFY(
      92              :         PtAP_l,
      93              :         "GeometricMultigridSolver requires ParOperator or ComplexParOperator operators!");
      94            0 :     if (l < n_levels - 1)
      95              :     {
      96            0 :       dbc_tdof_lists[l] = PtAP_l->GetEssentialTrueDofs();
      97              :     }
      98              : 
      99            0 :     auto *dist_smoother = dynamic_cast<DistRelaxationSmoother<OperType> *>(B[l].get());
     100            0 :     if (dist_smoother)
     101              :     {
     102            0 :       MFEM_VERIFY(mg_op->HasAuxiliaryOperators(),
     103              :                   "Distributive relaxation smoother relies on both primary space and "
     104              :                   "auxiliary space operators for multigrid smoothing!");
     105            0 :       dist_smoother->SetOperators(mg_op->GetOperatorAtLevel(l),
     106              :                                   mg_op->GetAuxiliaryOperatorAtLevel(l));
     107              :     }
     108              :     else
     109              :     {
     110            0 :       B[l]->SetOperator(mg_op->GetOperatorAtLevel(l));
     111              :     }
     112              : 
     113            0 :     X[l].SetSize(A[l]->Height());
     114            0 :     Y[l].SetSize(A[l]->Height());
     115            0 :     R[l].SetSize(A[l]->Height());
     116            0 :     X[l].UseDevice(true);
     117            0 :     Y[l].UseDevice(true);
     118            0 :     R[l].UseDevice(true);
     119              :   }
     120              : 
     121            0 :   this->height = op.Height();
     122            0 :   this->width = op.Width();
     123            0 : }
     124              : 
     125              : template <typename OperType>
     126            0 : void GeometricMultigridSolver<OperType>::Mult(const VecType &x, VecType &y) const
     127              : {
     128              :   // Initialize.
     129              :   const auto n_levels = A.size();
     130              :   MFEM_ASSERT(!this->initial_guess,
     131              :               "Geometric multigrid solver does not use initial guess!");
     132              :   MFEM_ASSERT(n_levels > 1 || pc_it == 1,
     133              :               "Single-level geometric multigrid will not work with multiple iterations!");
     134              : 
     135              :   // Apply V-cycle. The initial guess for y is zero'd at the first pre-smooth iteration.
     136            0 :   X.back() = x;
     137            0 :   for (int it = 0; it < pc_it; it++)
     138              :   {
     139            0 :     VCycle(n_levels - 1, (it > 0));
     140              :   }
     141            0 :   y = Y.back();
     142            0 : }
     143              : 
     144              : namespace
     145              : {
     146              : 
     147              : inline void RealMult(const Operator &op, const Vector &x, Vector &y)
     148              : {
     149            0 :   op.Mult(x, y);
     150              : }
     151              : 
     152              : inline void RealMult(const Operator &op, const ComplexVector &x, ComplexVector &y)
     153              : {
     154            0 :   op.Mult(x.Real(), y.Real());
     155            0 :   op.Mult(x.Imag(), y.Imag());
     156              : }
     157              : 
     158              : inline void RealMultTranspose(const Operator &op, const Vector &x, Vector &y)
     159              : {
     160            0 :   op.MultTranspose(x, y);
     161              : }
     162              : 
     163              : inline void RealMultTranspose(const Operator &op, const ComplexVector &x, ComplexVector &y)
     164              : {
     165            0 :   op.MultTranspose(x.Real(), y.Real());
     166            0 :   op.MultTranspose(x.Imag(), y.Imag());
     167              : }
     168              : 
     169              : }  // namespace
     170              : 
     171              : template <typename OperType>
     172            0 : void GeometricMultigridSolver<OperType>::VCycle(int l, bool initial_guess) const
     173              : {
     174              :   // Pre-smooth, with zero initial guess (Y = 0 set inside). This is the coarse solve at
     175              :   // level 0. Important to note that the smoothers must respect the initial guess flag
     176              :   // correctly (given X, Y, compute Y <- Y + B (X - A Y)) .
     177            0 :   B[l]->SetInitialGuess(initial_guess);
     178            0 :   if (l == 0)
     179              :   {
     180            0 :     BlockTimer bt(Timer::KSP_COARSE_SOLVE, use_timer);
     181            0 :     B[l]->Mult(X[l], Y[l]);
     182              :     return;
     183            0 :   }
     184            0 :   B[l]->Mult2(X[l], Y[l], R[l]);
     185              : 
     186              :   // Compute residual.
     187            0 :   A[l]->Mult(Y[l], R[l]);
     188            0 :   linalg::AXPBY(1.0, X[l], -1.0, R[l]);
     189              : 
     190              :   // Coarse grid correction.
     191            0 :   RealMultTranspose(*P[l - 1], R[l], X[l - 1]);
     192            0 :   if (dbc_tdof_lists[l - 1])
     193              :   {
     194            0 :     linalg::SetSubVector(X[l - 1], *dbc_tdof_lists[l - 1], 0.0);
     195              :   }
     196            0 :   VCycle(l - 1, false);
     197              : 
     198              :   // Prolongate and add.
     199            0 :   RealMult(*P[l - 1], Y[l - 1], R[l]);
     200            0 :   Y[l] += R[l];
     201              : 
     202              :   // Post-smooth, with nonzero initial guess.
     203            0 :   B[l]->SetInitialGuess(true);
     204            0 :   B[l]->MultTranspose2(X[l], Y[l], R[l]);
     205              : }
     206              : 
     207              : template class GeometricMultigridSolver<Operator>;
     208              : template class GeometricMultigridSolver<ComplexOperator>;
     209              : 
     210              : }  // namespace palace
        

Generated by: LCOV version 2.0-1